@@ -175,16 +175,20 @@ size_t hw_json_simd_write_bp_chunk(
175
175
176
176
size_t pc_ib = __builtin_popcountll (w64_ib );
177
177
178
+ // Ignoring non interesting bits, get a bitmask of all delimiters and
179
+ // opens and closes.
178
180
uint64_t ext_d = _pext_u64 (~(w64_a | w64_z ) , w64_ib );
179
181
uint64_t ext_a = _pext_u64 (w64_a , w64_ib );
180
182
uint64_t ext_z = _pext_u64 (w64_z , w64_ib );
181
183
184
+ // Merge with the remainder bits. Extract bits need to be shifted
185
+ // to avoid cloberring the remainder bits.
182
186
remainder_bits_d |= (ext_d << remainder_len );
183
187
remainder_bits_a |= (ext_a << remainder_len );
184
188
remainder_bits_z |= (ext_z << remainder_len );
185
189
186
190
if (remainder_len + pc_ib >= 64 ) {
187
- // Write full word
191
+ // Write full word because we have enough bits
188
192
w64_work_bp [w64s_ready ] =
189
193
_pdep_u64 (remainder_bits_a , 0x5555555555555555 ) |
190
194
_pdep_u64 (remainder_bits_a , 0xaaaaaaaaaaaaaaaa ) |
@@ -203,7 +207,7 @@ size_t hw_json_simd_write_bp_chunk(
203
207
204
208
w64s_ready += 1 ;
205
209
206
- // Set up for next iteration
210
+ // Set up for next iteration the bits that didn't fit
207
211
remainder_bits_d = ext_d >> (64 - remainder_len );
208
212
remainder_bits_a = ext_a >> (64 - remainder_len );
209
213
remainder_bits_z = ext_z >> (64 - remainder_len );
@@ -291,13 +295,20 @@ uint8_t hw_json_simd_escape_mask[2][256] =
291
295
}
292
296
};
293
297
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.
294
305
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: '\'
301
312
#ifdef __AVX2__
302
313
__m256i v_in_data = * (__m256i * )buffer ;
303
314
__m256i v_bytes_of_comma = _mm256_cmpeq_epi8 (v_in_data , _mm256_set1_epi8 (',' ));
@@ -344,6 +355,11 @@ void hw_json_simd_summarise(
344
355
#endif
345
356
}
346
357
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.
347
363
uint64_t hw_json_simd_bitwise_add (uint64_t a , uint64_t b , uint64_t * c ) {
348
364
uint64_t d = a + b + * c ;
349
365
@@ -394,13 +410,15 @@ uint64_t hw_json_simd_process_chunk(
394
410
395
411
for (size_t i = 0 ; i < m256_in_len ; ++ i ) {
396
412
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 '\'
402
418
}
403
419
420
+ // Generate an escape mask that can tell us which quote characters are escaped
421
+ // and which are not.
404
422
for (size_t i = 0 ; i < w8_out_len ; ++ i ) {
405
423
char w8 = w8_bits_of_b [i ];
406
424
size_t j = (* last_trailing_ones ) % 2 ;
@@ -411,22 +429,45 @@ uint64_t hw_json_simd_process_chunk(
411
429
}
412
430
413
431
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
414
433
w64_bits_of_q [i ] = w64_bits_of_e [i ] & w64_bits_of_q [i ];
415
434
416
435
uint64_t w64_bits_of_q_word = w64_bits_of_q [i ];
417
436
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.
421
460
uint64_t quote_mask = hw_json_simd_bitwise_add (qas , ~qzs , quote_mask_carry );
422
461
462
+ // Only characters outside of string literals a eligible to be interesting-bits so the
463
+ // others are masked out.
423
464
uint64_t w64_d = quote_mask & w64_bits_of_d [i ];
424
465
uint64_t w64_a = quote_mask & w64_bits_of_a [i ];
425
466
uint64_t w64_z = quote_mask & w64_bits_of_z [i ];
426
467
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
430
471
431
472
size_t pc = __builtin_popcountll (w64_bits_of_q [i ]);
432
473
* quote_odds_carry += pc ;
0 commit comments