simdjson  3.9.4
Ridiculously Fast JSON
array-inl.h
1 #ifndef SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
2 
3 #ifndef SIMDJSON_CONDITIONAL_INCLUDE
4 #define SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
5 #include "simdjson/generic/ondemand/base.h"
6 #include "simdjson/generic/ondemand/array.h"
7 #include "simdjson/generic/ondemand/array_iterator-inl.h"
8 #include "simdjson/generic/ondemand/json_iterator.h"
9 #include "simdjson/generic/ondemand/value.h"
10 #include "simdjson/generic/ondemand/value_iterator-inl.h"
11 #endif // SIMDJSON_CONDITIONAL_INCLUDE
12 
13 namespace simdjson {
14 namespace SIMDJSON_IMPLEMENTATION {
15 namespace ondemand {
16 
17 //
18 // ### Live States
19 //
20 // While iterating or looking up values, depth >= iter->depth. at_start may vary. Error is
21 // always SUCCESS:
22 //
23 // - Start: This is the state when the array is first found and the iterator is just past the `{`.
24 // In this state, at_start == true.
25 // - Next: After we hand a scalar value to the user, or an array/object which they then fully
26 // iterate over, the iterator is at the `,` before the next value (or `]`). In this state,
27 // depth == iter->depth, at_start == false, and error == SUCCESS.
28 // - Unfinished Business: When we hand an array/object to the user which they do not fully
29 // iterate over, we need to finish that iteration by skipping child values until we reach the
30 // Next state. In this state, depth > iter->depth, at_start == false, and error == SUCCESS.
31 //
32 // ## Error States
33 //
34 // In error states, we will yield exactly one more value before stopping. iter->depth == depth
35 // and at_start is always false. We decrement after yielding the error, moving to the Finished
36 // state.
37 //
38 // - Chained Error: When the array iterator is part of an error chain--for example, in
39 // `for (auto tweet : doc["tweets"])`, where the tweet element may be missing or not be an
40 // array--we yield that error in the loop, exactly once. In this state, error != SUCCESS and
41 // iter->depth == depth, and at_start == false. We decrement depth when we yield the error.
42 // - Missing Comma Error: When the iterator ++ method discovers there is no comma between elements,
43 // we flag that as an error and treat it exactly the same as a Chained Error. In this state,
44 // error == TAPE_ERROR, iter->depth == depth, and at_start == false.
45 //
46 // ## Terminal State
47 //
48 // The terminal state has iter->depth < depth. at_start is always false.
49 //
50 // - Finished: When we have reached a `]` or have reported an error, we are finished. We signal this
51 // by decrementing depth. In this state, iter->depth < depth, at_start == false, and
52 // error == SUCCESS.
53 //
54 
55 simdjson_inline array::array(const value_iterator &_iter) noexcept
56  : iter{_iter}
57 {
58 }
59 
60 simdjson_inline simdjson_result<array> array::start(value_iterator &iter) noexcept {
61  // We don't need to know if the array is empty to start iteration, but we do want to know if there
62  // is an error--thus `simdjson_unused`.
63  simdjson_unused bool has_value;
64  SIMDJSON_TRY( iter.start_array().get(has_value) );
65  return array(iter);
66 }
67 simdjson_inline simdjson_result<array> array::start_root(value_iterator &iter) noexcept {
68  simdjson_unused bool has_value;
69  SIMDJSON_TRY( iter.start_root_array().get(has_value) );
70  return array(iter);
71 }
72 simdjson_inline simdjson_result<array> array::started(value_iterator &iter) noexcept {
73  bool has_value;
74  SIMDJSON_TRY(iter.started_array().get(has_value));
75  return array(iter);
76 }
77 
78 simdjson_inline simdjson_result<array_iterator> array::begin() noexcept {
79 #if SIMDJSON_DEVELOPMENT_CHECKS
80  if (!iter.is_at_iterator_start()) { return OUT_OF_ORDER_ITERATION; }
81 #endif
82  return array_iterator(iter);
83 }
84 simdjson_inline simdjson_result<array_iterator> array::end() noexcept {
85  return array_iterator(iter);
86 }
87 simdjson_inline error_code array::consume() noexcept {
88  auto error = iter.json_iter().skip_child(iter.depth()-1);
89  if(error) { iter.abandon(); }
90  return error;
91 }
92 
94  const uint8_t * starting_point{iter.peek_start()};
95  auto error = consume();
96  if(error) { return error; }
97  // After 'consume()', we could be left pointing just beyond the document, but that
98  // is ok because we are not going to dereference the final pointer position, we just
99  // use it to compute the length in bytes.
100  const uint8_t * final_point{iter._json_iter->unsafe_pointer()};
101  return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point));
102 }
103 
104 SIMDJSON_PUSH_DISABLE_WARNINGS
105 SIMDJSON_DISABLE_STRICT_OVERFLOW_WARNING
106 simdjson_inline simdjson_result<size_t> array::count_elements() & noexcept {
107  size_t count{0};
108  // Important: we do not consume any of the values.
109  for(simdjson_unused auto v : *this) { count++; }
110  // The above loop will always succeed, but we want to report errors.
111  if(iter.error()) { return iter.error(); }
112  // We need to move back at the start because we expect users to iterate through
113  // the array after counting the number of elements.
114  iter.reset_array();
115  return count;
116 }
117 SIMDJSON_POP_DISABLE_WARNINGS
118 
119 simdjson_inline simdjson_result<bool> array::is_empty() & noexcept {
120  bool is_not_empty;
121  auto error = iter.reset_array().get(is_not_empty);
122  if(error) { return error; }
123  return !is_not_empty;
124 }
125 
126 inline simdjson_result<bool> array::reset() & noexcept {
127  return iter.reset_array();
128 }
129 
130 inline simdjson_result<value> array::at_pointer(std::string_view json_pointer) noexcept {
131  if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; }
132  json_pointer = json_pointer.substr(1);
133  // - means "the append position" or "the element after the end of the array"
134  // We don't support this, because we're returning a real element, not a position.
135  if (json_pointer == "-") { return INDEX_OUT_OF_BOUNDS; }
136 
137  // Read the array index
138  size_t array_index = 0;
139  size_t i;
140  for (i = 0; i < json_pointer.length() && json_pointer[i] != '/'; i++) {
141  uint8_t digit = uint8_t(json_pointer[i] - '0');
142  // Check for non-digit in array index. If it's there, we're trying to get a field in an object
143  if (digit > 9) { return INCORRECT_TYPE; }
144  array_index = array_index*10 + digit;
145  }
146 
147  // 0 followed by other digits is invalid
148  if (i > 1 && json_pointer[0] == '0') { return INVALID_JSON_POINTER; } // "JSON pointer array index has other characters after 0"
149 
150  // Empty string is invalid; so is a "/" with no digits before it
151  if (i == 0) { return INVALID_JSON_POINTER; } // "Empty string in JSON pointer array index"
152  // Get the child
153  auto child = at(array_index);
154  // If there is an error, it ends here
155  if(child.error()) {
156  return child;
157  }
158 
159  // If there is a /, we're not done yet, call recursively.
160  if (i < json_pointer.length()) {
161  child = child.at_pointer(json_pointer.substr(i));
162  }
163  return child;
164 }
165 
166 inline std::string json_path_to_pointer_conversion(std::string_view json_path) {
167  if (json_path.empty() || (json_path.front() != '.' &&
168  json_path.front() != '[')) {
169  return "-1"; // This is just a sentinel value, the caller should check for this and return an error.
170  }
171 
172  std::string result;
173  // Reserve space to reduce allocations, adjusting for potential increases due
174  // to escaping.
175  result.reserve(json_path.size() * 2);
176 
177  size_t i = 0;
178 
179  while (i < json_path.length()) {
180  if (json_path[i] == '.') {
181  result += '/';
182  } else if (json_path[i] == '[') {
183  result += '/';
184  ++i; // Move past the '['
185  while (i < json_path.length() && json_path[i] != ']') {
186  if (json_path[i] == '~') {
187  result += "~0";
188  } else if (json_path[i] == '/') {
189  result += "~1";
190  } else {
191  result += json_path[i];
192  }
193  ++i;
194  }
195  if (i == json_path.length() || json_path[i] != ']') {
196  return "-1"; // Using sentinel value that will be handled as an error by the caller.
197  }
198  } else {
199  if (json_path[i] == '~') {
200  result += "~0";
201  } else if (json_path[i] == '/') {
202  result += "~1";
203  } else {
204  result += json_path[i];
205  }
206  }
207  ++i;
208  }
209 
210  return result;
211 }
212 
213 inline simdjson_result<value> array::at_path(std::string_view json_path) noexcept {
214  auto json_pointer = json_path_to_pointer_conversion(json_path);
215  if (json_pointer == "-1") { return INVALID_JSON_POINTER; }
216  return at_pointer(json_pointer);
217 }
218 
219 simdjson_inline simdjson_result<value> array::at(size_t index) noexcept {
220  size_t i = 0;
221  for (auto value : *this) {
222  if (i == index) { return value; }
223  i++;
224  }
225  return INDEX_OUT_OF_BOUNDS;
226 }
227 
228 } // namespace ondemand
229 } // namespace SIMDJSON_IMPLEMENTATION
230 } // namespace simdjson
231 
232 namespace simdjson {
233 
234 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::simdjson_result(
235  SIMDJSON_IMPLEMENTATION::ondemand::array &&value
236 ) noexcept
237  : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array>(
238  std::forward<SIMDJSON_IMPLEMENTATION::ondemand::array>(value)
239  )
240 {
241 }
242 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::simdjson_result(
243  error_code error
244 ) noexcept
245  : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::array>(error)
246 {
247 }
248 
249 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::begin() noexcept {
250  if (error()) { return error(); }
251  return first.begin();
252 }
253 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::end() noexcept {
254  if (error()) { return error(); }
255  return first.end();
256 }
257 simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::count_elements() & noexcept {
258  if (error()) { return error(); }
259  return first.count_elements();
260 }
261 simdjson_inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::is_empty() & noexcept {
262  if (error()) { return error(); }
263  return first.is_empty();
264 }
265 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at(size_t index) noexcept {
266  if (error()) { return error(); }
267  return first.at(index);
268 }
269 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_pointer(std::string_view json_pointer) noexcept {
270  if (error()) { return error(); }
271  return first.at_pointer(json_pointer);
272 }
273 simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::at_path(std::string_view json_path) noexcept {
274  if (error()) { return error(); }
275  return first.at_path(json_path);
276 }
277 simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::array>::raw_json() noexcept {
278  if (error()) { return error(); }
279  return first.raw_json();
280 }
281 } // namespace simdjson
282 
283 #endif // SIMDJSON_GENERIC_ONDEMAND_ARRAY_INL_H
simdjson_inline simdjson_result< value > at(size_t index) noexcept
Get the value at the given index.
Definition: array-inl.h:219
value_iterator iter
Iterator marking current position.
Definition: array.h:180
simdjson_inline simdjson_result< bool > is_empty() &noexcept
This method scans the beginning of the array and checks whether the array is empty.
Definition: array-inl.h:119
simdjson_result< value > at_pointer(std::string_view json_pointer) noexcept
Get the value associated with the given JSON pointer.
Definition: array-inl.h:130
static simdjson_inline simdjson_result< array > start(value_iterator &iter) noexcept
Begin array iteration.
Definition: array-inl.h:60
simdjson_inline simdjson_result< array_iterator > begin() noexcept
Begin array iteration.
Definition: array-inl.h:78
simdjson_inline array() noexcept=default
Create a new invalid array.
simdjson_inline error_code consume() noexcept
Go to the end of the array, no matter where you are right now.
Definition: array-inl.h:87
simdjson_inline simdjson_result< size_t > count_elements() &noexcept
This method scans the array and counts the number of elements.
Definition: array-inl.h:106
simdjson_result< value > at_path(std::string_view json_path) noexcept
Get the value associated with the given JSONPath expression.
Definition: array-inl.h:213
static simdjson_inline simdjson_result< array > start_root(value_iterator &iter) noexcept
Begin array iteration from the root.
Definition: array-inl.h:67
static simdjson_inline simdjson_result< array > started(value_iterator &iter) noexcept
Begin array iteration.
Definition: array-inl.h:72
simdjson_inline simdjson_result< std::string_view > raw_json() noexcept
Consumes the array and returns a string_view instance corresponding to the array as represented in JS...
Definition: array-inl.h:93
simdjson_inline simdjson_result< array_iterator > end() noexcept
Sentinel representing the end of the array.
Definition: array-inl.h:84
simdjson_result< bool > reset() &noexcept
Reset the iterator so that we are pointing back at the beginning of the array.
Definition: array-inl.h:126
An ephemeral JSON value returned during iteration.
Definition: value.h:18
The top level simdjson namespace, containing everything the library provides.
Definition: base.h:8
error_code
All possible errors returned by simdjson.
Definition: error.h:19
@ INCORRECT_TYPE
JSON element has a different type than user expected.
Definition: error.h:37
@ OUT_OF_ORDER_ITERATION
tried to iterate an array or object out of order (checked when SIMDJSON_DEVELOPMENT_CHECKS=1)
Definition: error.h:46
@ INDEX_OUT_OF_BOUNDS
JSON array index too large.
Definition: error.h:39
@ INVALID_JSON_POINTER
Invalid JSON pointer syntax.
Definition: error.h:42
The result of a simdjson operation that could fail.
Definition: error.h:215
simdjson_inline error_code error() const noexcept
The error.
Definition: error-inl.h:131