1namespace simdjson {
2namespace SIMDJSON_IMPLEMENTATION {
3namespace 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
43simdjson_inline array::array(const value_iterator &_iter) noexcept
44 : iter{_iter}
45{
46}
47
48simdjson_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}
55simdjson_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}
60simdjson_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
66simdjson_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}
72simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
73 return array_iterator(iter);
74}
75simdjson_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
81simdjson_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
92SIMDJSON_PUSH_DISABLE_WARNINGS
93SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
94simdjson_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}
105SIMDJSON_POP_DISABLE_WARNINGS
106
107simdjson_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
114inline simdjson_result<bool> array::reset() & noexcept {
115 return iter.reset_array();
116}
117
118inline 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
154simdjson_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
167namespace simdjson {
168
169simdjson_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}
177simdjson_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
184simdjson_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}
188simdjson_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}
192simdjson_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}
196simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::is_empty() & noexcept {
197 if (error()) { return error(); }
198 return first.is_empty();
199}
200simdjson_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}
204simdjson_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