| 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 | |