simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
value_iterator-inl.h
1#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
2
3#ifndef SIMDJSON_CONDITIONAL_INCLUDE
4#define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
5#include "simdjson/generic/ondemand/base.h"
6#include "simdjson/generic/atomparsing.h"
7#include "simdjson/generic/numberparsing.h"
8#include "simdjson/generic/ondemand/json_iterator.h"
9#include "simdjson/generic/ondemand/value_iterator.h"
10#include "simdjson/generic/ondemand/json_type-inl.h"
11#include "simdjson/generic/ondemand/raw_json_string-inl.h"
12#endif // SIMDJSON_CONDITIONAL_INCLUDE
13
14namespace simdjson {
15namespace SIMDJSON_IMPLEMENTATION {
16namespace ondemand {
17
18#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
19simdjson_inline bool raw_json_string_is_quote_terminated(const uint8_t *json, uint32_t max_len) noexcept {
20 bool escaping{false};
21 for (uint32_t i = 1; i < max_len; i++) {
22 switch (json[i]) {
23 case '"':
24 if (!escaping) { return true; }
25 escaping = false;
26 break;
27 case '\\':
28 escaping = !escaping;
29 break;
30 default:
31 escaping = false;
32 break;
33 }
34 }
35 return false;
36}
37#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
38
39simdjson_inline value_iterator::value_iterator(
40 json_iterator *json_iter,
41 depth_t depth,
42 token_position start_position
43) noexcept : _json_iter{json_iter}, _depth{depth}, _start_position{start_position}
44{
45}
46
47simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_object() noexcept {
48 SIMDJSON_TRY( start_container('{', "Not an object", "object") );
49 return started_object();
50}
51
52simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_object() noexcept {
53 SIMDJSON_TRY( start_container('{', "Not an object", "object") );
54 return started_root_object();
55}
56
57simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_object() noexcept {
58 assert_at_container_start();
59#if SIMDJSON_DEVELOPMENT_CHECKS
60 _json_iter->set_start_position(_depth, start_position());
61#endif
62 if (*_json_iter->peek() == '}') {
63 logger::log_value(*_json_iter, "empty object");
64 _json_iter->return_current_and_advance();
65 SIMDJSON_TRY(end_container());
66 return false;
67 }
68 return true;
69}
70
71simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_object() noexcept {
72 // When in streaming mode, we cannot expect peek_last() to be the last structural element of the
73 // current document. It only works in the normal mode where we have indexed a single document.
74 // Note that adding a check for 'streaming' is not expensive since we only have at most
75 // one root element.
76 if ( ! _json_iter->streaming() ) {
77 // The following lines do not fully protect against garbage content within the
78 // object: e.g., `{"a":2} foo }`. Users concerned with garbage content should
79 // call `at_end()` on the document instance at the end of the processing to
80 // ensure that the processing has finished at the end.
81 //
82 if (*_json_iter->peek_last() != '}') {
83 _json_iter->abandon();
84 return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing } at end");
85 }
86 // If the last character is } *and* the first gibberish character is also '}'
87 // then on-demand could accidentally go over. So we need additional checks.
88 // https://github.com/simdjson/simdjson/issues/1834
89 // Checking that the document is balanced requires a full scan which is potentially
90 // expensive, but it only happens in edge cases where the first padding character is
91 // a closing bracket.
92 if ((*_json_iter->peek(_json_iter->end_position()) == '}') && (!_json_iter->balanced())) {
93 _json_iter->abandon();
94 // The exact error would require more work. It will typically be an unclosed object.
95 return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
96 }
97 }
98 return SUCCESS;
99}
100
101simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_object() noexcept {
102 auto error = check_root_object();
103 if(error) { return error; }
104 return started_object();
105}
106
107simdjson_warn_unused simdjson_inline error_code value_iterator::end_container() noexcept {
108#if SIMDJSON_CHECK_EOF
109 if (depth() > 1 && at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing parent ] or }"); }
110 // if (depth() <= 1 && !at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing [ or { at start"); }
111#endif // SIMDJSON_CHECK_EOF
112 _json_iter->ascend_to(depth()-1);
113 return SUCCESS;
114}
115
116simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_field() noexcept {
117 assert_at_next();
118 // It's illegal to call this unless there are more tokens: anything that ends in } or ] is
119 // obligated to verify there are more tokens if they are not the top level.
120 switch (*_json_iter->return_current_and_advance()) {
121 case '}':
122 logger::log_end_value(*_json_iter, "object");
123 SIMDJSON_TRY( end_container() );
124 return false;
125 case ',':
126 return true;
127 default:
128 return report_error(TAPE_ERROR, "Missing comma between object fields");
129 }
130}
131
132simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_raw(const std::string_view key) noexcept {
133 error_code error;
134 bool has_value;
135 //
136 // Initially, the object can be in one of a few different places:
137 //
138 // 1. The start of the object, at the first field:
139 //
140 // ```
141 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
142 // ^ (depth 2, index 1)
143 // ```
144 if (at_first_field()) {
145 has_value = true;
146
147 //
148 // 2. When a previous search did not yield a value or the object is empty:
149 //
150 // ```
151 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
152 // ^ (depth 0)
153 // { }
154 // ^ (depth 0, index 2)
155 // ```
156 //
157 } else if (!is_open()) {
158#if SIMDJSON_DEVELOPMENT_CHECKS
159 // If we're past the end of the object, we're being iterated out of order.
160 // Note: this is not perfect detection. It's possible the user is inside some other object; if so,
161 // this object iterator will blithely scan that object for fields.
162 if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
163#endif
164 return false;
165
166 // 3. When a previous search found a field or an iterator yielded a value:
167 //
168 // ```
169 // // When a field was not fully consumed (or not even touched at all)
170 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
171 // ^ (depth 2)
172 // // When a field was fully consumed
173 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
174 // ^ (depth 1)
175 // // When the last field was fully consumed
176 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
177 // ^ (depth 1)
178 // ```
179 //
180 } else {
181 if ((error = skip_child() )) { abandon(); return error; }
182 if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
183#if SIMDJSON_DEVELOPMENT_CHECKS
184 if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
185#endif
186 }
187 while (has_value) {
188 // Get the key and colon, stopping at the value.
189 raw_json_string actual_key;
190 // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
191 // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
192 // field_key() advances the pointer and checks that '"' is found (corresponding to a key).
193 // The depth is left unchanged by field_key().
194 if ((error = field_key().get(actual_key) )) { abandon(); return error; };
195 // field_value() will advance and check that we find a ':' separating the
196 // key and the value. It will also increment the depth by one.
197 if ((error = field_value() )) { abandon(); return error; }
198 // If it matches, stop and return
199 // We could do it this way if we wanted to allow arbitrary
200 // key content (including escaped quotes).
201 //if (actual_key.unsafe_is_equal(max_key_length, key)) {
202 // Instead we do the following which may trigger buffer overruns if the
203 // user provides an adversarial key (containing a well placed unescaped quote
204 // character and being longer than the number of bytes remaining in the JSON
205 // input).
206 if (actual_key.unsafe_is_equal(key)) {
207 logger::log_event(*this, "match", key, -2);
208 // If we return here, then we return while pointing at the ':' that we just checked.
209 return true;
210 }
211
212 // No match: skip the value and see if , or } is next
213 logger::log_event(*this, "no match", key, -2);
214 // The call to skip_child is meant to skip over the value corresponding to the key.
215 // After skip_child(), we are right before the next comma (',') or the final brace ('}').
216 SIMDJSON_TRY( skip_child() ); // Skip the value entirely
217 // The has_next_field() advances the pointer and check that either ',' or '}' is found.
218 // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
219 // then we are in error and we abort.
220 if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
221 }
222
223 // If the loop ended, we're out of fields to look at.
224 return false;
225}
226
227SIMDJSON_PUSH_DISABLE_WARNINGS
228SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
229simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::find_field_unordered_raw(const std::string_view key) noexcept {
237 error_code error;
238 bool has_value;
239
240 // First, we scan from that point to the end.
241 // If we don't find a match, we may loop back around, and scan from the beginning to that point.
242 token_position search_start = _json_iter->position();
243
244 // We want to know whether we need to go back to the beginning.
245 bool at_first = at_first_field();
247 // Initially, the object can be in one of a few different places:
248 //
249 // 1. At the first key:
250 //
251 // ```
252 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
253 // ^ (depth 2, index 1)
254 // ```
255 //
256 if (at_first) {
257 has_value = true;
258
259 // 2. When a previous search did not yield a value or the object is empty:
260 //
261 // ```
262 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
263 // ^ (depth 0)
264 // { }
265 // ^ (depth 0, index 2)
266 // ```
267 //
268 } else if (!is_open()) {
269
270#if SIMDJSON_DEVELOPMENT_CHECKS
271 // If we're past the end of the object, we're being iterated out of order.
272 // Note: this is not perfect detection. It's possible the user is inside some other object; if so,
273 // this object iterator will blithely scan that object for fields.
274 if (_json_iter->depth() < depth() - 1) { return OUT_OF_ORDER_ITERATION; }
275#endif
276 SIMDJSON_TRY(reset_object().get(has_value));
277 at_first = true;
278 // 3. When a previous search found a field or an iterator yielded a value:
279 //
280 // ```
281 // // When a field was not fully consumed (or not even touched at all)
282 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
283 // ^ (depth 2)
284 // // When a field was fully consumed
285 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
286 // ^ (depth 1)
287 // // When the last field was fully consumed
288 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
289 // ^ (depth 1)
290 // ```
291 //
292 } else {
293 // If someone queried a key but they not did access the value, then we are left pointing
294 // at the ':' and we need to move forward through the value... If the value was
295 // processed then skip_child() does not move the iterator (but may adjust the depth).
296 if ((error = skip_child() )) { abandon(); return error; }
297 search_start = _json_iter->position();
298 if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
299#if SIMDJSON_DEVELOPMENT_CHECKS
300 if (_json_iter->start_position(_depth) != start_position()) { return OUT_OF_ORDER_ITERATION; }
301#endif
302 }
303
304 // After initial processing, we will be in one of two states:
305 //
306 // ```
307 // // At the beginning of a field
308 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
309 // ^ (depth 1)
310 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
311 // ^ (depth 1)
312 // // At the end of the object
313 // { "a": [ 1, 2 ], "b": [ 3, 4 ] }
314 // ^ (depth 0)
315 // ```
316 //
317 // Next, we find a match starting from the current position.
318 while (has_value) {
319 SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
320
321 // Get the key and colon, stopping at the value.
322 raw_json_string actual_key;
323 // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
324 // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
325 // field_key() advances the pointer and checks that '"' is found (corresponding to a key).
326 // The depth is left unchanged by field_key().
327 if ((error = field_key().get(actual_key) )) { abandon(); return error; };
328 // field_value() will advance and check that we find a ':' separating the
329 // key and the value. It will also increment the depth by one.
330 if ((error = field_value() )) { abandon(); return error; }
331
332 // If it matches, stop and return
333 // We could do it this way if we wanted to allow arbitrary
334 // key content (including escaped quotes).
335 // if (actual_key.unsafe_is_equal(max_key_length, key)) {
336 // Instead we do the following which may trigger buffer overruns if the
337 // user provides an adversarial key (containing a well placed unescaped quote
338 // character and being longer than the number of bytes remaining in the JSON
339 // input).
340 if (actual_key.unsafe_is_equal(key)) {
341 logger::log_event(*this, "match", key, -2);
342 // If we return here, then we return while pointing at the ':' that we just checked.
343 return true;
344 }
345
346 // No match: skip the value and see if , or } is next
347 logger::log_event(*this, "no match", key, -2);
348 // The call to skip_child is meant to skip over the value corresponding to the key.
349 // After skip_child(), we are right before the next comma (',') or the final brace ('}').
350 SIMDJSON_TRY( skip_child() );
351 // The has_next_field() advances the pointer and check that either ',' or '}' is found.
352 // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
353 // then we are in error and we abort.
354 if ((error = has_next_field().get(has_value) )) { abandon(); return error; }
355 }
356 // Performance note: it maybe wasteful to rewind to the beginning when there might be
357 // no other query following. Indeed, it would require reskipping the whole object.
358 // Instead, you can just stay where you are. If there is a new query, there is always time
359 // to rewind.
360 if(at_first) { return false; }
361
362 // If we reach the end without finding a match, search the rest of the fields starting at the
363 // beginning of the object.
364 // (We have already run through the object before, so we've already validated its structure. We
365 // don't check errors in this bit.)
366 SIMDJSON_TRY(reset_object().get(has_value));
367 while (true) {
368 SIMDJSON_ASSUME(has_value); // we should reach search_start before ever reaching the end of the object
369 SIMDJSON_ASSUME( _json_iter->_depth == _depth ); // We must be at the start of a field
370
371 // Get the key and colon, stopping at the value.
372 raw_json_string actual_key;
373 // size_t max_key_length = _json_iter->peek_length() - 2; // -2 for the two quotes
374 // Note: _json_iter->peek_length() - 2 might overflow if _json_iter->peek_length() < 2.
375 // field_key() advances the pointer and checks that '"' is found (corresponding to a key).
376 // The depth is left unchanged by field_key().
377 error = field_key().get(actual_key); SIMDJSON_ASSUME(!error);
378 // field_value() will advance and check that we find a ':' separating the
379 // key and the value. It will also increment the depth by one.
380 error = field_value(); SIMDJSON_ASSUME(!error);
381
382 // If it matches, stop and return
383 // We could do it this way if we wanted to allow arbitrary
384 // key content (including escaped quotes).
385 // if (actual_key.unsafe_is_equal(max_key_length, key)) {
386 // Instead we do the following which may trigger buffer overruns if the
387 // user provides an adversarial key (containing a well placed unescaped quote
388 // character and being longer than the number of bytes remaining in the JSON
389 // input).
390 if (actual_key.unsafe_is_equal(key)) {
391 logger::log_event(*this, "match", key, -2);
392 // If we return here, then we return while pointing at the ':' that we just checked.
393 return true;
394 }
395
396 // No match: skip the value and see if , or } is next
397 logger::log_event(*this, "no match", key, -2);
398 // The call to skip_child is meant to skip over the value corresponding to the key.
399 // After skip_child(), we are right before the next comma (',') or the final brace ('}').
400 SIMDJSON_TRY( skip_child() );
401 // If we reached the end of the key-value pair we started from, then we know
402 // that the key is not there so we return false. We are either right before
403 // the next comma or the final brace.
404 if(_json_iter->position() == search_start) { return false; }
405 // The has_next_field() advances the pointer and check that either ',' or '}' is found.
406 // It returns true if ',' is found, false otherwise. If anything other than ',' or '}' is found,
407 // then we are in error and we abort.
408 error = has_next_field().get(has_value); SIMDJSON_ASSUME(!error);
409 // If we make the mistake of exiting here, then we could be left pointing at a key
410 // in the middle of an object. That's not an allowable state.
411 }
412 // If the loop ended, we're out of fields to look at. The program should
413 // never reach this point.
414 return false;
415}
416SIMDJSON_POP_DISABLE_WARNINGS
417
418simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::field_key() noexcept {
419 assert_at_next();
420
421 const uint8_t *key = _json_iter->return_current_and_advance();
422 if (*(key++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
423 return raw_json_string(key);
424}
425
426simdjson_warn_unused simdjson_inline error_code value_iterator::field_key_with_length(raw_json_string &key, std::size_t &len) noexcept {
427 assert_at_next();
428
429 const uint8_t *k = _json_iter->return_current_and_advance();
430 if (*(k++) != '"') { return report_error(TAPE_ERROR, "Object key is not a string"); }
431 // After return_current_and_advance(), the current token is the ':' that follows
432 // the key. The closing quote sits just before it (only JSON whitespace may
433 // intervene), so step back from the ':' to the closing quote to get the length.
434 // In minified JSON this is a single back-step.
435 const char *q = reinterpret_cast<const char *>(_json_iter->peek());
436 do { --q; } while (*q != '"');
437 key = raw_json_string(k);
438 len = static_cast<std::size_t>(q - reinterpret_cast<const char *>(k));
439 return SUCCESS;
440}
441
442simdjson_warn_unused simdjson_inline error_code value_iterator::field_value() noexcept {
443 assert_at_next();
444
445 if (*_json_iter->return_current_and_advance() != ':') { return report_error(TAPE_ERROR, "Missing colon in object field"); }
446 _json_iter->descend_to(depth()+1);
447 return SUCCESS;
448}
449
450simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_array() noexcept {
451 SIMDJSON_TRY( start_container('[', "Not an array", "array") );
452 return started_array();
453}
454
455simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::start_root_array() noexcept {
456 SIMDJSON_TRY( start_container('[', "Not an array", "array") );
457 return started_root_array();
458}
459
460inline std::string value_iterator::to_string() const noexcept {
461 auto answer = std::string("value_iterator [ depth : ") + std::to_string(_depth) + std::string(", ");
462 if(_json_iter != nullptr) { answer += _json_iter->to_string(); }
463 answer += std::string(" ]");
464 return answer;
465}
466
467simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_array() noexcept {
468 assert_at_container_start();
469 if (*_json_iter->peek() == ']') {
470 logger::log_value(*_json_iter, "empty array");
471 _json_iter->return_current_and_advance();
472 SIMDJSON_TRY( end_container() );
473 return false;
474 }
475 _json_iter->descend_to(depth()+1);
476#if SIMDJSON_DEVELOPMENT_CHECKS
477 _json_iter->set_start_position(_depth, start_position());
478#endif
479 return true;
480}
481
482simdjson_warn_unused simdjson_inline error_code value_iterator::check_root_array() noexcept {
483 // When in streaming mode, we cannot expect peek_last() to be the last structural element of the
484 // current document. It only works in the normal mode where we have indexed a single document.
485 // Note that adding a check for 'streaming' is not expensive since we only have at most
486 // one root element.
487 if ( ! _json_iter->streaming() ) {
488 // The following lines do not fully protect against garbage content within the
489 // array: e.g., `[1, 2] foo]`. Users concerned with garbage content should
490 // also call `at_end()` on the document instance at the end of the processing to
491 // ensure that the processing has finished at the end.
492 //
493 if (*_json_iter->peek_last() != ']') {
494 _json_iter->abandon();
495 return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "missing ] at end");
496 }
497 // If the last character is ] *and* the first gibberish character is also ']'
498 // then on-demand could accidentally go over. So we need additional checks.
499 // https://github.com/simdjson/simdjson/issues/1834
500 // Checking that the document is balanced requires a full scan which is potentially
501 // expensive, but it only happens in edge cases where the first padding character is
502 // a closing bracket.
503 if ((*_json_iter->peek(_json_iter->end_position()) == ']') && (!_json_iter->balanced())) {
504 _json_iter->abandon();
505 // The exact error would require more work. It will typically be an unclosed array.
506 return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "the document is unbalanced");
507 }
508 }
509 return SUCCESS;
510}
511
512simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::started_root_array() noexcept {
513 auto error = check_root_array();
514 if (error) { return error; }
515 return started_array();
516}
517
518simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::has_next_element() noexcept {
519 assert_at_next();
520
521 logger::log_event(*this, "has_next_element");
522 switch (*_json_iter->return_current_and_advance()) {
523 case ']':
524 logger::log_end_value(*_json_iter, "array");
525 SIMDJSON_TRY( end_container() );
526 return false;
527 case ',':
528 _json_iter->descend_to(depth()+1);
529 return true;
530 default:
531 return report_error(TAPE_ERROR, "Missing comma between array elements");
532 }
533}
534
535simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_bool(const uint8_t *json) const noexcept {
536 auto not_true = atomparsing::str4ncmp(json, "true");
537 auto not_false = atomparsing::str4ncmp(json, "fals") | (json[4] ^ 'e');
538 bool error = (not_true && not_false) || jsoncharutils::is_not_structural_or_whitespace(json[not_true ? 5 : 4]);
539 if (error) { return incorrect_type_error("Not a boolean"); }
540 return simdjson_result<bool>(!not_true);
541}
542simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::parse_null(const uint8_t *json) const noexcept {
543 bool is_null_string = !atomparsing::str4ncmp(json, "null") && jsoncharutils::is_structural_or_whitespace(json[4]);
544 // if we start with 'n', we must be a null
545 if(!is_null_string && json[0]=='n') { return incorrect_type_error("Not a null but starts with n"); }
546 return is_null_string;
547}
548
549simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_string(bool allow_replacement) noexcept {
550 return get_raw_json_string().unescape(json_iter(), allow_replacement);
551}
552template <typename string_type>
553simdjson_warn_unused simdjson_inline error_code value_iterator::get_string(string_type& receiver, bool allow_replacement) noexcept {
554 std::string_view content;
555 // Save the string buffer location so that we can restore it after get_string
556 auto saved_string_buf_loc = _json_iter->string_buf_loc();
557 auto err = get_string(allow_replacement).get(content);
558 if (err) { return err; }
559 internal::assign_utf8(receiver, content);
560 // Restore the string buffer location, effectively discarding any temporary string storage
561 _json_iter->string_buf_loc() = saved_string_buf_loc;
562 return SUCCESS;
563}
564simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_wobbly_string() noexcept {
565 return get_raw_json_string().unescape_wobbly(json_iter());
566}
567simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_raw_json_string() noexcept {
568 auto json = peek_scalar("string");
569 if (*json != '"') { return incorrect_type_error("Not a string"); }
570#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
571 if (_json_iter->allow_incomplete_json()) {
572 const size_t remaining_input_length = _json_iter->remaining_input_length(json);
573 const uint32_t max_len = remaining_input_length < peek_start_length() ? uint32_t(remaining_input_length) : peek_start_length();
574 if (!raw_json_string_is_quote_terminated(json, max_len)) {
575 return STRING_ERROR;
576 }
577 }
578#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
579 advance_scalar("string");
580 return raw_json_string(json+1);
581}
582simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64() noexcept {
583 auto result = numberparsing::parse_unsigned(peek_non_root_scalar("uint64"));
584 if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
585 return result;
586}
587simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_uint64_in_string() noexcept {
588 auto result = numberparsing::parse_unsigned_in_string(peek_non_root_scalar("uint64"));
589 if(result.error() == SUCCESS) { advance_non_root_scalar("uint64"); }
590 return result;
591}
592simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64() noexcept {
593 auto result = numberparsing::parse_integer(peek_non_root_scalar("int64"));
594 if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
595 return result;
596}
597simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_int64_in_string() noexcept {
598 auto result = numberparsing::parse_integer_in_string(peek_non_root_scalar("int64"));
599 if(result.error() == SUCCESS) { advance_non_root_scalar("int64"); }
600 return result;
601}
602simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double() noexcept {
603 auto result = numberparsing::parse_double(peek_non_root_scalar("double"));
604 if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
605 return result;
606}
607simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_double_in_string() noexcept {
608 auto result = numberparsing::parse_double_in_string(peek_non_root_scalar("double"));
609 if(result.error() == SUCCESS) { advance_non_root_scalar("double"); }
610 return result;
611}
612simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float() noexcept {
613 auto result = numberparsing::parse_float(peek_non_root_scalar("float"));
614 if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
615 return result;
616}
617simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_float_in_string() noexcept {
618 auto result = numberparsing::parse_float_in_string(peek_non_root_scalar("float"));
619 if(result.error() == SUCCESS) { advance_non_root_scalar("float"); }
620 return result;
621}
622simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_bool() noexcept {
623 auto result = parse_bool(peek_non_root_scalar("bool"));
624 if(result.error() == SUCCESS) { advance_non_root_scalar("bool"); }
625 return result;
626}
627simdjson_inline simdjson_result<bool> value_iterator::is_null() noexcept {
628 bool is_null_value;
629 SIMDJSON_TRY(parse_null(peek_non_root_scalar("null")).get(is_null_value));
630 if(is_null_value) { advance_non_root_scalar("null"); }
631 return is_null_value;
632}
633simdjson_inline bool value_iterator::is_negative() noexcept {
634 return numberparsing::is_negative(peek_non_root_scalar("numbersign"));
635}
636simdjson_inline bool value_iterator::is_root_negative() noexcept {
637 return numberparsing::is_negative(peek_root_scalar("numbersign"));
638}
639simdjson_inline simdjson_result<bool> value_iterator::is_integer() noexcept {
640 return numberparsing::is_integer(peek_non_root_scalar("integer"));
641}
642simdjson_inline simdjson_result<number_type> value_iterator::get_number_type() noexcept {
643 return numberparsing::get_number_type(peek_non_root_scalar("integer"));
644}
645simdjson_inline simdjson_result<number> value_iterator::get_number() noexcept {
646 number num;
647 error_code error = numberparsing::parse_number(peek_non_root_scalar("number"), num);
648 if(error) { return error; }
649 return num;
650}
651
652simdjson_inline simdjson_result<bool> value_iterator::is_root_integer(bool check_trailing) noexcept {
653 auto max_len = peek_root_length();
654 auto json = peek_root_scalar("is_root_integer");
655 uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
656 tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
657 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
658 return false; // if there are more than 20 characters, it cannot be represented as an integer.
659 }
660 auto answer = numberparsing::is_integer(tmpbuf);
661 // If the parsing was a success, we must still check that it is
662 // a single scalar. Note that we parse first because of cases like '[]' where
663 // getting TRAILING_CONTENT is wrong.
664 if(check_trailing && (answer.error() == SUCCESS) && (!_json_iter->is_single_token())) { return TRAILING_CONTENT; }
665 return answer;
666}
667
668simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::number_type> value_iterator::get_root_number_type(bool check_trailing) noexcept {
669 auto max_len = peek_root_length();
670 auto json = peek_root_scalar("number");
671 // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
672 // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
673 // number: -0.<fraction>e-308.
674 uint8_t tmpbuf[1074+8+1+1];
675 tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
676 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
677 if(numberparsing::check_if_integer(json, max_len)) {
678 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
679 logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
680 return number_type::big_integer;
681 }
682 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
683 return NUMBER_ERROR;
684 }
685 auto answer = numberparsing::get_number_type(tmpbuf);
686 if (check_trailing && (answer.error() == SUCCESS) && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
687 return answer;
688}
689simdjson_inline simdjson_result<number> value_iterator::get_root_number(bool check_trailing) noexcept {
690 auto max_len = peek_root_length();
691 auto json = peek_root_scalar("number");
692 // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
693 // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
694 // number: -0.<fraction>e-308.
695 // NOTE: the current approach doesn't work for very big integer numbers containing more than 1074 digits.
696 uint8_t tmpbuf[1074+8+1+1];
697 tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
698 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
699 if(numberparsing::check_if_integer(json, max_len)) {
700 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
701 logger::log_error(*_json_iter, start_position(), depth(), "Found big integer");
702 return BIGINT_ERROR;
703 }
704 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters and not a big integer");
705 return NUMBER_ERROR;
706 }
707 number num;
708 error_code error = numberparsing::parse_number(tmpbuf, num);
709 if(error) { return error; }
710 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
711 advance_root_scalar("number");
712 return num;
713}
714simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_string(bool check_trailing, bool allow_replacement) noexcept {
715 return get_root_raw_json_string(check_trailing).unescape(json_iter(), allow_replacement);
716}
717template <typename string_type>
718simdjson_warn_unused simdjson_inline error_code value_iterator::get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept {
719 std::string_view content;
720 // Save the string buffer location so that we can restore it after get_string
721 auto saved_string_buf_loc = _json_iter->string_buf_loc();
722 auto err = get_root_string(check_trailing, allow_replacement).get(content);
723 if (err) { return err; }
724 internal::assign_utf8(receiver, content);
725 // Restore the string buffer location, effectively discarding any temporary string storage
726 _json_iter->string_buf_loc() = saved_string_buf_loc;
727 return SUCCESS;
728}
729simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> value_iterator::get_root_wobbly_string(bool check_trailing) noexcept {
730 return get_root_raw_json_string(check_trailing).unescape_wobbly(json_iter());
731}
732simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> value_iterator::get_root_raw_json_string(bool check_trailing) noexcept {
733 auto json = peek_scalar("string");
734 if (*json != '"') { return incorrect_type_error("Not a string"); }
735 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
736#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
737 if (_json_iter->allow_incomplete_json()) {
738 const size_t remaining_input_length = _json_iter->remaining_input_length(json);
739 const uint32_t max_len = remaining_input_length < peek_root_length() ? uint32_t(remaining_input_length) : peek_root_length();
740 if (!raw_json_string_is_quote_terminated(json, max_len)) {
741 return STRING_ERROR;
742 }
743 }
744#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
745 advance_scalar("string");
746 return raw_json_string(json+1);
747}
748simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64(bool check_trailing) noexcept {
749 auto max_len = peek_root_length();
750 auto json = peek_root_scalar("uint64");
751 uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
752 tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
753 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
754 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
755 return NUMBER_ERROR;
756 }
757 auto result = numberparsing::parse_unsigned(tmpbuf);
758 if(result.error() == SUCCESS) {
759 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
760 advance_root_scalar("uint64");
761 }
762 return result;
763}
764simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> value_iterator::get_root_uint64_in_string(bool check_trailing) noexcept {
765 auto max_len = peek_root_length();
766 auto json = peek_root_scalar("uint64");
767 uint8_t tmpbuf[20+1+1]{}; // <20 digits> is the longest possible unsigned integer
768 tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
769 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
770 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
771 return NUMBER_ERROR;
772 }
773 auto result = numberparsing::parse_unsigned_in_string(tmpbuf);
774 if(result.error() == SUCCESS) {
775 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
776 advance_root_scalar("uint64");
777 }
778 return result;
779}
780simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64(bool check_trailing) noexcept {
781 auto max_len = peek_root_length();
782 auto json = peek_root_scalar("int64");
783 uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
784 tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
785 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
786 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
787 return NUMBER_ERROR;
788 }
789
790 auto result = numberparsing::parse_integer(tmpbuf);
791 if(result.error() == SUCCESS) {
792 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
793 advance_root_scalar("int64");
794 }
795 return result;
796}
797simdjson_warn_unused simdjson_inline simdjson_result<int64_t> value_iterator::get_root_int64_in_string(bool check_trailing) noexcept {
798 auto max_len = peek_root_length();
799 auto json = peek_root_scalar("int64");
800 uint8_t tmpbuf[20+1+1]; // -<19 digits> is the longest possible integer
801 tmpbuf[20+1] = '\0'; // make sure that buffer is always null terminated.
802 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 20+1)) {
803 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 20 characters");
804 return NUMBER_ERROR;
805 }
806
807 auto result = numberparsing::parse_integer_in_string(tmpbuf);
808 if(result.error() == SUCCESS) {
809 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
810 advance_root_scalar("int64");
811 }
812 return result;
813}
814simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double(bool check_trailing) noexcept {
815 auto max_len = peek_root_length();
816 auto json = peek_root_scalar("double");
817 // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
818 // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
819 // number: -0.<fraction>e-308.
820 uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
821 tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
822 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
823 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
824 return NUMBER_ERROR;
825 }
826 auto result = numberparsing::parse_double(tmpbuf);
827 if(result.error() == SUCCESS) {
828 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
829 advance_root_scalar("double");
830 }
831 return result;
832}
833
834simdjson_warn_unused simdjson_inline simdjson_result<double> value_iterator::get_root_double_in_string(bool check_trailing) noexcept {
835 auto max_len = peek_root_length();
836 auto json = peek_root_scalar("double");
837 // Per https://www.exploringbinary.com/maximum-number-of-decimal-digits-in-binary-floating-point-numbers/,
838 // 1074 is the maximum number of significant fractional digits. Add 8 more digits for the biggest
839 // number: -0.<fraction>e-308.
840 uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
841 tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
842 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
843 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
844 return NUMBER_ERROR;
845 }
846 auto result = numberparsing::parse_double_in_string(tmpbuf);
847 if(result.error() == SUCCESS) {
848 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
849 advance_root_scalar("double");
850 }
851 return result;
852}
853
854simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float(bool check_trailing) noexcept {
855 auto max_len = peek_root_length();
856 auto json = peek_root_scalar("float");
857 // We use the same buffer size as get_root_double: the number of significant
858 // digits that matter is smaller for binary32, but the JSON document may still
859 // spell out a long number that we must parse (and round) faithfully.
860 uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
861 tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
862 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
863 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
864 return NUMBER_ERROR;
865 }
866 auto result = numberparsing::parse_float(tmpbuf);
867 if(result.error() == SUCCESS) {
868 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
869 advance_root_scalar("float");
870 }
871 return result;
872}
873
874simdjson_warn_unused simdjson_inline simdjson_result<float> value_iterator::get_root_float_in_string(bool check_trailing) noexcept {
875 auto max_len = peek_root_length();
876 auto json = peek_root_scalar("float");
877 uint8_t tmpbuf[1074+8+1+1]; // +1 for null termination.
878 tmpbuf[1074+8+1] = '\0'; // make sure that buffer is always null terminated.
879 if (!_json_iter->copy_to_buffer(json, max_len, tmpbuf, 1074+8+1)) {
880 logger::log_error(*_json_iter, start_position(), depth(), "Root number more than 1082 characters");
881 return NUMBER_ERROR;
882 }
883 auto result = numberparsing::parse_float_in_string(tmpbuf);
884 if(result.error() == SUCCESS) {
885 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
886 advance_root_scalar("float");
887 }
888 return result;
889}
890
891simdjson_warn_unused simdjson_inline simdjson_result<bool> value_iterator::get_root_bool(bool check_trailing) noexcept {
892 auto max_len = peek_root_length();
893 auto json = peek_root_scalar("bool");
894 // We have a boolean if we have either "true" or "false" and the next character is either
895 // a structural character or whitespace. We also check that the length is correct:
896 // "true" and "false" are 4 and 5 characters long, respectively.
897 bool value_true = (max_len >= 4 && !atomparsing::str4ncmp(json, "true") &&
898 (max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
899 bool value_false = (max_len >= 5 && !atomparsing::str4ncmp(json, "false") &&
900 (max_len == 5 || jsoncharutils::is_structural_or_whitespace(json[5])));
901 if(value_true == false && value_false == false) { return incorrect_type_error("Not a boolean"); }
902 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
903 advance_root_scalar("bool");
904 return value_true;
905}
906
907simdjson_inline simdjson_result<bool> value_iterator::is_root_null(bool check_trailing) noexcept {
908 auto max_len = peek_root_length();
909 auto json = peek_root_scalar("null");
910 bool result = (max_len >= 4 && !atomparsing::str4ncmp(json, "null") &&
911 (max_len == 4 || jsoncharutils::is_structural_or_whitespace(json[4])));
912 if(result) { // we have something that looks like a null.
913 if (check_trailing && !_json_iter->is_single_token()) { return TRAILING_CONTENT; }
914 advance_root_scalar("null");
915 } else if (json[0] == 'n') {
916 return incorrect_type_error("Not a null but starts with n");
917 }
918 return result;
919}
920
921simdjson_warn_unused simdjson_inline error_code value_iterator::skip_child() noexcept {
922 SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
923 SIMDJSON_ASSUME( _json_iter->_depth >= _depth );
924
925 return _json_iter->skip_child(depth());
926}
927
928simdjson_inline value_iterator value_iterator::child() const noexcept {
929 assert_at_child();
930 return { _json_iter, depth()+1, _json_iter->token.position() };
931}
932
933// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
934// relating depth and iterator depth, which is a desired effect. It does not happen if is_open is
935// marked non-inline.
936SIMDJSON_PUSH_DISABLE_WARNINGS
937SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
938simdjson_inline bool value_iterator::is_open() const noexcept {
939 return _json_iter->depth() >= depth();
940}
941SIMDJSON_POP_DISABLE_WARNINGS
942
943simdjson_inline bool value_iterator::at_end() const noexcept {
944 return _json_iter->at_end();
945}
946
947simdjson_inline bool value_iterator::at_start() const noexcept {
948 return _json_iter->token.position() == start_position();
949}
950
951simdjson_inline bool value_iterator::at_first_field() const noexcept {
952 SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
953 return _json_iter->token.position() == start_position() + 1;
954}
955
956simdjson_inline void value_iterator::abandon() noexcept {
957 _json_iter->abandon();
958}
959
960simdjson_warn_unused simdjson_inline depth_t value_iterator::depth() const noexcept {
961 return _depth;
962}
963simdjson_warn_unused simdjson_inline error_code value_iterator::error() const noexcept {
964 return _json_iter->error;
965}
966simdjson_warn_unused simdjson_inline uint8_t *&value_iterator::string_buf_loc() noexcept {
967 return _json_iter->string_buf_loc();
968}
969simdjson_warn_unused simdjson_inline const json_iterator &value_iterator::json_iter() const noexcept {
970 return *_json_iter;
971}
972simdjson_warn_unused simdjson_inline json_iterator &value_iterator::json_iter() noexcept {
973 return *_json_iter;
974}
975
976simdjson_inline const uint8_t *value_iterator::peek_start() const noexcept {
977 return _json_iter->peek(start_position());
978}
979simdjson_inline uint32_t value_iterator::peek_start_length() const noexcept {
980 return _json_iter->peek_length(start_position());
981}
982simdjson_inline uint32_t value_iterator::peek_root_length() const noexcept {
983 return _json_iter->peek_root_length(start_position());
984}
985
986simdjson_inline const uint8_t *value_iterator::peek_scalar(const char *type) noexcept {
987 logger::log_value(*_json_iter, start_position(), depth(), type);
988 // If we're not at the position anymore, we don't want to advance the cursor.
989 if (!is_at_start()) { return peek_start(); }
990
991 // Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
992 assert_at_start();
993 return _json_iter->peek();
994}
995
996simdjson_inline void value_iterator::advance_scalar(const char *type) noexcept {
997 logger::log_value(*_json_iter, start_position(), depth(), type);
998 // If we're not at the position anymore, we don't want to advance the cursor.
999 if (!is_at_start()) { return; }
1000
1001 // Get the JSON and advance the cursor, decreasing depth to signify that we have retrieved the value.
1002 assert_at_start();
1003 _json_iter->return_current_and_advance();
1004 _json_iter->ascend_to(depth()-1);
1005}
1006
1007simdjson_warn_unused simdjson_inline error_code value_iterator::start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept {
1008 logger::log_start_value(*_json_iter, start_position(), depth(), type);
1009 // If we're not at the position anymore, we don't want to advance the cursor.
1010 const uint8_t *json;
1011 if (!is_at_start()) {
1012#if SIMDJSON_DEVELOPMENT_CHECKS
1013 if (!is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
1014#endif
1015 json = peek_start();
1016 if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
1017 } else {
1018 assert_at_start();
1024 json = _json_iter->peek();
1025 if (*json != start_char) { return incorrect_type_error(incorrect_type_message); }
1026 _json_iter->return_current_and_advance();
1027 }
1028
1029
1030 return SUCCESS;
1031}
1032
1033
1034simdjson_inline const uint8_t *value_iterator::peek_root_scalar(const char *type) noexcept {
1035 logger::log_value(*_json_iter, start_position(), depth(), type);
1036 if (!is_at_start()) { return peek_start(); }
1037
1038 assert_at_root();
1039 return _json_iter->peek();
1040}
1041simdjson_inline const uint8_t *value_iterator::peek_non_root_scalar(const char *type) noexcept {
1042 logger::log_value(*_json_iter, start_position(), depth(), type);
1043 if (!is_at_start()) { return peek_start(); }
1044
1045 assert_at_non_root_start();
1046 return _json_iter->peek();
1047}
1048
1049simdjson_inline void value_iterator::advance_root_scalar(const char *type) noexcept {
1050 logger::log_value(*_json_iter, start_position(), depth(), type);
1051 if (!is_at_start()) { return; }
1052
1053 assert_at_root();
1054 _json_iter->return_current_and_advance();
1055 _json_iter->ascend_to(depth()-1);
1056}
1057simdjson_inline void value_iterator::advance_non_root_scalar(const char *type) noexcept {
1058 logger::log_value(*_json_iter, start_position(), depth(), type);
1059 if (!is_at_start()) { return; }
1060
1061 assert_at_non_root_start();
1062 _json_iter->return_current_and_advance();
1063 _json_iter->ascend_to(depth()-1);
1064}
1065
1066simdjson_inline error_code value_iterator::incorrect_type_error(const char *message) const noexcept {
1067 logger::log_error(*_json_iter, start_position(), depth(), message);
1068 return INCORRECT_TYPE;
1069}
1070
1071simdjson_inline bool value_iterator::is_at_start() const noexcept {
1072 return position() == start_position();
1073}
1074
1075simdjson_inline bool value_iterator::is_at_key() const noexcept {
1076 // Keys are at the same depth as the object.
1077 // Note here that we could be safer and check that we are within an object,
1078 // but we do not.
1079 //
1080 // As long as we are at the object's depth, in a valid document,
1081 // we will only ever be at { , : or the actual string key: ".
1082 return _depth == _json_iter->_depth && *_json_iter->peek() == '"';
1083}
1084
1085simdjson_inline bool value_iterator::is_at_iterator_start() const noexcept {
1086 // We can legitimately be either at the first value ([1]), or after the array if it's empty ([]).
1087 auto delta = position() - start_position();
1088 return delta == 1 || delta == 2;
1089}
1090
1091inline void value_iterator::assert_at_start() const noexcept {
1092 SIMDJSON_ASSUME( _json_iter->token._position == _start_position );
1093 SIMDJSON_ASSUME( _json_iter->_depth == _depth );
1094 SIMDJSON_ASSUME( _depth > 0 );
1095}
1096
1097inline void value_iterator::assert_at_container_start() const noexcept {
1098 SIMDJSON_ASSUME( _json_iter->token._position == _start_position + 1 );
1099 SIMDJSON_ASSUME( _json_iter->_depth == _depth );
1100 SIMDJSON_ASSUME( _depth > 0 );
1101}
1102
1103inline void value_iterator::assert_at_next() const noexcept {
1104 SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
1105 SIMDJSON_ASSUME( _json_iter->_depth == _depth );
1106 SIMDJSON_ASSUME( _depth > 0 );
1107}
1108
1109simdjson_inline void value_iterator::move_at_start() noexcept {
1110 _json_iter->_depth = _depth;
1111 _json_iter->token.set_position(_start_position);
1112}
1113
1114simdjson_inline void value_iterator::move_at_container_start() noexcept {
1115 _json_iter->_depth = _depth;
1116 _json_iter->token.set_position(_start_position + 1);
1117}
1118
1119simdjson_inline void value_iterator::reenter_at(token_position position, depth_t depth) noexcept {
1120 // Unlike reenter_child(), this does not require the live depth to be
1121 // exactly one level shallower than depth, nor does it validate against
1122 // the parser's per-depth container-start bookkeeping: neither holds in
1123 // general for a caller-supplied snapshot (see object_position). What
1124 // must still always hold, regardless of what was captured or how far
1125 // the live iterator has since moved, is that position and depth are
1126 // themselves sane values -- this is the same bound reenter_child()
1127 // itself applies unconditionally.
1128 SIMDJSON_ASSUME(position != nullptr);
1129 SIMDJSON_ASSUME(depth >= 1 && depth < INT32_MAX);
1130 _json_iter->_depth = depth;
1131 _json_iter->token.set_position(position);
1132}
1133
1134simdjson_inline simdjson_result<bool> value_iterator::reset_array() noexcept {
1135 if(error()) { return error(); }
1136 move_at_container_start();
1137 return started_array();
1138}
1139
1140simdjson_inline simdjson_result<bool> value_iterator::reset_object() noexcept {
1141 if(error()) { return error(); }
1142 move_at_container_start();
1143 return started_object();
1144}
1145
1146inline void value_iterator::assert_at_child() const noexcept {
1147 SIMDJSON_ASSUME( _json_iter->token._position > _start_position );
1148 SIMDJSON_ASSUME( _json_iter->_depth == _depth + 1 );
1149 SIMDJSON_ASSUME( _depth > 0 );
1150}
1151
1152inline void value_iterator::assert_at_root() const noexcept {
1153 assert_at_start();
1154 SIMDJSON_ASSUME( _depth == 1 );
1155}
1156
1157inline void value_iterator::assert_at_non_root_start() const noexcept {
1158 assert_at_start();
1159 SIMDJSON_ASSUME( _depth > 1 );
1160}
1161
1162inline void value_iterator::assert_is_valid() const noexcept {
1163 SIMDJSON_ASSUME( _json_iter != nullptr );
1164}
1165
1166simdjson_inline bool value_iterator::is_valid() const noexcept {
1167 return _json_iter != nullptr;
1168}
1169
1170simdjson_inline simdjson_result<json_type> value_iterator::type() const noexcept {
1171 switch (*peek_start()) {
1172 case '{':
1173 return json_type::object;
1174 case '[':
1175 return json_type::array;
1176 case '"':
1177 return json_type::string;
1178 case 'n':
1179 return json_type::null;
1180 case 't': case 'f':
1181 return json_type::boolean;
1182 case '-':
1183 case '0': case '1': case '2': case '3': case '4':
1184 case '5': case '6': case '7': case '8': case '9':
1185 return json_type::number;
1186 default:
1187 return json_type::unknown;
1188 }
1189}
1190
1191simdjson_inline token_position value_iterator::start_position() const noexcept {
1192 return _start_position;
1193}
1194
1195simdjson_inline token_position value_iterator::position() const noexcept {
1196 return _json_iter->position();
1197}
1198
1199simdjson_inline token_position value_iterator::end_position() const noexcept {
1200 return _json_iter->end_position();
1201}
1202
1203simdjson_inline token_position value_iterator::last_position() const noexcept {
1204 return _json_iter->last_position();
1205}
1206
1207simdjson_inline error_code value_iterator::report_error(error_code error, const char *message) noexcept {
1208 return _json_iter->report_error(error, message);
1209}
1210
1211} // namespace ondemand
1212} // namespace SIMDJSON_IMPLEMENTATION
1213} // namespace simdjson
1214
1215namespace simdjson {
1216
1217simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>::simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept
1218 : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>(value)) {}
1219simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>::simdjson_result(error_code error) noexcept
1220 : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator>(error) {}
1221
1222} // namespace simdjson
1223
1224#endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_INL_H
A string escaped per JSON rules, terminated with quote (").
simdjson_inline bool unsafe_is_equal(size_t length, std::string_view target) const noexcept
This compares the current instance to the std::string_view target: returns true if they are byte-by-b...
simdjson_warn_unused simdjson_inline simdjson_result< bool > started_array() noexcept
Start an array iteration, after the user has already checked and moved past the [.
simdjson_warn_unused simdjson_inline simdjson_result< bool > start_array() noexcept
Check for an opening [ and start an array iteration.
simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept
Checks whether an array could be started from the root.
simdjson_warn_unused simdjson_inline simdjson_result< bool > has_next_element() noexcept
Moves to the next element in an array.
simdjson_warn_unused simdjson_inline simdjson_result< bool > start_root_array() noexcept
Check for an opening [ and start an array iteration while at the root.
simdjson_warn_unused simdjson_inline simdjson_result< bool > started_root_array() noexcept
Start an array iteration from the root, after the user has already checked and moved past the [.
simdjson_warn_unused simdjson_inline value_iterator child() const noexcept
Get a child value iterator.
simdjson_warn_unused simdjson_inline error_code field_key_with_length(raw_json_string &key, std::size_t &len) noexcept
Get the current field's key together with its raw byte length.
simdjson_warn_unused simdjson_inline simdjson_result< bool > started_object() noexcept
Start an object iteration after the user has already checked and moved past the {.
simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept
Checks whether an object could be started from the root.
simdjson_warn_unused simdjson_inline simdjson_result< bool > start_object() noexcept
Start an object iteration.
simdjson_warn_unused simdjson_inline simdjson_result< bool > find_field_unordered_raw(const std::string_view key) noexcept
Find the field with the given key without regard to order, and without unescaping.
simdjson_warn_unused simdjson_inline simdjson_result< bool > has_next_field() noexcept
Moves to the next field in an object.
simdjson_warn_unused simdjson_inline simdjson_result< raw_json_string > field_key() noexcept
Get the current field's key.
simdjson_warn_unused simdjson_inline simdjson_result< bool > started_root_object() noexcept
Start an object iteration from the root, after the user has already checked and moved past the {.
simdjson_warn_unused simdjson_inline error_code field_value() noexcept
Pass the : in the field and move to its value.
simdjson_warn_unused simdjson_inline simdjson_result< bool > find_field_raw(const std::string_view key) noexcept
Find the next field with the given key, without unescaping.
simdjson_warn_unused simdjson_inline simdjson_result< bool > start_root_object() noexcept
Start an object iteration from the root.
@ object
A JSON object ( { "a": 1, "b" 2, ... } )
@ number
A JSON number ( 1 or -2.3 or 4.5e6 ...)
@ string
A JSON string ( "a" or "hello world\n" ...)
int32_t depth_t
Represents the depth of a JSON value (number of nested arrays/objects).
Definition base.h:18
The top level simdjson namespace, containing everything the library provides.
Definition base.h:8
error_code
All possible errors returned by simdjson.
Definition error.h:19
@ INCORRECT_TYPE
JSON element has a different type than user expected.
Definition error.h:37
@ OUT_OF_ORDER_ITERATION
tried to iterate an array or object out of order (checked when SIMDJSON_DEVELOPMENT_CHECKS=1)
Definition error.h:46
@ TAPE_ERROR
Something went wrong, this is a generic error. Fatal/unrecoverable error.
Definition error.h:23
@ STRING_ERROR
Problem while parsing a string.
Definition error.h:25
@ SUCCESS
No error.
Definition error.h:20
@ TRAILING_CONTENT
Unexpected trailing content in the JSON input.
Definition error.h:51
@ INCOMPLETE_ARRAY_OR_OBJECT
The document ends early. Fatal/unrecoverable error.
Definition error.h:48
@ NUMBER_ERROR
Problem while parsing a number.
Definition error.h:29
@ BIGINT_ERROR
The integer value exceeds 64 bits.
Definition error.h:30
The result of a simdjson operation that could fail.
Definition error.h:281