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