simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
serialization-inl.h
1
2#ifndef SIMDJSON_SERIALIZATION_INL_H
3#define SIMDJSON_SERIALIZATION_INL_H
4
5#include "simdjson/dom/base.h"
6#include "simdjson/dom/parser.h"
7#include "simdjson/dom/serialization.h"
8#include "simdjson/internal/tape_type.h"
9
10#include "simdjson/dom/array-inl.h"
11#include "simdjson/dom/object-inl.h"
12#include "simdjson/internal/tape_ref-inl.h"
13
14#include <cmath>
15#include <cstring>
16
17namespace simdjson {
18namespace dom {
19inline bool parser::print_json(std::ostream &os) const noexcept {
20 if (!valid) {
21 return false;
22 }
23 simdjson::internal::string_builder<> sb;
24 sb.append(doc.root());
25 std::string_view answer = sb.str();
26 os << answer;
27 return true;
28}
29
30inline std::ostream &operator<<(std::ostream &out,
32 simdjson::internal::string_builder<> sb;
33 sb.append(value);
34 return (out << sb.str());
35}
36#if SIMDJSON_EXCEPTIONS
37inline std::ostream &
38operator<<(std::ostream &out,
40 if (x.error()) {
42 }
43 return (out << x.value());
44}
45#endif
46inline std::ostream &operator<<(std::ostream &out, simdjson::dom::array value) {
47 simdjson::internal::string_builder<> sb;
48 sb.append(value);
49 return (out << sb.str());
50}
51#if SIMDJSON_EXCEPTIONS
52inline std::ostream &
53operator<<(std::ostream &out,
55 if (x.error()) {
57 }
58 return (out << x.value());
59}
60#endif
61inline std::ostream &operator<<(std::ostream &out,
63 simdjson::internal::string_builder<> sb;
64 sb.append(value);
65 return (out << sb.str());
66}
67#if SIMDJSON_EXCEPTIONS
68inline std::ostream &
69operator<<(std::ostream &out,
71 if (x.error()) {
73 }
74 return (out << x.value());
75}
76#endif
77
78} // namespace dom
79
80/***
81 * Number utility functions
82 **/
83namespace {
88struct escape_sequence {
89 uint8_t length;
90 const char
91 string[7]; // technically, we only ever need 6 characters, we pad to 8
92};
102static char *fast_itoa(char *output, int64_t value) noexcept {
103 // This is a standard implementation of itoa.
104 char buffer[20];
105 uint64_t value_positive;
106 // In general, negating a signed integer is unsafe.
107 if (value < 0) {
108 *output++ = '-';
109 // Doing value_positive = -value; while avoiding
110 // undefined behavior warnings.
111 // It assumes two complement's which is universal at this
112 // point in time.
113 std::memcpy(&value_positive, &value, sizeof(value));
114 value_positive = (~value_positive) + 1; // this is a negation
115 } else {
116 value_positive = value;
117 }
118 // We work solely with value_positive. It *might* be easier
119 // for an optimizing compiler to deal with an unsigned variable
120 // as far as performance goes.
121 const char *const end_buffer = buffer + 20;
122 char *write_pointer = buffer + 19;
123 // A faster approach is possible if we expect large integers:
124 // unroll the loop (work in 100s, 1000s) and use some kind of
125 // memoization.
126 while (value_positive >= 10) {
127 *write_pointer-- = char('0' + (value_positive % 10));
128 value_positive /= 10;
129 }
130 *write_pointer = char('0' + value_positive);
131 size_t len = end_buffer - write_pointer;
132 std::memcpy(output, write_pointer, len);
133 return output + len;
134}
144static char *fast_itoa(char *output, uint64_t value) noexcept {
145 // This is a standard implementation of itoa.
146 char buffer[20];
147 const char *const end_buffer = buffer + 20;
148 char *write_pointer = buffer + 19;
149 // A faster approach is possible if we expect large integers:
150 // unroll the loop (work in 100s, 1000s) and use some kind of
151 // memoization.
152 while (value >= 10) {
153 *write_pointer-- = char('0' + (value % 10));
154 value /= 10;
155 };
156 *write_pointer = char('0' + value);
157 size_t len = end_buffer - write_pointer;
158 std::memcpy(output, write_pointer, len);
159 return output + len;
160}
161
162} // anonymous namespace
163namespace internal {
164
165/***
166 * Minifier/formatter code.
167 **/
168
169template <class formatter>
170simdjson_inline void base_formatter<formatter>::number(uint64_t x) {
171 char number_buffer[24];
172 char *newp = fast_itoa(number_buffer, x);
173 chars(number_buffer, newp);
174}
175
176template <class formatter>
177simdjson_inline void base_formatter<formatter>::number(int64_t x) {
178 char number_buffer[24];
179 char *newp = fast_itoa(number_buffer, x);
180 chars(number_buffer, newp);
181}
182
183template <class formatter>
184simdjson_inline void base_formatter<formatter>::number(double x) {
185#if SIMDJSON_ENABLE_NAN_INF
186 if (simdjson_unlikely(!std::isfinite(x))) {
187 if (std::isnan(x)) {
188 char const *s = "NaN";
189 chars(s, s + 3);
190 } else {
191 if (x < 0) {
192 one_char('-');
193 }
194 char const *s = "Infinity";
195 chars(s, s + 8);
196 }
197 return;
198 }
199#endif
200
201 // Must be to_chars_buffer_size (40): only ~24 chars are emitted, but
202 // to_chars over-writes with fixed-size 16/17-byte copies for inlining.
203 char number_buffer[simdjson::internal::to_chars_buffer_size];
204 // Currently, passing the nullptr to the second argument is
205 // safe because our implementation does not check the second
206 // argument.
207 char *newp = simdjson::internal::to_chars(number_buffer, nullptr, x);
208 chars(number_buffer, newp);
209}
210
211template <class formatter>
212simdjson_inline void base_formatter<formatter>::start_array() {
213 one_char('[');
214}
215
216template <class formatter>
217simdjson_inline void base_formatter<formatter>::end_array() {
218 one_char(']');
219}
220
221template <class formatter>
222simdjson_inline void base_formatter<formatter>::start_object() {
223 one_char('{');
224}
225
226template <class formatter>
227simdjson_inline void base_formatter<formatter>::end_object() {
228 one_char('}');
229}
230
231template <class formatter>
232simdjson_inline void base_formatter<formatter>::comma() {
233 one_char(',');
234}
235
236template <class formatter>
237simdjson_inline void base_formatter<formatter>::true_atom() {
238 const char *s = "true";
239 chars(s, s + 4);
240}
241
242template <class formatter>
243simdjson_inline void base_formatter<formatter>::false_atom() {
244 const char *s = "false";
245 chars(s, s + 5);
246}
247
248template <class formatter>
249simdjson_inline void base_formatter<formatter>::null_atom() {
250 const char *s = "null";
251 chars(s, s + 4);
252}
253
254template <class formatter>
255simdjson_inline void base_formatter<formatter>::one_char(char c) {
256 buffer.push_back(c);
257}
258
259template <class formatter>
260simdjson_inline void base_formatter<formatter>::chars(const char *begin,
261 const char *end) {
262 buffer.append(begin, end);
263}
264
265template <class formatter>
266simdjson_inline void
267base_formatter<formatter>::key(std::string_view unescaped) {
268 string(unescaped);
269 one_char(':');
270}
271
272template <class formatter>
273simdjson_inline void
274base_formatter<formatter>::string(std::string_view unescaped) {
275 one_char('\"');
276 size_t i = 0;
277 // Fast path for the case where we have no control character, no ", and no
278 // backslash. This should include most keys.
279 //
280 // We would like to use 'bool' but some compilers take offense to bitwise
281 // operation with bool types.
282 constexpr static char needs_escaping[] = {
283 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
284 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
285 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
286 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
287 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
288 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
289 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
290 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
291 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
292 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
293 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
294 for (; i + 8 <= unescaped.length(); i += 8) {
295 // Poor's man vectorization. This could get much faster if we used SIMD.
296 //
297 // It is not the case that replacing '|' with '||' would be neutral
298 // performance-wise.
299 if (needs_escaping[uint8_t(unescaped[i])] |
300 needs_escaping[uint8_t(unescaped[i + 1])] |
301 needs_escaping[uint8_t(unescaped[i + 2])] |
302 needs_escaping[uint8_t(unescaped[i + 3])] |
303 needs_escaping[uint8_t(unescaped[i + 4])] |
304 needs_escaping[uint8_t(unescaped[i + 5])] |
305 needs_escaping[uint8_t(unescaped[i + 6])] |
306 needs_escaping[uint8_t(unescaped[i + 7])]) {
307 break;
308 }
309 }
310 for (; i < unescaped.length(); i++) {
311 if (needs_escaping[uint8_t(unescaped[i])]) {
312 break;
313 }
314 }
315 // The following is also possible and omits a 256-byte table, but it is
316 // slower: for (; (i < unescaped.length()) && (uint8_t(unescaped[i]) > 0x1F)
317 // && (unescaped[i] != '\"') && (unescaped[i] != '\\'); i++) {}
318
319 // At least for long strings, the following should be fast. We could
320 // do better by integrating the checks and the insertion.
321 chars(unescaped.data(), unescaped.data() + i);
322 // We caught a control character if we enter this loop (slow).
323 // Note that we are do not restart from the beginning, but rather we continue
324 // from the point where we encountered something that requires escaping.
325 for (; i < unescaped.length(); i++) {
326 switch (unescaped[i]) {
327 case '\"': {
328 const char *s = "\\\"";
329 chars(s, s + 2);
330 } break;
331 case '\\': {
332 const char *s = "\\\\";
333 chars(s, s + 2);
334 } break;
335 default:
336 if (uint8_t(unescaped[i]) <= 0x1F) {
337 // If packed, this uses 8 * 32 bytes.
338 // Note that we expect most compilers to embed this code in the data
339 // section.
340 constexpr static escape_sequence escaped[32] = {
341 {6, "\\u0000"}, {6, "\\u0001"}, {6, "\\u0002"}, {6, "\\u0003"},
342 {6, "\\u0004"}, {6, "\\u0005"}, {6, "\\u0006"}, {6, "\\u0007"},
343 {2, "\\b"}, {2, "\\t"}, {2, "\\n"}, {6, "\\u000b"},
344 {2, "\\f"}, {2, "\\r"}, {6, "\\u000e"}, {6, "\\u000f"},
345 {6, "\\u0010"}, {6, "\\u0011"}, {6, "\\u0012"}, {6, "\\u0013"},
346 {6, "\\u0014"}, {6, "\\u0015"}, {6, "\\u0016"}, {6, "\\u0017"},
347 {6, "\\u0018"}, {6, "\\u0019"}, {6, "\\u001a"}, {6, "\\u001b"},
348 {6, "\\u001c"}, {6, "\\u001d"}, {6, "\\u001e"}, {6, "\\u001f"}};
349 auto u = escaped[uint8_t(unescaped[i])];
350 chars(u.string, u.string + u.length);
351 } else {
352 one_char(unescaped[i]);
353 }
354 } // switch
355 } // for
356 one_char('\"');
357}
358
359template <class formatter> inline void base_formatter<formatter>::clear() {
360 buffer.clear();
361}
362
363template <class formatter>
364simdjson_inline std::string_view base_formatter<formatter>::str() const {
365 return buffer.str();
366}
367
368simdjson_inline void mini_formatter::print_newline() { return; }
369
370simdjson_inline void mini_formatter::print_indents(size_t depth) {
371 (void)depth;
372 return;
373}
374
375simdjson_inline void mini_formatter::print_space() { return; }
376
377simdjson_inline void pretty_formatter::print_newline() { one_char('\n'); }
378
379simdjson_inline void pretty_formatter::print_indents(size_t depth) {
380 if (this->indent_step <= 0) {
381 return;
382 }
383 for (size_t i = 0; i < this->indent_step * depth; i++) {
384 one_char(' ');
385 }
386}
387
388simdjson_inline void pretty_formatter::print_space() { one_char(' '); }
389
390/***
391 * String building code.
392 **/
393
394template <class serializer>
395inline void string_builder<serializer>::append(simdjson::dom::element value) {
396 // using tape_type = simdjson::internal::tape_type;
397 size_t depth = 0;
398 constexpr size_t MAX_DEPTH = 16;
399 bool is_object[MAX_DEPTH];
400 is_object[0] = false;
401 bool after_value = false;
402
403 internal::tape_ref iter(value.tape);
404 do {
405 // print commas after each value
406 if (after_value) {
407 format.comma();
408 format.print_newline();
409 }
410
411 format.print_indents(depth);
412
413 // If we are in an object, print the next key and :, and skip to the next
414 // value.
415 if (is_object[depth]) {
416 format.key(iter.get_string_view());
417 format.print_space();
418 iter.json_index++;
419 }
420 switch (iter.tape_ref_type()) {
421
422 // Arrays
423 case tape_type::START_ARRAY: {
424 // If we're too deep, we need to recurse to go deeper.
425 depth++;
426 if (simdjson_unlikely(depth >= MAX_DEPTH)) {
427 append(simdjson::dom::array(iter));
428 iter.json_index = iter.matching_brace_index() - 1; // Jump to the ]
429 depth--;
430 break;
431 }
432
433 // Output start [
434 format.start_array();
435 iter.json_index++;
436
437 // Handle empty [] (we don't want to come back around and print commas)
438 if (iter.tape_ref_type() == tape_type::END_ARRAY) {
439 format.end_array();
440 depth--;
441 break;
442 }
443
444 is_object[depth] = false;
445 after_value = false;
446 format.print_newline();
447 continue;
448 }
449
450 // Objects
451 case tape_type::START_OBJECT: {
452 // If we're too deep, we need to recurse to go deeper.
453 depth++;
454 if (simdjson_unlikely(depth >= MAX_DEPTH)) {
455 append(simdjson::dom::object(iter));
456 iter.json_index = iter.matching_brace_index() - 1; // Jump to the }
457 depth--;
458 break;
459 }
460
461 // Output start {
462 format.start_object();
463 iter.json_index++;
464
465 // Handle empty {} (we don't want to come back around and print commas)
466 if (iter.tape_ref_type() == tape_type::END_OBJECT) {
467 format.end_object();
468 depth--;
469 break;
470 }
471
472 is_object[depth] = true;
473 after_value = false;
474 format.print_newline();
475 continue;
476 }
477
478 // Scalars
479 case tape_type::STRING:
480 format.string(iter.get_string_view());
481 break;
482 case tape_type::BIGINT: {
483 // Big integer stored as string -- output raw digits (no quotes)
484 auto sv = iter.get_string_view();
485 format.chars(sv.data(), sv.data() + sv.size());
486 break;
487 }
488 case tape_type::INT64:
489 format.number(iter.next_tape_value<int64_t>());
490 iter.json_index++; // numbers take up 2 spots, so we need to increment
491 // extra
492 break;
493 case tape_type::UINT64:
494 format.number(iter.next_tape_value<uint64_t>());
495 iter.json_index++; // numbers take up 2 spots, so we need to increment
496 // extra
497 break;
498 case tape_type::DOUBLE:
499 format.number(iter.next_tape_value<double>());
500 iter.json_index++; // numbers take up 2 spots, so we need to increment
501 // extra
502 break;
503 case tape_type::TRUE_VALUE:
504 format.true_atom();
505 break;
506 case tape_type::FALSE_VALUE:
507 format.false_atom();
508 break;
509 case tape_type::NULL_VALUE:
510 format.null_atom();
511 break;
512
513 // These are impossible
514 case tape_type::END_ARRAY:
515 case tape_type::END_OBJECT:
516 case tape_type::ROOT:
517 SIMDJSON_UNREACHABLE();
518 }
519 iter.json_index++;
520 after_value = true;
521
522 // Handle multiple ends in a row
523 while (depth != 0 && (iter.tape_ref_type() == tape_type::END_ARRAY ||
524 iter.tape_ref_type() == tape_type::END_OBJECT)) {
525 format.print_newline();
526 depth--;
527 format.print_indents(depth);
528 if (iter.tape_ref_type() == tape_type::END_ARRAY) {
529 format.end_array();
530 } else {
531 format.end_object();
532 }
533 iter.json_index++;
534 }
535
536 // Stop when we're at depth 0
537 } while (depth != 0);
538
539 format.print_newline();
540}
541
542template <class serializer>
543inline void string_builder<serializer>::append(simdjson::dom::object value) {
544 format.start_object();
545 auto pair = value.begin();
546 auto end = value.end();
547 if (pair != end) {
548 append(*pair);
549 for (++pair; pair != end; ++pair) {
550 format.comma();
551 append(*pair);
552 }
553 }
554 format.end_object();
555}
556
557template <class serializer>
558inline void string_builder<serializer>::append(simdjson::dom::array value) {
559 format.start_array();
560 auto iter = value.begin();
561 auto end = value.end();
562 if (iter != end) {
563 append(*iter);
564 for (++iter; iter != end; ++iter) {
565 format.comma();
566 append(*iter);
567 }
568 }
569 format.end_array();
570}
571
572template <class serializer>
573simdjson_inline void
574string_builder<serializer>::append(simdjson::dom::key_value_pair kv) {
575 format.key(kv.key);
576 append(kv.value);
577}
578
579template <class serializer>
580simdjson_inline void string_builder<serializer>::clear() {
581 format.clear();
582}
583
584template <class serializer>
585simdjson_inline std::string_view string_builder<serializer>::str() const {
586 return format.str();
587}
588
589} // namespace internal
590} // namespace simdjson
591
592#endif
JSON array.
Definition array.h:15
A JSON element.
Definition element.h:36
Key/value pair in an object.
Definition object.h:258
std::string_view key
key in the key-value pair
Definition object.h:261
element value
value in the key-value pair
Definition object.h:263
JSON object.
Definition object.h:16
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
Exception thrown when an exception-supporting simdjson method is called.
Definition error.h:92
The result of a simdjson operation that could fail.
Definition error.h:281
simdjson_inline error_code error() const noexcept
The error.
Definition error-inl.h:168
simdjson_inline T & value() &noexcept(false)
Get the result value.
Definition error-inl.h:175