simdjson  3.1.6
Ridiculously Fast JSON
json_iterator-inl.h
1 namespace simdjson {
2 namespace SIMDJSON_IMPLEMENTATION {
3 namespace ondemand {
4 
5 simdjson_inline json_iterator::json_iterator(json_iterator &&other) noexcept
6  : token(std::forward<token_iterator>(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 }
16 simdjson_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 
28 simdjson_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 
43 inline 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 
50 inline 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).
74 SIMDJSON_PUSH_DISABLE_WARNINGS
75 SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
76 simdjson_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(*this, "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(*this, "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(*this, "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(*this, "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(*this, "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(*this, "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(*this, "skip");
139  _depth--;
140  if (depth() <= parent_depth) { return SUCCESS; }
141  break;
142  default:
143  logger::log_value(*this, "skip", "");
144  break;
145  }
146  }
147 
148  return report_error(TAPE_ERROR, "not enough close braces");
149 }
150 
151 SIMDJSON_POP_DISABLE_WARNINGS
152 
153 simdjson_inline bool json_iterator::at_root() const noexcept {
154  return position() == root_position();
155 }
156 
157 simdjson_inline bool json_iterator::is_single_token() const noexcept {
158  return parser->implementation->n_structural_indexes == 1;
159 }
160 
161 simdjson_inline bool json_iterator::streaming() const noexcept {
162  return _streaming;
163 }
164 
165 simdjson_inline token_position json_iterator::root_position() const noexcept {
166  return _root;
167 }
168 
169 simdjson_inline void json_iterator::assert_at_document_depth() const noexcept {
170  SIMDJSON_ASSUME( _depth == 1 );
171 }
172 
173 simdjson_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 
182 simdjson_inline void json_iterator::assert_more_tokens(uint32_t required_tokens) const noexcept {
183  assert_valid_position(token._position + required_tokens - 1);
184 }
185 
186 simdjson_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 
193 simdjson_inline bool json_iterator::at_end() const noexcept {
194  return position() == end_position();
195 }
196 simdjson_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 
201 inline 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(_depth)
205  + std::string(", structural : '") + std::string(current_structural,1)
206  + std::string("', offset : ") + std::to_string(token.current_offset())
207  + std::string("', error : ") + error_message(error)
208  + std::string(" ]");
209 }
210 
211 inline 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(-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 
225 simdjson_inline bool json_iterator::is_alive() const noexcept {
226  return parser;
227 }
228 
229 simdjson_inline void json_iterator::abandon() noexcept {
230  parser = nullptr;
231  _depth = 0;
232 }
233 
234 simdjson_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 
241 simdjson_inline const uint8_t *json_iterator::unsafe_pointer() const noexcept {
242  // deliberately done without safety guard:
243  return token.peek(0);
244 }
245 
246 simdjson_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 
253 simdjson_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 
260 simdjson_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 
269 simdjson_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 
276 simdjson_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 }
284 simdjson_inline const uint8_t *json_iterator::peek_last() const noexcept {
285  return token.peek(last_position());
286 }
287 
288 simdjson_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 
294 simdjson_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 
300 simdjson_inline depth_t json_iterator::depth() const noexcept {
301  return _depth;
302 }
303 
304 simdjson_inline uint8_t *&json_iterator::string_buf_loc() noexcept {
305  return _string_buf_loc;
306 }
307 
308 simdjson_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(*this, message);
311  error = _error;
312  return error;
313 }
314 
315 simdjson_inline token_position json_iterator::position() const noexcept {
316  return token.position();
317 }
318 
319 simdjson_inline simdjson_result<std::string_view> json_iterator::unescape(raw_json_string in, bool allow_replacement) noexcept {
320  return parser->unescape(in, _string_buf_loc, allow_replacement);
321 }
322 
323 simdjson_inline simdjson_result<std::string_view> json_iterator::unescape_wobbly(raw_json_string in) noexcept {
324  return parser->unescape_wobbly(in, _string_buf_loc);
325 }
326 
327 simdjson_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 
342 simdjson_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 
347 simdjson_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 
355 simdjson_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(*this, message);
358  return _error;
359 }
360 
361 template<int N>
362 simdjson_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(tmpbuf, json, max_len);
373  tmpbuf[max_len] = ' ';
374  return true;
375 }
376 
377 } // namespace ondemand
378 } // namespace SIMDJSON_IMPLEMENTATION
379 } // namespace simdjson
380 
381 namespace simdjson {
382 
383 simdjson_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>(value)) {}
385 simdjson_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
int32_t depth_t
Represents the depth of a JSON value (number of nested arrays/objects).
Definition: ondemand.h:11
We want to support argument-dependent lookup (ADL).
const char * error_message(error_code error) noexcept
Get the error message for the given error code.
Definition: error-inl.h:21
error_code
All possible errors returned by simdjson.
Definition: error.h:17
@ INCORRECT_TYPE
JSON element has a different type than user expected.
Definition: error.h:34
@ OUT_OF_BOUNDS
Attempted to access location outside of document.
Definition: error.h:47
@ TAPE_ERROR
Something went wrong, this is a generic error.
Definition: error.h:21
@ NO_SUCH_FIELD
JSON field not found in object.
Definition: error.h:37
@ SUCCESS
No error.
Definition: error.h:18
@ INCOMPLETE_ARRAY_OR_OBJECT
The document ends early.
Definition: error.h:45
@ UNINITIALIZED
unknown error, or uninitialized document
Definition: error.h:29