Skip to content

Commit 8219ca8

Browse files
committed
Document simd-spliced
1 parent fb228d3 commit 8219ca8

File tree

1 file changed

+60
-19
lines changed

1 file changed

+60
-19
lines changed

cbits/simd-spliced.c

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,20 @@ size_t hw_json_simd_write_bp_chunk(
175175

176176
size_t pc_ib = __builtin_popcountll(w64_ib);
177177

178+
// Ignoring non interesting bits, get a bitmask of all delimiters and
179+
// opens and closes.
178180
uint64_t ext_d = _pext_u64(~(w64_a | w64_z) , w64_ib);
179181
uint64_t ext_a = _pext_u64(w64_a , w64_ib);
180182
uint64_t ext_z = _pext_u64(w64_z , w64_ib);
181183

184+
// Merge with the remainder bits. Extract bits need to be shifted
185+
// to avoid cloberring the remainder bits.
182186
remainder_bits_d |= (ext_d << remainder_len);
183187
remainder_bits_a |= (ext_a << remainder_len);
184188
remainder_bits_z |= (ext_z << remainder_len);
185189

186190
if (remainder_len + pc_ib >= 64) {
187-
// Write full word
191+
// Write full word because we have enough bits
188192
w64_work_bp[w64s_ready] =
189193
_pdep_u64(remainder_bits_a, 0x5555555555555555) |
190194
_pdep_u64(remainder_bits_a, 0xaaaaaaaaaaaaaaaa) |
@@ -203,7 +207,7 @@ size_t hw_json_simd_write_bp_chunk(
203207

204208
w64s_ready += 1;
205209

206-
// Set up for next iteration
210+
// Set up for next iteration the bits that didn't fit
207211
remainder_bits_d = ext_d >> (64 - remainder_len);
208212
remainder_bits_a = ext_a >> (64 - remainder_len);
209213
remainder_bits_z = ext_z >> (64 - remainder_len);
@@ -291,13 +295,20 @@ uint8_t hw_json_simd_escape_mask[2][256] =
291295
}
292296
};
293297

298+
// Summarise the input buffer into masks that indicate characters that have bearing on how the
299+
// JSON fragment is structured. Of these only ":,{}[]" are candidates to be interesting-bits.
300+
// However, not every such occurence is an interesting-bit because they may be inside a string
301+
// literal. What makes this more complicated is that the string literal may itself contain
302+
// escaped characters.
303+
// This function generates the masks for the characters of interest so that a later stage may
304+
// using them to determine the interesting-bits.
294305
void hw_json_simd_summarise(
295-
uint8_t *buffer,
296-
uint32_t *out_mask_d,
297-
uint32_t *out_mask_a,
298-
uint32_t *out_mask_z,
299-
uint32_t *out_mask_q,
300-
uint32_t *out_mask_b) {
306+
uint8_t *buffer, // Input buffer of 32 bytes containing a JSON fragment
307+
uint32_t *out_mask_d, // Output buffer for receiving the mask for delimiter characters: ':,'
308+
uint32_t *out_mask_a, // Output buffer for receiving the mask of the opening character: '{['
309+
uint32_t *out_mask_z, // Output buffer for receiving the mask of the closing character: ']}'
310+
uint32_t *out_mask_q, // Output buffer for receiving the mask of the quote character: '"'
311+
uint32_t *out_mask_b) { // Output buffer for receiving the mask of the backslash character: '\'
301312
#ifdef __AVX2__
302313
__m256i v_in_data = *(__m256i *)buffer;
303314
__m256i v_bytes_of_comma = _mm256_cmpeq_epi8(v_in_data, _mm256_set1_epi8(','));
@@ -344,6 +355,11 @@ void hw_json_simd_summarise(
344355
#endif
345356
}
346357

358+
// Add two words 'a' and 'b' and a carry bit 'c' together.
359+
// Detect this three-way addition overflow and set the carry bit accordingly in 'c'.
360+
// A utility function that can be used to add two arbitrary bit strings of the same length together.
361+
// The function itself doesn't peform the entire addition, but only word-wise in a way that propagates
362+
// the carry.
347363
uint64_t hw_json_simd_bitwise_add(uint64_t a, uint64_t b, uint64_t *c) {
348364
uint64_t d = a + b + *c;
349365

@@ -394,13 +410,15 @@ uint64_t hw_json_simd_process_chunk(
394410

395411
for (size_t i = 0; i < m256_in_len; ++i) {
396412
hw_json_simd_summarise(in_buffer + (i * 32),
397-
w32_bits_of_d + i,
398-
w32_bits_of_a + i,
399-
w32_bits_of_z + i,
400-
w32_bits_of_q + i,
401-
w32_bits_of_b + i);
413+
w32_bits_of_d + i, // Mask of delimiter characters ':,'
414+
w32_bits_of_a + i, // Mask of opening characters '{['
415+
w32_bits_of_z + i, // Mask of closing characters ']}'
416+
w32_bits_of_q + i, // Mask of quote characters '"'
417+
w32_bits_of_b + i); // Mask of backslash characters '\'
402418
}
403419

420+
// Generate an escape mask that can tell us which quote characters are escaped
421+
// and which are not.
404422
for (size_t i = 0; i < w8_out_len; ++i) {
405423
char w8 = w8_bits_of_b[i];
406424
size_t j = (*last_trailing_ones) % 2;
@@ -411,22 +429,45 @@ uint64_t hw_json_simd_process_chunk(
411429
}
412430

413431
for (size_t i = 0; i < w64_out_len; ++i) {
432+
// Use the escape mask to remove the escaped quote characters from the quote mask
414433
w64_bits_of_q[i] = w64_bits_of_e[i] & w64_bits_of_q[i];
415434

416435
uint64_t w64_bits_of_q_word = w64_bits_of_q[i];
417436

418-
uint64_t qas = _pdep_u64(0x5555555555555555 << ((*quote_odds_carry ) & 1), w64_bits_of_q_word);
419-
uint64_t qzs = _pdep_u64(0x5555555555555555 << ((*quote_evens_carry) & 1), w64_bits_of_q_word);
420-
437+
// In the following, the bitmask 0x5555555555555555 has all the odd bits selected. For example:
438+
// 0101010101010101010101010101010101010101010101010101010101010101.
439+
// Figure out which quote characters are open (qas) and which are closing quotes (qaz).
440+
// Whether or not the quote is open or closed depends on if there has been unpaired
441+
// quote carrying over from the previous fragment.
442+
uint64_t qas = _pdep_u64(0x5555555555555555 << ((*quote_odds_carry ) & 1), w64_bits_of_q_word); // All the open quotes
443+
uint64_t qzs = _pdep_u64(0x5555555555555555 << ((*quote_evens_carry) & 1), w64_bits_of_q_word); // All the closed quotes
444+
445+
// The quote mask tells us which characters are eligible to be interesting-bits (ie the ones not
446+
// inside a string literal).
447+
448+
// Example:
449+
// input: *"__"*""**"__""_ <- every character that is '*' is unquoted and _ is quoted
450+
// quote_mask_carry: 0
451+
// w64_bits_of_q_word: 0100101100100110
452+
// qas: 0100001000100010
453+
// qaz: 0000100100000100
454+
// ~qaz: 1111011011111011
455+
// quote_mask: 1000110111000100
456+
// input: *"__"*""**"__""_ <- every character that is '*' is unquoted and _ is quoted
457+
// In the above quote_mask and input, every * (unquoted) matches a 1 and every _ (quoted) matches a 0.
458+
// We don't care about the rest of the bits because they correspond to quotes which cannot be
459+
// interesting-bits.
421460
uint64_t quote_mask = hw_json_simd_bitwise_add(qas, ~qzs, quote_mask_carry);
422461

462+
// Only characters outside of string literals a eligible to be interesting-bits so the
463+
// others are masked out.
423464
uint64_t w64_d = quote_mask & w64_bits_of_d[i];
424465
uint64_t w64_a = quote_mask & w64_bits_of_a[i];
425466
uint64_t w64_z = quote_mask & w64_bits_of_z[i];
426467

427-
w64_result_ib[i] = w64_d | w64_a | w64_z;
428-
w64_result_a[i] = w64_a;
429-
w64_result_z[i] = w64_z;
468+
w64_result_ib[i] = w64_d | w64_a | w64_z; // Interesting bits
469+
w64_result_a[i] = w64_a; // Opening characters
470+
w64_result_z[i] = w64_z; // Closing characters
430471

431472
size_t pc = __builtin_popcountll(w64_bits_of_q[i]);
432473
*quote_odds_carry += pc;

0 commit comments

Comments
 (0)