/src/simdjson/include/simdjson/generic/ondemand/object-inl.h
Line | Count | Source |
1 | | #ifndef SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H |
2 | | |
3 | | #ifndef SIMDJSON_CONDITIONAL_INCLUDE |
4 | | #define SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H |
5 | | #include "simdjson/generic/ondemand/base.h" |
6 | | #include "simdjson/generic/ondemand/field.h" |
7 | | #include "simdjson/generic/ondemand/object.h" |
8 | | #include "simdjson/generic/ondemand/object_iterator.h" |
9 | | #include "simdjson/generic/ondemand/raw_json_string.h" |
10 | | #include "simdjson/generic/ondemand/json_iterator.h" |
11 | | #include "simdjson/generic/ondemand/value-inl.h" |
12 | | #include "simdjson/jsonpathutil.h" |
13 | | #include <utility> |
14 | | #if SIMDJSON_SUPPORTS_CONCEPTS |
15 | | #include <tuple> // std::forward_as_tuple/get for the variadic for_each adapter |
16 | | #endif |
17 | | #if SIMDJSON_STATIC_REFLECTION |
18 | | #include "simdjson/generic/ondemand/json_string_builder.h" // for constevalutil::fixed_string |
19 | | #include <meta> |
20 | | #endif |
21 | | #endif // SIMDJSON_CONDITIONAL_INCLUDE |
22 | | |
23 | | namespace simdjson { |
24 | | namespace SIMDJSON_IMPLEMENTATION { |
25 | | namespace ondemand { |
26 | | |
27 | 0 | simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) & noexcept { |
28 | 0 | bool has_value; |
29 | 0 | SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); |
30 | 0 | if (!has_value) { |
31 | 0 | logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data()); |
32 | 0 | return NO_SUCH_FIELD; |
33 | 0 | } |
34 | 0 | return value(iter.child()); |
35 | 0 | } |
36 | 0 | simdjson_inline simdjson_result<value> object::find_field_unordered(const std::string_view key) && noexcept { |
37 | 0 | bool has_value; |
38 | 0 | SIMDJSON_TRY( iter.find_field_unordered_raw(key).get(has_value) ); |
39 | 0 | if (!has_value) { |
40 | 0 | logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data()); |
41 | 0 | return NO_SUCH_FIELD; |
42 | 0 | } |
43 | 0 | return value(iter.child()); |
44 | 0 | } |
45 | 0 | simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) & noexcept { |
46 | 0 | #if SIMDJSON_DEVELOPMENT_CHECKS |
47 | 0 | if (locked) return OUT_OF_ORDER_ITERATION; |
48 | 0 | #endif |
49 | 0 | return find_field_unordered(key); |
50 | 0 | } |
51 | 0 | simdjson_inline simdjson_result<value> object::operator[](const std::string_view key) && noexcept { |
52 | 0 | #if SIMDJSON_DEVELOPMENT_CHECKS |
53 | 0 | if (locked) return OUT_OF_ORDER_ITERATION; |
54 | 0 | #endif |
55 | 0 | return std::forward<object>(*this).find_field_unordered(key); |
56 | 0 | } |
57 | 6.83k | simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) & noexcept { |
58 | 6.83k | #if SIMDJSON_DEVELOPMENT_CHECKS |
59 | 6.83k | if (locked) return OUT_OF_ORDER_ITERATION; |
60 | 6.83k | #endif |
61 | 6.83k | bool has_value; |
62 | 6.83k | SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); |
63 | 5.08k | if (!has_value) { |
64 | 1.49k | logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data()); |
65 | 1.49k | return NO_SUCH_FIELD; |
66 | 1.49k | } |
67 | 3.58k | return value(iter.child()); |
68 | 5.08k | } |
69 | 0 | simdjson_inline simdjson_result<value> object::find_field(const std::string_view key) && noexcept { |
70 | 0 | #if SIMDJSON_DEVELOPMENT_CHECKS |
71 | 0 | if (locked) return OUT_OF_ORDER_ITERATION; |
72 | 0 | #endif |
73 | 0 | bool has_value; |
74 | 0 | SIMDJSON_TRY( iter.find_field_raw(key).get(has_value) ); |
75 | 0 | if (!has_value) { |
76 | 0 | logger::log_line(iter.json_iter(), "ERROR: ", "Cannot find key %.*s", "", -1, 0, logger::log_level::error, static_cast<int>(key.size()), key.data()); |
77 | 0 | return NO_SUCH_FIELD; |
78 | 0 | } |
79 | 0 | return value(iter.child()); |
80 | 0 | } |
81 | | |
82 | | #if SIMDJSON_SUPPORTS_CONCEPTS |
83 | | template <typename Selector, typename Func> |
84 | | requires key_selector_type<Selector> && |
85 | | std::is_invocable_v<Func&, std::size_t, value> |
86 | | simdjson_flatten simdjson_inline for_each_result object::for_each(Func&& on_match) |
87 | | noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, value>) { |
88 | | // Single pass driven directly by the value_iterator, mirroring |
89 | | // find_field_unordered_raw + value(iter.child()). Compared to walking via |
90 | | // object_iterator/field, this avoids constructing a simdjson_result<field> and |
91 | | // a field (key + value) for every field -- and the development-check bookkeeping |
92 | | // in object_iterator -- building a value only for the fields that actually match. |
93 | | // We operate on a copy of the iterator, as object::begin() would. |
94 | | #if SIMDJSON_DEVELOPMENT_CHECKS |
95 | | // Mirror object::begin(): for_each must start at the beginning of the object, |
96 | | // not from some position left behind by a prior find_field on the same object. |
97 | | if (!iter.is_at_iterator_start()) { return {OUT_OF_ORDER_ITERATION, 0}; } |
98 | | #endif |
99 | | value_iterator it = iter; |
100 | | std::size_t matched = 0; |
101 | | // Track which selector indices have already matched, as a compile-time bitset |
102 | | // (one 64-bit word per 64 keys): the callback fires once per key, on its first |
103 | | // occurrence, and we stop as soon as every key has matched. |
104 | | constexpr std::size_t seen_words = (Selector::size() + 63) / 64; |
105 | | std::array<std::uint64_t, seen_words> seen{}; |
106 | | while (it.is_open()) { |
107 | | raw_json_string key; |
108 | | error_code error; |
109 | | std::size_t idx; |
110 | | if constexpr (Selector::window.ok) { |
111 | | // A window selector confirms a key from its raw bytes alone (the closing |
112 | | // quote bounds it), so we take the length-free path: field_key (no backward |
113 | | // scan, mirroring the ordered find_field) feeding match_raw(raw_json_string). |
114 | | if ((error = it.field_key().get(key))) { it.abandon(); return {error, matched}; } |
115 | | if ((error = it.field_value())) { it.abandon(); return {error, matched}; } |
116 | | idx = Selector::match_raw(key); |
117 | | } else { |
118 | | // Otherwise derive the key length from the structural index (the following |
119 | | // ':' token) to feed the length-aware hash, avoiding a forward SIMD scan. |
120 | | std::size_t key_len; |
121 | | if ((error = it.field_key_with_length(key, key_len))) { it.abandon(); return {error, matched}; } |
122 | | if ((error = it.field_value())) { it.abandon(); return {error, matched}; } |
123 | | idx = Selector::match_raw(key.raw(), key_len); |
124 | | } |
125 | | if (idx < Selector::size()) { |
126 | | const std::uint64_t seen_bit = std::uint64_t{1} << (idx & 63); |
127 | | std::uint64_t &seen_word = seen[idx >> 6]; |
128 | | if (!(seen_word & seen_bit)) { |
129 | | seen_word |= seen_bit; |
130 | | value matched_value(it.child()); |
131 | | // The callback may return void or anything convertible to error_code |
132 | | // (error_code itself, or a for_each_result from a nested for_each). When |
133 | | // it yields an error_code, we stop at the first non-SUCCESS result and |
134 | | // propagate it so the caller can surface value-parse errors (for example, |
135 | | // a type mismatch on a matched field). A void-returning callback is |
136 | | // responsible for handling its own errors. |
137 | | if constexpr (std::is_convertible_v<decltype(on_match(idx, matched_value)), error_code>) { |
138 | | // Unlike the internal-error paths above, a callback error does not |
139 | | // abandon the iterator: we leave it recoverable so the caller can keep |
140 | | // using the object (or its parent) after handling the error. |
141 | | if ((error = on_match(idx, matched_value))) { return {error, matched}; } |
142 | | } else { |
143 | | on_match(idx, matched_value); |
144 | | } |
145 | | if (++matched >= Selector::size()) { break; } |
146 | | } |
147 | | } |
148 | | // Mirror object_iterator::operator++'s safety rail: if the callback consumed |
149 | | // the value and left the iterator closed or in error (e.g. a void callback |
150 | | // that swallowed a fatal sub-iteration error), stop here rather than calling |
151 | | // skip_child on a closed iterator. |
152 | | if (!it.is_open()) { break; } |
153 | | // Skip the value (a no-op if the callback consumed it) and step to the next |
154 | | // field; has_next_field() ends the container on '}', which closes the loop. |
155 | | if ((error = it.skip_child())) { it.abandon(); return {error, matched}; } |
156 | | if ((error = it.has_next_field().error())) { return {error, matched}; } |
157 | | } |
158 | | return {SUCCESS, matched}; |
159 | | } |
160 | | |
161 | | namespace key_selector_for_each_detail { |
162 | | |
163 | | // Dispatch a matched value to the I-th handler in the tuple (0-based). A handler |
164 | | // is either an invocable taking the value (void- or error_code-returning) or a |
165 | | // deserialization target, in which case we assign via value::get. We always |
166 | | // return error_code so the core (index, value) for_each can treat the adapter |
167 | | // uniformly. |
168 | | template <typename Tuple, std::size_t... Is> |
169 | | simdjson_really_inline error_code dispatch_value( |
170 | | std::size_t idx, Tuple& handlers, value v, std::index_sequence<Is...>) { |
171 | | error_code err = SUCCESS; |
172 | | auto try_one = [&](auto Ic) { |
173 | | constexpr std::size_t I = decltype(Ic)::value; |
174 | | if (idx == I) { |
175 | | auto&& h = std::get<I>(handlers); |
176 | | using H = std::remove_reference_t<decltype(h)>; |
177 | | if constexpr (std::is_invocable_v<H&, value>) { |
178 | | // A handler returning void runs for its side effects; one returning |
179 | | // anything convertible to error_code (error_code, or a for_each_result |
180 | | // from a nested for_each) has its error captured and propagated. |
181 | | if constexpr (std::is_convertible_v<decltype(h(v)), error_code>) { |
182 | | err = h(v); |
183 | | } else { |
184 | | h(v); |
185 | | } |
186 | | } else { |
187 | | // Direct deserialization target: assign the matched value into it. |
188 | | err = v.get(h); |
189 | | } |
190 | | } |
191 | | }; |
192 | | (try_one(std::integral_constant<std::size_t, Is>{}), ...); |
193 | | return err; |
194 | | } |
195 | | |
196 | | } // namespace key_selector_for_each_detail |
197 | | |
198 | | template <typename Selector, typename... Handlers> |
199 | | requires key_selector_type<Selector> && |
200 | | (sizeof...(Handlers) == Selector::size()) && |
201 | | (key_selector_for_each_detail::field_handler<Handlers> && ...) |
202 | | simdjson_flatten simdjson_inline for_each_result object::for_each(Handlers&&... on_match) |
203 | | noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) { |
204 | | auto handlers = std::forward_as_tuple(std::forward<Handlers>(on_match)...); |
205 | | // Reuse the single (index, value) implementation via a tiny adapter. |
206 | | // The adapter is called once per *matched* key (very few); the hot path |
207 | | // (iteration + match_raw + seen bitset) stays exactly the same. |
208 | | return this->template for_each<Selector>( |
209 | | [&](std::size_t i, value v) -> error_code { |
210 | | return key_selector_for_each_detail::dispatch_value( |
211 | | i, handlers, v, std::make_index_sequence<Selector::size()>{}); |
212 | | }); |
213 | | } |
214 | | |
215 | | template <constevalutil::fixed_string... Keys, typename... Handlers> |
216 | | requires (sizeof...(Handlers) == sizeof...(Keys)) && |
217 | | (sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) && |
218 | | (key_selector_for_each_detail::field_handler<Handlers> && ...) |
219 | | simdjson_inline for_each_result object::for_each(Handlers&&... on_match) |
220 | | noexcept(key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) { |
221 | | using Selector = key_selector<Keys...>; |
222 | | return this->template for_each<Selector>(std::forward<Handlers>(on_match)...); |
223 | | } |
224 | | #endif |
225 | | |
226 | 11.6k | simdjson_inline simdjson_result<object> object::start(value_iterator &iter) noexcept { |
227 | 11.6k | SIMDJSON_TRY( iter.start_object().error() ); |
228 | 10.9k | return object(iter); |
229 | 11.6k | } |
230 | 3.45k | simdjson_inline simdjson_result<object> object::start_root(value_iterator &iter) noexcept { |
231 | 3.45k | SIMDJSON_TRY( iter.start_root_object().error() ); |
232 | 3.42k | return object(iter); |
233 | 3.45k | } |
234 | 0 | simdjson_warn_unused simdjson_inline error_code object::consume() noexcept { |
235 | 0 | if(iter.is_at_key()) { |
236 | 0 | /** |
237 | 0 | * whenever you are pointing at a key, calling skip_child() is |
238 | 0 | * unsafe because you will hit a string and you will assume that |
239 | 0 | * it is string value, and this mistake will lead you to make bad |
240 | 0 | * depth computation. |
241 | 0 | */ |
242 | 0 | /** |
243 | 0 | * We want to 'consume' the key. We could really |
244 | 0 | * just do _json_iter->return_current_and_advance(); at this |
245 | 0 | * point, but, for clarity, we will use the high-level API to |
246 | 0 | * eat the key. We assume that the compiler optimizes away |
247 | 0 | * most of the work. |
248 | 0 | */ |
249 | 0 | simdjson_unused raw_json_string actual_key; |
250 | 0 | auto error = iter.field_key().get(actual_key); |
251 | 0 | if (error) { iter.abandon(); return error; }; |
252 | 0 | // Let us move to the value while we are at it. |
253 | 0 | if ((error = iter.field_value())) { iter.abandon(); return error; } |
254 | 0 | } |
255 | 0 | auto error_skip = iter.json_iter().skip_child(iter.depth()-1); |
256 | 0 | if(error_skip) { iter.abandon(); } |
257 | 0 | return error_skip; |
258 | 0 | } |
259 | | |
260 | 0 | simdjson_inline simdjson_result<std::string_view> object::raw_json() noexcept { |
261 | 0 | #if SIMDJSON_DEVELOPMENT_CHECKS |
262 | 0 | if (locked) return OUT_OF_ORDER_ITERATION; |
263 | 0 | #endif |
264 | 0 | const uint8_t * starting_point{iter.peek_start()}; |
265 | 0 | auto error = consume(); |
266 | 0 | if(error) { return error; } |
267 | 0 | const uint8_t * final_point{iter._json_iter->peek()}; |
268 | 0 | return std::string_view(reinterpret_cast<const char*>(starting_point), size_t(final_point - starting_point)); |
269 | 0 | } |
270 | | |
271 | 0 | simdjson_inline simdjson_result<object> object::started(value_iterator &iter) noexcept { |
272 | 0 | SIMDJSON_TRY( iter.started_object().error() ); |
273 | 0 | return object(iter); |
274 | 0 | } |
275 | | |
276 | 0 | simdjson_inline object object::resume(const value_iterator &iter) noexcept { |
277 | 0 | return iter; |
278 | 0 | } |
279 | | |
280 | | simdjson_inline object::object(const value_iterator &_iter) noexcept |
281 | 14.3k | : iter{_iter} |
282 | 14.3k | { |
283 | 14.3k | } |
284 | | |
285 | 6.13k | simdjson_inline simdjson_result<object_iterator> object::begin() noexcept { |
286 | 6.13k | #if SIMDJSON_DEVELOPMENT_CHECKS |
287 | 6.13k | if (!iter.is_at_iterator_start() || locked) { return OUT_OF_ORDER_ITERATION; } |
288 | 6.13k | return object_iterator(iter, this); |
289 | 0 | #endif |
290 | 0 | return object_iterator(iter); |
291 | 6.13k | } |
292 | 6.13k | simdjson_inline simdjson_result<object_iterator> object::end() noexcept { |
293 | 6.13k | return object_iterator(iter); |
294 | 6.13k | } |
295 | | |
296 | 3.54k | inline simdjson_result<value> object::at_pointer(std::string_view json_pointer) noexcept { |
297 | 3.54k | if (json_pointer[0] != '/') { return INVALID_JSON_POINTER; } |
298 | 3.54k | json_pointer = json_pointer.substr(1); |
299 | 3.54k | size_t slash = json_pointer.find('/'); |
300 | 3.54k | std::string_view key = json_pointer.substr(0, slash); |
301 | | // Grab the child with the given key |
302 | 3.54k | simdjson_result<value> child; |
303 | | |
304 | | // If there is an escape character in the key, unescape it and then get the child. |
305 | 3.54k | size_t escape = key.find('~'); |
306 | 3.54k | if (escape != std::string_view::npos) { |
307 | | // Unescape the key |
308 | 849 | std::string unescaped(key); |
309 | 512k | do { |
310 | 512k | switch (unescaped[escape+1]) { |
311 | 106k | case '0': |
312 | 106k | unescaped.replace(escape, 2, "~"); |
313 | 106k | break; |
314 | 405k | case '1': |
315 | 405k | unescaped.replace(escape, 2, "/"); |
316 | 405k | break; |
317 | 0 | default: |
318 | 0 | return INVALID_JSON_POINTER; // "Unexpected ~ escape character in JSON pointer"); |
319 | 512k | } |
320 | 512k | escape = unescaped.find('~', escape+1); |
321 | 512k | } while (escape != std::string::npos); |
322 | 849 | child = find_field(unescaped); // Take note find_field does not unescape keys when matching |
323 | 2.69k | } else { |
324 | 2.69k | child = find_field(key); |
325 | 2.69k | } |
326 | 3.54k | if(child.error()) { |
327 | 1.76k | return child; // we do not continue if there was an error |
328 | 1.76k | } |
329 | | // If there is a /, we have to recurse and look up more of the path |
330 | 1.77k | if (slash != std::string_view::npos) { |
331 | 1.73k | child = child.at_pointer(json_pointer.substr(slash)); |
332 | 1.73k | } |
333 | 1.77k | return child; |
334 | 3.54k | } |
335 | | |
336 | 1.71k | inline simdjson_result<value> object::at_path(std::string_view json_path) noexcept { |
337 | 1.71k | auto json_pointer = json_path_to_pointer_conversion(json_path); |
338 | 1.71k | if (json_pointer == "-1") { |
339 | 158 | return INVALID_JSON_POINTER; |
340 | 158 | } |
341 | 1.55k | return at_pointer(json_pointer); |
342 | 1.71k | } |
343 | | |
344 | | #if SIMDJSON_SUPPORTS_CONCEPTS |
345 | | template <typename Func> |
346 | | requires std::invocable<Func, value> |
347 | | #else |
348 | | template <typename Func> |
349 | | #endif |
350 | 9.90k | inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { |
351 | 9.90k | auto result_pair = get_next_key_and_json_path(json_path); |
352 | 9.90k | std::string_view key = result_pair.first; |
353 | 9.90k | std::string_view remaining_path = result_pair.second; |
354 | | // Empty key means the path segment could not be parsed (including malformed |
355 | | // bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating |
356 | | // "no progress" as a recursive lookup of "". |
357 | 9.90k | if (key.empty()) { |
358 | 476 | return INVALID_JSON_POINTER; |
359 | 476 | } |
360 | | // Handle when its the case for wildcard |
361 | 9.43k | if (key == "*") { |
362 | | // Loop through each field in the object |
363 | 12.2k | for (auto field : *this) { |
364 | 12.2k | value val; |
365 | 12.2k | SIMDJSON_TRY(field.value().get(val)); |
366 | 11.7k | if (remaining_path.empty()) { |
367 | 5.95k | callback(val); |
368 | 5.95k | } else { |
369 | 5.81k | SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); |
370 | 5.47k | } |
371 | 11.7k | } |
372 | 5.34k | return SUCCESS; |
373 | 6.13k | } else { |
374 | 3.29k | value val; |
375 | 3.29k | SIMDJSON_TRY(find_field(key).get(val)); |
376 | | |
377 | 1.81k | if (remaining_path.empty()) { |
378 | 534 | callback(val); |
379 | 534 | return SUCCESS; |
380 | 1.27k | } else { |
381 | 1.27k | return val.for_each_at_path_with_wildcard(remaining_path, callback); |
382 | 1.27k | } |
383 | 1.81k | } |
384 | 9.43k | } fuzz_atpath.cpp:simdjson::error_code simdjson::fallback::ondemand::object::for_each_at_path_with_wildcard<(anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0&>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, (anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0&) Line | Count | Source | 350 | 8.19k | inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { | 351 | 8.19k | auto result_pair = get_next_key_and_json_path(json_path); | 352 | 8.19k | std::string_view key = result_pair.first; | 353 | 8.19k | std::string_view remaining_path = result_pair.second; | 354 | | // Empty key means the path segment could not be parsed (including malformed | 355 | | // bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating | 356 | | // "no progress" as a recursive lookup of "". | 357 | 8.19k | if (key.empty()) { | 358 | 209 | return INVALID_JSON_POINTER; | 359 | 209 | } | 360 | | // Handle when its the case for wildcard | 361 | 7.98k | if (key == "*") { | 362 | | // Loop through each field in the object | 363 | 6.35k | for (auto field : *this) { | 364 | 6.35k | value val; | 365 | 6.35k | SIMDJSON_TRY(field.value().get(val)); | 366 | 6.09k | if (remaining_path.empty()) { | 367 | 3.90k | callback(val); | 368 | 3.90k | } else { | 369 | 2.18k | SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); | 370 | 1.96k | } | 371 | 6.09k | } | 372 | 5.21k | return SUCCESS; | 373 | 5.70k | } else { | 374 | 2.28k | value val; | 375 | 2.28k | SIMDJSON_TRY(find_field(key).get(val)); | 376 | | | 377 | 1.72k | if (remaining_path.empty()) { | 378 | 529 | callback(val); | 379 | 529 | return SUCCESS; | 380 | 1.19k | } else { | 381 | 1.19k | return val.for_each_at_path_with_wildcard(remaining_path, callback); | 382 | 1.19k | } | 383 | 1.72k | } | 384 | 7.98k | } |
fuzz_atpath.cpp:simdjson::error_code simdjson::fallback::ondemand::object::for_each_at_path_with_wildcard<(anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, (anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0&&) Line | Count | Source | 350 | 1.71k | inline error_code object::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { | 351 | 1.71k | auto result_pair = get_next_key_and_json_path(json_path); | 352 | 1.71k | std::string_view key = result_pair.first; | 353 | 1.71k | std::string_view remaining_path = result_pair.second; | 354 | | // Empty key means the path segment could not be parsed (including malformed | 355 | | // bracket-quoted keys). Match DOM at_path_with_wildcard and avoid treating | 356 | | // "no progress" as a recursive lookup of "". | 357 | 1.71k | if (key.empty()) { | 358 | 267 | return INVALID_JSON_POINTER; | 359 | 267 | } | 360 | | // Handle when its the case for wildcard | 361 | 1.44k | if (key == "*") { | 362 | | // Loop through each field in the object | 363 | 5.86k | for (auto field : *this) { | 364 | 5.86k | value val; | 365 | 5.86k | SIMDJSON_TRY(field.value().get(val)); | 366 | 5.68k | if (remaining_path.empty()) { | 367 | 2.05k | callback(val); | 368 | 3.63k | } else { | 369 | 3.63k | SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); | 370 | 3.51k | } | 371 | 5.68k | } | 372 | 136 | return SUCCESS; | 373 | 1.01k | } else { | 374 | 1.01k | value val; | 375 | 1.01k | SIMDJSON_TRY(find_field(key).get(val)); | 376 | | | 377 | 91 | if (remaining_path.empty()) { | 378 | 5 | callback(val); | 379 | 5 | return SUCCESS; | 380 | 86 | } else { | 381 | 86 | return val.for_each_at_path_with_wildcard(remaining_path, callback); | 382 | 86 | } | 383 | 91 | } | 384 | 1.44k | } |
|
385 | | |
386 | 0 | simdjson_inline simdjson_result<size_t> object::count_fields() & noexcept { |
387 | 0 | size_t count{0}; |
388 | 0 | // Important: we do not consume any of the values. |
389 | 0 | for(simdjson_unused auto v : *this) { count++; } |
390 | 0 | // The above loop will always succeed, but we want to report errors. |
391 | 0 | if(iter.error()) { return iter.error(); } |
392 | 0 | // We need to move back at the start because we expect users to iterate through |
393 | 0 | // the object after counting the number of elements. |
394 | 0 | iter.reset_object(); |
395 | 0 | return count; |
396 | 0 | } |
397 | | |
398 | 0 | simdjson_inline simdjson_result<bool> object::is_empty() & noexcept { |
399 | 0 | #if SIMDJSON_DEVELOPMENT_CHECKS |
400 | 0 | if (locked) return OUT_OF_ORDER_ITERATION; |
401 | 0 | #endif |
402 | 0 | bool is_not_empty; |
403 | 0 | auto error = iter.reset_object().get(is_not_empty); |
404 | 0 | if(error) { return error; } |
405 | 0 | return !is_not_empty; |
406 | 0 | } |
407 | | |
408 | 0 | simdjson_inline simdjson_result<bool> object::reset() & noexcept { |
409 | 0 | #if SIMDJSON_DEVELOPMENT_CHECKS |
410 | 0 | if (locked) return OUT_OF_ORDER_ITERATION; |
411 | 0 | #endif |
412 | 0 | return iter.reset_object(); |
413 | 0 | } |
414 | | |
415 | 0 | simdjson_inline object_position object::get_current_position() const noexcept { |
416 | 0 | return object_position{iter.position(), iter.json_iter().depth()}; |
417 | 0 | } |
418 | | |
419 | 0 | simdjson_inline error_code object::revert_position(object_position position) noexcept { |
420 | 0 | #if SIMDJSON_DEVELOPMENT_CHECKS |
421 | 0 | if (locked) return OUT_OF_ORDER_ITERATION; |
422 | 0 | #endif |
423 | 0 | // json_iterator::reenter_child() requires the live depth to be exactly |
424 | 0 | // one level shallower than the target (matching how every other depth |
425 | 0 | // transition in this iterator works), and under SIMDJSON_DEVELOPMENT_CHECKS |
426 | 0 | // additionally validates against the parser's per-depth container-start |
427 | 0 | // bookkeeping. Neither applies here: depending on what was captured and |
428 | 0 | // what has happened since (a scalar field fully consumed, a compound |
429 | 0 | // value left open, a find_field() miss that scanned past everything), |
430 | 0 | // the live depth when reverting can be any number of levels away from |
431 | 0 | // the captured one, and the captured depth is not necessarily a |
432 | 0 | // container's own start. reenter_at() moves directly, matching how |
433 | 0 | // reset_object() itself repositions without going through reenter_child(). |
434 | 0 | iter.reenter_at(position.position, position.depth); |
435 | 0 | return SUCCESS; |
436 | 0 | } |
437 | | |
438 | | #if SIMDJSON_DEVELOPMENT_CHECKS |
439 | 12.2k | simdjson_inline void object::set_locked(bool _locked) noexcept { |
440 | 12.2k | locked = _locked; |
441 | 12.2k | } |
442 | | #endif |
443 | | |
444 | | #if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION |
445 | | |
446 | | template<constevalutil::fixed_string... FieldNames, typename T> |
447 | | requires(std::is_class_v<T> && (sizeof...(FieldNames) > 0)) |
448 | | simdjson_warn_unused simdjson_inline error_code object::extract_into(T& out) & noexcept { |
449 | | // Helper to check if a field name matches any of the requested fields |
450 | | auto should_extract = [](std::string_view field_name) constexpr -> bool { |
451 | | return ((FieldNames.view() == field_name) || ...); |
452 | | }; |
453 | | |
454 | | // Iterate through all members of T using reflection |
455 | | template for (constexpr auto mem : std::define_static_array( |
456 | | std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) { |
457 | | |
458 | | if constexpr (!std::meta::is_const(mem) && std::meta::is_public(mem)) { |
459 | | constexpr std::string_view key = std::define_static_string(std::meta::identifier_of(mem)); |
460 | | |
461 | | // Only extract this field if it's in our list of requested fields |
462 | | if constexpr (should_extract(key)) { |
463 | | // Try to find and extract the field |
464 | | if constexpr (concepts::optional_type<decltype(out.[:mem:])>) { |
465 | | // For optional fields, it's ok if they're missing |
466 | | auto field_result = find_field_unordered(key); |
467 | | if (!field_result.error()) { |
468 | | auto error = field_result.get(out.[:mem:]); |
469 | | if (error && error != NO_SUCH_FIELD) { |
470 | | return error; |
471 | | } |
472 | | } else if (field_result.error() != NO_SUCH_FIELD) { |
473 | | return field_result.error(); |
474 | | } else { |
475 | | out.[:mem:].reset(); |
476 | | } |
477 | | } else { |
478 | | // For required fields (in the requested list), fail if missing |
479 | | SIMDJSON_TRY((*this)[key].get(out.[:mem:])); |
480 | | } |
481 | | } |
482 | | } |
483 | | }; |
484 | | |
485 | | return SUCCESS; |
486 | | } |
487 | | |
488 | | #endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION |
489 | | |
490 | | } // namespace ondemand |
491 | | } // namespace SIMDJSON_IMPLEMENTATION |
492 | | } // namespace simdjson |
493 | | |
494 | | namespace simdjson { |
495 | | |
496 | | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::simdjson_result(SIMDJSON_IMPLEMENTATION::ondemand::object &&value) noexcept |
497 | 14.3k | : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object>(std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(value)) {} |
498 | | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::simdjson_result(error_code error) noexcept |
499 | 830 | : implementation_simdjson_result_base<SIMDJSON_IMPLEMENTATION::ondemand::object>(error) {} |
500 | | |
501 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::begin() noexcept { |
502 | 0 | if (error()) { return error(); } |
503 | 0 | return first.begin(); |
504 | 0 | } |
505 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_iterator> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::end() noexcept { |
506 | 0 | if (error()) { return error(); } |
507 | 0 | return first.end(); |
508 | 0 | } |
509 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field_unordered(std::string_view key) & noexcept { |
510 | 0 | if (error()) { return error(); } |
511 | 0 | return first.find_field_unordered(key); |
512 | 0 | } |
513 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field_unordered(std::string_view key) && noexcept { |
514 | 0 | if (error()) { return error(); } |
515 | 0 | return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field_unordered(key); |
516 | 0 | } |
517 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::operator[](std::string_view key) & noexcept { |
518 | 0 | if (error()) { return error(); } |
519 | 0 | return first[key]; |
520 | 0 | } |
521 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::operator[](std::string_view key) && noexcept { |
522 | 0 | if (error()) { return error(); } |
523 | 0 | return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first)[key]; |
524 | 0 | } |
525 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(std::string_view key) & noexcept { |
526 | 0 | if (error()) { return error(); } |
527 | 0 | return first.find_field(key); |
528 | 0 | } |
529 | 0 | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::find_field(std::string_view key) && noexcept { |
530 | 0 | if (error()) { return error(); } |
531 | 0 | return std::forward<SIMDJSON_IMPLEMENTATION::ondemand::object>(first).find_field(key); |
532 | 0 | } |
533 | | |
534 | 1.98k | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_pointer(std::string_view json_pointer) noexcept { |
535 | 1.98k | if (error()) { return error(); } |
536 | 1.98k | return first.at_pointer(json_pointer); |
537 | 1.98k | } |
538 | | |
539 | | simdjson_inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::value> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::at_path( |
540 | 1.72k | std::string_view json_path) noexcept { |
541 | 1.72k | if (error()) { |
542 | 12 | return error(); |
543 | 12 | } |
544 | 1.71k | return first.at_path(json_path); |
545 | 1.72k | } |
546 | | |
547 | | #if SIMDJSON_SUPPORTS_CONCEPTS |
548 | | template <typename Func> |
549 | | requires std::invocable<Func, SIMDJSON_IMPLEMENTATION::ondemand::value> |
550 | | #else |
551 | | template <typename Func> |
552 | | #endif |
553 | 9.91k | simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { |
554 | 9.91k | if (error()) { return error(); } |
555 | 9.90k | return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); |
556 | 9.91k | } fuzz_atpath.cpp:simdjson::error_code simdjson::simdjson_result<simdjson::fallback::ondemand::object>::for_each_at_path_with_wildcard<(anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0&>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, (anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0&) Line | Count | Source | 553 | 8.19k | simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { | 554 | 8.19k | if (error()) { return error(); } | 555 | 8.19k | return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); | 556 | 8.19k | } |
fuzz_atpath.cpp:simdjson::error_code simdjson::simdjson_result<simdjson::fallback::ondemand::object>::for_each_at_path_with_wildcard<(anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, (anonymous namespace)::fuzz_ondemand(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >)::$_0&&) Line | Count | Source | 553 | 1.72k | simdjson_inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each_at_path_with_wildcard(std::string_view json_path, Func&& callback) noexcept { | 554 | 1.72k | if (error()) { return error(); } | 555 | 1.71k | return first.for_each_at_path_with_wildcard(json_path, std::forward<Func>(callback)); | 556 | 1.72k | } |
|
557 | | |
558 | | #if SIMDJSON_SUPPORTS_CONCEPTS |
559 | | template <typename Selector, typename Func> |
560 | | requires SIMDJSON_IMPLEMENTATION::ondemand::key_selector_type<Selector> && |
561 | | std::is_invocable_v<Func&, std::size_t, SIMDJSON_IMPLEMENTATION::ondemand::value> |
562 | | simdjson_inline SIMDJSON_IMPLEMENTATION::ondemand::for_each_result |
563 | | simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each(Func&& on_match) |
564 | | noexcept(std::is_nothrow_invocable_v<Func&, std::size_t, SIMDJSON_IMPLEMENTATION::ondemand::value>) { |
565 | | if (error()) { return {error(), 0}; } |
566 | | return first.template for_each<Selector>(std::forward<Func>(on_match)); |
567 | | } |
568 | | |
569 | | template <typename Selector, typename... Handlers> |
570 | | requires SIMDJSON_IMPLEMENTATION::ondemand::key_selector_type<Selector> && |
571 | | (sizeof...(Handlers) == Selector::size()) && |
572 | | (SIMDJSON_IMPLEMENTATION::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...) |
573 | | simdjson_inline SIMDJSON_IMPLEMENTATION::ondemand::for_each_result |
574 | | simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each(Handlers&&... on_match) |
575 | | noexcept(SIMDJSON_IMPLEMENTATION::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) { |
576 | | if (error()) { return {error(), 0}; } |
577 | | return first.template for_each<Selector>(std::forward<Handlers>(on_match)...); |
578 | | } |
579 | | |
580 | | template <constevalutil::fixed_string... Keys, typename... Handlers> |
581 | | requires (sizeof...(Handlers) == sizeof...(Keys)) && |
582 | | (sizeof...(Keys) >= 1) && (sizeof...(Keys) <= 255) && |
583 | | (SIMDJSON_IMPLEMENTATION::ondemand::key_selector_for_each_detail::field_handler<Handlers> && ...) |
584 | | simdjson_inline SIMDJSON_IMPLEMENTATION::ondemand::for_each_result |
585 | | simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::for_each(Handlers&&... on_match) |
586 | | noexcept(SIMDJSON_IMPLEMENTATION::ondemand::key_selector_for_each_detail::nothrow_field_handlers_v<Handlers...>) { |
587 | | if (error()) { return {error(), 0}; } |
588 | | return first.template for_each<Keys...>(std::forward<Handlers>(on_match)...); |
589 | | } |
590 | | #endif |
591 | | |
592 | 0 | inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::reset() noexcept { |
593 | 0 | if (error()) { return error(); } |
594 | 0 | return first.reset(); |
595 | 0 | } |
596 | | |
597 | 0 | inline simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object_position> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::get_current_position() noexcept { |
598 | 0 | if (error()) { return error(); } |
599 | 0 | return first.get_current_position(); |
600 | 0 | } |
601 | | |
602 | 0 | inline error_code simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::revert_position(SIMDJSON_IMPLEMENTATION::ondemand::object_position position) noexcept { |
603 | 0 | if (error()) { return error(); } |
604 | 0 | return first.revert_position(position); |
605 | 0 | } |
606 | | |
607 | 0 | inline simdjson_result<bool> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::is_empty() noexcept { |
608 | 0 | if (error()) { return error(); } |
609 | 0 | return first.is_empty(); |
610 | 0 | } |
611 | | |
612 | 0 | simdjson_inline simdjson_result<size_t> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::count_fields() & noexcept { |
613 | 0 | if (error()) { return error(); } |
614 | 0 | return first.count_fields(); |
615 | 0 | } |
616 | | |
617 | 0 | simdjson_inline simdjson_result<std::string_view> simdjson_result<SIMDJSON_IMPLEMENTATION::ondemand::object>::raw_json() noexcept { |
618 | 0 | if (error()) { return error(); } |
619 | 0 | return first.raw_json(); |
620 | 0 | } |
621 | | |
622 | | } // namespace simdjson |
623 | | |
624 | | #endif // SIMDJSON_GENERIC_ONDEMAND_OBJECT_INL_H |