1 | #ifndef SIMDJSON_INLINE_ELEMENT_H |
2 | #define SIMDJSON_INLINE_ELEMENT_H |
3 | |
4 | #include "simdjson/dom/array.h" |
5 | #include "simdjson/dom/element.h" |
6 | #include "simdjson/dom/object.h" |
7 | #include <cstring> |
8 | #include <utility> |
9 | |
10 | namespace simdjson { |
11 | |
12 | // |
13 | // simdjson_result<dom::element> inline implementation |
14 | // |
15 | simdjson_inline simdjson_result<dom::element>::simdjson_result() noexcept |
16 | : internal::simdjson_result_base<dom::element>() {} |
17 | simdjson_inline simdjson_result<dom::element>::simdjson_result(dom::element &&value) noexcept |
18 | : internal::simdjson_result_base<dom::element>(std::forward<dom::element>(t&: value)) {} |
19 | simdjson_inline simdjson_result<dom::element>::simdjson_result(error_code error) noexcept |
20 | : internal::simdjson_result_base<dom::element>(error) {} |
21 | inline simdjson_result<dom::element_type> simdjson_result<dom::element>::type() const noexcept { |
22 | if (error()) { return error(); } |
23 | return first.type(); |
24 | } |
25 | |
26 | template<typename T> |
27 | simdjson_inline bool simdjson_result<dom::element>::is() const noexcept { |
28 | return !error() && first.is<T>(); |
29 | } |
30 | template<typename T> |
31 | simdjson_inline simdjson_result<T> simdjson_result<dom::element>::get() const noexcept { |
32 | if (error()) { return error(); } |
33 | return first.get<T>(); |
34 | } |
35 | template<typename T> |
36 | simdjson_warn_unused simdjson_inline error_code simdjson_result<dom::element>::get(T &value) const noexcept { |
37 | if (error()) { return error(); } |
38 | return first.get<T>(value); |
39 | } |
40 | |
41 | simdjson_inline simdjson_result<dom::array> simdjson_result<dom::element>::get_array() const noexcept { |
42 | if (error()) { return error(); } |
43 | return first.get_array(); |
44 | } |
45 | simdjson_inline simdjson_result<dom::object> simdjson_result<dom::element>::get_object() const noexcept { |
46 | if (error()) { return error(); } |
47 | return first.get_object(); |
48 | } |
49 | simdjson_inline simdjson_result<const char *> simdjson_result<dom::element>::get_c_str() const noexcept { |
50 | if (error()) { return error(); } |
51 | return first.get_c_str(); |
52 | } |
53 | simdjson_inline simdjson_result<size_t> simdjson_result<dom::element>::get_string_length() const noexcept { |
54 | if (error()) { return error(); } |
55 | return first.get_string_length(); |
56 | } |
57 | simdjson_inline simdjson_result<std::string_view> simdjson_result<dom::element>::get_string() const noexcept { |
58 | if (error()) { return error(); } |
59 | return first.get_string(); |
60 | } |
61 | simdjson_inline simdjson_result<int64_t> simdjson_result<dom::element>::get_int64() const noexcept { |
62 | if (error()) { return error(); } |
63 | return first.get_int64(); |
64 | } |
65 | simdjson_inline simdjson_result<uint64_t> simdjson_result<dom::element>::get_uint64() const noexcept { |
66 | if (error()) { return error(); } |
67 | return first.get_uint64(); |
68 | } |
69 | simdjson_inline simdjson_result<double> simdjson_result<dom::element>::get_double() const noexcept { |
70 | if (error()) { return error(); } |
71 | return first.get_double(); |
72 | } |
73 | simdjson_inline simdjson_result<bool> simdjson_result<dom::element>::get_bool() const noexcept { |
74 | if (error()) { return error(); } |
75 | return first.get_bool(); |
76 | } |
77 | |
78 | simdjson_inline bool simdjson_result<dom::element>::is_array() const noexcept { |
79 | return !error() && first.is_array(); |
80 | } |
81 | simdjson_inline bool simdjson_result<dom::element>::is_object() const noexcept { |
82 | return !error() && first.is_object(); |
83 | } |
84 | simdjson_inline bool simdjson_result<dom::element>::is_string() const noexcept { |
85 | return !error() && first.is_string(); |
86 | } |
87 | simdjson_inline bool simdjson_result<dom::element>::is_int64() const noexcept { |
88 | return !error() && first.is_int64(); |
89 | } |
90 | simdjson_inline bool simdjson_result<dom::element>::is_uint64() const noexcept { |
91 | return !error() && first.is_uint64(); |
92 | } |
93 | simdjson_inline bool simdjson_result<dom::element>::is_double() const noexcept { |
94 | return !error() && first.is_double(); |
95 | } |
96 | simdjson_inline bool simdjson_result<dom::element>::is_number() const noexcept { |
97 | return !error() && first.is_number(); |
98 | } |
99 | simdjson_inline bool simdjson_result<dom::element>::is_bool() const noexcept { |
100 | return !error() && first.is_bool(); |
101 | } |
102 | |
103 | simdjson_inline bool simdjson_result<dom::element>::is_null() const noexcept { |
104 | return !error() && first.is_null(); |
105 | } |
106 | |
107 | simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](std::string_view key) const noexcept { |
108 | if (error()) { return error(); } |
109 | return first[key]; |
110 | } |
111 | simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::operator[](const char *key) const noexcept { |
112 | if (error()) { return error(); } |
113 | return first[key]; |
114 | } |
115 | simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_pointer(const std::string_view json_pointer) const noexcept { |
116 | if (error()) { return error(); } |
117 | return first.at_pointer(json_pointer); |
118 | } |
119 | #ifndef SIMDJSON_DISABLE_DEPRECATED_API |
120 | [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 " )]] |
121 | simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(const std::string_view json_pointer) const noexcept { |
122 | SIMDJSON_PUSH_DISABLE_WARNINGS |
123 | SIMDJSON_DISABLE_DEPRECATED_WARNING |
124 | if (error()) { return error(); } |
125 | return first.at(json_pointer); |
126 | SIMDJSON_POP_DISABLE_WARNINGS |
127 | } |
128 | #endif // SIMDJSON_DISABLE_DEPRECATED_API |
129 | simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at(size_t index) const noexcept { |
130 | if (error()) { return error(); } |
131 | return first.at(index); |
132 | } |
133 | simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_key(std::string_view key) const noexcept { |
134 | if (error()) { return error(); } |
135 | return first.at_key(key); |
136 | } |
137 | simdjson_inline simdjson_result<dom::element> simdjson_result<dom::element>::at_key_case_insensitive(std::string_view key) const noexcept { |
138 | if (error()) { return error(); } |
139 | return first.at_key_case_insensitive(key); |
140 | } |
141 | |
142 | #if SIMDJSON_EXCEPTIONS |
143 | |
144 | simdjson_inline simdjson_result<dom::element>::operator bool() const noexcept(false) { |
145 | return get<bool>(); |
146 | } |
147 | simdjson_inline simdjson_result<dom::element>::operator const char *() const noexcept(false) { |
148 | return get<const char *>(); |
149 | } |
150 | simdjson_inline simdjson_result<dom::element>::operator std::string_view() const noexcept(false) { |
151 | return get<std::string_view>(); |
152 | } |
153 | simdjson_inline simdjson_result<dom::element>::operator uint64_t() const noexcept(false) { |
154 | return get<uint64_t>(); |
155 | } |
156 | simdjson_inline simdjson_result<dom::element>::operator int64_t() const noexcept(false) { |
157 | return get<int64_t>(); |
158 | } |
159 | simdjson_inline simdjson_result<dom::element>::operator double() const noexcept(false) { |
160 | return get<double>(); |
161 | } |
162 | simdjson_inline simdjson_result<dom::element>::operator dom::array() const noexcept(false) { |
163 | return get<dom::array>(); |
164 | } |
165 | simdjson_inline simdjson_result<dom::element>::operator dom::object() const noexcept(false) { |
166 | return get<dom::object>(); |
167 | } |
168 | |
169 | simdjson_inline dom::array::iterator simdjson_result<dom::element>::begin() const noexcept(false) { |
170 | if (error()) { throw simdjson_error(error()); } |
171 | return first.begin(); |
172 | } |
173 | simdjson_inline dom::array::iterator simdjson_result<dom::element>::end() const noexcept(false) { |
174 | if (error()) { throw simdjson_error(error()); } |
175 | return first.end(); |
176 | } |
177 | |
178 | #endif // SIMDJSON_EXCEPTIONS |
179 | |
180 | namespace dom { |
181 | |
182 | // |
183 | // element inline implementation |
184 | // |
185 | simdjson_inline element::element() noexcept : tape{} {} |
186 | simdjson_inline element::element(const internal::tape_ref &_tape) noexcept : tape{_tape} { } |
187 | |
188 | inline element_type element::type() const noexcept { |
189 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
190 | auto tape_type = tape.tape_ref_type(); |
191 | return tape_type == internal::tape_type::FALSE_VALUE ? element_type::BOOL : static_cast<element_type>(tape_type); |
192 | } |
193 | |
194 | inline simdjson_result<bool> element::get_bool() const noexcept { |
195 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
196 | if(tape.is_true()) { |
197 | return true; |
198 | } else if(tape.is_false()) { |
199 | return false; |
200 | } |
201 | return INCORRECT_TYPE; |
202 | } |
203 | inline simdjson_result<const char *> element::get_c_str() const noexcept { |
204 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
205 | switch (tape.tape_ref_type()) { |
206 | case internal::tape_type::STRING: { |
207 | return tape.get_c_str(); |
208 | } |
209 | default: |
210 | return INCORRECT_TYPE; |
211 | } |
212 | } |
213 | inline simdjson_result<size_t> element::get_string_length() const noexcept { |
214 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
215 | switch (tape.tape_ref_type()) { |
216 | case internal::tape_type::STRING: { |
217 | return tape.get_string_length(); |
218 | } |
219 | default: |
220 | return INCORRECT_TYPE; |
221 | } |
222 | } |
223 | inline simdjson_result<std::string_view> element::get_string() const noexcept { |
224 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
225 | switch (tape.tape_ref_type()) { |
226 | case internal::tape_type::STRING: |
227 | return tape.get_string_view(); |
228 | default: |
229 | return INCORRECT_TYPE; |
230 | } |
231 | } |
232 | inline simdjson_result<uint64_t> element::get_uint64() const noexcept { |
233 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
234 | if(simdjson_unlikely(!tape.is_uint64())) { // branch rarely taken |
235 | if(tape.is_int64()) { |
236 | int64_t result = tape.next_tape_value<int64_t>(); |
237 | if (result < 0) { |
238 | return NUMBER_OUT_OF_RANGE; |
239 | } |
240 | return uint64_t(result); |
241 | } |
242 | return INCORRECT_TYPE; |
243 | } |
244 | return tape.next_tape_value<int64_t>(); |
245 | } |
246 | inline simdjson_result<int64_t> element::get_int64() const noexcept { |
247 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
248 | if(simdjson_unlikely(!tape.is_int64())) { // branch rarely taken |
249 | if(tape.is_uint64()) { |
250 | uint64_t result = tape.next_tape_value<uint64_t>(); |
251 | // Wrapping max in parens to handle Windows issue: https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std |
252 | if (result > uint64_t((std::numeric_limits<int64_t>::max)())) { |
253 | return NUMBER_OUT_OF_RANGE; |
254 | } |
255 | return static_cast<int64_t>(result); |
256 | } |
257 | return INCORRECT_TYPE; |
258 | } |
259 | return tape.next_tape_value<int64_t>(); |
260 | } |
261 | inline simdjson_result<double> element::get_double() const noexcept { |
262 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
263 | // Performance considerations: |
264 | // 1. Querying tape_ref_type() implies doing a shift, it is fast to just do a straight |
265 | // comparison. |
266 | // 2. Using a switch-case relies on the compiler guessing what kind of code generation |
267 | // we want... But the compiler cannot know that we expect the type to be "double" |
268 | // most of the time. |
269 | // We can expect get<double> to refer to a double type almost all the time. |
270 | // It is important to craft the code accordingly so that the compiler can use this |
271 | // information. (This could also be solved with profile-guided optimization.) |
272 | if(simdjson_unlikely(!tape.is_double())) { // branch rarely taken |
273 | if(tape.is_uint64()) { |
274 | return double(tape.next_tape_value<uint64_t>()); |
275 | } else if(tape.is_int64()) { |
276 | return double(tape.next_tape_value<int64_t>()); |
277 | } |
278 | return INCORRECT_TYPE; |
279 | } |
280 | // this is common: |
281 | return tape.next_tape_value<double>(); |
282 | } |
283 | inline simdjson_result<array> element::get_array() const noexcept { |
284 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
285 | switch (tape.tape_ref_type()) { |
286 | case internal::tape_type::START_ARRAY: |
287 | return array(tape); |
288 | default: |
289 | return INCORRECT_TYPE; |
290 | } |
291 | } |
292 | inline simdjson_result<object> element::get_object() const noexcept { |
293 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
294 | switch (tape.tape_ref_type()) { |
295 | case internal::tape_type::START_OBJECT: |
296 | return object(tape); |
297 | default: |
298 | return INCORRECT_TYPE; |
299 | } |
300 | } |
301 | |
302 | template<typename T> |
303 | simdjson_warn_unused simdjson_inline error_code element::get(T &value) const noexcept { |
304 | return get<T>().get(value); |
305 | } |
306 | // An element-specific version prevents recursion with simdjson_result::get<element>(value) |
307 | template<> |
308 | simdjson_warn_unused simdjson_inline error_code element::get<element>(element &value) const noexcept { |
309 | value = element(tape); |
310 | return SUCCESS; |
311 | } |
312 | template<typename T> |
313 | inline void element::tie(T &value, error_code &error) && noexcept { |
314 | error = get<T>(value); |
315 | } |
316 | |
317 | template<typename T> |
318 | simdjson_inline bool element::is() const noexcept { |
319 | auto result = get<T>(); |
320 | return !result.error(); |
321 | } |
322 | |
323 | template<> inline simdjson_result<array> element::get<array>() const noexcept { return get_array(); } |
324 | template<> inline simdjson_result<object> element::get<object>() const noexcept { return get_object(); } |
325 | template<> inline simdjson_result<const char *> element::get<const char *>() const noexcept { return get_c_str(); } |
326 | template<> inline simdjson_result<std::string_view> element::get<std::string_view>() const noexcept { return get_string(); } |
327 | template<> inline simdjson_result<int64_t> element::get<int64_t>() const noexcept { return get_int64(); } |
328 | template<> inline simdjson_result<uint64_t> element::get<uint64_t>() const noexcept { return get_uint64(); } |
329 | template<> inline simdjson_result<double> element::get<double>() const noexcept { return get_double(); } |
330 | template<> inline simdjson_result<bool> element::get<bool>() const noexcept { return get_bool(); } |
331 | |
332 | inline bool element::is_array() const noexcept { return is<array>(); } |
333 | inline bool element::is_object() const noexcept { return is<object>(); } |
334 | inline bool element::is_string() const noexcept { return is<std::string_view>(); } |
335 | inline bool element::is_int64() const noexcept { return is<int64_t>(); } |
336 | inline bool element::is_uint64() const noexcept { return is<uint64_t>(); } |
337 | inline bool element::is_double() const noexcept { return is<double>(); } |
338 | inline bool element::is_bool() const noexcept { return is<bool>(); } |
339 | inline bool element::is_number() const noexcept { return is_int64() || is_uint64() || is_double(); } |
340 | |
341 | inline bool element::is_null() const noexcept { |
342 | return tape.is_null_on_tape(); |
343 | } |
344 | |
345 | #if SIMDJSON_EXCEPTIONS |
346 | |
347 | inline element::operator bool() const noexcept(false) { return get<bool>(); } |
348 | inline element::operator const char*() const noexcept(false) { return get<const char *>(); } |
349 | inline element::operator std::string_view() const noexcept(false) { return get<std::string_view>(); } |
350 | inline element::operator uint64_t() const noexcept(false) { return get<uint64_t>(); } |
351 | inline element::operator int64_t() const noexcept(false) { return get<int64_t>(); } |
352 | inline element::operator double() const noexcept(false) { return get<double>(); } |
353 | inline element::operator array() const noexcept(false) { return get<array>(); } |
354 | inline element::operator object() const noexcept(false) { return get<object>(); } |
355 | |
356 | inline array::iterator element::begin() const noexcept(false) { |
357 | return get<array>().begin(); |
358 | } |
359 | inline array::iterator element::end() const noexcept(false) { |
360 | return get<array>().end(); |
361 | } |
362 | |
363 | #endif // SIMDJSON_EXCEPTIONS |
364 | |
365 | inline simdjson_result<element> element::operator[](std::string_view key) const noexcept { |
366 | return at_key(key); |
367 | } |
368 | inline simdjson_result<element> element::operator[](const char *key) const noexcept { |
369 | return at_key(key); |
370 | } |
371 | |
372 | inline simdjson_result<element> element::at_pointer(std::string_view json_pointer) const noexcept { |
373 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
374 | switch (tape.tape_ref_type()) { |
375 | case internal::tape_type::START_OBJECT: |
376 | return object(tape).at_pointer(json_pointer); |
377 | case internal::tape_type::START_ARRAY: |
378 | return array(tape).at_pointer(json_pointer); |
379 | default: { |
380 | if(!json_pointer.empty()) { // a non-empty string is invalid on an atom |
381 | return INVALID_JSON_POINTER; |
382 | } |
383 | // an empty string means that we return the current node |
384 | dom::element copy(*this); |
385 | return simdjson_result<element>(std::move(copy)); |
386 | } |
387 | } |
388 | } |
389 | #ifndef SIMDJSON_DISABLE_DEPRECATED_API |
390 | [[deprecated("For standard compliance, use at_pointer instead, and prefix your pointers with a slash '/', see RFC6901 " )]] |
391 | inline simdjson_result<element> element::at(std::string_view json_pointer) const noexcept { |
392 | // version 0.4 of simdjson allowed non-compliant pointers |
393 | auto std_pointer = (json_pointer.empty() ? "" : "/" ) + std::string(json_pointer.begin(), json_pointer.end()); |
394 | return at_pointer(json_pointer: std_pointer); |
395 | } |
396 | #endif // SIMDJSON_DISABLE_DEPRECATED_API |
397 | |
398 | inline simdjson_result<element> element::at(size_t index) const noexcept { |
399 | return get<array>().at(index); |
400 | } |
401 | inline simdjson_result<element> element::at_key(std::string_view key) const noexcept { |
402 | return get<object>().at_key(key); |
403 | } |
404 | inline simdjson_result<element> element::at_key_case_insensitive(std::string_view key) const noexcept { |
405 | return get<object>().at_key_case_insensitive(key); |
406 | } |
407 | |
408 | inline bool element::dump_raw_tape(std::ostream &out) const noexcept { |
409 | SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 |
410 | return tape.doc->dump_raw_tape(os&: out); |
411 | } |
412 | |
413 | |
414 | inline std::ostream& operator<<(std::ostream& out, element_type type) { |
415 | switch (type) { |
416 | case element_type::ARRAY: |
417 | return out << "array" ; |
418 | case element_type::OBJECT: |
419 | return out << "object" ; |
420 | case element_type::INT64: |
421 | return out << "int64_t" ; |
422 | case element_type::UINT64: |
423 | return out << "uint64_t" ; |
424 | case element_type::DOUBLE: |
425 | return out << "double" ; |
426 | case element_type::STRING: |
427 | return out << "string" ; |
428 | case element_type::BOOL: |
429 | return out << "bool" ; |
430 | case element_type::NULL_VALUE: |
431 | return out << "null" ; |
432 | default: |
433 | return out << "unexpected content!!!" ; // abort() usage is forbidden in the library |
434 | } |
435 | } |
436 | |
437 | } // namespace dom |
438 | |
439 | } // namespace simdjson |
440 | |
441 | #endif // SIMDJSON_INLINE_ELEMENT_H |
442 | |