Skip to content

Commit bbd61eb

Browse files
committed
Let tape writing be put in a register
1 parent e15e1e2 commit bbd61eb

File tree

5 files changed

+152
-81
lines changed

5 files changed

+152
-81
lines changed

include/simdjson/internal/dom_parser_implementation.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,6 @@ class dom_parser_implementation {
107107
*/
108108
virtual ~dom_parser_implementation() = default;
109109

110-
/** Next location to write to in the tape */
111-
uint32_t current_loc{0};
112-
113110
/** Number of structural indices passed from stage 1 to stage 2 */
114111
uint32_t n_structural_indexes{0};
115112
/** Structural indices passed from stage 1 to stage 2 */

src/generic/stage2/logger.h

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ namespace logger {
2525
if (LOG_ENABLED) {
2626
log_depth = 0;
2727
printf("\n");
28-
printf("| %-*s | %-*s | %*s | %*s | %*s | %-*s | %-*s |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", 4, "Curr", 4, "Next", 5, "Next#", LOG_DETAIL_LEN, "Detail", LOG_INDEX_LEN, "index");
29-
printf("|%.*s|%.*s|%.*s|%.*s|%.*s|%.*s|%.*s|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, 4+2, DASHES, 4+2, DASHES, 5+2, DASHES, LOG_DETAIL_LEN+2, DASHES, LOG_INDEX_LEN+2, DASHES);
28+
printf("| %-*s | %-*s | %*s | %*s | %*s | %-*s | %-*s | %-*s |\n", LOG_EVENT_LEN, "Event", LOG_BUFFER_LEN, "Buffer", 4, "Curr", 4, "Next", 5, "Next#", 5, "Tape#", LOG_DETAIL_LEN, "Detail", LOG_INDEX_LEN, "index");
29+
printf("|%.*s|%.*s|%.*s|%.*s|%.*s|%.*s|%.*s|%.*s|\n", LOG_EVENT_LEN+2, DASHES, LOG_BUFFER_LEN+2, DASHES, 4+2, DASHES, 4+2, DASHES, 5+2, DASHES, 5+2, DASHES, LOG_DETAIL_LEN+2, DASHES, LOG_INDEX_LEN+2, DASHES);
3030
}
3131
}
3232

@@ -44,25 +44,17 @@ namespace logger {
4444
{
4545
// Print the next N characters in the buffer.
4646
printf("| ");
47-
if (structurals.at_beginning()) {
48-
// If the pointer is at the beginning, print a space followed by the beginning characters
49-
// Print spaces for unprintable or newline characters.
50-
printf(" ");
51-
for (int i=0;i<LOG_BUFFER_LEN-1;i++) {
52-
printf("%c", printable_char(structurals.buf[i]));
53-
}
54-
} else {
55-
// Otherwise, print the characters starting from the buffer position.
56-
// Print spaces for unprintable or newline characters.
57-
for (int i=0;i<LOG_BUFFER_LEN;i++) {
58-
printf("%c", printable_char(structurals.current()[i]));
59-
}
47+
// Otherwise, print the characters starting from the buffer position.
48+
// Print spaces for unprintable or newline characters.
49+
for (int i=0;i<LOG_BUFFER_LEN;i++) {
50+
printf("%c", printable_char(structurals.current()[i]));
6051
}
6152
printf(" ");
6253
}
63-
printf("| %c ", printable_char(structurals.at_beginning() ? ' ' : structurals.current_char()));
54+
printf("| %c ", printable_char(structurals.current_char()));
6455
printf("| %c ", printable_char(structurals.peek_next_char()));
6556
printf("| %5u ", structurals.parser.structural_indexes[*(structurals.current_structural+1)]);
57+
printf("| %5u ", structurals.next_tape_index());
6658
printf("| %-*s ", LOG_DETAIL_LEN, detail);
6759
printf("| %*u ", LOG_INDEX_LEN, *structurals.current_structural);
6860
printf("|\n");

src/generic/stage2/numberparsing.h

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,14 @@ never_inline bool parse_large_integer(const uint8_t *const src,
291291
// as a positive signed integer, but the negative version is
292292
// possible.
293293
constexpr int64_t signed_answer = INT64_MIN;
294-
writer.write_s64(signed_answer);
294+
writer.append_s64(signed_answer);
295295
#ifdef JSON_TEST_NUMBERS // for unit testing
296296
found_integer(signed_answer, src);
297297
#endif
298298
} else {
299299
// we can negate safely
300300
int64_t signed_answer = -static_cast<int64_t>(i);
301-
writer.write_s64(signed_answer);
301+
writer.append_s64(signed_answer);
302302
#ifdef JSON_TEST_NUMBERS // for unit testing
303303
found_integer(signed_answer, src);
304304
#endif
@@ -311,12 +311,12 @@ never_inline bool parse_large_integer(const uint8_t *const src,
311311
#ifdef JSON_TEST_NUMBERS // for unit testing
312312
found_integer(i, src);
313313
#endif
314-
writer.write_s64(i);
314+
writer.append_s64(i);
315315
} else {
316316
#ifdef JSON_TEST_NUMBERS // for unit testing
317317
found_unsigned_integer(i, src);
318318
#endif
319-
writer.write_u64(i);
319+
writer.append_u64(i);
320320
}
321321
}
322322
return is_structural_or_whitespace(*p);
@@ -326,7 +326,7 @@ template<typename W>
326326
bool slow_float_parsing(UNUSED const char * src, W writer) {
327327
double d;
328328
if (parse_float_strtod(src, &d)) {
329-
writer.write_double(d);
329+
writer.append_double(d);
330330
#ifdef JSON_TEST_NUMBERS // for unit testing
331331
found_float(d, (const uint8_t *)src);
332332
#endif
@@ -350,10 +350,10 @@ bool slow_float_parsing(UNUSED const char * src, W writer) {
350350
template<typename W>
351351
really_inline bool parse_number(UNUSED const uint8_t *const src,
352352
UNUSED bool found_minus,
353-
W writer) {
353+
W &writer) {
354354
#ifdef SIMDJSON_SKIPNUMBERPARSING // for performance analysis, it is sometimes
355355
// useful to skip parsing
356-
writer.write_s64(0); // always write zero
356+
writer.append_s64(0); // always write zero
357357
return true; // always succeeds
358358
#else
359359
const char *p = reinterpret_cast<const char *>(src);
@@ -497,22 +497,30 @@ really_inline bool parse_number(UNUSED const uint8_t *const src,
497497
}
498498
// we over-decrement by one when there is a '.'
499499
digit_count -= int(start - start_digits);
500-
if (digit_count >= 19) {
500+
if (unlikely(digit_count >= 19)) {
501501
// Ok, chances are good that we had an overflow!
502502
// this is almost never going to get called!!!
503503
// we start anew, going slowly!!!
504504
// This will happen in the following examples:
505505
// 10000000000000000000000000000000000000000000e+308
506506
// 3.1415926535897932384626433832795028841971693993751
507507
//
508-
return slow_float_parsing((const char *) src, writer);
508+
bool success = slow_float_parsing((const char *) src, writer);
509+
// The number was already written, but we made a copy of the writer
510+
// when we passed it to the parse_large_integer() function, so
511+
writer.skip_double();
512+
return success;
509513
}
510514
}
511515
if (unlikely(exponent < FASTFLOAT_SMALLEST_POWER) ||
512516
(exponent > FASTFLOAT_LARGEST_POWER)) { // this is uncommon!!!
513517
// this is almost never going to get called!!!
514518
// we start anew, going slowly!!!
515-
return slow_float_parsing((const char *) src, writer);
519+
bool success = slow_float_parsing((const char *) src, writer);
520+
// The number was already written, but we made a copy of the writer when we passed it to the
521+
// slow_float_parsing() function, so we have to skip those tape spots now that we've returned
522+
writer.skip_double();
523+
return success;
516524
}
517525
bool success = true;
518526
double d = compute_float_64(exponent, i, negative, &success);
@@ -521,7 +529,7 @@ really_inline bool parse_number(UNUSED const uint8_t *const src,
521529
success = parse_float_strtod((const char *)src, &d);
522530
}
523531
if (success) {
524-
writer.write_double(d);
532+
writer.append_double(d);
525533
#ifdef JSON_TEST_NUMBERS // for unit testing
526534
found_float(d, src);
527535
#endif
@@ -536,10 +544,14 @@ really_inline bool parse_number(UNUSED const uint8_t *const src,
536544
if (unlikely(digit_count >= 18)) { // this is uncommon!!!
537545
// there is a good chance that we had an overflow, so we need
538546
// need to recover: we parse the whole thing again.
539-
return parse_large_integer(src, writer, found_minus);
547+
bool success = parse_large_integer(src, writer, found_minus);
548+
// The number was already written, but we made a copy of the writer
549+
// when we passed it to the parse_large_integer() function, so
550+
writer.skip_large_integer();
551+
return success;
540552
}
541553
i = negative ? 0 - i : i;
542-
writer.write_s64(i);
554+
writer.append_s64(i);
543555
#ifdef JSON_TEST_NUMBERS // for unit testing
544556
found_integer(i, src);
545557
#endif

src/generic/stage2/structural_parser.h

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
namespace stage2 {
77
namespace { // Make everything here private
88

9+
#include "generic/stage2/tape_writer.h"
10+
911
#ifdef SIMDJSON_USE_COMPUTED_GOTO
1012
#define INIT_ADDRESSES() { &&array_begin, &&array_continue, &&error, &&finish, &&object_begin, &&object_continue }
1113
#define GOTO(address) { goto *(address); }
@@ -46,45 +48,25 @@ struct unified_machine_addresses {
4648
#undef FAIL_IF
4749
#define FAIL_IF(EXPR) { if (EXPR) { return addresses.error; } }
4850

49-
struct number_writer {
50-
dom_parser_implementation &parser;
51-
52-
really_inline void write_s64(int64_t value) noexcept {
53-
append_tape(0, internal::tape_type::INT64);
54-
std::memcpy(&parser.doc->tape[parser.current_loc], &value, sizeof(value));
55-
++parser.current_loc;
56-
}
57-
really_inline void write_u64(uint64_t value) noexcept {
58-
append_tape(0, internal::tape_type::UINT64);
59-
parser.doc->tape[parser.current_loc++] = value;
60-
}
61-
really_inline void write_double(double value) noexcept {
62-
append_tape(0, internal::tape_type::DOUBLE);
63-
static_assert(sizeof(value) == sizeof(parser.doc->tape[parser.current_loc]), "mismatch size");
64-
memcpy(&parser.doc->tape[parser.current_loc++], &value, sizeof(double));
65-
// doc->tape[doc->current_loc++] = *((uint64_t *)&d);
66-
}
67-
really_inline void append_tape(uint64_t val, internal::tape_type t) noexcept {
68-
parser.doc->tape[parser.current_loc++] = val | ((uint64_t(char(t))) << 56);
69-
}
70-
}; // struct number_writer
71-
7251
struct structural_parser : structural_iterator {
52+
/** Lets you append to the tape */
53+
tape_writer tape;
7354
/** Next write location in the string buf for stage 2 parsing */
74-
uint8_t *current_string_buf_loc{};
55+
uint8_t *current_string_buf_loc;
7556
/** Current depth (nested objects and arrays) */
76-
uint32_t depth;
57+
uint32_t depth{0};
7758

7859
// For non-streaming, to pass an explicit 0 as next_structural, which enables optimizations
7960
really_inline structural_parser(dom_parser_implementation &_parser, uint32_t start_structural_index)
8061
: structural_iterator(_parser, start_structural_index),
81-
depth{0} {
62+
tape{parser.doc->tape.get()},
63+
current_string_buf_loc{parser.doc->string_buf.get()} {
8264
}
8365

8466
WARN_UNUSED really_inline bool start_scope(ret_address_t continue_state) {
85-
parser.containing_scope[depth].tape_index = parser.current_loc;
67+
parser.containing_scope[depth].tape_index = next_tape_index();
8668
parser.containing_scope[depth].count = 0;
87-
parser.current_loc++; // We don't actually *write* the start element until the end.
69+
tape.skip(); // We don't actually *write* the start element until the end.
8870
parser.ret_address[depth] = continue_state;
8971
depth++;
9072
bool exceeded_max_depth = depth >= parser.max_depth();
@@ -112,14 +94,18 @@ struct structural_parser : structural_iterator {
11294
depth--;
11395
// write our doc->tape location to the header scope
11496
// The root scope gets written *at* the previous location.
115-
append_tape(parser.containing_scope[depth].tape_index, end);
97+
tape.append(parser.containing_scope[depth].tape_index, end);
11698
// count can overflow if it exceeds 24 bits... so we saturate
11799
// the convention being that a cnt of 0xffffff or more is undetermined in value (>= 0xffffff).
118100
const uint32_t start_tape_index = parser.containing_scope[depth].tape_index;
119101
const uint32_t count = parser.containing_scope[depth].count;
120102
const uint32_t cntsat = count > 0xFFFFFF ? 0xFFFFFF : count;
121103
// This is a load and an OR. It would be possible to just write once at doc->tape[d.tape_index]
122-
write_tape(start_tape_index, parser.current_loc | (uint64_t(cntsat) << 32), start);
104+
tape_writer::write(parser.doc->tape[start_tape_index], next_tape_index() | (uint64_t(cntsat) << 32), start);
105+
}
106+
107+
really_inline uint32_t next_tape_index() {
108+
return uint32_t(tape.next_tape_loc - parser.doc->tape.get());
123109
}
124110

125111
really_inline void end_object() {
@@ -135,14 +121,6 @@ struct structural_parser : structural_iterator {
135121
end_scope(internal::tape_type::ROOT, internal::tape_type::ROOT);
136122
}
137123

138-
really_inline void append_tape(uint64_t val, internal::tape_type t) noexcept {
139-
parser.doc->tape[parser.current_loc++] = val | ((uint64_t(char(t))) << 56);
140-
}
141-
142-
really_inline void write_tape(uint32_t loc, uint64_t val, internal::tape_type t) noexcept {
143-
parser.doc->tape[loc] = val | ((uint64_t(char(t))) << 56);
144-
}
145-
146124
// increment_count increments the count of keys in an object or values in an array.
147125
// Note that if you are at the level of the values or elements, the count
148126
// must be increment in the preceding depth (depth-1) where the array or
@@ -153,7 +131,7 @@ struct structural_parser : structural_iterator {
153131

154132
really_inline uint8_t *on_start_string() noexcept {
155133
// we advance the point, accounting for the fact that we have a NULL termination
156-
append_tape(current_string_buf_loc - parser.doc->string_buf.get(), internal::tape_type::STRING);
134+
tape.append(current_string_buf_loc - parser.doc->string_buf.get(), internal::tape_type::STRING);
157135
return current_string_buf_loc + sizeof(uint32_t);
158136
}
159137

@@ -183,8 +161,7 @@ struct structural_parser : structural_iterator {
183161

184162
WARN_UNUSED really_inline bool parse_number(const uint8_t *src, bool found_minus) {
185163
log_value("number");
186-
number_writer writer{parser};
187-
bool succeeded = numberparsing::parse_number(src, found_minus, writer);
164+
bool succeeded = numberparsing::parse_number(src, found_minus, tape);
188165
if (!succeeded) { log_error("Invalid number"); }
189166
return !succeeded;
190167
}
@@ -200,17 +177,17 @@ struct structural_parser : structural_iterator {
200177
case 't':
201178
log_value("true");
202179
FAIL_IF( !atomparsing::is_valid_true_atom(current()) );
203-
append_tape(0, internal::tape_type::TRUE_VALUE);
180+
tape.append(0, internal::tape_type::TRUE_VALUE);
204181
return continue_state;
205182
case 'f':
206183
log_value("false");
207184
FAIL_IF( !atomparsing::is_valid_false_atom(current()) );
208-
append_tape(0, internal::tape_type::FALSE_VALUE);
185+
tape.append(0, internal::tape_type::FALSE_VALUE);
209186
return continue_state;
210187
case 'n':
211188
log_value("null");
212189
FAIL_IF( !atomparsing::is_valid_null_atom(current()) );
213-
append_tape(0, internal::tape_type::NULL_VALUE);
190+
tape.append(0, internal::tape_type::NULL_VALUE);
214191
return continue_state;
215192
case '0': case '1': case '2': case '3': case '4':
216193
case '5': case '6': case '7': case '8': case '9':
@@ -285,8 +262,6 @@ struct structural_parser : structural_iterator {
285262

286263
really_inline void init() {
287264
log_start();
288-
current_string_buf_loc = parser.doc->string_buf.get();
289-
parser.current_loc = 0;
290265
parser.error = UNINITIALIZED;
291266
}
292267

@@ -362,17 +337,17 @@ WARN_UNUSED static error_code parse_structurals(dom_parser_implementation &dom_p
362337
case 't':
363338
parser.log_value("true");
364339
FAIL_IF( !atomparsing::is_valid_true_atom(parser.current(), parser.remaining_len()) );
365-
parser.append_tape(0, internal::tape_type::TRUE_VALUE);
340+
parser.tape.append(0, internal::tape_type::TRUE_VALUE);
366341
goto finish;
367342
case 'f':
368343
parser.log_value("false");
369344
FAIL_IF( !atomparsing::is_valid_false_atom(parser.current(), parser.remaining_len()) );
370-
parser.append_tape(0, internal::tape_type::FALSE_VALUE);
345+
parser.tape.append(0, internal::tape_type::FALSE_VALUE);
371346
goto finish;
372347
case 'n':
373348
parser.log_value("null");
374349
FAIL_IF( !atomparsing::is_valid_null_atom(parser.current(), parser.remaining_len()) );
375-
parser.append_tape(0, internal::tape_type::NULL_VALUE);
350+
parser.tape.append(0, internal::tape_type::NULL_VALUE);
376351
goto finish;
377352
case '0': case '1': case '2': case '3': case '4':
378353
case '5': case '6': case '7': case '8': case '9':

0 commit comments

Comments
 (0)