diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h index e9a66995..289e1046 100644 --- a/include/simdjson/generic/ondemand/document-inl.h +++ b/include/simdjson/generic/ondemand/document-inl.h @@ -12,43 +12,46 @@ simdjson_really_inline document document::start(json_iterator &&iter) noexcept { return document(std::forward(iter)); } -simdjson_really_inline value document::as_value() noexcept { - return as_value_iterator(); +simdjson_really_inline value_iterator document::resume_value_iterator() noexcept { + return value_iterator(&iter, 1, iter.root_checkpoint()); } -simdjson_really_inline value_iterator document::as_value_iterator() noexcept { +simdjson_really_inline value_iterator document::get_root_value_iterator() noexcept { iter.assert_at_root(); - return value_iterator(&iter, 1, iter.root_checkpoint()); + return resume_value_iterator(); } -simdjson_really_inline value_iterator document::as_non_root_value_iterator() noexcept { - return value_iterator(&iter, 1, iter.root_checkpoint()); +simdjson_really_inline value document::resume_value() noexcept { + return resume_value_iterator(); +} +simdjson_really_inline value document::get_root_value() noexcept { + return get_root_value_iterator(); } simdjson_really_inline simdjson_result document::get_array() & noexcept { - return as_value().get_array(); + return get_root_value().get_array(); } simdjson_really_inline simdjson_result document::get_object() & noexcept { - return as_value().get_object(); + return get_root_value().get_object(); } simdjson_really_inline simdjson_result document::get_uint64() noexcept { - return as_value_iterator().require_root_uint64(); + return get_root_value_iterator().require_root_uint64(); } simdjson_really_inline simdjson_result document::get_int64() noexcept { - return as_value_iterator().require_root_int64(); + return get_root_value_iterator().require_root_int64(); } simdjson_really_inline simdjson_result document::get_double() noexcept { - return as_value_iterator().require_root_double(); + return get_root_value_iterator().require_root_double(); } simdjson_really_inline simdjson_result document::get_string() & noexcept { - return as_value().get_string(); + return get_root_value().get_string(); } simdjson_really_inline simdjson_result document::get_raw_json_string() & noexcept { - return as_value().get_raw_json_string(); + return get_root_value().get_raw_json_string(); } simdjson_really_inline simdjson_result document::get_bool() noexcept { - return as_value_iterator().require_root_bool(); + return get_root_value_iterator().require_root_bool(); } simdjson_really_inline bool document::is_null() noexcept { - return as_value_iterator().is_root_null(); + return get_root_value_iterator().is_root_null(); } template<> simdjson_really_inline simdjson_result document::get() & noexcept { return get_array(); } @@ -89,21 +92,24 @@ simdjson_really_inline simdjson_result document::begin() & noexc simdjson_really_inline simdjson_result document::end() & noexcept { return {}; } + +simdjson_really_inline simdjson_result document::find_field(std::string_view key) & noexcept { + return resume_value().find_field(key); +} +simdjson_really_inline simdjson_result document::find_field(const char *key) & noexcept { + return resume_value().find_field(key); +} +simdjson_really_inline simdjson_result document::find_field_unordered(std::string_view key) & noexcept { + return resume_value().find_field_unordered(key); +} +simdjson_really_inline simdjson_result document::find_field_unordered(const char *key) & noexcept { + return resume_value().find_field_unordered(key); +} simdjson_really_inline simdjson_result document::operator[](std::string_view key) & noexcept { - if (iter.at_root()) { - return get_object()[key]; - } else { - // If we're not at the root, this is not the first key we've grabbed - return object::resume(as_non_root_value_iterator())[key]; - } + return resume_value()[key]; } simdjson_really_inline simdjson_result document::operator[](const char *key) & noexcept { - if (iter.at_root()) { - return get_object()[key]; - } else { - // If we're not at the root, this is not the first key we've grabbed - return object::resume(as_non_root_value_iterator())[key]; - } + return resume_value()[key]; } } // namespace ondemand @@ -136,6 +142,14 @@ simdjson_really_inline simdjson_result simdjson_result::end() & noexcept { return {}; } +simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} simdjson_really_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; @@ -144,6 +158,14 @@ simdjson_really_inline simdjson_result if (error()) { return error(); } return first[key]; } +simdjson_really_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} simdjson_really_inline simdjson_result simdjson_result::get_array() & noexcept { if (error()) { return error(); } return first.get_array(); diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h index e83c2bf6..01c4ffeb 100644 --- a/include/simdjson/generic/ondemand/document.h +++ b/include/simdjson/generic/ondemand/document.h @@ -206,30 +206,64 @@ public: simdjson_really_inline simdjson_result end() & noexcept; /** - * Look up a field by name on an object. + * Look up a field by name on an object (order-sensitive). * - * Important notes: + * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the + * JSON `{ "x": 1, "y": 2, "z": 3 }`: * - * * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * * **Once Only:** You may only look up a single field on a document. To look up multiple fields, - * use `.get_object()` or cast to `object`. + * ```c++ + * simdjson::builtin::ondemand::parser parser; + * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); + * double z = obj.find_field("z"); + * double y = obj.find_field("y"); + * double x = obj.find_field("x"); + * ``` + * + * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. + * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. * * @param key The key to look up. - * @returns The value of the field, NO_SUCH_FIELD if the field is not in the object, or - * INCORRECT_TYPE if the JSON value is not an array. + * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. */ + simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(const char *key) & noexcept; + + /** + * Look up a field by name on an object, without regard to key order. + * + * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies + * and often appears negligible. It starts out normally, starting out at the last field; but if + * the field is not found, it scans from the beginning of the object to see if it missed it. That + * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object + * in question is large. The fact that the extra code is there also bumps the executable size. + * + * It is the default, however, because it would be highly surprising (and hard to debug) if the + * default behavior failed to look up a field just because it was in the wrong order--and many + * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. + * + * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the + * field wasn't there when they aren't). + * + * @param key The key to look up. + * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. + */ + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](const char *key) & noexcept; protected: simdjson_really_inline document(ondemand::json_iterator &&iter) noexcept; simdjson_really_inline const uint8_t *text(uint32_t idx) const noexcept; - simdjson_really_inline value as_value() noexcept; - simdjson_really_inline value_iterator as_value_iterator() noexcept; - simdjson_really_inline value_iterator as_non_root_value_iterator() noexcept; + simdjson_really_inline value_iterator resume_value_iterator() noexcept; + simdjson_really_inline value_iterator get_root_value_iterator() noexcept; + simdjson_really_inline value resume_value() noexcept; + simdjson_really_inline value get_root_value() noexcept; static simdjson_really_inline document start(ondemand::json_iterator &&iter) noexcept; // @@ -290,8 +324,12 @@ public: simdjson_really_inline simdjson_result begin() & noexcept; simdjson_really_inline simdjson_result end() & noexcept; + simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_really_inline simdjson_result find_field(const char *key) & noexcept; simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_really_inline simdjson_result operator[](const char *key) & noexcept; + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_really_inline simdjson_result find_field_unordered(const char *key) & noexcept; }; } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/json_iterator-inl.h b/include/simdjson/generic/ondemand/json_iterator-inl.h index 30c2e4dd..0d4caf5c 100644 --- a/include/simdjson/generic/ondemand/json_iterator-inl.h +++ b/include/simdjson/generic/ondemand/json_iterator-inl.h @@ -132,11 +132,13 @@ simdjson_really_inline uint32_t json_iterator::peek_length(int32_t delta) const } simdjson_really_inline void json_iterator::ascend_to(depth_t parent_depth) noexcept { + SIMDJSON_ASSUME(parent_depth >= 0 && parent_depth < INT32_MAX - 1); SIMDJSON_ASSUME(_depth == parent_depth + 1); _depth = parent_depth; } simdjson_really_inline void json_iterator::descend_to(depth_t child_depth) noexcept { + SIMDJSON_ASSUME(child_depth >= 1 && child_depth < INT32_MAX); SIMDJSON_ASSUME(_depth == child_depth - 1); _depth = child_depth; } diff --git a/include/simdjson/generic/ondemand/object-inl.h b/include/simdjson/generic/ondemand/object-inl.h index 9aac2dc1..9dcb0324 100644 --- a/include/simdjson/generic/ondemand/object-inl.h +++ b/include/simdjson/generic/ondemand/object-inl.h @@ -2,55 +2,31 @@ namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace ondemand { -// -// ### Live States -// -// While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is -// always SUCCESS: -// -// - Start: This is the state when the object is first found and the iterator is just past the {. -// In this state, at_start == true. -// - Next: After we hand a scalar value to the user, or an array/object which they then fully -// iterate over, the iterator is at the , or } before the next value. In this state, -// depth == iter.depth, at_start == false, and error == SUCCESS. -// - Unfinished Business: When we hand an array/object to the user which they do not fully -// iterate over, we need to finish that iteration by skipping child values until we reach the -// Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS. -// -// ## Error States -// -// In error states, we will yield exactly one more value before stopping. iter.depth == depth -// and at_start is always false. We decrement after yielding the error, moving to the Finished -// state. -// -// - Chained Error: When the object iterator is part of an error chain--for example, in -// `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an -// object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and -// iter.depth == depth, and at_start == false. We decrement depth when we yield the error. -// - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields, -// we flag that as an error and treat it exactly the same as a Chained Error. In this state, -// error == TAPE_ERROR, iter.depth == depth, and at_start == false. -// -// Errors that occur while reading a field to give to the user (such as when the key is not a -// string or the field is missing a colon) are yielded immediately. Depth is then decremented, -// moving to the Finished state without transitioning through an Error state at all. -// -// ## Terminal State -// -// The terminal state has iter.depth < depth. at_start is always false. -// -// - Finished: When we have reached a }, we are finished. We signal this by decrementing depth. -// In this state, iter.depth < depth, at_start == false, and error == SUCCESS. -// - +simdjson_really_inline simdjson_result object::find_field_unordered(const std::string_view key) & noexcept { + bool has_value; + SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); + if (!has_value) { return NO_SUCH_FIELD; } + return value(iter.child()); +} +simdjson_really_inline simdjson_result object::find_field_unordered(const std::string_view key) && noexcept { + bool has_value; + SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); + if (!has_value) { return NO_SUCH_FIELD; } + return value(iter.child()); +} simdjson_really_inline simdjson_result object::operator[](const std::string_view key) & noexcept { + return find_field_unordered(key); +} +simdjson_really_inline simdjson_result object::operator[](const std::string_view key) && noexcept { + return find_field_unordered(key); +} +simdjson_really_inline simdjson_result object::find_field(const std::string_view key) & noexcept { bool has_value; SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); if (!has_value) { return NO_SUCH_FIELD; } return value(iter.child()); } - -simdjson_really_inline simdjson_result object::operator[](const std::string_view key) && noexcept { +simdjson_really_inline simdjson_result object::find_field(const std::string_view key) && noexcept { bool has_value; SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); if (!has_value) { return NO_SUCH_FIELD; } @@ -108,6 +84,14 @@ simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} simdjson_really_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; @@ -116,5 +100,13 @@ simdjson_really_inline simdjson_result if (error()) { return error(); } return std::forward(first)[key]; } +simdjson_really_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/object.h b/include/simdjson/generic/ondemand/object.h index 019e2ae1..1063ed9a 100644 --- a/include/simdjson/generic/ondemand/object.h +++ b/include/simdjson/generic/ondemand/object.h @@ -20,29 +20,54 @@ public: simdjson_really_inline object_iterator end() noexcept; /** - * Look up a field by name on an object. + * Look up a field by name on an object (order-sensitive). * - * Important notes: + * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the + * JSON `{ "x": 1, "y": 2, "z": 3 }`: * - * * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * * **Order Sensitive:** Each field lookup will only move forward in the object. In particular, - * the following code reads z, then y, then x, and thus will not retrieve x or y if fed the - * JSON `{ "x": 1, "y": 2, "z": 3 }`: + * ```c++ + * simdjson::builtin::ondemand::parser parser; + * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); + * double z = obj.find_field("z"); + * double y = obj.find_field("y"); + * double x = obj.find_field("x"); + * ``` * - * ```c++ - * simdjson::builtin::ondemand::parser parser; - * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); - * double z = obj["z"]; - * double y = obj["y"]; - * double x = obj["x"]; - * ``` + * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. + * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. * * @param key The key to look up. * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. */ + simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(std::string_view key) && noexcept; + + /** + * Look up a field by name on an object, without regard to key order. + * + * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies + * and often appears negligible. It starts out normally, starting out at the last field; but if + * the field is not found, it scans from the beginning of the object to see if it missed it. That + * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object + * in question is large. The fact that the extra code is there also bumps the executable size. + * + * It is the default, however, because it would be highly surprising (and hard to debug) if the + * default behavior failed to look up a field just because it was in the wrong order--and many + * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. + * + * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the + * field wasn't there when they aren't). + * + * @param key The key to look up. + * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. + */ + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](std::string_view key) && noexcept; protected: @@ -76,6 +101,10 @@ public: simdjson_really_inline simdjson_result begin() noexcept; simdjson_really_inline simdjson_result end() noexcept; + simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; + simdjson_really_inline simdjson_result find_field(std::string_view key) && noexcept; + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_really_inline simdjson_result operator[](std::string_view key) && noexcept; }; diff --git a/include/simdjson/generic/ondemand/value-inl.h b/include/simdjson/generic/ondemand/value-inl.h index 9c8f222c..a46dcc83 100644 --- a/include/simdjson/generic/ondemand/value-inl.h +++ b/include/simdjson/generic/ondemand/value-inl.h @@ -6,6 +6,13 @@ simdjson_really_inline value::value(const value_iterator &_iter) noexcept : iter{_iter} { } +simdjson_really_inline value value::start(const value_iterator &iter) noexcept { + return iter; +} +simdjson_really_inline value value::resume(const value_iterator &iter) noexcept { + return iter; +} + simdjson_really_inline simdjson_result value::get_array() && noexcept { return array::start(iter); } @@ -18,6 +25,21 @@ simdjson_really_inline simdjson_result value::get_object() && noexcept { simdjson_really_inline simdjson_result value::get_object() & noexcept { return object::try_start(iter); } +simdjson_really_inline simdjson_result value::start_or_resume_object() & noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} +simdjson_really_inline simdjson_result value::start_or_resume_object() && noexcept { + if (iter.at_start()) { + return get_object(); + } else { + return object::resume(iter); + } +} + simdjson_really_inline simdjson_result value::get_raw_json_string() && noexcept { return iter.require_raw_json_string(); } @@ -145,17 +167,43 @@ simdjson_really_inline simdjson_result value::end() & noexcept { return {}; } +simdjson_really_inline simdjson_result value::find_field(std::string_view key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_really_inline simdjson_result value::find_field(std::string_view key) && noexcept { + return std::forward(*this).start_or_resume_object().find_field(key); +} +simdjson_really_inline simdjson_result value::find_field(const char *key) & noexcept { + return start_or_resume_object().find_field(key); +} +simdjson_really_inline simdjson_result value::find_field(const char *key) && noexcept { + return std::forward(*this).start_or_resume_object().find_field(key); +} + +simdjson_really_inline simdjson_result value::find_field_unordered(std::string_view key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_really_inline simdjson_result value::find_field_unordered(std::string_view key) && noexcept { + return std::forward(*this).start_or_resume_object().find_field_unordered(key); +} +simdjson_really_inline simdjson_result value::find_field_unordered(const char *key) & noexcept { + return start_or_resume_object().find_field_unordered(key); +} +simdjson_really_inline simdjson_result value::find_field_unordered(const char *key) && noexcept { + return std::forward(*this).start_or_resume_object().find_field_unordered(key); +} + simdjson_really_inline simdjson_result value::operator[](std::string_view key) & noexcept { - return get_object()[key]; + return start_or_resume_object()[key]; } simdjson_really_inline simdjson_result value::operator[](std::string_view key) && noexcept { - return std::forward(*this).get_object()[key]; + return std::forward(*this).start_or_resume_object()[key]; } simdjson_really_inline simdjson_result value::operator[](const char *key) & noexcept { - return get_object()[key]; + return start_or_resume_object()[key]; } simdjson_really_inline simdjson_result value::operator[](const char *key) && noexcept { - return std::forward(*this).get_object()[key]; + return std::forward(*this).start_or_resume_object()[key]; } } // namespace ondemand @@ -188,6 +236,40 @@ simdjson_really_inline simdjson_result simdjson_result::find_field(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field(const char *key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field(key); +} + +simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(std::string_view key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(const char *key) & noexcept { + if (error()) { return error(); } + return first.find_field_unordered(key); +} +simdjson_really_inline simdjson_result simdjson_result::find_field_unordered(const char *key) && noexcept { + if (error()) { return error(); } + return std::forward(first).find_field_unordered(key); +} + simdjson_really_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } return first[key]; diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h index 46ba794e..3faad26c 100644 --- a/include/simdjson/generic/ondemand/value.h +++ b/include/simdjson/generic/ondemand/value.h @@ -245,32 +245,71 @@ public: simdjson_really_inline simdjson_result end() & noexcept; /** - * Look up a field by name on an object. + * Look up a field by name on an object (order-sensitive). * - * Important notes: + * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the + * JSON `{ "x": 1, "y": 2, "z": 3 }`: * - * * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * * **Once Only:** You may only look up a single field on a value. To look up multiple fields, - * you must cast to object or call `.get_object()`. + * ```c++ + * simdjson::builtin::ondemand::parser parser; + * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); + * double z = obj.find_field("z"); + * double y = obj.find_field("y"); + * double x = obj.find_field("x"); + * ``` + * + * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. + * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. * * @param key The key to look up. - * @returns The value of the field, NO_SUCH_FIELD if the field is not in the object, or - * INCORRECT_TYPE if the JSON value is not an array. + * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. */ + simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(std::string_view key) && noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(const char *key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(const char *key) && noexcept; + + /** + * Look up a field by name on an object, without regard to key order. + * + * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies + * and often appears negligible. It starts out normally, starting out at the last field; but if + * the field is not found, it scans from the beginning of the object to see if it missed it. That + * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object + * in question is large. The fact that the extra code is there also bumps the executable size. + * + * It is the default, however, because it would be highly surprising (and hard to debug) if the + * default behavior failed to look up a field just because it was in the wrong order--and many + * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. + * + * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the + * field wasn't there when they aren't). + * + * @param key The key to look up. + * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. + */ + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(const char *key) && noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](std::string_view key) && noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](const char *key) & noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](const char *key) && noexcept; protected: /** * Create a value. - * - * Use value::read() instead of this. */ simdjson_really_inline value(const value_iterator &iter) noexcept; @@ -279,6 +318,25 @@ protected: */ simdjson_really_inline void skip() noexcept; + /** + * Start a value at the current position. + * + * (It should already be started; this is just a self-documentation method.) + */ + static simdjson_really_inline value start(const value_iterator &iter) noexcept; + + /** + * Resume a value. + */ + static simdjson_really_inline value resume(const value_iterator &iter) noexcept; + + /** + * Get the object, starting or resuming it as necessary + */ + simdjson_really_inline simdjson_result start_or_resume_object() & noexcept; + /** @overload simdjson_really_inline simdjson_result start_or_resume_object() & noexcept; */ + simdjson_really_inline simdjson_result start_or_resume_object() && noexcept; + // simdjson_really_inline void log_value(const char *type) const noexcept; // simdjson_really_inline void log_error(const char *message) const noexcept; @@ -362,25 +420,66 @@ public: simdjson_really_inline simdjson_result end() & noexcept; /** - * Look up a field by name on an object. + * Look up a field by name on an object (order-sensitive). * - * Important notes: + * The following code reads z, then y, then x, and thus will not retrieve x or y if fed the + * JSON `{ "x": 1, "y": 2, "z": 3 }`: * - * * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. - * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. - * * **Once Only:** You may only look up a single field on a value. To look up multiple fields, - * you must cast to object or call `.get_object()`. + * ```c++ + * simdjson::builtin::ondemand::parser parser; + * auto obj = parser.parse(R"( { "x": 1, "y": 2, "z": 3 } )"_padded); + * double z = obj.find_field("z"); + * double y = obj.find_field("y"); + * double x = obj.find_field("x"); + * ``` + * + * **Raw Keys:** The lookup will be done against the *raw* key, and will not unescape keys. + * e.g. `object["a"]` will match `{ "a": 1 }`, but will *not* match `{ "\u0061": 1 }`. * * @param key The key to look up. - * @returns The value of the field, NO_SUCH_FIELD if the field is not in the object, or - * INCORRECT_TYPE if the JSON value is not an array. + * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. */ + simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(std::string_view key) && noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(const char *key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field(const char *key) && noexcept; + + /** + * Look up a field by name on an object, without regard to key order. + * + * **Performance Notes:** This is a bit less performant than find_field(), though its effect varies + * and often appears negligible. It starts out normally, starting out at the last field; but if + * the field is not found, it scans from the beginning of the object to see if it missed it. That + * missing case has a non-cache-friendly bump and lots of extra scanning, especially if the object + * in question is large. The fact that the extra code is there also bumps the executable size. + * + * It is the default, however, because it would be highly surprising (and hard to debug) if the + * default behavior failed to look up a field just because it was in the wrong order--and many + * APIs assume this. Therefore, you must be explicit if you want to treat objects as out of order. + * + * Use find_field() if you are sure fields will be in order (or are willing to treat it as if the + * field wasn't there when they aren't). + * + * @param key The key to look up. + * @returns The value of the field, or NO_SUCH_FIELD if the field is not in the object. + */ + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) && noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(const char *key) & noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ + simdjson_really_inline simdjson_result find_field_unordered(const char *key) && noexcept; + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](std::string_view key) && noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](const char *key) & noexcept; - /** @overload simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; */ + /** @overload simdjson_really_inline simdjson_result find_field_unordered(std::string_view key) & noexcept; */ simdjson_really_inline simdjson_result operator[](const char *key) && noexcept; }; diff --git a/include/simdjson/generic/ondemand/value_iterator-inl.h b/include/simdjson/generic/ondemand/value_iterator-inl.h index be187f6e..da8cb138 100644 --- a/include/simdjson/generic/ondemand/value_iterator-inl.h +++ b/include/simdjson/generic/ondemand/value_iterator-inl.h @@ -55,6 +55,73 @@ simdjson_warn_unused simdjson_really_inline simdjson_result value_iterator error_code error; bool has_value; + // + // Initially, the object can be in one of a few different places: + // + // 1. The start of the object, at the first field: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2, index 1) + // ``` + // + // 2. When a previous search did not yield a value or the object is empty: + // + // ``` + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 0) + // { } + // ^ (depth 0, index 2) + // ``` + // + if (!is_open()) { return false; } + if (at_first_field()) { + has_value = true; + + // 3. When a previous search found a field or an iterator yielded a value: + // + // ``` + // // When a field was not fully consumed (or not even touched at all) + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 2) + // // When a field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // // When the last field was fully consumed + // { "a": [ 1, 2 ], "b": [ 3, 4 ] } + // ^ (depth 1) + // ``` + // + } else { + if ((error = skip_child() )) { abandon(); return error; } + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } + } + while (has_value) { + // Get the key and colon, stopping at the value. + raw_json_string actual_key; + if ((error = field_key().get(actual_key) )) { abandon(); return error; }; + if ((error = field_value() )) { abandon(); return error; } + + // If it matches, stop and return + if (actual_key == key) { + logger::log_event(*this, "match", key, -2); + return true; + } + + // No match: skip the value and see if , or } is next + logger::log_event(*this, "no match", key, -2); + SIMDJSON_TRY( skip_child() ); // Skip the value entirely + if ((error = has_next_field().get(has_value) )) { abandon(); return error; } + } + + // If the loop ended, we're out of fields to look at. + return false; +} + +simdjson_warn_unused simdjson_really_inline simdjson_result value_iterator::find_field_unordered_raw(const std::string_view key) noexcept { + error_code error; + bool has_value; + // // Initially, the object can be in one of a few different places: // @@ -476,6 +543,14 @@ simdjson_really_inline bool value_iterator::is_open() const noexcept { return _json_iter->depth() >= depth(); } +simdjson_really_inline bool value_iterator::at_eof() const noexcept { + return _json_iter->at_eof(); +} + +simdjson_really_inline bool value_iterator::at_start() const noexcept { + return _json_iter->token.index == _start_index; +} + simdjson_really_inline bool value_iterator::at_first_field() const noexcept { SIMDJSON_ASSUME( _json_iter->token.index > _start_index ); return _json_iter->token.index == _start_index + 1; diff --git a/include/simdjson/generic/ondemand/value_iterator.h b/include/simdjson/generic/ondemand/value_iterator.h index 6f1ee546..2ff0b339 100644 --- a/include/simdjson/generic/ondemand/value_iterator.h +++ b/include/simdjson/generic/ondemand/value_iterator.h @@ -50,6 +50,11 @@ public: */ simdjson_really_inline bool at_eof() const noexcept; + /** + * Tell whether the iterator is at the start of the value + */ + simdjson_really_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. */ @@ -148,7 +153,8 @@ public: simdjson_warn_unused simdjson_really_inline error_code find_field(const std::string_view key) noexcept; /** - * Find the next field with the given key, *without* unescaping. + * 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. * @@ -165,6 +171,26 @@ public: */ simdjson_warn_unused simdjson_really_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_really_inline simdjson_result find_field_unordered_raw(const std::string_view key) noexcept; + /** @} */ /** diff --git a/tests/ondemand/ondemand_basictests.cpp b/tests/ondemand/ondemand_basictests.cpp index dbfd9b00..14ff71df 100644 --- a/tests/ondemand/ondemand_basictests.cpp +++ b/tests/ondemand/ondemand_basictests.cpp @@ -1202,6 +1202,175 @@ namespace dom_api_tests { ASSERT_ERROR( doc_result["d"], NO_SUCH_FIELD ); return true; })); + + json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded; + SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) { + ondemand::value object; + ASSERT_SUCCESS( doc_result["outer"].get(object) ); + ASSERT_EQUAL( object["a"].get_uint64().first, 1 ); + ASSERT_EQUAL( object["b"].get_uint64().first, 2 ); + ASSERT_EQUAL( object["c/d"].get_uint64().first, 3 ); + + ASSERT_EQUAL( object["a"].get_uint64().first, 1 ); + ASSERT_ERROR( object["d"], NO_SUCH_FIELD ); + return true; + })); + SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) { + simdjson_result object = doc_result["outer"]; + ASSERT_EQUAL( object["a"].get_uint64().first, 1 ); + ASSERT_EQUAL( object["b"].get_uint64().first, 2 ); + ASSERT_EQUAL( object["c/d"].get_uint64().first, 3 ); + + ASSERT_EQUAL( object["a"].get_uint64().first, 1 ); + ASSERT_ERROR( object["d"], NO_SUCH_FIELD ); + return true; + })); + TEST_SUCCEED(); + } + + bool object_find_field_unordered() { + TEST_START(); + auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded; + SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) { + ondemand::object object; + ASSERT_SUCCESS( doc_result.get(object) ); + + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().first, 3 ); + + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { + simdjson_result object; + object = doc_result.get_object(); + + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().first, 3 ); + + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) { + ondemand::document doc; + ASSERT_SUCCESS( std::move(doc_result).get(doc) ); + ASSERT_EQUAL( doc.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_EQUAL( doc.find_field_unordered("b").get_uint64().first, 2 ); + ASSERT_EQUAL( doc.find_field_unordered("c/d").get_uint64().first, 3 ); + + ASSERT_EQUAL( doc.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_ERROR( doc.find_field_unordered("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { + ASSERT_EQUAL( doc_result.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_EQUAL( doc_result.find_field_unordered("b").get_uint64().first, 2 ); + ASSERT_EQUAL( doc_result.find_field_unordered("c/d").get_uint64().first, 3 ); + + ASSERT_EQUAL( doc_result.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_ERROR( doc_result.find_field_unordered("d"), NO_SUCH_FIELD ); + return true; + })); + + json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded; + SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) { + ondemand::value object; + ASSERT_SUCCESS( doc_result.find_field_unordered("outer").get(object) ); + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().first, 3 ); + + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { + simdjson_result object = doc_result.find_field_unordered("outer"); + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field_unordered("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field_unordered("c/d").get_uint64().first, 3 ); + + ASSERT_EQUAL( object.find_field_unordered("a").get_uint64().first, 1 ); + ASSERT_ERROR( object.find_field_unordered("d"), NO_SUCH_FIELD ); + return true; + })); + TEST_SUCCEED(); + } + + bool object_find_field() { + TEST_START(); + auto json = R"({ "a": 1, "b": 2, "c/d": 3})"_padded; + SUBTEST("ondemand::object", test_ondemand_doc(json, [&](auto doc_result) { + ondemand::object object; + ASSERT_SUCCESS( doc_result.get(object) ); + + ASSERT_EQUAL( object.find_field("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field("c/d").get_uint64().first, 3 ); + + ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD ); + ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { + simdjson_result object; + object = doc_result.get_object(); + + ASSERT_EQUAL( object.find_field("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field("c/d").get_uint64().first, 3 ); + + ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD ); + ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("ondemand::document", test_ondemand_doc(json, [&](auto doc_result) { + ondemand::document doc; + ASSERT_SUCCESS( std::move(doc_result).get(doc) ); + ASSERT_EQUAL( doc.find_field("a").get_uint64().first, 1 ); + ASSERT_EQUAL( doc.find_field("b").get_uint64().first, 2 ); + ASSERT_EQUAL( doc.find_field("c/d").get_uint64().first, 3 ); + + ASSERT_ERROR( doc.find_field("a"), NO_SUCH_FIELD ); + ASSERT_ERROR( doc.find_field("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { + ASSERT_EQUAL( doc_result.find_field("a").get_uint64().first, 1 ); + ASSERT_EQUAL( doc_result.find_field("b").get_uint64().first, 2 ); + ASSERT_EQUAL( doc_result.find_field("c/d").get_uint64().first, 3 ); + + ASSERT_ERROR( doc_result.find_field("a"), NO_SUCH_FIELD ); + ASSERT_ERROR( doc_result.find_field("d"), NO_SUCH_FIELD ); + return true; + })); + + json = R"({ "outer": { "a": 1, "b": 2, "c/d": 3 } })"_padded; + SUBTEST("ondemand::value", test_ondemand_doc(json, [&](auto doc_result) { + ondemand::value object; + ASSERT_SUCCESS( doc_result.find_field("outer").get(object) ); + ASSERT_EQUAL( object.find_field("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field("c/d").get_uint64().first, 3 ); + + ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD ); + ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD ); + return true; + })); + SUBTEST("simdjson_result", test_ondemand_doc(json, [&](auto doc_result) { + simdjson_result object = doc_result.find_field("outer"); + ASSERT_EQUAL( object.find_field("a").get_uint64().first, 1 ); + ASSERT_EQUAL( object.find_field("b").get_uint64().first, 2 ); + ASSERT_EQUAL( object.find_field("c/d").get_uint64().first, 3 ); + + ASSERT_ERROR( object.find_field("a"), NO_SUCH_FIELD ); + ASSERT_ERROR( object.find_field("d"), NO_SUCH_FIELD ); + return true; + })); TEST_SUCCEED(); } @@ -1387,6 +1556,8 @@ namespace dom_api_tests { boolean_values() && null_value() && object_index() && + object_find_field_unordered() && + object_find_field() && nested_object_index() && iterate_object_partial_children() && iterate_array_partial_children() && @@ -1413,7 +1584,7 @@ namespace ordering_tests { auto json = "{\"coordinates\":[{\"x\":1.1,\"y\":2.2,\"z\":3.3}]}"_padded; - bool in_order() { + bool in_order_object_index() { TEST_START(); ondemand::parser parser{}; auto doc = parser.iterate(json); @@ -1428,7 +1599,37 @@ namespace ordering_tests { return (x == 1.1) && (y == 2.2) && (z == 3.3); } - bool out_of_order() { + bool in_order_object_find_field_unordered() { + TEST_START(); + ondemand::parser parser{}; + auto doc = parser.iterate(json); + double x{0}; + double y{0}; + double z{0}; + for (ondemand::object point_object : doc["coordinates"]) { + x += double(point_object.find_field_unordered("x")); + y += double(point_object.find_field_unordered("y")); + z += double(point_object.find_field_unordered("z")); + } + return (x == 1.1) && (y == 2.2) && (z == 3.3); + } + + bool in_order_object_find_field() { + TEST_START(); + ondemand::parser parser{}; + auto doc = parser.iterate(json); + double x{0}; + double y{0}; + double z{0}; + for (ondemand::object point_object : doc["coordinates"]) { + x += double(point_object.find_field("x")); + y += double(point_object.find_field("y")); + z += double(point_object.find_field("z")); + } + return (x == 1.1) && (y == 2.2) && (z == 3.3); + } + + bool out_of_order_object_index() { TEST_START(); ondemand::parser parser{}; auto doc = parser.iterate(json); @@ -1443,7 +1644,37 @@ namespace ordering_tests { return (x == 1.1) && (y == 2.2) && (z == 3.3); } - bool foreach_lookup() { + bool out_of_order_object_find_field_unordered() { + TEST_START(); + ondemand::parser parser{}; + auto doc = parser.iterate(json); + double x{0}; + double y{0}; + double z{0}; + for (ondemand::object point_object : doc["coordinates"]) { + z += double(point_object.find_field_unordered("z")); + x += double(point_object.find_field_unordered("x")); + y += double(point_object.find_field_unordered("y")); + } + return (x == 1.1) && (y == 2.2) && (z == 3.3); + } + + bool out_of_order_object_find_field() { + TEST_START(); + ondemand::parser parser{}; + auto doc = parser.iterate(json); + double x{0}; + double y{0}; + double z{0}; + for (ondemand::object point_object : doc["coordinates"]) { + z += double(point_object.find_field("z")); + ASSERT_ERROR( point_object.find_field("x"), NO_SUCH_FIELD ); + ASSERT_ERROR( point_object.find_field("y"), NO_SUCH_FIELD ); + } + return (x == 0) && (y == 0) && (z == 3.3); + } + + bool foreach_object_field_lookup() { TEST_START(); ondemand::parser parser{}; auto doc = parser.iterate(json); @@ -1464,9 +1695,13 @@ namespace ordering_tests { bool run() { return #if SIMDJSON_EXCEPTIONS - in_order() && - out_of_order() && - foreach_lookup() && + in_order_object_index() && + in_order_object_find_field_unordered() && + in_order_object_find_field() && + out_of_order_object_index() && + out_of_order_object_find_field_unordered() && + out_of_order_object_find_field() && + foreach_object_field_lookup() && #endif true; }