simdjson 4.2.3
Ridiculously Fast JSON
Loading...
Searching...
No Matches
compile_time_accessors.h
1
42#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
43
44#ifndef SIMDJSON_CONDITIONAL_INCLUDE
45#define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
46
47#endif // SIMDJSON_CONDITIONAL_INCLUDE
48
49// Arguably, we should just check SIMDJSON_STATIC_REFLECTION since it
50// is unlikely that we will have reflection support without concepts support.
51#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
52
53#include <string_view>
54#include <cstddef>
55#include <array>
56
57namespace simdjson {
58namespace SIMDJSON_IMPLEMENTATION {
59namespace ondemand {
60/***
61 * JSONPath implementation for compile-time access
62 * RFC 9535 JSONPath: Query Expressions for JSON, https://www.rfc-editor.org/rfc/rfc9535
63 */
64namespace json_path {
65
66// Note: value type must be fully defined before this header is included
67// This is ensured by including this in amalgamated.h after value-inl.h
68
69using ::simdjson::SIMDJSON_IMPLEMENTATION::ondemand::value;
70
71// Path step types
72enum class step_type {
73 field, // .field_name or ["field_name"]
74 array_index // [index]
75};
76
77// Represents a single step in a JSON path
78template<std::size_t N>
79struct path_step {
80 step_type type;
81 char key[N]; // Field name (empty for array indices)
82 std::size_t index; // Array index (0 for field access)
83
84 constexpr path_step(step_type t, const char (&k)[N], std::size_t idx = 0)
85 : type(t), index(idx) {
86 for (std::size_t i = 0; i < N; ++i) {
87 key[i] = k[i];
88 }
89 }
90
91 constexpr std::string_view key_view() const {
92 return {key, N - 1};
93 }
94};
95
96// Helper to create field step
97template<std::size_t N>
98consteval auto make_field_step(const char (&name)[N]) {
99 return path_step<N>(step_type::field, name, 0);
100}
101
102// Helper to create array index step
103consteval auto make_index_step(std::size_t idx) {
104 return path_step<1>(step_type::array_index, "", idx);
105}
106
107// Parse state for compile-time JSON path parsing
108struct parse_result {
109 bool success;
110 std::size_t pos;
111 std::string_view error_msg;
112};
113
114// Compile-time JSON path parser
115// Supports subset: .field, ["field"], [index], nested combinations
116template<constevalutil::fixed_string Path>
117struct json_path_parser {
118 static constexpr std::string_view path_str = Path.view();
119
120 // Skip leading $ if present
121 static consteval std::size_t skip_root() {
122 if (!path_str.empty() && path_str[0] == '$') {
123 return 1;
124 }
125 return 0;
126 }
127
128 // Count the number of steps in the path at compile time
129 static consteval std::size_t count_steps() {
130 std::size_t count = 0;
131 std::size_t i = skip_root();
132
133 while (i < path_str.size()) {
134 if (path_str[i] == '.') {
135 // Field access: .field
136 ++i;
137 if (i >= path_str.size()) break;
138
139 // Skip field name
140 while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[') {
141 ++i;
142 }
143 ++count;
144 } else if (path_str[i] == '[') {
145 // Array or bracket notation
146 ++i;
147 if (i >= path_str.size()) break;
148
149 if (path_str[i] == '"' || path_str[i] == '\'') {
150 // Field access: ["field"] or ['field']
151 char quote = path_str[i];
152 ++i;
153 while (i < path_str.size() && path_str[i] != quote) {
154 ++i;
155 }
156 if (i < path_str.size()) ++i; // skip closing quote
157 if (i < path_str.size() && path_str[i] == ']') ++i;
158 } else {
159 // Array index: [0], [123]
160 while (i < path_str.size() && path_str[i] != ']') {
161 ++i;
162 }
163 if (i < path_str.size()) ++i; // skip ]
164 }
165 ++count;
166 } else {
167 ++i;
168 }
169 }
170
171 return count;
172 }
173
174 // Parse a field name at compile time
175 static consteval std::size_t parse_field_name(std::size_t start, char* out, std::size_t max_len) {
176 std::size_t len = 0;
177 std::size_t i = start;
178
179 while (i < path_str.size() && path_str[i] != '.' && path_str[i] != '[' && len < max_len - 1) {
180 out[len++] = path_str[i++];
181 }
182 out[len] = '\0';
183 return i;
184 }
185
186 // Parse an array index at compile time
187 static consteval std::pair<std::size_t, std::size_t> parse_array_index(std::size_t start) {
188 std::size_t index = 0;
189 std::size_t i = start;
190
191 while (i < path_str.size() && path_str[i] >= '0' && path_str[i] <= '9') {
192 index = index * 10 + (path_str[i] - '0');
193 ++i;
194 }
195
196 return {i, index};
197 }
198};
199
200// Compile-time path accessor generator
201template<typename T, constevalutil::fixed_string Path>
202struct path_accessor {
204
205 static constexpr auto parser = json_path_parser<Path>();
206 static constexpr std::size_t num_steps = parser.count_steps();
207 static constexpr std::string_view path_view = Path.view();
208
209 // Compile-time accessor generation
210 // If T is a struct, validates the path at compile time
211 // If T is void, skips validation
212 template<typename DocOrValue>
213 static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
214 // Validate path at compile time if T is a struct
215 if constexpr (std::is_class_v<T>) {
216 constexpr bool path_valid = validate_path();
217 static_assert(path_valid, "JSON path does not match struct definition");
218 }
219
220 // Parse the path at compile time to build access steps
221 return access_impl<parser.skip_root()>(doc_or_val.get_value());
222 }
223
224 // Extract value at path directly into target with compile-time type validation
225 // Example: std::string name; path_accessor<User, ".name">::extract_field(doc, name);
226 template<typename DocOrValue, typename FieldType>
227 static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
228 static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
229
230 // Validate path exists in struct definition
231 constexpr bool path_valid = validate_path();
232 static_assert(path_valid, "JSON path does not match struct definition");
233
234 // Get the type at the end of the path
235 constexpr auto final_type = get_final_type();
236
237 // Verify target type matches the field type
238 static_assert(final_type == ^^FieldType, "Target type does not match the field type at the path");
239
240 // All validation done at compile time - just navigate and extract
241 auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
242 if (json_value.error()) return json_value.error();
243
244 return json_value.get(target);
245 }
246
247private:
248 // Get the final type by walking the path through the struct type
249 template<typename U = T>
250 static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
251 auto current_type = ^^T;
252 std::size_t i = parser.skip_root();
253
254 while (i < path_view.size()) {
255 if (path_view[i] == '.') {
256 // .field syntax
257 ++i;
258 std::size_t field_start = i;
259 while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
260 ++i;
261 }
262
263 std::string_view field_name = path_view.substr(field_start, i - field_start);
264
265 auto members = std::meta::nonstatic_data_members_of(
266 current_type, std::meta::access_context::unchecked()
267 );
268
269 for (auto mem : members) {
270 if (std::meta::identifier_of(mem) == field_name) {
271 current_type = std::meta::type_of(mem);
272 break;
273 }
274 }
275
276 } else if (path_view[i] == '[') {
277 ++i;
278 if (i >= path_view.size()) break;
279
280 if (path_view[i] == '"' || path_view[i] == '\'') {
281 // ["field"] syntax
282 char quote = path_view[i];
283 ++i;
284 std::size_t field_start = i;
285 while (i < path_view.size() && path_view[i] != quote) {
286 ++i;
287 }
288
289 std::string_view field_name = path_view.substr(field_start, i - field_start);
290 if (i < path_view.size()) ++i; // skip quote
291 if (i < path_view.size() && path_view[i] == ']') ++i;
292
293 auto members = std::meta::nonstatic_data_members_of(
294 current_type, std::meta::access_context::unchecked()
295 );
296
297 for (auto mem : members) {
298 if (std::meta::identifier_of(mem) == field_name) {
299 current_type = std::meta::type_of(mem);
300 break;
301 }
302 }
303
304 } else {
305 // [index] syntax - extract element type
306 while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
307 ++i;
308 }
309 if (i < path_view.size() && path_view[i] == ']') ++i;
310
311 current_type = get_element_type_reflected(current_type);
312 }
313 } else {
314 ++i;
315 }
316 }
317
318 return current_type;
319 }
320
321private:
322 // Walk path and extract directly into final field using compile-time reflection
323 template<std::meta::info CurrentType, std::size_t PathPos, typename TargetType>
324 static inline error_code extract_with_reflection(simdjson_result<value> current, TargetType& target_ref) noexcept {
325 if (current.error()) return current.error();
326
327 // Base case: end of path - extract into target
328 if constexpr (PathPos >= path_view.size()) {
329 return current.get(target_ref);
330 }
331 // Field access: .field_name
332 else if constexpr (path_view[PathPos] == '.') {
333 constexpr auto field_info = parse_next_field(PathPos);
334 constexpr std::string_view field_name = std::get<0>(field_info);
335 constexpr std::size_t next_pos = std::get<1>(field_info);
336
337 constexpr auto member_info = find_member_by_name(CurrentType, field_name);
338 static_assert(member_info != ^^void, "Field not found in struct");
339
340 constexpr auto member_type = std::meta::type_of(member_info);
341
342 auto obj_result = current.get_object();
343 if (obj_result.error()) return obj_result.error();
344 auto obj = obj_result.value_unsafe();
345 auto field_value = obj.find_field_unordered(field_name);
346
347 if constexpr (next_pos >= path_view.size()) {
348 return field_value.get(target_ref);
349 } else {
350 return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
351 }
352 }
353 // Bracket notation: [index] or ["field"]
354 else if constexpr (path_view[PathPos] == '[') {
355 constexpr auto bracket_info = parse_bracket(PathPos);
356 constexpr bool is_field = std::get<0>(bracket_info);
357 constexpr std::size_t next_pos = std::get<2>(bracket_info);
358
359 if constexpr (is_field) {
360 constexpr std::string_view field_name = std::get<1>(bracket_info);
361 constexpr auto member_info = find_member_by_name(CurrentType, field_name);
362 static_assert(member_info != ^^void, "Field not found in struct");
363 constexpr auto member_type = std::meta::type_of(member_info);
364
365 auto obj_result = current.get_object();
366 if (obj_result.error()) return obj_result.error();
367 auto obj = obj_result.value_unsafe();
368 auto field_value = obj.find_field_unordered(field_name);
369
370 if constexpr (next_pos >= path_view.size()) {
371 return field_value.get(target_ref);
372 } else {
373 return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
374 }
375 } else {
376 constexpr std::size_t index = std::get<3>(bracket_info);
377 constexpr auto elem_type = get_element_type_reflected(CurrentType);
378 static_assert(elem_type != ^^void, "Could not determine array element type");
379
380 auto arr_result = current.get_array();
381 if (arr_result.error()) return arr_result.error();
382 auto arr = arr_result.value_unsafe();
383 auto elem_value = arr.at(index);
384
385 if constexpr (next_pos >= path_view.size()) {
386 return elem_value.get(target_ref);
387 } else {
388 return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
389 }
390 }
391 }
392 // Skip unexpected characters and continue
393 else {
394 return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
395 }
396 }
397
398 // Find member by name in reflected type
399 static consteval std::meta::info find_member_by_name(std::meta::info type_refl, std::string_view name) {
400 auto members = std::meta::nonstatic_data_members_of(type_refl, std::meta::access_context::unchecked());
401 for (auto mem : members) {
402 if (std::meta::identifier_of(mem) == name) {
403 return mem;
404 }
405 }
406 }
407
408 // Generate compile-time accessor code by walking the path
409 template<std::size_t PathPos>
410 static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
411 if (current.error()) return current;
412
413 if constexpr (PathPos >= path_view.size()) {
414 return current;
415 } else if constexpr (path_view[PathPos] == '.') {
416 constexpr auto field_info = parse_next_field(PathPos);
417 constexpr std::string_view field_name = std::get<0>(field_info);
418 constexpr std::size_t next_pos = std::get<1>(field_info);
419
420 auto obj_result = current.get_object();
421 if (obj_result.error()) return obj_result.error();
422
423 auto obj = obj_result.value_unsafe();
424 auto next_value = obj.find_field_unordered(field_name);
425
426 return access_impl<next_pos>(next_value);
427
428 } else if constexpr (path_view[PathPos] == '[') {
429 constexpr auto bracket_info = parse_bracket(PathPos);
430 constexpr bool is_field = std::get<0>(bracket_info);
431 constexpr std::size_t next_pos = std::get<2>(bracket_info);
432
433 if constexpr (is_field) {
434 constexpr std::string_view field_name = std::get<1>(bracket_info);
435
436 auto obj_result = current.get_object();
437 if (obj_result.error()) return obj_result.error();
438
439 auto obj = obj_result.value_unsafe();
440 auto next_value = obj.find_field_unordered(field_name);
441
442 return access_impl<next_pos>(next_value);
443
444 } else {
445 constexpr std::size_t index = std::get<3>(bracket_info);
446
447 auto arr_result = current.get_array();
448 if (arr_result.error()) return arr_result.error();
449
450 auto arr = arr_result.value_unsafe();
451 auto next_value = arr.at(index);
452
453 return access_impl<next_pos>(next_value);
454 }
455 } else {
456 return access_impl<PathPos + 1>(current);
457 }
458 }
459
460 // Parse next field name
461 static consteval auto parse_next_field(std::size_t start) {
462 std::size_t i = start + 1;
463 std::size_t field_start = i;
464 while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
465 ++i;
466 }
467 std::string_view field_name = path_view.substr(field_start, i - field_start);
468 return std::make_tuple(field_name, i);
469 }
470
471 // Parse bracket notation: returns (is_field, field_name, next_pos, index)
472 static consteval auto parse_bracket(std::size_t start) {
473 std::size_t i = start + 1; // skip '['
474
475 if (i < path_view.size() && (path_view[i] == '"' || path_view[i] == '\'')) {
476 // Field access
477 char quote = path_view[i];
478 ++i;
479 std::size_t field_start = i;
480 while (i < path_view.size() && path_view[i] != quote) {
481 ++i;
482 }
483 std::string_view field_name = path_view.substr(field_start, i - field_start);
484 if (i < path_view.size()) ++i; // skip closing quote
485 if (i < path_view.size() && path_view[i] == ']') ++i;
486
487 return std::make_tuple(true, field_name, i, std::size_t(0));
488 } else {
489 // Array index
490 std::size_t index = 0;
491 while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
492 index = index * 10 + (path_view[i] - '0');
493 ++i;
494 }
495 if (i < path_view.size() && path_view[i] == ']') ++i;
496
497 return std::make_tuple(false, std::string_view{}, i, index);
498 }
499 }
500
501public:
502 // Check if reflected type is array-like (C-style array or indexable container)
503 // Uses reflection to test: 1) std::meta::is_array_type() for C arrays
504 // 2) std::meta::substitute() to test concepts::indexable_container concept
505 static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
506 if (std::meta::is_array_type(type_reflection)) {
507 return true;
508 }
509
510 if (std::meta::can_substitute(^^concepts::indexable_container_v, {type_reflection})) {
511 return std::meta::extract<bool>(std::meta::substitute(^^concepts::indexable_container_v, {type_reflection}));
512 }
513 return false;
514 }
515
516 // Extract element type from reflected array or container
517 // For C arrays: uses std::meta::remove_extent()
518 // For containers: finds value_type member using std::meta::members_of()
519 static consteval std::meta::info get_element_type_reflected(std::meta::info type_reflection) {
520 if (std::meta::is_array_type(type_reflection)) {
521 return std::meta::remove_extent(type_reflection);
522 }
523
524 auto members = std::meta::members_of(type_reflection, std::meta::access_context::unchecked());
525 for (auto mem : members) {
526 if (std::meta::is_type(mem)) {
527 auto name = std::meta::identifier_of(mem);
528 if (name == "value_type") {
529 return mem;
530 }
531 }
532 }
533 return ^^void;
534 }
535
536private:
537 // Check if type has member with given name
538 template<typename Type>
539 static consteval bool has_member(std::string_view member_name) {
540 constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
541 for (auto mem : members) {
542 if (std::meta::identifier_of(mem) == member_name) {
543 return true;
544 }
545 }
546 return false;
547 }
548
549 // Get type of member by name
550 template<typename Type>
551 static consteval auto get_member_type(std::string_view member_name) {
552 constexpr auto members = std::meta::nonstatic_data_members_of(^^Type, std::meta::access_context::unchecked());
553 for (auto mem : members) {
554 if (std::meta::identifier_of(mem) == member_name) {
555 return std::meta::type_of(mem);
556 }
557 }
558 return ^^void;
559 }
560
561 // Check if non-reflected type is array-like
562 template<typename Type>
563 static consteval bool is_container_type() {
564 using BaseType = std::remove_cvref_t<Type>;
565 if constexpr (requires { typename BaseType::value_type; }) {
566 return true;
567 }
568 if constexpr (std::is_array_v<BaseType>) {
569 return true;
570 }
571 return false;
572 }
573
574 // Extract element type from non-reflected container
575 template<typename Type>
576 using extract_element_type = std::conditional_t<
577 requires { typename std::remove_cvref_t<Type>::value_type; },
578 typename std::remove_cvref_t<Type>::value_type,
579 std::conditional_t<
580 std::is_array_v<std::remove_cvref_t<Type>>,
581 std::remove_extent_t<std::remove_cvref_t<Type>>,
582 void
583 >
584 >;
585
586 // Validate path matches struct definition
587 static consteval bool validate_path() {
588 if constexpr (!std::is_class_v<T>) {
589 return true;
590 }
591
592 auto current_type = ^^T;
593 std::size_t i = parser.skip_root();
594
595 while (i < path_view.size()) {
596 if (path_view[i] == '.') {
597 ++i;
598 std::size_t field_start = i;
599 while (i < path_view.size() && path_view[i] != '.' && path_view[i] != '[') {
600 ++i;
601 }
602
603 std::string_view field_name = path_view.substr(field_start, i - field_start);
604
605 bool found = false;
606 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
607
608 for (auto mem : members) {
609 if (std::meta::identifier_of(mem) == field_name) {
610 current_type = std::meta::type_of(mem);
611 found = true;
612 break;
613 }
614 }
615
616 if (!found) {
617 return false;
618 }
619
620 } else if (path_view[i] == '[') {
621 ++i;
622 if (i >= path_view.size()) return false;
623
624 if (path_view[i] == '"' || path_view[i] == '\'') {
625 char quote = path_view[i];
626 ++i;
627 std::size_t field_start = i;
628 while (i < path_view.size() && path_view[i] != quote) {
629 ++i;
630 }
631
632 std::string_view field_name = path_view.substr(field_start, i - field_start);
633 if (i < path_view.size()) ++i;
634 if (i < path_view.size() && path_view[i] == ']') ++i;
635
636 bool found = false;
637 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
638
639 for (auto mem : members) {
640 if (std::meta::identifier_of(mem) == field_name) {
641 current_type = std::meta::type_of(mem);
642 found = true;
643 break;
644 }
645 }
646
647 if (!found) {
648 return false;
649 }
650
651 } else {
652 while (i < path_view.size() && path_view[i] >= '0' && path_view[i] <= '9') {
653 ++i;
654 }
655
656 if (i < path_view.size() && path_view[i] == ']') ++i;
657
658 if (!is_array_like_reflected(current_type)) {
659 return false;
660 }
661
662 auto new_type = get_element_type_reflected(current_type);
663
664 if (new_type == ^^void) {
665 return false;
666 }
667
668 current_type = new_type;
669 }
670 } else {
671 ++i;
672 }
673 }
674
675 return true;
676 }
677};
678
679// Compile-time path accessor with validation
680template<typename T, constevalutil::fixed_string Path, typename DocOrValue>
681inline simdjson_result<::simdjson::SIMDJSON_IMPLEMENTATION::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
682 using accessor = path_accessor<T, Path>;
683 return accessor::access(doc_or_val);
684}
685
686// Overload without type parameter (no validation)
687template<constevalutil::fixed_string Path, typename DocOrValue>
688inline simdjson_result<::simdjson::SIMDJSON_IMPLEMENTATION::ondemand::value> at_path_compiled(DocOrValue& doc_or_val) noexcept {
689 using accessor = path_accessor<void, Path>;
690 return accessor::access(doc_or_val);
691}
692
693// ============================================================================
694// JSON Pointer Compile-Time Support (RFC 6901)
695// ============================================================================
696
697// JSON Pointer parser: /field/0/nested (slash-separated)
698template<constevalutil::fixed_string Pointer>
699struct json_pointer_parser {
700 static constexpr std::string_view pointer_str = Pointer.view();
701
702 // Unescape token: ~0 -> ~, ~1 -> /
703 static consteval void unescape_token(std::string_view src, char* dest, std::size_t& out_len) {
704 out_len = 0;
705 for (std::size_t i = 0; i < src.size(); ++i) {
706 if (src[i] == '~' && i + 1 < src.size()) {
707 if (src[i + 1] == '0') {
708 dest[out_len++] = '~';
709 ++i;
710 } else if (src[i + 1] == '1') {
711 dest[out_len++] = '/';
712 ++i;
713 } else {
714 dest[out_len++] = src[i];
715 }
716 } else {
717 dest[out_len++] = src[i];
718 }
719 }
720 }
721
722 // Check if token is numeric
723 static consteval bool is_numeric(std::string_view token) {
724 if (token.empty()) return false;
725 if (token[0] == '0' && token.size() > 1) return false;
726 for (char c : token) {
727 if (c < '0' || c > '9') return false;
728 }
729 return true;
730 }
731
732 // Parse numeric token to index
733 static consteval std::size_t parse_index(std::string_view token) {
734 std::size_t result = 0;
735 for (char c : token) {
736 result = result * 10 + (c - '0');
737 }
738 return result;
739 }
740
741 // Count tokens in pointer
742 static consteval std::size_t count_tokens() {
743 if (pointer_str.empty() || pointer_str == "/") return 0;
744
745 std::size_t count = 0;
746 std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
747
748 while (pos < pointer_str.size()) {
749 ++count;
750 std::size_t next_slash = pointer_str.find('/', pos);
751 if (next_slash == std::string_view::npos) break;
752 pos = next_slash + 1;
753 }
754
755 return count;
756 }
757
758 // Get Nth token
759 static consteval std::string_view get_token(std::size_t token_index) {
760 std::size_t pos = pointer_str[0] == '/' ? 1 : 0;
761 std::size_t current_token = 0;
762
763 while (current_token < token_index) {
764 std::size_t next_slash = pointer_str.find('/', pos);
765 pos = next_slash + 1;
766 ++current_token;
767 }
768
769 std::size_t token_end = pointer_str.find('/', pos);
770 if (token_end == std::string_view::npos) token_end = pointer_str.size();
771
772 return pointer_str.substr(pos, token_end - pos);
773 }
774};
775
776// JSON Pointer accessor
777template<typename T, constevalutil::fixed_string Pointer>
778struct pointer_accessor {
779 using parser = json_pointer_parser<Pointer>;
780 static constexpr std::string_view pointer_view = Pointer.view();
781 static constexpr std::size_t token_count = parser::count_tokens();
782
783 // Validate pointer against struct definition
784 static consteval bool validate_pointer() {
785 if constexpr (!std::is_class_v<T>) {
786 return true;
787 }
788
789 auto current_type = ^^T;
790 std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
791
792 while (pos < pointer_view.size()) {
793 // Extract token up to next /
794 std::size_t token_end = pointer_view.find('/', pos);
795 if (token_end == std::string_view::npos) token_end = pointer_view.size();
796
797 std::string_view token = pointer_view.substr(pos, token_end - pos);
798
799 if (parser::is_numeric(token)) {
800 if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
801 return false;
802 }
803 current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
804 } else {
805 bool found = false;
806 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
807
808 for (auto mem : members) {
809 if (std::meta::identifier_of(mem) == token) {
810 current_type = std::meta::type_of(mem);
811 found = true;
812 break;
813 }
814 }
815
816 if (!found) return false;
817 }
818
819 pos = token_end + 1;
820 }
821
822 return true;
823 }
824
825 // Recursive accessor
826 template<std::size_t TokenIndex>
827 static inline simdjson_result<value> access_impl(simdjson_result<value> current) noexcept {
828 if constexpr (TokenIndex >= token_count) {
829 return current;
830 } else {
831 constexpr std::string_view token = parser::get_token(TokenIndex);
832
833 if constexpr (parser::is_numeric(token)) {
834 constexpr std::size_t index = parser::parse_index(token);
835 auto arr = current.get_array().value_unsafe();
836 auto next_value = arr.at(index);
837 return access_impl<TokenIndex + 1>(next_value);
838 } else {
839 auto obj = current.get_object().value_unsafe();
840 auto next_value = obj.find_field_unordered(token);
841 return access_impl<TokenIndex + 1>(next_value);
842 }
843 }
844 }
845
846 // Access JSON value at pointer
847 template<typename DocOrValue>
848 static inline simdjson_result<value> access(DocOrValue& doc_or_val) noexcept {
849 if constexpr (std::is_class_v<T>) {
850 constexpr bool pointer_valid = validate_pointer();
851 static_assert(pointer_valid, "JSON Pointer does not match struct definition");
852 }
853
854 if (pointer_view.empty() || pointer_view == "/") {
855 if constexpr (requires { doc_or_val.get_value(); }) {
856 return doc_or_val.get_value();
857 } else {
858 return doc_or_val;
859 }
860 }
861
862 simdjson_result<value> current = doc_or_val.get_value();
863 return access_impl<0>(current);
864 }
865
866 // Extract value at pointer directly into target with type validation
867 template<typename DocOrValue, typename FieldType>
868 static inline error_code extract_field(DocOrValue& doc_or_val, FieldType& target) noexcept {
869 static_assert(std::is_class_v<T>, "extract_field requires T to be a struct type for validation");
870
871 constexpr bool pointer_valid = validate_pointer();
872 static_assert(pointer_valid, "JSON Pointer does not match struct definition");
873
874 constexpr auto final_type = get_final_type();
875 static_assert(final_type == ^^FieldType, "Target type does not match the field type at the pointer");
876
877 simdjson_result<value> current_value = doc_or_val.get_value();
878 auto json_value = access_impl<0>(current_value);
879 if (json_value.error()) return json_value.error();
880
881 return json_value.get(target);
882 }
883
884private:
885 // Get final type by walking pointer through struct
886 template<typename U = T>
887 static consteval std::enable_if_t<std::is_class_v<U>, std::meta::info> get_final_type() {
888 auto current_type = ^^T;
889 std::size_t pos = pointer_view[0] == '/' ? 1 : 0;
890
891 while (pos < pointer_view.size()) {
892 std::size_t token_end = pointer_view.find('/', pos);
893 if (token_end == std::string_view::npos) token_end = pointer_view.size();
894
895 std::string_view token = pointer_view.substr(pos, token_end - pos);
896
897 if (parser::is_numeric(token)) {
898 current_type = path_accessor<T, "">::get_element_type_reflected(current_type);
899 } else {
900 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
901
902 for (auto mem : members) {
903 if (std::meta::identifier_of(mem) == token) {
904 current_type = std::meta::type_of(mem);
905 break;
906 }
907 }
908 }
909
910 pos = token_end + 1;
911 }
912
913 return current_type;
914 }
915};
916
917// Compile-time JSON Pointer accessor with validation
918template<typename T, constevalutil::fixed_string Pointer, typename DocOrValue>
919inline simdjson_result<::simdjson::SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
920 using accessor = pointer_accessor<T, Pointer>;
921 return accessor::access(doc_or_val);
922}
923
924// Overload without type parameter (no validation)
925template<constevalutil::fixed_string Pointer, typename DocOrValue>
926inline simdjson_result<::simdjson::SIMDJSON_IMPLEMENTATION::ondemand::value> at_pointer_compiled(DocOrValue& doc_or_val) noexcept {
927 using accessor = pointer_accessor<void, Pointer>;
928 return accessor::access(doc_or_val);
929}
930
931} // namespace json_path
932} // namespace ondemand
933} // namespace SIMDJSON_IMPLEMENTATION
934} // namespace simdjson
935
936#endif // SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
937#endif // SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
938
An ephemeral JSON value returned during iteration.
Definition value.h:22
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