#ifndef SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H #ifndef SIMDJSON_CONDITIONAL_INCLUDE #define SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H #include "simdjson/generic/ondemand/base.h" #include "simdjson/generic/implementation_simdjson_result_base.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace ondemand { /** * Iterates through a single JSON value at a particular depth. * * Does not keep track of the type of value: provides methods for objects, arrays and scalars and expects * the caller to call the right ones. * * @private This is not intended for external use. */ class value_iterator { protected: /** The underlying JSON iterator */ json_iterator *_json_iter{}; /** The depth of this value */ depth_t _depth{}; /** * The starting token index for this value */ token_position _start_position{}; public: simdjson_inline value_iterator() noexcept = default; /** * Denote that we're starting a document. */ simdjson_inline void start_document() noexcept; /** * Skips a non-iterated or partially-iterated JSON value, whether it is a scalar, array or object. * * Optimized for scalars. */ simdjson_warn_unused simdjson_inline error_code skip_child() noexcept; /** * Tell whether the iterator is at the EOF mark */ simdjson_inline bool at_end() const noexcept; /** * Tell whether the iterator is at the start of the value */ simdjson_inline bool at_start() const noexcept; /** * Tell whether the value is open--if the value has not been used, or the array/object is still open. */ simdjson_inline bool is_open() const noexcept; /** * Tell whether the value is at an object's first field (just after the {). */ simdjson_inline bool at_first_field() const noexcept; /** * Abandon all iteration. */ simdjson_inline void abandon() noexcept; /** * Get the child value as a value_iterator. */ simdjson_inline value_iterator child_value() const noexcept; /** * Get the depth of this value. */ simdjson_inline int32_t depth() const noexcept; /** * Get the JSON type of this value. * * @error TAPE_ERROR when the JSON value is a bad token like "}" "," or "alse". */ simdjson_inline simdjson_result type() const noexcept; /** * @addtogroup object Object iteration * * Methods to iterate and find object fields. These methods generally *assume* the value is * actually an object; the caller is responsible for keeping track of that fact. * * @{ */ /** * Start an object iteration. * * @returns Whether the object had any fields (returns false for empty). * @error INCORRECT_TYPE if there is no opening { */ simdjson_warn_unused simdjson_inline simdjson_result start_object() noexcept; /** * Start an object iteration from the root. * * @returns Whether the object had any fields (returns false for empty). * @error INCORRECT_TYPE if there is no opening { * @error TAPE_ERROR if there is no matching } at end of document */ simdjson_warn_unused simdjson_inline simdjson_result start_root_object() noexcept; /** * Checks whether an object could be started from the root. May be called by start_root_object. * * @returns SUCCESS if it is possible to safely start an object from the root (document level). * @error INCORRECT_TYPE if there is no opening { * @error TAPE_ERROR if there is no matching } at end of document */ simdjson_warn_unused simdjson_inline error_code check_root_object() noexcept; /** * Start an object iteration after the user has already checked and moved past the {. * * Does not move the iterator unless the object is empty ({}). * * @returns Whether the object had any fields (returns false for empty). * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* * array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that * invalidates the document. */ simdjson_warn_unused simdjson_inline simdjson_result started_object() noexcept; /** * Start an object iteration from the root, after the user has already checked and moved past the {. * * Does not move the iterator unless the object is empty ({}). * * @returns Whether the object had any fields (returns false for empty). * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* * array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that * invalidates the document. */ simdjson_warn_unused simdjson_inline simdjson_result started_root_object() noexcept; /** * Moves to the next field in an object. * * Looks for , and }. If } is found, the object is finished and the iterator advances past it. * Otherwise, it advances to the next value. * * @return whether there is another field in the object. * @error TAPE_ERROR If there is a comma missing between fields. * @error TAPE_ERROR If there is a comma, but not enough tokens remaining to have a key, :, and value. */ simdjson_warn_unused simdjson_inline simdjson_result has_next_field() noexcept; /** * Get the current field's key. */ simdjson_warn_unused simdjson_inline simdjson_result field_key() noexcept; /** * Pass the : in the field and move to its value. */ simdjson_warn_unused simdjson_inline error_code field_value() noexcept; /** * Find the next field with the given key. * * Assumes you have called next_field() or otherwise matched the previous value. * * This means the iterator must be sitting at the next key: * * ``` * { "a": 1, "b": 2 } * ^ * ``` * * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may * fail to match some keys with escapes (\u, \n, etc.). */ simdjson_warn_unused simdjson_inline error_code find_field(const std::string_view key) noexcept; /** * Find the next field with the given key, *without* unescaping. This assumes object order: it * will not find the field if it was already passed when looking for some *other* field. * * Assumes you have called next_field() or otherwise matched the previous value. * * This means the iterator must be sitting at the next key: * * ``` * { "a": 1, "b": 2 } * ^ * ``` * * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may * fail to match some keys with escapes (\u, \n, etc.). */ simdjson_warn_unused simdjson_inline simdjson_result find_field_raw(const std::string_view key) noexcept; /** * Find the field with the given key without regard to order, and *without* unescaping. * * This is an unordered object lookup: if the field is not found initially, it will cycle around and scan from the beginning. * * Assumes you have called next_field() or otherwise matched the previous value. * * This means the iterator must be sitting at the next key: * * ``` * { "a": 1, "b": 2 } * ^ * ``` * * Key is *raw JSON,* meaning it will be matched against the verbatim JSON without attempting to * unescape it. This works well for typical ASCII and UTF-8 keys (almost all of them), but may * fail to match some keys with escapes (\u, \n, etc.). */ simdjson_warn_unused simdjson_inline simdjson_result find_field_unordered_raw(const std::string_view key) noexcept; /** @} */ /** * @addtogroup array Array iteration * Methods to iterate over array elements. These methods generally *assume* the value is actually * an object; the caller is responsible for keeping track of that fact. * @{ */ /** * Check for an opening [ and start an array iteration. * * @returns Whether the array had any elements (returns false for empty). * @error INCORRECT_TYPE If there is no [. */ simdjson_warn_unused simdjson_inline simdjson_result start_array() noexcept; /** * Check for an opening [ and start an array iteration while at the root. * * @returns Whether the array had any elements (returns false for empty). * @error INCORRECT_TYPE If there is no [. * @error TAPE_ERROR if there is no matching ] at end of document */ simdjson_warn_unused simdjson_inline simdjson_result start_root_array() noexcept; /** * Checks whether an array could be started from the root. May be called by start_root_array. * * @returns SUCCESS if it is possible to safely start an array from the root (document level). * @error INCORRECT_TYPE If there is no [. * @error TAPE_ERROR if there is no matching ] at end of document */ simdjson_warn_unused simdjson_inline error_code check_root_array() noexcept; /** * Start an array iteration, after the user has already checked and moved past the [. * * Does not move the iterator unless the array is empty ([]). * * @returns Whether the array had any elements (returns false for empty). * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* * array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that * invalidates the document. */ simdjson_warn_unused simdjson_inline simdjson_result started_array() noexcept; /** * Start an array iteration from the root, after the user has already checked and moved past the [. * * Does not move the iterator unless the array is empty ([]). * * @returns Whether the array had any elements (returns false for empty). * @error INCOMPLETE_ARRAY_OR_OBJECT If there are no more tokens (implying the *parent* * array or object is incomplete). An INCOMPLETE_ARRAY_OR_OBJECT is an unrecoverable error that * invalidates the document. */ simdjson_warn_unused simdjson_inline simdjson_result started_root_array() noexcept; /** * Moves to the next element in an array. * * Looks for , and ]. If ] is found, the array is finished and the iterator advances past it. * Otherwise, it advances to the next value. * * @return Whether there is another element in the array. * @error TAPE_ERROR If there is a comma missing between elements. */ simdjson_warn_unused simdjson_inline simdjson_result has_next_element() noexcept; /** * Get a child value iterator. */ simdjson_warn_unused simdjson_inline value_iterator child() const noexcept; /** @} */ /** * @defgroup scalar Scalar values * @addtogroup scalar * @{ */ simdjson_warn_unused simdjson_inline simdjson_result get_string(bool allow_replacement) noexcept; template simdjson_warn_unused simdjson_inline error_code get_string(string_type& receiver, bool allow_replacement) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_wobbly_string() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_raw_json_string() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_uint64() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_uint64_in_string() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_int64() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_int64_in_string() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_double() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_double_in_string() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_bool() noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_null() noexcept; simdjson_warn_unused simdjson_inline bool is_negative() noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_integer() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_number_type() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_number() noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_string(bool check_trailing, bool allow_replacement) noexcept; template simdjson_warn_unused simdjson_inline error_code get_root_string(string_type& receiver, bool check_trailing, bool allow_replacement) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_wobbly_string(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_raw_json_string(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_uint64_in_string(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_int64(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_int64_in_string(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_double(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_double_in_string(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_bool(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline bool is_root_negative() noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_integer(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_number_type(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result get_root_number(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline simdjson_result is_root_null(bool check_trailing) noexcept; simdjson_warn_unused simdjson_inline error_code error() const noexcept; simdjson_inline uint8_t *&string_buf_loc() noexcept; simdjson_inline const json_iterator &json_iter() const noexcept; simdjson_inline json_iterator &json_iter() noexcept; simdjson_inline void assert_is_valid() const noexcept; simdjson_inline bool is_valid() const noexcept; /** @} */ protected: /** * Restarts an array iteration. * @returns Whether the array has any elements (returns false for empty). */ simdjson_inline simdjson_result reset_array() noexcept; /** * Restarts an object iteration. * @returns Whether the object has any fields (returns false for empty). */ simdjson_inline simdjson_result reset_object() noexcept; /** * move_at_start(): moves us so that we are pointing at the beginning of * the container. It updates the index so that at_start() is true and it * syncs the depth. The user can then create a new container instance. * * Usage: used with value::count_elements(). **/ simdjson_inline void move_at_start() noexcept; /** * move_at_container_start(): moves us so that we are pointing at the beginning of * the container so that assert_at_container_start() passes. * * Usage: used with reset_array() and reset_object(). **/ simdjson_inline void move_at_container_start() noexcept; /* Useful for debugging and logging purposes. */ inline std::string to_string() const noexcept; simdjson_inline value_iterator(json_iterator *json_iter, depth_t depth, token_position start_index) noexcept; simdjson_inline simdjson_result parse_null(const uint8_t *json) const noexcept; simdjson_inline simdjson_result parse_bool(const uint8_t *json) const noexcept; simdjson_inline const uint8_t *peek_start() const noexcept; simdjson_inline uint32_t peek_start_length() const noexcept; simdjson_inline uint32_t peek_root_length() const noexcept; /** * The general idea of the advance_... methods and the peek_* methods * is that you first peek and check that you have desired type. If you do, * and only if you do, then you advance. * * We used to unconditionally advance. But this made reasoning about our * current state difficult. * Suppose you always advance. Look at the 'value' matching the key * "shadowable" in the following example... * * ({"globals":{"a":{"shadowable":[}}}}) * * If the user thinks it is a Boolean and asks for it, then we check the '[', * decide it is not a Boolean, but still move into the next character ('}'). Now * we are left pointing at '}' right after a '['. And we have not yet reported * an error, only that we do not have a Boolean. * * If, instead, you just stand your ground until it is content that you know, then * you will only even move beyond the '[' if the user tells you that you have an * array. So you will be at the '}' character inside the array and, hopefully, you * will then catch the error because an array cannot start with '}', but the code * processing Boolean values does not know this. * * So the contract is: first call 'peek_...' and then call 'advance_...' only * if you have determined that it is a type you can handle. * * Unfortunately, it makes the code more verbose, longer and maybe more error prone. */ simdjson_inline void advance_scalar(const char *type) noexcept; simdjson_inline void advance_root_scalar(const char *type) noexcept; simdjson_inline void advance_non_root_scalar(const char *type) noexcept; simdjson_inline const uint8_t *peek_scalar(const char *type) noexcept; simdjson_inline const uint8_t *peek_root_scalar(const char *type) noexcept; simdjson_inline const uint8_t *peek_non_root_scalar(const char *type) noexcept; simdjson_warn_unused simdjson_inline error_code start_container(uint8_t start_char, const char *incorrect_type_message, const char *type) noexcept; simdjson_warn_unused simdjson_inline error_code end_container() noexcept; /** * Advance to a place expecting a value (increasing depth). * * @return The current token (the one left behind). * @error TAPE_ERROR If the document ended early. */ simdjson_inline simdjson_result advance_to_value() noexcept; simdjson_warn_unused simdjson_inline error_code incorrect_type_error(const char *message) const noexcept; simdjson_warn_unused simdjson_inline error_code error_unless_more_tokens(uint32_t tokens=1) const noexcept; simdjson_inline bool is_at_start() const noexcept; /** * is_at_iterator_start() returns true on an array or object after it has just been * created, whether the instance is empty or not. * * Usage: used by array::begin() in debug mode (SIMDJSON_DEVELOPMENT_CHECKS) */ simdjson_inline bool is_at_iterator_start() const noexcept; /** * Assuming that we are within an object, this returns true if we * are pointing at a key. * * Usage: the skip_child() method should never be used while we are pointing * at a key inside an object. */ simdjson_inline bool is_at_key() const noexcept; inline void assert_at_start() const noexcept; inline void assert_at_container_start() const noexcept; inline void assert_at_root() const noexcept; inline void assert_at_child() const noexcept; inline void assert_at_next() const noexcept; inline void assert_at_non_root_start() const noexcept; /** Get the starting position of this value */ simdjson_inline token_position start_position() const noexcept; /** @copydoc error_code json_iterator::position() const noexcept; */ simdjson_inline token_position position() const noexcept; /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position last_position() const noexcept; /** @copydoc error_code json_iterator::end_position() const noexcept; */ simdjson_inline token_position end_position() const noexcept; /** @copydoc error_code json_iterator::report_error(error_code error, const char *message) noexcept; */ simdjson_warn_unused simdjson_inline error_code report_error(error_code error, const char *message) noexcept; friend class document; friend class object; friend class object_iterator; friend class array; friend class value; friend class field; }; // value_iterator } // namespace ondemand } // namespace SIMDJSON_IMPLEMENTATION } // namespace simdjson namespace simdjson { template<> struct simdjson_result : public SIMDJSON_IMPLEMENTATION::implementation_simdjson_result_base { public: simdjson_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::value_iterator &&value) noexcept; ///< @private simdjson_inline simdjson_result(error_code error) noexcept; ///< @private simdjson_inline simdjson_result() noexcept = default; }; } // namespace simdjson #endif // SIMDJSON_GENERIC_ONDEMAND_VALUE_ITERATOR_H