| 1 | namespace simdjson { |
| 2 | namespace SIMDJSON_IMPLEMENTATION { |
| 3 | namespace ondemand { |
| 4 | |
| 5 | // |
| 6 | // object_iterator |
| 7 | // |
| 8 | |
| 9 | simdjson_inline object_iterator::object_iterator(const value_iterator &_iter) noexcept |
| 10 | : iter{_iter} |
| 11 | {} |
| 12 | |
| 13 | simdjson_inline simdjson_result<field> object_iterator::operator*() noexcept { |
| 14 | error_code error = iter.error(); |
| 15 | if (error) { iter.abandon(); return error; } |
| 16 | auto result = field::start(parent_iter&: iter); |
| 17 | // TODO this is a safety rail ... users should exit loops as soon as they receive an error. |
| 18 | // Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free. |
| 19 | if (result.error()) { iter.abandon(); } |
| 20 | return result; |
| 21 | } |
| 22 | simdjson_inline bool object_iterator::operator==(const object_iterator &other) const noexcept { |
| 23 | return !(*this != other); |
| 24 | } |
| 25 | simdjson_inline bool object_iterator::operator!=(const object_iterator &) const noexcept { |
| 26 | return iter.is_open(); |
| 27 | } |
| 28 | |
| 29 | SIMDJSON_PUSH_DISABLE_WARNINGS |
| 30 | SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING |
| 31 | simdjson_inline object_iterator &object_iterator::operator++() noexcept { |
| 32 | // TODO this is a safety rail ... users should exit loops as soon as they receive an error. |
| 33 | // Nonetheless, let's see if performance is OK with this if statement--the compiler may give it to us for free. |
| 34 | if (!iter.is_open()) { return *this; } // Iterator will be released if there is an error |
| 35 | |
| 36 | simdjson_unused error_code error; |
| 37 | if ((error = iter.skip_child() )) { return *this; } |
| 38 | |
| 39 | simdjson_unused bool has_value; |
| 40 | if ((error = iter.has_next_field().get(value&: has_value) )) { return *this; }; |
| 41 | return *this; |
| 42 | } |
| 43 | SIMDJSON_POP_DISABLE_WARNINGS |
| 44 | |
| 45 | // |
| 46 | // ### Live States |
| 47 | // |
| 48 | // While iterating or looking up values, depth >= iter.depth. at_start may vary. Error is |
| 49 | // always SUCCESS: |
| 50 | // |
| 51 | // - Start: This is the state when the object is first found and the iterator is just past the {. |
| 52 | // In this state, at_start == true. |
| 53 | // - Next: After we hand a scalar value to the user, or an array/object which they then fully |
| 54 | // iterate over, the iterator is at the , or } before the next value. In this state, |
| 55 | // depth == iter.depth, at_start == false, and error == SUCCESS. |
| 56 | // - Unfinished Business: When we hand an array/object to the user which they do not fully |
| 57 | // iterate over, we need to finish that iteration by skipping child values until we reach the |
| 58 | // Next state. In this state, depth > iter.depth, at_start == false, and error == SUCCESS. |
| 59 | // |
| 60 | // ## Error States |
| 61 | // |
| 62 | // In error states, we will yield exactly one more value before stopping. iter.depth == depth |
| 63 | // and at_start is always false. We decrement after yielding the error, moving to the Finished |
| 64 | // state. |
| 65 | // |
| 66 | // - Chained Error: When the object iterator is part of an error chain--for example, in |
| 67 | // `for (auto tweet : doc["tweets"])`, where the tweet field may be missing or not be an |
| 68 | // object--we yield that error in the loop, exactly once. In this state, error != SUCCESS and |
| 69 | // iter.depth == depth, and at_start == false. We decrement depth when we yield the error. |
| 70 | // - Missing Comma Error: When the iterator ++ method discovers there is no comma between fields, |
| 71 | // we flag that as an error and treat it exactly the same as a Chained Error. In this state, |
| 72 | // error == TAPE_ERROR, iter.depth == depth, and at_start == false. |
| 73 | // |
| 74 | // Errors that occur while reading a field to give to the user (such as when the key is not a |
| 75 | // string or the field is missing a colon) are yielded immediately. Depth is then decremented, |
| 76 | // moving to the Finished state without transitioning through an Error state at all. |
| 77 | // |
| 78 | // ## Terminal State |
| 79 | // |
| 80 | // The terminal state has iter.depth < depth. at_start is always false. |
| 81 | // |
| 82 | // - Finished: When we have reached a }, we are finished. We signal this by decrementing depth. |
| 83 | // In this state, iter.depth < depth, at_start == false, and error == SUCCESS. |
| 84 | // |
| 85 | |
| 86 | } // namespace ondemand |
| 87 | } // namespace SIMDJSON_IMPLEMENTATION |
| 88 | } // namespace simdjson |
| 89 | |
| 90 | namespace simdjson { |
| 91 | |
| 92 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::simdjson_result( |
| 93 | SIMDJSON_IMPLEMENTATION::ondemand::object_iterator &&value |
| 94 | ) noexcept |
| 95 | : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>(t&: value)) |
| 96 | { |
| 97 | first.iter.assert_is_valid(); |
| 98 | } |
| 99 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::simdjson_result(error_code error) noexcept |
| 100 | : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>({}, error) |
| 101 | { |
| 102 | } |
| 103 | |
| 104 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::field> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator*() noexcept { |
| 105 | if (error()) { return error(); } |
| 106 | return *first; |
| 107 | } |
| 108 | // If we're iterating and there is an error, return the error once. |
| 109 | simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator==(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> &other) const noexcept { |
| 110 | if (!first.iter.is_valid()) { return !error(); } |
| 111 | return first == other.first; |
| 112 | } |
| 113 | // If we're iterating and there is an error, return the error once. |
| 114 | simdjson_inline bool simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator!=(const simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> &other) const noexcept { |
| 115 | if (!first.iter.is_valid()) { return error(); } |
| 116 | return first != other.first; |
| 117 | } |
| 118 | // Checks for ']' and ',' |
| 119 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> &simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator>::operator++() noexcept { |
| 120 | // Clear the error if there is one, so we don't yield it twice |
| 121 | if (error()) { second = SUCCESS; return *this; } |
| 122 | ++first; |
| 123 | return *this; |
| 124 | } |
| 125 | |
| 126 | } // namespace simdjson |
| 127 | |