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