diff --git a/benchmark/partial_tweets/ondemand.h b/benchmark/partial_tweets/ondemand.h index b6d2e5a1..f3a2d9a8 100644 --- a/benchmark/partial_tweets/ondemand.h +++ b/benchmark/partial_tweets/ondemand.h @@ -40,7 +40,7 @@ simdjson_really_inline bool OnDemand::Run(const padded_string &json) { // Walk the document, parsing the tweets as we go auto doc = parser.iterate(json); - for (ondemand::object tweet : doc["statuses"].get_array()) { + for (ondemand::object tweet : doc["statuses"]) { tweets.emplace_back(partial_tweets::tweet{ tweet["created_at"], tweet["id"], diff --git a/src/generic/ondemand.h b/src/generic/ondemand.h index 13a20d3f..b547161e 100644 --- a/src/generic/ondemand.h +++ b/src/generic/ondemand.h @@ -2,6 +2,8 @@ #include "generic/ondemand/raw_json_string.h" #include "generic/ondemand/token_iterator.h" #include "generic/ondemand/json_iterator.h" +#include "generic/ondemand/array_iterator.h" +#include "generic/ondemand/object_iterator.h" #include "generic/ondemand/array.h" #include "generic/ondemand/document.h" #include "generic/ondemand/value.h" @@ -13,6 +15,8 @@ #include "generic/ondemand/raw_json_string-inl.h" #include "generic/ondemand/token_iterator-inl.h" #include "generic/ondemand/json_iterator-inl.h" +#include "generic/ondemand/array_iterator-inl.h" +#include "generic/ondemand/object_iterator-inl.h" #include "generic/ondemand/array-inl.h" #include "generic/ondemand/document-inl.h" #include "generic/ondemand/value-inl.h" diff --git a/src/generic/ondemand/array-inl.h b/src/generic/ondemand/array-inl.h index aa7dfb65..76f7ab05 100644 --- a/src/generic/ondemand/array-inl.h +++ b/src/generic/ondemand/array-inl.h @@ -42,7 +42,7 @@ namespace ondemand { simdjson_really_inline array::array() noexcept = default; simdjson_really_inline array::array(json_iterator_ref &&_iter) noexcept - : iter{std::forward(_iter)}, error{SUCCESS} + : iter{std::forward(_iter)} { } simdjson_really_inline array::array(array &&other) noexcept = default; @@ -66,36 +66,14 @@ simdjson_really_inline array array::started(json_iterator_ref &&iter) noexcept { if (!iter->started_array()) { iter.release(); } return array(std::forward(iter)); } -simdjson_really_inline array::iterator array::begin() & noexcept { - return *this; + +simdjson_really_inline array_iterator array::begin() & noexcept { + return iter; } -simdjson_really_inline array::iterator array::end() & noexcept { +simdjson_really_inline array_iterator array::end() & noexcept { return {}; } -simdjson_really_inline array::iterator::iterator(array &_a) noexcept : a{&_a} {} - -simdjson_really_inline array::iterator::iterator() noexcept = default; -simdjson_really_inline array::iterator::iterator(const array::iterator &_a) noexcept = default; -simdjson_really_inline array::iterator &array::iterator::operator=(const array::iterator &_a) noexcept = default; - -simdjson_really_inline simdjson_result array::iterator::operator*() noexcept { - if (a->error) { a->iter.release(); return a->error; } - return value::start(a->iter.borrow()); -} -simdjson_really_inline bool array::iterator::operator==(const array::iterator &other) noexcept { - return !(*this != other); -} -simdjson_really_inline bool array::iterator::operator!=(const array::iterator &) noexcept { - return a->iter.is_alive(); -} -simdjson_really_inline array::iterator &array::iterator::operator++() noexcept { - bool has_value; - a->error = a->iter->has_next_element().get(has_value); // If there's an error, has_next stays true. - if (!(a->error || has_value)) { a->iter.release(); } - return *this; -} - } // namespace ondemand } // namespace SIMDJSON_IMPLEMENTATION } // unnamed namespace @@ -117,69 +95,13 @@ simdjson_really_inline simdjson_result { } -simdjson_really_inline simdjson_result simdjson_result::begin() & noexcept { +simdjson_really_inline simdjson_result simdjson_result::begin() & noexcept { if (error()) { return error(); } return first.begin(); } -simdjson_really_inline simdjson_result simdjson_result::end() & noexcept { +simdjson_really_inline simdjson_result simdjson_result::end() & noexcept { if (error()) { return error(); } return first.end(); } -// -// array::iterator -// - -simdjson_really_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base({}, SUCCESS) -{ -} - -simdjson_really_inline simdjson_result::simdjson_result( - SIMDJSON_IMPLEMENTATION::ondemand::array::iterator &&value -) noexcept - : internal::simdjson_result_base(std::forward(value)) -{ -} -simdjson_really_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base({}, error) -{ -} - -simdjson_really_inline simdjson_result::simdjson_result( - const simdjson_result &a -) noexcept - : internal::simdjson_result_base(a) -{ -} - -simdjson_really_inline simdjson_result &simdjson_result::operator=( - const simdjson_result &a -) noexcept { - first = a.first; - second = a.second; - return *this; -} - -simdjson_really_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { second = SUCCESS; return error(); } - return *first; -} -// Assumes it's being compared with the end. true if depth < iter->depth. -simdjson_really_inline bool simdjson_result::operator==(const simdjson_result &other) noexcept { - if (error()) { return true; } - return first == other.first; -} -// Assumes it's being compared with the end. true if depth >= iter->depth. -simdjson_really_inline bool simdjson_result::operator!=(const simdjson_result &other) noexcept { - if (error()) { return false; } - return first != other.first; -} -// Checks for ']' and ',' -simdjson_really_inline simdjson_result &simdjson_result::operator++() noexcept { - if (error()) { return *this; } - ++first; - return *this; -} - } // namespace simdjson diff --git a/src/generic/ondemand/array.h b/src/generic/ondemand/array.h index e957d849..de8c9890 100644 --- a/src/generic/ondemand/array.h +++ b/src/generic/ondemand/array.h @@ -19,33 +19,8 @@ public: array(const array &) = delete; array &operator=(const array &) = delete; - class iterator { - public: - simdjson_really_inline iterator(array &a) noexcept; - simdjson_really_inline iterator(const array::iterator &a) noexcept; - simdjson_really_inline iterator &operator=(const array::iterator &a) noexcept; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - simdjson_really_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - // Assumes it's being compared with the end. true if depth < iter->depth. - simdjson_really_inline bool operator==(const array::iterator &) noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_really_inline bool operator!=(const array::iterator &) noexcept; - // Checks for ']' and ',' - simdjson_really_inline array::iterator &operator++() noexcept; - private: - array *a{}; - simdjson_really_inline iterator() noexcept; - friend class array; - friend struct simdjson_result; - }; - - simdjson_really_inline array::iterator begin() & noexcept; - simdjson_really_inline array::iterator end() & noexcept; + simdjson_really_inline array_iterator begin() & noexcept; + simdjson_really_inline array_iterator end() & noexcept; protected: /** @@ -77,15 +52,6 @@ protected: * is first used, and never changes afterwards. */ json_iterator_ref iter{}; - /** - * Error, if there is one. Errors are only yielded once. - * - * PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first - * iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If - * this is not elided, we should make sure it's at least not using up a register. Failing that, - * we should store it in document so there's only one of them. - */ - error_code error{}; friend class value; friend struct simdjson_result; @@ -104,31 +70,8 @@ public: simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::array &&value) noexcept; ///< @private simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_really_inline simdjson_result begin() & noexcept; - simdjson_really_inline simdjson_result end() & noexcept; -}; - -template<> -struct simdjson_result : public internal::simdjson_result_base { -public: - simdjson_really_inline simdjson_result() noexcept; - simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::array::iterator &&value) noexcept; ///< @private - simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_really_inline simdjson_result(const simdjson_result &a) noexcept; - simdjson_really_inline simdjson_result &operator=(const simdjson_result &a) noexcept; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - simdjson_really_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - // Assumes it's being compared with the end. true if depth < iter->depth. - simdjson_really_inline bool operator==(const simdjson_result &) noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_really_inline bool operator!=(const simdjson_result &) noexcept; - // Checks for ']' and ',' - simdjson_really_inline simdjson_result &operator++() noexcept; + simdjson_really_inline simdjson_result begin() & noexcept; + simdjson_really_inline simdjson_result end() & noexcept; }; } // namespace simdjson diff --git a/src/generic/ondemand/array_iterator-inl.h b/src/generic/ondemand/array_iterator-inl.h new file mode 100644 index 00000000..4557e8e6 --- /dev/null +++ b/src/generic/ondemand/array_iterator-inl.h @@ -0,0 +1,86 @@ +namespace { +namespace SIMDJSON_IMPLEMENTATION { +namespace ondemand { + +simdjson_really_inline array_iterator::array_iterator(json_iterator_ref &_iter) noexcept : iter{&_iter} {} + +simdjson_really_inline array_iterator::array_iterator() noexcept = default; +simdjson_really_inline array_iterator::array_iterator(const array_iterator &_a) noexcept = default; +simdjson_really_inline array_iterator &array_iterator::operator=(const array_iterator &_a) noexcept = default; + +simdjson_really_inline simdjson_result array_iterator::operator*() noexcept { + if (error) { iter->release(); return error; } + return value::start(iter->borrow()); +} +simdjson_really_inline bool array_iterator::operator==(const array_iterator &other) noexcept { + return !(*this != other); +} +simdjson_really_inline bool array_iterator::operator!=(const array_iterator &) noexcept { + return iter->is_alive(); +} +simdjson_really_inline array_iterator &array_iterator::operator++() noexcept { + bool has_value; + error = (*iter)->has_next_element().get(has_value); // If there's an error, has_next stays true. + if (!(error || has_value)) { iter->release(); } + return *this; +} + +} // namespace ondemand +} // namespace SIMDJSON_IMPLEMENTATION +} // unnamed namespace + +namespace simdjson { + +simdjson_really_inline simdjson_result::simdjson_result() noexcept + : internal::simdjson_result_base({}, SUCCESS) +{ +} + +simdjson_really_inline simdjson_result::simdjson_result( + SIMDJSON_IMPLEMENTATION::ondemand::array_iterator &&value +) noexcept + : internal::simdjson_result_base(std::forward(value)) +{ +} +simdjson_really_inline simdjson_result::simdjson_result(error_code error) noexcept + : internal::simdjson_result_base({}, error) +{ +} + +simdjson_really_inline simdjson_result::simdjson_result( + const simdjson_result &a +) noexcept + : internal::simdjson_result_base(a) +{ +} + +simdjson_really_inline simdjson_result &simdjson_result::operator=( + const simdjson_result &a +) noexcept { + first = a.first; + second = a.second; + return *this; +} + +simdjson_really_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { second = SUCCESS; return error(); } + return *first; +} +// Assumes it's being compared with the end. true if depth < iter->depth. +simdjson_really_inline bool simdjson_result::operator==(const simdjson_result &other) noexcept { + if (error()) { return true; } + return first == other.first; +} +// Assumes it's being compared with the end. true if depth >= iter->depth. +simdjson_really_inline bool simdjson_result::operator!=(const simdjson_result &other) noexcept { + if (error()) { return false; } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_really_inline simdjson_result &simdjson_result::operator++() noexcept { + if (error()) { return *this; } + ++first; + return *this; +} + +} // namespace simdjson diff --git a/src/generic/ondemand/array_iterator.h b/src/generic/ondemand/array_iterator.h new file mode 100644 index 00000000..2d530082 --- /dev/null +++ b/src/generic/ondemand/array_iterator.h @@ -0,0 +1,78 @@ +#include "simdjson/error.h" + +namespace { +namespace SIMDJSON_IMPLEMENTATION { +namespace ondemand { + +class value; +class document; + +/** + * A forward-only JSON array. + */ +class array_iterator { +public: + simdjson_really_inline array_iterator(json_iterator_ref &iter) noexcept; + simdjson_really_inline array_iterator(const array_iterator &a) noexcept; + simdjson_really_inline array_iterator &operator=(const array_iterator &a) noexcept; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + simdjson_really_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + // Assumes it's being compared with the end. true if depth < iter->depth. + simdjson_really_inline bool operator==(const array_iterator &) noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_really_inline bool operator!=(const array_iterator &) noexcept; + // Checks for ']' and ',' + simdjson_really_inline array_iterator &operator++() noexcept; +private: + json_iterator_ref *iter{}; + /** + * Error, if there is one. Errors are only yielded once. + * + * PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first + * iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If + * this is not elided, we should make sure it's at least not using up a register. Failing that, + * we should store it in document so there's only one of them. + */ + error_code error{}; + + simdjson_really_inline array_iterator() noexcept; + + friend class array; + friend struct simdjson_result; +}; + +} // namespace ondemand +} // namespace SIMDJSON_IMPLEMENTATION +} // unnamed namespace + +namespace simdjson { + +template<> +struct simdjson_result : public internal::simdjson_result_base { +public: + simdjson_really_inline simdjson_result() noexcept; + simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::array_iterator &&value) noexcept; ///< @private + simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private + simdjson_really_inline simdjson_result(const simdjson_result &a) noexcept; + simdjson_really_inline simdjson_result &operator=(const simdjson_result &a) noexcept; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + simdjson_really_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + // Assumes it's being compared with the end. true if depth < iter->depth. + simdjson_really_inline bool operator==(const simdjson_result &) noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_really_inline bool operator!=(const simdjson_result &) noexcept; + // Checks for ']' and ',' + simdjson_really_inline simdjson_result &operator++() noexcept; +}; + +} // namespace simdjson diff --git a/src/generic/ondemand/field.h b/src/generic/ondemand/field.h index 90f9491f..3ea77c92 100644 --- a/src/generic/ondemand/field.h +++ b/src/generic/ondemand/field.h @@ -22,7 +22,7 @@ protected: static simdjson_really_inline simdjson_result start(json_iterator_ref &&iter) noexcept; static simdjson_really_inline simdjson_result start(json_iterator_ref &&iter, raw_json_string key) noexcept; friend struct simdjson_result; - friend class object; + friend class object_iterator; }; } // namespace ondemand diff --git a/src/generic/ondemand/object-inl.h b/src/generic/ondemand/object-inl.h index 24039273..8c42de05 100644 --- a/src/generic/ondemand/object-inl.h +++ b/src/generic/ondemand/object-inl.h @@ -46,8 +46,7 @@ namespace ondemand { simdjson_really_inline object::object() noexcept = default; simdjson_really_inline object::object(json_iterator_ref &&_iter) noexcept : iter{std::forward(_iter)}, - at_start{true}, - error{SUCCESS} + at_start{true} { } simdjson_really_inline object::object(object &&other) noexcept = default; @@ -63,22 +62,22 @@ simdjson_really_inline object::~object() noexcept { } simdjson_really_inline error_code object::find_field(const std::string_view key) noexcept { - if (error) { return report_error(); } if (!iter.is_alive()) { return NO_SUCH_FIELD; } // Unless this is the first field, we need to advance past the , and check for } + error_code error; bool has_value; if (at_start) { at_start = false; has_value = true; } else { - if ((error = iter->has_next_field().get(has_value) )) { return report_error(); } + if ((error = iter->has_next_field().get(has_value) )) { iter.release(); return error; } } while (has_value) { // Get the key raw_json_string actual_key; - if ((error = iter->field_key().get(actual_key) )) { return report_error(); }; - if ((error = iter->field_value() )) { return report_error(); } + if ((error = iter->field_key().get(actual_key) )) { iter.release(); return error; }; + if ((error = iter->field_value() )) { iter.release(); return error; } // Check if it matches if (actual_key == key) { @@ -87,7 +86,7 @@ simdjson_really_inline error_code object::find_field(const std::string_view key) } logger::log_event(*iter, "no match", key, -2); SIMDJSON_TRY( iter->skip() ); // Skip the value entirely - if ((error = iter->has_next_field().get(has_value) )) { return report_error(); } + if ((error = iter->has_next_field().get(has_value) )) { iter.release(); return error; } } // If the loop ended, we're out of fields to look at. @@ -115,46 +114,13 @@ simdjson_really_inline object object::started(json_iterator_ref &&iter) noexcept if (!iter->started_object()) { iter.release(); } return object(std::forward(iter)); } -simdjson_really_inline object::iterator object::begin() noexcept { - return *this; +simdjson_really_inline object_iterator object::begin() noexcept { + SIMDJSON_ASSUME(!iter.is_alive() || at_start); + at_start = false; + return iter; } -simdjson_really_inline object::iterator object::end() noexcept { - return *this; -} - -simdjson_really_inline error_code object::report_error() noexcept { - SIMDJSON_ASSUME(error); - iter.release(); - return error; -} - -// -// object::iterator -// - -simdjson_really_inline object::iterator::iterator(object &_o) noexcept : o{&_o} {} - -simdjson_really_inline object::iterator::iterator() noexcept = default; -simdjson_really_inline object::iterator::iterator(const object::iterator &_o) noexcept = default; -simdjson_really_inline object::iterator &object::iterator::operator=(const object::iterator &_o) noexcept = default; - -simdjson_really_inline simdjson_result object::iterator::operator*() noexcept { - if (o->error) { return o->report_error(); } - return field::start(o->iter.borrow()); -} -simdjson_really_inline bool object::iterator::operator==(const object::iterator &other) noexcept { - return !(*this != other); -} -simdjson_really_inline bool object::iterator::operator!=(const object::iterator &) noexcept { - return o->iter.is_alive(); -} -simdjson_really_inline object::iterator &object::iterator::operator++() noexcept { - if (o->error) { return *this; } - o->at_start = false; - bool has_value; - o->error = o->iter->has_next_field().get(has_value); - if (!(o->error || has_value)) { o->iter.release(); } - return *this; +simdjson_really_inline object_iterator object::end() noexcept { + return {}; } } // namespace ondemand @@ -168,13 +134,13 @@ simdjson_really_inline simdjson_result::simdjson_result(error_code error) noexcept : internal::simdjson_result_base(error) {} -simdjson_really_inline simdjson_result simdjson_result::begin() noexcept { +simdjson_really_inline simdjson_result simdjson_result::begin() noexcept { if (error()) { return error(); } - return SIMDJSON_IMPLEMENTATION::ondemand::object::iterator(first); + return first.begin(); } -simdjson_really_inline simdjson_result simdjson_result::end() noexcept { +simdjson_really_inline simdjson_result simdjson_result::end() noexcept { if (error()) { return error(); } - return {}; + return first.end(); } simdjson_really_inline simdjson_result simdjson_result::operator[](std::string_view key) & noexcept { if (error()) { return error(); } @@ -185,60 +151,4 @@ simdjson_really_inline simdjson_result return std::forward(first)[key]; } -// -// object::iterator -// - -simdjson_really_inline simdjson_result::simdjson_result() noexcept - : internal::simdjson_result_base({}, SUCCESS) -{ -} - -simdjson_really_inline simdjson_result::simdjson_result( - SIMDJSON_IMPLEMENTATION::ondemand::object::iterator &&value -) noexcept - : internal::simdjson_result_base(std::forward(value)) -{ -} -simdjson_really_inline simdjson_result::simdjson_result(error_code error) noexcept - : internal::simdjson_result_base({}, error) -{ -} - -simdjson_really_inline simdjson_result::simdjson_result( - const simdjson_result &o -) noexcept - : internal::simdjson_result_base(o) -{ -} - -simdjson_really_inline simdjson_result &simdjson_result::operator=( - const simdjson_result &o -) noexcept { - first = o.first; - second = o.second; - return *this; -} - -simdjson_really_inline simdjson_result simdjson_result::operator*() noexcept { - if (error()) { second = SUCCESS; return error(); } - return *first; -} -// Assumes it's being compared with the end. true if depth < iter->depth. -simdjson_really_inline bool simdjson_result::operator==(const simdjson_result &other) noexcept { - if (error()) { return true; } - return first == other.first; -} -// Assumes it's being compared with the end. true if depth >= iter->depth. -simdjson_really_inline bool simdjson_result::operator!=(const simdjson_result &other) noexcept { - if (error()) { return false; } - return first != other.first; -} -// Checks for ']' and ',' -simdjson_really_inline simdjson_result &simdjson_result::operator++() noexcept { - if (error()) { return *this; } - ++first; - return *this; -} - } // namespace simdjson diff --git a/src/generic/ondemand/object.h b/src/generic/ondemand/object.h index 0718cec7..d5fba196 100644 --- a/src/generic/ondemand/object.h +++ b/src/generic/ondemand/object.h @@ -16,32 +16,8 @@ public: object(const object &) = delete; object &operator=(const object &) = delete; - class iterator { - public: - simdjson_really_inline iterator(object &o) noexcept; - simdjson_really_inline iterator(const object::iterator &o) noexcept; - simdjson_really_inline iterator &operator=(const object::iterator &o) noexcept; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - simdjson_really_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - // Assumes it's being compared with the end. true if depth < iter->depth. - simdjson_really_inline bool operator==(const object::iterator &) noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_really_inline bool operator!=(const object::iterator &) noexcept; - // Checks for ']' and ',' - simdjson_really_inline object::iterator &operator++() noexcept; - private: - object *o{}; - simdjson_really_inline iterator() noexcept; - friend struct simdjson_result; - }; - - simdjson_really_inline object::iterator begin() noexcept; - simdjson_really_inline object::iterator end() noexcept; + simdjson_really_inline object_iterator begin() noexcept; + simdjson_really_inline object_iterator end() noexcept; simdjson_really_inline simdjson_result operator[](const std::string_view key) & noexcept; simdjson_really_inline simdjson_result operator[](const std::string_view key) && noexcept; @@ -63,8 +39,6 @@ protected: */ simdjson_really_inline object(json_iterator_ref &&_iter) noexcept; - simdjson_really_inline error_code report_error() noexcept; - simdjson_really_inline error_code find_field(const std::string_view key) noexcept; /** @@ -81,15 +55,6 @@ protected: * or * call, and SSA optimizers commonly do first-iteration loop optimization. */ bool at_start{}; - /** - * Error, if there is one. Errors are only yielded once. - * - * PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first - * iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If - * this is not elided, we should make sure it's at least not using up a register. Failing that, - * we should store it in document so there's only one of them. - */ - error_code error{}; friend class value; friend class document; @@ -108,33 +73,10 @@ public: simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::object &&value) noexcept; ///< @private simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_really_inline simdjson_result begin() noexcept; - simdjson_really_inline simdjson_result end() noexcept; + simdjson_really_inline simdjson_result begin() noexcept; + simdjson_really_inline simdjson_result end() noexcept; simdjson_really_inline simdjson_result operator[](std::string_view key) & noexcept; simdjson_really_inline simdjson_result operator[](std::string_view key) && noexcept; }; -template<> -struct simdjson_result : public internal::simdjson_result_base { -public: - simdjson_really_inline simdjson_result() noexcept; - simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::object::iterator &&value) noexcept; ///< @private - simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private - simdjson_really_inline simdjson_result(const simdjson_result &a) noexcept; - simdjson_really_inline simdjson_result &operator=(const simdjson_result &a) noexcept; - - // - // Iterator interface - // - - // Reads key and value, yielding them to the user. - simdjson_really_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. - // Assumes it's being compared with the end. true if depth < iter->depth. - simdjson_really_inline bool operator==(const simdjson_result &) noexcept; - // Assumes it's being compared with the end. true if depth >= iter->depth. - simdjson_really_inline bool operator!=(const simdjson_result &) noexcept; - // Checks for ']' and ',' - simdjson_really_inline simdjson_result &operator++() noexcept; -}; - } // namespace simdjson diff --git a/src/generic/ondemand/object_iterator-inl.h b/src/generic/ondemand/object_iterator-inl.h new file mode 100644 index 00000000..74bfd185 --- /dev/null +++ b/src/generic/ondemand/object_iterator-inl.h @@ -0,0 +1,91 @@ +namespace { +namespace SIMDJSON_IMPLEMENTATION { +namespace ondemand { + +// +// object_iterator +// + +simdjson_really_inline object_iterator::object_iterator(json_iterator_ref &_iter) noexcept : iter{&_iter} {} + +simdjson_really_inline object_iterator::object_iterator() noexcept = default; +simdjson_really_inline object_iterator::object_iterator(const object_iterator &_o) noexcept = default; +simdjson_really_inline object_iterator &object_iterator::operator=(const object_iterator &_o) noexcept = default; + +simdjson_really_inline simdjson_result object_iterator::operator*() noexcept { + if (error) { iter->release(); return error; } + return field::start(iter->borrow()); +} +simdjson_really_inline bool object_iterator::operator==(const object_iterator &other) noexcept { + return !(*this != other); +} +simdjson_really_inline bool object_iterator::operator!=(const object_iterator &) noexcept { + return iter->is_alive(); +} +simdjson_really_inline object_iterator &object_iterator::operator++() noexcept { + if (error) { return *this; } + bool has_value; + error = (*iter)->has_next_field().get(has_value); + if (!(error || has_value)) { iter->release(); } + return *this; +} + +} // namespace ondemand +} // namespace SIMDJSON_IMPLEMENTATION +} // unnamed namespace + +namespace simdjson { + +simdjson_really_inline simdjson_result::simdjson_result() noexcept + : internal::simdjson_result_base({}, SUCCESS) +{ +} + +simdjson_really_inline simdjson_result::simdjson_result( + SIMDJSON_IMPLEMENTATION::ondemand::object_iterator &&value +) noexcept + : internal::simdjson_result_base(std::forward(value)) +{ +} +simdjson_really_inline simdjson_result::simdjson_result(error_code error) noexcept + : internal::simdjson_result_base({}, error) +{ +} + +simdjson_really_inline simdjson_result::simdjson_result( + const simdjson_result &o +) noexcept + : internal::simdjson_result_base(o) +{ +} + +simdjson_really_inline simdjson_result &simdjson_result::operator=( + const simdjson_result &o +) noexcept { + first = o.first; + second = o.second; + return *this; +} + +simdjson_really_inline simdjson_result simdjson_result::operator*() noexcept { + if (error()) { second = SUCCESS; return error(); } + return *first; +} +// Assumes it's being compared with the end. true if depth < iter->depth. +simdjson_really_inline bool simdjson_result::operator==(const simdjson_result &other) noexcept { + if (error()) { return true; } + return first == other.first; +} +// Assumes it's being compared with the end. true if depth >= iter->depth. +simdjson_really_inline bool simdjson_result::operator!=(const simdjson_result &other) noexcept { + if (error()) { return false; } + return first != other.first; +} +// Checks for ']' and ',' +simdjson_really_inline simdjson_result &simdjson_result::operator++() noexcept { + if (error()) { return *this; } + ++first; + return *this; +} + +} // namespace simdjson diff --git a/src/generic/ondemand/object_iterator.h b/src/generic/ondemand/object_iterator.h new file mode 100644 index 00000000..e55ec701 --- /dev/null +++ b/src/generic/ondemand/object_iterator.h @@ -0,0 +1,73 @@ +#include "simdjson/error.h" + +namespace { +namespace SIMDJSON_IMPLEMENTATION { +namespace ondemand { + +class field; + +class object_iterator { +public: + simdjson_really_inline object_iterator(json_iterator_ref &iter) noexcept; + simdjson_really_inline object_iterator(const object_iterator &o) noexcept; + simdjson_really_inline object_iterator &operator=(const object_iterator &o) noexcept; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + // MUST ONLY BE CALLED ONCE PER ITERATION. + simdjson_really_inline simdjson_result operator*() noexcept; + // Assumes it's being compared with the end. true if depth < iter->depth. + simdjson_really_inline bool operator==(const object_iterator &) noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_really_inline bool operator!=(const object_iterator &) noexcept; + // Checks for ']' and ',' + simdjson_really_inline object_iterator &operator++() noexcept; +private: + json_iterator_ref *iter{}; + /** + * Error, if there is one. Errors are only yielded once. + * + * PERF NOTE: we *hope* this will be elided into control flow, as it is only used (a) in the first + * iteration of the loop, or (b) for the final iteration after a missing comma is found in ++. If + * this is not elided, we should make sure it's at least not using up a register. Failing that, + * we should store it in document so there's only one of them. + */ + error_code error{}; + simdjson_really_inline object_iterator() noexcept; + friend struct simdjson_result; + friend class object; +}; + +} // namespace ondemand +} // namespace SIMDJSON_IMPLEMENTATION +} // unnamed namespace + +namespace simdjson { + +template<> +struct simdjson_result : public internal::simdjson_result_base { +public: + simdjson_really_inline simdjson_result() noexcept; + simdjson_really_inline simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::object_iterator &&value) noexcept; ///< @private + simdjson_really_inline simdjson_result(error_code error) noexcept; ///< @private + simdjson_really_inline simdjson_result(const simdjson_result &a) noexcept; + simdjson_really_inline simdjson_result &operator=(const simdjson_result &a) noexcept; + + // + // Iterator interface + // + + // Reads key and value, yielding them to the user. + simdjson_really_inline simdjson_result operator*() noexcept; // MUST ONLY BE CALLED ONCE PER ITERATION. + // Assumes it's being compared with the end. true if depth < iter->depth. + simdjson_really_inline bool operator==(const simdjson_result &) noexcept; + // Assumes it's being compared with the end. true if depth >= iter->depth. + simdjson_really_inline bool operator!=(const simdjson_result &) noexcept; + // Checks for ']' and ',' + simdjson_really_inline simdjson_result &operator++() noexcept; +}; + +} // namespace simdjson diff --git a/src/generic/ondemand/value-inl.h b/src/generic/ondemand/value-inl.h index 90a9ea95..dedda5fd 100644 --- a/src/generic/ondemand/value-inl.h +++ b/src/generic/ondemand/value-inl.h @@ -141,6 +141,17 @@ simdjson_really_inline value::operator bool() && noexcept(false) { } #endif +simdjson_really_inline simdjson_result value::begin() & noexcept { + if (*json != '[') { + log_error("not an array"); + return INCORRECT_TYPE; + } + if (!iter->started_array()) { iter.release(); } + return array_iterator(iter); +} +simdjson_really_inline simdjson_result value::end() & noexcept { + return {}; +} simdjson_really_inline simdjson_result value::operator[](std::string_view key) && noexcept { return std::forward(*this).get_object()[key]; } @@ -188,14 +199,14 @@ simdjson_really_inline simdjson_result { } -// simdjson_really_inline simdjson_result simdjson_result::begin() & noexcept { -// if (error()) { return error(); } -// return std::move(first).get_array().begin(); -// } -// simdjson_really_inline simdjson_result simdjson_result::end() & noexcept { -// if (error()) { return error(); } -// return {}; -// } +simdjson_really_inline simdjson_result simdjson_result::begin() & noexcept { + if (error()) { return error(); } + return first.begin(); +} +simdjson_really_inline simdjson_result simdjson_result::end() & noexcept { + if (error()) { return error(); } + return {}; +} simdjson_really_inline simdjson_result simdjson_result::operator[](std::string_view key) noexcept { if (error()) { return error(); } return std::forward(first)[key]; diff --git a/src/generic/ondemand/value.h b/src/generic/ondemand/value.h index 1b3c1f52..390cadd0 100644 --- a/src/generic/ondemand/value.h +++ b/src/generic/ondemand/value.h @@ -9,6 +9,7 @@ class document; class field; class object; class raw_json_string; +class array_iterator; /** * An ephemeral JSON value returned during iteration. @@ -49,8 +50,8 @@ public: simdjson_really_inline operator bool() && noexcept(false); #endif - // simdjson_really_inline simdjson_result> begin() & noexcept; - // simdjson_really_inline simdjson_result> end() & noexcept; + simdjson_really_inline simdjson_result begin() & noexcept; + simdjson_really_inline simdjson_result end() & noexcept; simdjson_really_inline simdjson_result operator[](std::string_view key) && noexcept; simdjson_really_inline simdjson_result operator[](const char *key) && noexcept; @@ -78,7 +79,7 @@ protected: const uint8_t *json{}; // The JSON text of the value friend class document; - friend class array; + friend class array_iterator; friend class field; friend class object; friend struct simdjson_result; @@ -121,8 +122,8 @@ public: simdjson_really_inline operator bool() && noexcept(false); #endif - // simdjson_really_inline simdjson_result begin() & noexcept; - // simdjson_really_inline simdjson_result end() & noexcept; + simdjson_really_inline simdjson_result begin() & noexcept; + simdjson_really_inline simdjson_result end() & noexcept; simdjson_really_inline simdjson_result operator[](std::string_view key) noexcept; simdjson_really_inline simdjson_result operator[](const char *key) noexcept; };