1 | namespace simdjson { |
2 | namespace SIMDJSON_IMPLEMENTATION { |
3 | namespace ondemand { |
4 | |
5 | // |
6 | // ### Live States |
7 | // |
8 | // While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is |
9 | // always SUCCESS: |
10 | // |
11 | // - Start: This is the state when the array is first found and the iterator is just past the `{`. |
12 | // In this state, at_start == true. |
13 | // - Next: After we hand a scalar value to the user, or an array/object which they then fully |
14 | // iterate over, the iterator is at the `,` before the next value (or `]`). In this state, |
15 | // depth == iter->depth, at_start == false, and error == SUCCESS. |
16 | // - Unfinished Business: When we hand an array/object to the user which they do not fully |
17 | // iterate over, we need to finish that iteration by skipping child values until we reach the |
18 | // Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS. |
19 | // |
20 | // ## Error States |
21 | // |
22 | // In error states, we will yield exactly one more value before stopping. iter->depth == depth |
23 | // and at_start is always false. We decrement after yielding the error, moving to the Finished |
24 | // state. |
25 | // |
26 | // - Chained Error: When the array iterator is part of an error chain--for example, in |
27 | // `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an |
28 | // array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and |
29 | // iter->depth == depth, and at_start == false. We decrement depth when we yield the error. |
30 | // - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements, |
31 | // we flag that as an error and treat it exactly the same as a Chained Error. In this state, |
32 | // error == TAPE_ERROR, iter->depth == depth, and at_start == false. |
33 | // |
34 | // ## Terminal State |
35 | // |
36 | // The terminal state has iter->depth < depth. at_start is always false. |
37 | // |
38 | // - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this |
39 | // by decrementing depth. In this state, iter->depth < depth, at_start == false, and |
40 | // error == SUCCESS. |
41 | // |
42 | |
43 | simdjson_inline array::array(const value_iterator &_iter) noexcept |
44 | : iter{_iter} |
45 | { |
46 | } |
47 | |
48 | simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept { |
49 | // We don't need to know if the array is empty to start iteration, but we do want to know if there |
50 | // is an error--thus `simdjson_unused`. |
51 | simdjson_unused bool has_value; |
52 | SIMDJSON_TRY( iter.start_array().get(has_value) ); |
53 | return array(iter); |
54 | } |
55 | simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept { |
56 | simdjson_unused bool has_value; |
57 | SIMDJSON_TRY( iter.start_root_array().get(has_value) ); |
58 | return array(iter); |
59 | } |
60 | simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept { |
61 | bool has_value; |
62 | SIMDJSON_TRY(iter.started_array().get(has_value)); |
63 | return array(iter); |
64 | } |
65 | |
66 | simdjson_inline simdjson_result<array_iterator> array::begin() noexcept { |
67 | #if SIMDJSON_DEVELOPMENT_CHECKS |
68 | if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; } |
69 | #endif |
70 | return array_iterator(iter); |
71 | } |
72 | simdjson_inline simdjson_result<array_iterator> array::end() noexcept { |
73 | return array_iterator(iter); |
74 | } |
75 | simdjson_inline error_code array::consume() noexcept { |
76 | auto error = iter.json_iter().skip_child(parent_depth: iter.depth()-1); |
77 | if(error) { iter.abandon(); } |
78 | return error; |
79 | } |
80 | |
81 | simdjson_inline simdjson_result<std::string_view> array::raw_json() noexcept { |
82 | const uint8_t * starting_point{iter.peek_start()}; |
83 | auto error = consume(); |
84 | if(error) { return error; } |
85 | // After 'consume()', we could be left pointing just beyond the document, but that |
86 | // is ok because we are not going to dereference the final pointer position, we just |
87 | // use it to compute the length in bytes. |
88 | const uint8_t * final_point{iter._json_iter->unsafe_pointer()}; |
89 | return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point)); |
90 | } |
91 | |
92 | SIMDJSON_PUSH_DISABLE_WARNINGS |
93 | SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING |
94 | simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept { |
95 | size_t count{0}; |
96 | // Important: we do not consume any of the values. |
97 | for(simdjson_unused auto v : *this) { count++; } |
98 | // The above loop will always succeed, but we want to report errors. |
99 | if(iter.error()) { return iter.error(); } |
100 | // We need to move back at the start because we expect users to iterate through |
101 | // the array after counting the number of elements. |
102 | iter.reset_array(); |
103 | return count; |
104 | } |
105 | SIMDJSON_POP_DISABLE_WARNINGS |
106 | |
107 | simdjson_inline simdjson_result<bool> array::is_empty() & noexcept { |
108 | bool is_not_empty; |
109 | auto error = iter.reset_array().get(value&: is_not_empty); |
110 | if(error) { return error; } |
111 | return !is_not_empty; |
112 | } |
113 | |
114 | inline simdjson_result<bool> array::reset() & noexcept { |
115 | return iter.reset_array(); |
116 | } |
117 | |
118 | inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept { |
119 | if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; } |
120 | json_pointer = json_pointer.substr(pos: 1); |
121 | // - means "the append position" or "the element after the end of the array" |
122 | // We don't support this, because we're returning a real element, not a position. |
123 | if (json_pointer == "-" ) { return INDEX_OUT_OF_BOUNDS; } |
124 | |
125 | // Read the array index |
126 | size_t array_index = 0; |
127 | size_t i; |
128 | for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) { |
129 | uint8_t digit = uint8_t(json_pointer[i] - '0'); |
130 | // Check for non-digit in array index. If it's there, we're trying to get a field in an object |
131 | if (digit > 9) { return INCORRECT_TYPE; } |
132 | array_index = array_index*10 + digit; |
133 | } |
134 | |
135 | // 0 followed by other digits is invalid |
136 | if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0" |
137 | |
138 | // Empty string is invalid; so is a "/" with no digits before it |
139 | if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index" |
140 | // Get the child |
141 | auto child = at(index: array_index); |
142 | // If there is an error, it ends here |
143 | if(child.error()) { |
144 | return child; |
145 | } |
146 | |
147 | // If there is a /, we're not done yet, call recursively. |
148 | if (i < json_pointer.length()) { |
149 | child = child.at_pointer(json_pointer: json_pointer.substr(pos: i)); |
150 | } |
151 | return child; |
152 | } |
153 | |
154 | simdjson_inline simdjson_result<value> array::at(size_t index) noexcept { |
155 | size_t i = 0; |
156 | for (auto value : *this) { |
157 | if (i == index) { return value; } |
158 | i++; |
159 | } |
160 | return INDEX_OUT_OF_BOUNDS; |
161 | } |
162 | |
163 | } // namespace ondemand |
164 | } // namespace SIMDJSON_IMPLEMENTATION |
165 | } // namespace simdjson |
166 | |
167 | namespace simdjson { |
168 | |
169 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::simdjson_result( |
170 | SIMDJSON_IMPLEMENTATION::ondemand::array &&value |
171 | ) noexcept |
172 | : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array>( |
173 | std::forward<SIMDJSON_IMPLEMENTATION::ondemand::array>(t&: value) |
174 | ) |
175 | { |
176 | } |
177 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::simdjson_result( |
178 | error_code error |
179 | ) noexcept |
180 | : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array>(error) |
181 | { |
182 | } |
183 | |
184 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::begin() noexcept { |
185 | if (error()) { return error(); } |
186 | return first.begin(); |
187 | } |
188 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::end() noexcept { |
189 | if (error()) { return error(); } |
190 | return first.end(); |
191 | } |
192 | simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::count_elements() & noexcept { |
193 | if (error()) { return error(); } |
194 | return first.count_elements(); |
195 | } |
196 | simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::is_empty() & noexcept { |
197 | if (error()) { return error(); } |
198 | return first.is_empty(); |
199 | } |
200 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at(size_t index) noexcept { |
201 | if (error()) { return error(); } |
202 | return first.at(index); |
203 | } |
204 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept { |
205 | if (error()) { return error(); } |
206 | return first.at_pointer(json_pointer); |
207 | } |
208 | } // namespace simdjson |
209 | |