1namespace simdjson {
2namespace SIMDJSON_IMPLEMENTATION {
3namespace ondemand {
4
5class document;
6class object;
7class array;
8class value;
9class raw_json_string;
10class parser;
11
12/**
13 * Iterates through a single JSON value at a particular depth.
14 *
15 * Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects
16 * the caller to call the right ones.
17 *
18 * @private This is not intended for external use.
19 */
20class value_iterator {
21protected:
22 /** The underlying JSON iterator */
23 json_iterator *_json_iter{};
24 /** The depth of this value */
25 depth_t _depth{};
26 /**
27 * The starting token index for this value
28 */
29 token_position _start_position{};
30
31public:
32 simdjson_inline value_iterator() noexcept = default;
33
34 /**
35 * Denote that we're starting a document.
36 */
37 simdjson_inline void start_document() noexcept;
38
39 /**
40 * Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object.
41 *
42 * Optimized for scalars.
43 */
44 simdjson_warn_unused simdjson_inline error_code skip_child() noexcept;
45
46 /**
47 * Tell whether the iterator is at the EOF mark
48 */
49 simdjson_inline bool at_end() const noexcept;
50
51 /**
52 * Tell whether the iterator is at the start of the value
53 */
54 simdjson_inline bool at_start() const noexcept;
55
56 /**
57 * Tell whether the value is open--if the value has not been used, or the array/object is still open.
58 */
59 simdjson_inline bool is_open() const noexcept;
60
61 /**
62 * Tell whether the value is at an object's first field (just after the {).
63 */
64 simdjson_inline bool at_first_field() const noexcept;
65
66 /**
67 * Abandon all iteration.
68 */
69 simdjson_inline void abandon() noexcept;
70
71 /**
72 * Get the child value as a value_iterator.
73 */
74 simdjson_inline value_iterator child_value() const noexcept;
75
76 /**
77 * Get the depth of this value.
78 */
79 simdjson_inline int32_t depth() const noexcept;
80
81 /**
82 * Get the JSON type of this value.
83 *
84 * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse".
85 */
86 simdjson_inline simdjson_result<json_type> type() const noexcept;
87
88 /**
89 * @addtogroup object Object iteration
90 *
91 * Methods to iterate and find object fields. These methods generally *assume* the value is
92 * actually an object; the caller is responsible for keeping track of that fact.
93 *
94 * @{
95 */
96
97 /**
98 * Start an object iteration.
99 *
100 * @returns Whether the object had any fields (returns false for empty).
101 * @error INCORRECT_TYPE if there is no opening {
102 */
103 simdjson_warn_unused simdjson_inline simdjson_result<bool> start_object() noexcept;
104 /**
105 * Start an object iteration from the root.
106 *
107 * @returns Whether the object had any fields (returns false for empty).
108 * @error INCORRECT_TYPE if there is no opening {
109 * @error TAPE_ERROR if there is no matching } at end of document
110 */
111 simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_object() noexcept;
112
113 /**
114 * Start an object iteration after the user has already checked and moved past the {.
115 *
116 * Does not move the iterator unless the object is empty ({}).
117 *
118 * @returns Whether the object had any fields (returns false for empty).
119 * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
120 * array or object is incomplete).
121 */
122 simdjson_warn_unused simdjson_inline simdjson_result<bool> started_object() noexcept;
123 /**
124 * Start an object iteration from the root, after the user has already checked and moved past the {.
125 *
126 * Does not move the iterator unless the object is empty ({}).
127 *
128 * @returns Whether the object had any fields (returns false for empty).
129 * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
130 * array or object is incomplete).
131 */
132 simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_object() noexcept;
133
134 /**
135 * Moves to the next field in an object.
136 *
137 * Looks for , and }. If } is found, the object is finished and the iterator advances past it.
138 * Otherwise, it advances to the next value.
139 *
140 * @return whether there is another field in the object.
141 * @error TAPE_ERROR If there is a comma missing between fields.
142 * @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value.
143 */
144 simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_field() noexcept;
145
146 /**
147 * Get the current field's key.
148 */
149 simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> field_key() noexcept;
150
151 /**
152 * Pass the : in the field and move to its value.
153 */
154 simdjson_warn_unused simdjson_inline error_code field_value() noexcept;
155
156 /**
157 * Find the next field with the given key.
158 *
159 * Assumes you have called next_field() or otherwise matched the previous value.
160 *
161 * This means the iterator must be sitting at the next key:
162 *
163 * ```
164 * { "a": 1, "b": 2 }
165 * ^
166 * ```
167 *
168 * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
169 * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
170 * fail to match some keys with escapes (\u, \n, etc.).
171 */
172 simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept;
173
174 /**
175 * Find the next field with the given key, *without* unescaping. This assumes object order: it
176 * will not find the field if it was already passed when looking for some *other* field.
177 *
178 * Assumes you have called next_field() or otherwise matched the previous value.
179 *
180 * This means the iterator must be sitting at the next key:
181 *
182 * ```
183 * { "a": 1, "b": 2 }
184 * ^
185 * ```
186 *
187 * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
188 * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
189 * fail to match some keys with escapes (\u, \n, etc.).
190 */
191 simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_raw(const std::string_view key) noexcept;
192
193 /**
194 * Find the field with the given key without regard to order, and *without* unescaping.
195 *
196 * This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning.
197 *
198 * Assumes you have called next_field() or otherwise matched the previous value.
199 *
200 * This means the iterator must be sitting at the next key:
201 *
202 * ```
203 * { "a": 1, "b": 2 }
204 * ^
205 * ```
206 *
207 * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to
208 * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may
209 * fail to match some keys with escapes (\u, \n, etc.).
210 */
211 simdjson_warn_unused simdjson_inline simdjson_result<bool> find_field_unordered_raw(const std::string_view key) noexcept;
212
213 /** @} */
214
215 /**
216 * @addtogroup array Array iteration
217 * Methods to iterate over array elements. These methods generally *assume* the value is actually
218 * an object; the caller is responsible for keeping track of that fact.
219 * @{
220 */
221
222 /**
223 * Check for an opening [ and start an array iteration.
224 *
225 * @returns Whether the array had any elements (returns false for empty).
226 * @error INCORRECT_TYPE If there is no [.
227 */
228 simdjson_warn_unused simdjson_inline simdjson_result<bool> start_array() noexcept;
229 /**
230 * Check for an opening [ and start an array iteration while at the root.
231 *
232 * @returns Whether the array had any elements (returns false for empty).
233 * @error INCORRECT_TYPE If there is no [.
234 * @error TAPE_ERROR if there is no matching ] at end of document
235 */
236 simdjson_warn_unused simdjson_inline simdjson_result<bool> start_root_array() noexcept;
237
238 /**
239 * Start an array iteration, after the user has already checked and moved past the [.
240 *
241 * Does not move the iterator unless the array is empty ([]).
242 *
243 * @returns Whether the array had any elements (returns false for empty).
244 * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
245 * array or object is incomplete).
246 */
247 simdjson_warn_unused simdjson_inline simdjson_result<bool> started_array() noexcept;
248 /**
249 * Start an array iteration from the root, after the user has already checked and moved past the [.
250 *
251 * Does not move the iterator unless the array is empty ([]).
252 *
253 * @returns Whether the array had any elements (returns false for empty).
254 * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent*
255 * array or object is incomplete).
256 */
257 simdjson_warn_unused simdjson_inline simdjson_result<bool> started_root_array() noexcept;
258
259 /**
260 * Moves to the next element in an array.
261 *
262 * Looks for , and ]. If ] is found, the array is finished and the iterator advances past it.
263 * Otherwise, it advances to the next value.
264 *
265 * @return Whether there is another element in the array.
266 * @error TAPE_ERROR If there is a comma missing between elements.
267 */
268 simdjson_warn_unused simdjson_inline simdjson_result<bool> has_next_element() noexcept;
269
270 /**
271 * Get a child value iterator.
272 */
273 simdjson_warn_unused simdjson_inline value_iterator child() const noexcept;
274
275 /** @} */
276
277 /**
278 * @defgroup scalar Scalar values
279 * @addtogroup scalar
280 * @{
281 */
282
283 simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_string(bool allow_replacement) noexcept;
284 simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_wobbly_string() noexcept;
285 simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_raw_json_string() noexcept;
286 simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64() noexcept;
287 simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_uint64_in_string() noexcept;
288 simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64() noexcept;
289 simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_int64_in_string() noexcept;
290 simdjson_warn_unused simdjson_inline simdjson_result<double> get_double() noexcept;
291 simdjson_warn_unused simdjson_inline simdjson_result<double> get_double_in_string() noexcept;
292 simdjson_warn_unused simdjson_inline simdjson_result<bool> get_bool() noexcept;
293 simdjson_warn_unused simdjson_inline simdjson_result<bool> is_null() noexcept;
294 simdjson_warn_unused simdjson_inline bool is_negative() noexcept;
295 simdjson_warn_unused simdjson_inline simdjson_result<bool> is_integer() noexcept;
296 simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_number_type() noexcept;
297 simdjson_warn_unused simdjson_inline simdjson_result<number> get_number() noexcept;
298
299 simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_string(bool check_trailing, bool allow_replacement) noexcept;
300 simdjson_warn_unused simdjson_inline simdjson_result<std::string_view> get_root_wobbly_string(bool check_trailing) noexcept;
301 simdjson_warn_unused simdjson_inline simdjson_result<raw_json_string> get_root_raw_json_string(bool check_trailing) noexcept;
302 simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64(bool check_trailing) noexcept;
303 simdjson_warn_unused simdjson_inline simdjson_result<uint64_t> get_root_uint64_in_string(bool check_trailing) noexcept;
304 simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64(bool check_trailing) noexcept;
305 simdjson_warn_unused simdjson_inline simdjson_result<int64_t> get_root_int64_in_string(bool check_trailing) noexcept;
306 simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double(bool check_trailing) noexcept;
307 simdjson_warn_unused simdjson_inline simdjson_result<double> get_root_double_in_string(bool check_trailing) noexcept;
308 simdjson_warn_unused simdjson_inline simdjson_result<bool> get_root_bool(bool check_trailing) noexcept;
309 simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept;
310 simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_integer(bool check_trailing) noexcept;
311 simdjson_warn_unused simdjson_inline simdjson_result<number_type> get_root_number_type(bool check_trailing) noexcept;
312 simdjson_warn_unused simdjson_inline simdjson_result<number> get_root_number(bool check_trailing) noexcept;
313 simdjson_warn_unused simdjson_inline simdjson_result<bool> is_root_null(bool check_trailing) noexcept;
314
315 simdjson_inline error_code error() const noexcept;
316 simdjson_inline uint8_t *&string_buf_loc() noexcept;
317 simdjson_inline const json_iterator &json_iter() const noexcept;
318 simdjson_inline json_iterator &json_iter() noexcept;
319
320 simdjson_inline void assert_is_valid() const noexcept;
321 simdjson_inline bool is_valid() const noexcept;
322
323 /** @} */
324protected:
325 /**
326 * Restarts an array iteration.
327 * @returns Whether the array has any elements (returns false for empty).
328 */
329 simdjson_inline simdjson_result<bool> reset_array() noexcept;
330 /**
331 * Restarts an object iteration.
332 * @returns Whether the object has any fields (returns false for empty).
333 */
334 simdjson_inline simdjson_result<bool> reset_object() noexcept;
335 /**
336 * move_at_start(): moves us so that we are pointing at the beginning of
337 * the container. It updates the index so that at_start() is true and it
338 * syncs the depth. The user can then create a new container instance.
339 *
340 * Usage: used with value::count_elements().
341 **/
342 simdjson_inline void move_at_start() noexcept;
343
344 /**
345 * move_at_container_start(): moves us so that we are pointing at the beginning of
346 * the container so that assert_at_container_start() passes.
347 *
348 * Usage: used with reset_array() and reset_object().
349 **/
350 simdjson_inline void move_at_container_start() noexcept;
351 /* Useful for debugging and logging purposes. */
352 inline std::string to_string() const noexcept;
353 simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept;
354
355 simdjson_inline simdjson_result<bool> parse_null(const uint8_t *json) const noexcept;
356 simdjson_inline simdjson_result<bool> parse_bool(const uint8_t *json) const noexcept;
357 simdjson_inline const uint8_t *peek_start() const noexcept;
358 simdjson_inline uint32_t peek_start_length() const noexcept;
359
360 /**
361 * The general idea of the advance_... methods and the peek_* methods
362 * is that you first peek and check that you have desired type. If you do,
363 * and only if you do, then you advance.
364 *
365 * We used to unconditionally advance. But this made reasoning about our
366 * current state difficult.
367 * Suppose you always advance. Look at the 'value' matching the key
368 * "shadowable" in the following example...
369 *
370 * ({"globals":{"a":{"shadowable":[}}}})
371 *
372 * If the user thinks it is a Boolean and asks for it, then we check the '[',
373 * decide it is not a Boolean, but still move into the next character ('}'). Now
374 * we are left pointing at '}' right after a '['. And we have not yet reported
375 * an error, only that we do not have a Boolean.
376 *
377 * If, instead, you just stand your ground until it is content that you know, then
378 * you will only even move beyond the '[' if the user tells you that you have an
379 * array. So you will be at the '}' character inside the array and, hopefully, you
380 * will then catch the error because an array cannot start with '}', but the code
381 * processing Boolean values does not know this.
382 *
383 * So the contract is: first call 'peek_...' and then call 'advance_...' only
384 * if you have determined that it is a type you can handle.
385 *
386 * Unfortunately, it makes the code more verbose, longer and maybe more error prone.
387 */
388
389 simdjson_inline void advance_scalar(const char *type) noexcept;
390 simdjson_inline void advance_root_scalar(const char *type) noexcept;
391 simdjson_inline void advance_non_root_scalar(const char *type) noexcept;
392
393 simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept;
394 simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept;
395 simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept;
396
397
398 simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept;
399 simdjson_inline error_code end_container() noexcept;
400
401 /**
402 * Advance to a place expecting a value (increasing depth).
403 *
404 * @return The current token (the one left behind).
405 * @error TAPE_ERROR If the document ended early.
406 */
407 simdjson_inline simdjson_result<const uint8_t *> advance_to_value() noexcept;
408
409 simdjson_inline error_code incorrect_type_error(const char *message) const noexcept;
410 simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept;
411
412 simdjson_inline bool is_at_start() const noexcept;
413 /**
414 * is_at_iterator_start() returns true on an array or object after it has just been
415 * created, whether the instance is empty or not.
416 *
417 * Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS)
418 */
419 simdjson_inline bool is_at_iterator_start() const noexcept;
420
421 /**
422 * Assuming that we are within an object, this returns true if we
423 * are pointing at a key.
424 *
425 * Usage: the skip_child() method should never be used while we are pointing
426 * at a key inside an object.
427 */
428 simdjson_inline bool is_at_key() const noexcept;
429
430 inline void assert_at_start() const noexcept;
431 inline void assert_at_container_start() const noexcept;
432 inline void assert_at_root() const noexcept;
433 inline void assert_at_child() const noexcept;
434 inline void assert_at_next() const noexcept;
435 inline void assert_at_non_root_start() const noexcept;
436
437 /** Get the starting position of this value */
438 simdjson_inline token_position start_position() const noexcept;
439
440 /** @copydoc error_code json_iterator::position() const noexcept; */
441 simdjson_inline token_position position() const noexcept;
442 /** @copydoc error_code json_iterator::end_position() const noexcept; */
443 simdjson_inline token_position last_position() const noexcept;
444 /** @copydoc error_code json_iterator::end_position() const noexcept; */
445 simdjson_inline token_position end_position() const noexcept;
446 /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */
447 simdjson_inline error_code report_error(error_code error, const char *message) noexcept;
448
449 friend class document;
450 friend class object;
451 friend class array;
452 friend class value;
453}; // value_iterator
454
455} // namespace ondemand
456} // namespace SIMDJSON_IMPLEMENTATION
457} // namespace simdjson
458
459namespace simdjson {
460
461template<>
462struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator> {
463public:
464 simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept; ///< @private
465 simdjson_inline simdjson_result(error_code error) noexcept; ///< @private
466 simdjson_inline simdjson_result() noexcept = default;
467};
468
469} // namespace simdjson
470