simdjson 5.0.2
Ridiculously Fast JSON
Loading...
Searching...
No Matches
json_iterator-inl.h
1#ifndef SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
2
3#ifndef SIMDJSON_CONDITIONAL_INCLUDE
4#define SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
5#include "simdjson/internal/dom_parser_implementation.h"
6#include "simdjson/generic/ondemand/base.h"
7#include "simdjson/generic/ondemand/json_iterator.h"
8#include "simdjson/generic/ondemand/parser.h"
9#include "simdjson/generic/ondemand/raw_json_string.h"
10#include "simdjson/generic/ondemand/logger-inl.h"
11#include "simdjson/generic/ondemand/parser-inl.h"
12#include "simdjson/generic/ondemand/token_iterator-inl.h"
13#endif // SIMDJSON_CONDITIONAL_INCLUDE
14
15namespace simdjson {
16namespace SIMDJSON_IMPLEMENTATION {
17namespace ondemand {
18
19simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
20 : token(std::forward<token_iterator>(other.token)),
21 parser{other.parser},
22 _string_buf_loc{other._string_buf_loc},
23 error{other.error},
24 _depth{other._depth},
25 _root{other._root},
26 _streaming{other._streaming}
27#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
28 , _allow_incomplete_json{other._allow_incomplete_json}
29#endif
30{
31 other.parser = nullptr;
32}
33simdjson_inline json_iterator &json_iterator::operator=(json_iterator &&other) noexcept {
34 token = other.token;
35 parser = other.parser;
36 _string_buf_loc = other._string_buf_loc;
37 error = other.error;
38 _depth = other._depth;
39 _root = other._root;
40 _streaming = other._streaming;
41#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
42 _allow_incomplete_json = other._allow_incomplete_json;
43#endif
44 other.parser = nullptr;
45 return *this;
46}
47
48simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser) noexcept
49 : token(buf, &_parser->implementation->structural_indexes[0]),
50 parser{_parser},
51 _string_buf_loc{parser->string_buf.get()},
52 _depth{1},
53 _root{parser->implementation->structural_indexes.get()},
54 _streaming{false}
55
56{
57 logger::log_headers();
58#if SIMDJSON_CHECK_EOF
59 assert_more_tokens();
60#endif
61}
62
63#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
64simdjson_inline json_iterator::json_iterator(const uint8_t *buf, ondemand::parser *_parser, bool streaming) noexcept
65 : token(buf, &_parser->implementation->structural_indexes[0]),
66 parser{_parser},
67 _string_buf_loc{parser->string_buf.get()},
68 _depth{1},
69 _root{parser->implementation->structural_indexes.get()},
70 _streaming{streaming},
71 _allow_incomplete_json{true}
72
73{
74 logger::log_headers();
75#if SIMDJSON_CHECK_EOF
76 assert_more_tokens();
77#endif
78}
79#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
80
81inline void json_iterator::rewind() noexcept {
82 token.set_position( root_position() );
83 logger::log_headers(); // We start again
84 _string_buf_loc = parser->string_buf.get();
85 _depth = 1;
86}
87
88inline bool json_iterator::balanced() const noexcept {
89 token_iterator ti(token);
90 int32_t count{0};
91 ti.set_position( root_position() );
92 while(ti.peek() <= peek_last()) {
93 switch (*ti.return_current_and_advance())
94 {
95 case '[': case '{':
96 count++;
97 break;
98 case ']': case '}':
99 count--;
100 break;
101 default:
102 break;
103 }
104 }
105 return count == 0;
106}
107
108
109// GCC 7 warns when the first line of this function is inlined away into oblivion due to the caller
110// relating depth and parent_depth, which is a desired effect. The warning does not show up if the
111// skip_child() function is not marked inline).
112SIMDJSON_PUSH_DISABLE_WARNINGS
113SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
114simdjson_warn_unused simdjson_inline error_code json_iterator::skip_child(depth_t parent_depth) noexcept {
115 if (depth() <= parent_depth) { return SUCCESS; }
116 switch (*return_current_and_advance()) {
117 // TODO consider whether matching braces is a requirement: if non-matching braces indicates
118 // *missing* braces, then future lookups are not in the object/arrays they think they are,
119 // violating the rule "validate enough structure that the user can be confident they are
120 // looking at the right values."
121 // PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
122
123 // For the first open array/object in a value, we've already incremented depth, so keep it the same
124 // We never stop at colon, but if we did, it wouldn't affect depth
125 case '[': case '{': case ':':
126 logger::log_start_value(*this, "skip");
127 break;
128 // If there is a comma, we have just finished a value in an array/object, and need to get back in
129 case ',':
130 logger::log_value(*this, "skip");
131 break;
132 // ] or } means we just finished a value and need to jump out of the array/object
133 case ']': case '}':
134 logger::log_end_value(*this, "skip");
135 _depth--;
136 if (depth() <= parent_depth) { return SUCCESS; }
137#if SIMDJSON_CHECK_EOF
138 // If there are no more tokens, the parent is incomplete.
139 if (at_end()) { return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "Missing [ or { at start"); }
140#endif // SIMDJSON_CHECK_EOF
141 break;
142 case '"':
143 // At the end, peek() would read the sentinel, which points into the padding.
144 if(!at_end() && *peek() == ':') {
145 // We are at a key!!!
146 // This might happen if you just started an object and you skip it immediately.
147 // Performance note: it would be nice to get rid of this check as it is somewhat
148 // expensive.
149 // https://github.com/simdjson/simdjson/issues/1742
150 logger::log_value(*this, "key");
151 return_current_and_advance(); // eat up the ':'
152 break; // important!!!
153 }
154 simdjson_fallthrough;
155 // Anything else must be a scalar value
156 default:
157 // For the first scalar, we will have incremented depth already, so we decrement it here.
158 logger::log_value(*this, "skip");
159 _depth--;
160 if (depth() <= parent_depth) { return SUCCESS; }
161 break;
162 }
163
164 // Now that we've considered the first value, we only increment/decrement for arrays/objects
165 while (position() < end_position()) {
166 switch (*return_current_and_advance()) {
167 case '[': case '{':
168 logger::log_start_value(*this, "skip");
169 _depth++;
170 break;
171 // TODO consider whether matching braces is a requirement: if non-matching braces indicates
172 // *missing* braces, then future lookups are not in the object/arrays they think they are,
173 // violating the rule "validate enough structure that the user can be confident they are
174 // looking at the right values."
175 // PERF TODO we can eliminate the switch here with a lookup of how much to add to depth
176 case ']': case '}':
177 logger::log_end_value(*this, "skip");
178 _depth--;
179 if (depth() <= parent_depth) { return SUCCESS; }
180 break;
181 default:
182 logger::log_value(*this, "skip", "");
183 break;
184 }
185 }
186
187 return report_error(INCOMPLETE_ARRAY_OR_OBJECT, "not enough close braces");
188}
189
190SIMDJSON_POP_DISABLE_WARNINGS
191
192simdjson_inline bool json_iterator::at_root() const noexcept {
193 return position() == root_position();
194}
195
196simdjson_inline bool json_iterator::is_single_token() const noexcept {
197 return parser->implementation->n_structural_indexes == 1;
198}
199
200simdjson_inline bool json_iterator::streaming() const noexcept {
201 return _streaming;
202}
203
204#ifdef SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
205simdjson_inline bool json_iterator::allow_incomplete_json() const noexcept {
206 return _allow_incomplete_json;
207}
208
209simdjson_inline size_t json_iterator::remaining_input_length(const uint8_t *json) const noexcept {
210 const uint8_t *end = token.buf + parser->_document_len;
211 return json < end ? size_t(end - json) : 0;
212}
213#endif // SIMDJSON_EXPERIMENTAL_ALLOW_INCOMPLETE_JSON
214
215simdjson_inline token_position json_iterator::root_position() const noexcept {
216 return _root;
217}
218
219simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
220 SIMDJSON_ASSUME( _depth == 1 );
221}
222
223simdjson_inline void json_iterator::assert_at_root() const noexcept {
224 SIMDJSON_ASSUME( _depth == 1 );
225#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
226 // Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
227 // has side effects that will be discarded.
228 SIMDJSON_ASSUME( token.position() == _root );
229#endif
230}
231
232simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
233 assert_valid_position(token._position + required_tokens - 1);
234}
235
236simdjson_inline void json_iterator::assert_valid_position(token_position position) const noexcept {
237 (void)position; // Suppress unused parameter warning
238#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
239 SIMDJSON_ASSUME( position >= &parser->implementation->structural_indexes[0] );
240 SIMDJSON_ASSUME( position < &parser->implementation->structural_indexes[parser->implementation->n_structural_indexes] );
241#endif
242}
243
244simdjson_inline bool json_iterator::at_end() const noexcept {
245 return position() == end_position();
246}
247simdjson_inline token_position json_iterator::end_position() const noexcept {
248 uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
249 return &parser->implementation->structural_indexes[n_structural_indexes];
250}
251
252inline std::string json_iterator::to_string() const noexcept {
253 if( !is_alive() ) { return "dead json_iterator instance"; }
254 const char * current_structural = reinterpret_cast<const char *>(token.peek());
255 return std::string("json_iterator [ depth : ") + std::to_string(_depth)
256 + std::string(", structural : '") + std::string(current_structural,1)
257 + std::string("', offset : ") + std::to_string(token.current_offset())
258 + std::string("', error : ") + error_message(error)
259 + std::string(" ]");
260}
261
262inline simdjson_result<const char *> json_iterator::current_location() const noexcept {
263 if (!is_alive()) { // Unrecoverable error
264 if (!at_root()) {
265 return reinterpret_cast<const char *>(token.peek(-1));
266 } else {
267 return reinterpret_cast<const char *>(token.peek());
268 }
269 }
270 if (at_end()) {
271 return OUT_OF_BOUNDS;
272 }
273 return reinterpret_cast<const char *>(token.peek());
274}
275
276simdjson_inline bool json_iterator::is_alive() const noexcept {
277 return parser;
278}
279
280simdjson_inline void json_iterator::abandon() noexcept {
281 parser = nullptr;
282 _depth = 0;
283}
284
285simdjson_inline const uint8_t *json_iterator::return_current_and_advance() noexcept {
286#if SIMDJSON_CHECK_EOF
287 assert_more_tokens();
288#endif // SIMDJSON_CHECK_EOF
289 return token.return_current_and_advance();
290}
291
292simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
293 // deliberately done without safety guard:
294 return token.peek();
295}
296
297simdjson_inline const uint8_t *json_iterator::peek(int32_t delta) const noexcept {
298#if SIMDJSON_CHECK_EOF
299 assert_more_tokens(delta+1);
300#endif // SIMDJSON_CHECK_EOF
301 return token.peek(delta);
302}
303
304simdjson_inline uint32_t json_iterator::peek_length(int32_t delta) const noexcept {
305#if SIMDJSON_CHECK_EOF
306 assert_more_tokens(delta+1);
307#endif // #if SIMDJSON_CHECK_EOF
308 return token.peek_length(delta);
309}
310
311simdjson_inline const uint8_t *json_iterator::peek(token_position position) const noexcept {
312 // todo: currently we require end-of-string buffering, but the following
313 // assert_valid_position should be turned on if/when we lift that condition.
314 // assert_valid_position(position);
315 // This is almost surely related to SIMDJSON_CHECK_EOF but given that SIMDJSON_CHECK_EOF
316 // is ON by default, we have no choice but to disable it for real with a comment.
317 return token.peek(position);
318}
319
320simdjson_inline uint32_t json_iterator::peek_length(token_position position) const noexcept {
321#if SIMDJSON_CHECK_EOF
322 assert_valid_position(position);
323#endif // SIMDJSON_CHECK_EOF
324 return token.peek_length(position);
325}
326simdjson_inline uint32_t json_iterator::peek_root_length(token_position position) const noexcept {
327#if SIMDJSON_CHECK_EOF
328 assert_valid_position(position);
329#endif // SIMDJSON_CHECK_EOF
330 return token.peek_root_length(position);
331}
332
333simdjson_inline token_position json_iterator::last_position() const noexcept {
334 // The following line fails under some compilers...
335 // SIMDJSON_ASSUME(parser->implementation->n_structural_indexes > 0);
336 // since it has side-effects.
337 uint32_t n_structural_indexes{parser->implementation->n_structural_indexes};
338 SIMDJSON_ASSUME(n_structural_indexes > 0);
339 return &parser->implementation->structural_indexes[n_structural_indexes - 1];
340}
341simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
342 return token.peek(last_position());
343}
344
345simdjson_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept {
346 SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1);
347 SIMDJSON_ASSUME(_depth == parent_depth + 1);
348 _depth = parent_depth;
349}
350
351simdjson_inline void json_iterator::descend_to(depth_t child_depth) noexcept {
352 SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
353 SIMDJSON_ASSUME(_depth == child_depth - 1);
354 _depth = child_depth;
355}
356
357simdjson_inline depth_t json_iterator::depth() const noexcept {
358 return _depth;
359}
360
361simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
362 return _string_buf_loc;
363}
364
365simdjson_warn_unused simdjson_inline error_code json_iterator::report_error(error_code _error, const char *message) noexcept {
366 SIMDJSON_ASSUME(_error != SUCCESS && _error != UNINITIALIZED && _error != INCORRECT_TYPE && _error != NO_SUCH_FIELD);
367 logger::log_error(*this, message);
368 error = _error;
369 return error;
370}
371
372simdjson_inline token_position json_iterator::position() const noexcept {
373 return token.position();
374}
375
376simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
377#if SIMDJSON_DEVELOPMENT_CHECKS
378 auto result = parser->unescape(in, _string_buf_loc, allow_replacement);
379#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
380 // Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
381 // has side effects that will be discarded.
382 SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
383#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
384 return result;
385#else
386 return parser->unescape(in, _string_buf_loc, allow_replacement);
387#endif
388}
389
390simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
391#if SIMDJSON_DEVELOPMENT_CHECKS
392 auto result = parser->unescape_wobbly(in, _string_buf_loc);
393#if !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
394 // Under Visual Studio, the next SIMDJSON_ASSUME fails with: the argument
395 // has side effects that will be discarded.
396 SIMDJSON_ASSUME(!parser->string_buffer_overflow(_string_buf_loc));
397#endif // !defined(SIMDJSON_VISUAL_STUDIO) && !defined(SIMDJSON_CLANG_VISUAL_STUDIO)
398 return result;
399#else
400 return parser->unescape_wobbly(in, _string_buf_loc);
401#endif
402}
403
404simdjson_inline void json_iterator::reenter_child(token_position position, depth_t child_depth) noexcept {
405 SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX);
406 SIMDJSON_ASSUME(_depth == child_depth - 1);
407#if SIMDJSON_DEVELOPMENT_CHECKS
408#ifndef SIMDJSON_CLANG_VISUAL_STUDIO
409 SIMDJSON_ASSUME(size_t(child_depth) < parser->max_depth());
410 SIMDJSON_ASSUME(position >= parser->start_positions[child_depth]);
411#endif
412#endif
413 token.set_position(position);
414 _depth = child_depth;
415}
416
417simdjson_warn_unused simdjson_inline error_code json_iterator::consume_character(char c) noexcept {
418 if (*peek() == c) {
419 return_current_and_advance();
420 return SUCCESS;
421 }
422 return TAPE_ERROR;
423}
424
425#if SIMDJSON_DEVELOPMENT_CHECKS
426
427simdjson_inline token_position json_iterator::start_position(depth_t depth) const noexcept {
428 SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
429 return size_t(depth) < parser->max_depth() ? parser->start_positions[depth] : 0;
430}
431
432simdjson_inline void json_iterator::set_start_position(depth_t depth, token_position position) noexcept {
433 SIMDJSON_ASSUME(size_t(depth) < parser->max_depth());
434 if(size_t(depth) < parser->max_depth()) { parser->start_positions[depth] = position; }
435}
436
437#endif
438
439
440simdjson_warn_unused simdjson_inline error_code json_iterator::optional_error(error_code _error, const char *message) noexcept {
441 SIMDJSON_ASSUME(_error == INCORRECT_TYPE || _error == NO_SUCH_FIELD);
442 logger::log_error(*this, message);
443 return _error;
444}
445
446
447simdjson_warn_unused simdjson_inline bool json_iterator::copy_to_buffer(const uint8_t *json, uint32_t max_len, uint8_t *tmpbuf, size_t N) noexcept {
448 // This function is not expected to be called in performance-sensitive settings.
449 // Let us guard against silly cases:
450 if((N < max_len) || (N == 0)) { return false; }
451 // Copy to the buffer.
452 std::memcpy(tmpbuf, json, max_len);
453 if(N > max_len) { // We pad whatever remains with ' '.
454 std::memset(tmpbuf + max_len, ' ', N - max_len);
455 }
456 return true;
457}
458
459} // namespace ondemand
460} // namespace SIMDJSON_IMPLEMENTATION
461} // namespace simdjson
462
463namespace simdjson {
464
465simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_iterator>::simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::json_iterator &&value) noexcept
466 : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::json_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::json_iterator>(value)) {}
467simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::json_iterator>::simdjson_result(error_code error) noexcept
468 : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::json_iterator>(error) {}
469
470} // namespace simdjson
471
472#endif // SIMDJSON_GENERIC_ONDEMAND_JSON_ITERATOR_INL_H
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
const char * error_message(error_code error) noexcept
It is the convention throughout the code that the macro SIMDJSON_DEVELOPMENT_CHECKS determines whethe...
Definition error-inl.h:25
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_BOUNDS
Attempted to access location outside of document.
Definition error.h:50
@ TAPE_ERROR
Something went wrong, this is a generic error. Fatal/unrecoverable error.
Definition error.h:23
@ NO_SUCH_FIELD
JSON field not found in object.
Definition error.h:40
@ SUCCESS
No error.
Definition error.h:20
@ INCOMPLETE_ARRAY_OR_OBJECT
The document ends early. Fatal/unrecoverable error.
Definition error.h:48
@ UNINITIALIZED
unknown error, or uninitialized document
Definition error.h:32