1 | namespace simdjson { |
2 | namespace SIMDJSON_IMPLEMENTATION { |
3 | namespace ondemand { |
4 | |
5 | class document; |
6 | class object; |
7 | class array; |
8 | class value; |
9 | class raw_json_string; |
10 | class 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 | */ |
20 | class value_iterator { |
21 | protected: |
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 | |
31 | public: |
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 | /** @} */ |
324 | protected: |
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 | |
459 | namespace simdjson { |
460 | |
461 | template<> |
462 | struct simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator> : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::value_iterator> { |
463 | public: |
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 | |