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