42#ifndef SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
44#ifndef SIMDJSON_CONDITIONAL_INCLUDE
45#define SIMDJSON_GENERIC_ONDEMAND_COMPILE_TIME_ACCESSORS_H
51#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_STATIC_REFLECTION
58namespace SIMDJSON_IMPLEMENTATION {
69using ::simdjson::SIMDJSON_IMPLEMENTATION::ondemand::value;
78template<std::
size_t N>
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) {
91 constexpr std::string_view key_view()
const {
97template<std::
size_t N>
98consteval auto make_field_step(
const char (&name)[N]) {
99 return path_step<N>(step_type::field, name, 0);
103consteval auto make_index_step(std::size_t idx) {
104 return path_step<1>(step_type::array_index,
"", idx);
111 std::string_view error_msg;
116template<constevalutil::fixed_
string Path>
117struct json_path_parser {
118 static constexpr std::string_view path_str = Path.view();
121 static consteval std::size_t skip_root() {
122 if (!path_str.empty() && path_str[0] ==
'$') {
129 static consteval std::size_t count_steps() {
130 std::size_t count = 0;
131 std::size_t i = skip_root();
133 while (i < path_str.size()) {
134 if (path_str[i] ==
'.') {
137 if (i >= path_str.size())
break;
140 while (i < path_str.size() && path_str[i] !=
'.' && path_str[i] !=
'[') {
144 }
else if (path_str[i] ==
'[') {
147 if (i >= path_str.size())
break;
149 if (path_str[i] ==
'"' || path_str[i] ==
'\'') {
151 char quote = path_str[i];
153 while (i < path_str.size() && path_str[i] != quote) {
156 if (i < path_str.size()) ++i;
157 if (i < path_str.size() && path_str[i] ==
']') ++i;
160 while (i < path_str.size() && path_str[i] !=
']') {
163 if (i < path_str.size()) ++i;
175 static consteval std::size_t parse_field_name(std::size_t start,
char* out, std::size_t max_len) {
177 std::size_t i = start;
179 while (i < path_str.size() && path_str[i] !=
'.' && path_str[i] !=
'[' && len < max_len - 1) {
180 out[len++] = path_str[i++];
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;
191 while (i < path_str.size() && path_str[i] >=
'0' && path_str[i] <=
'9') {
192 index = index * 10 + (path_str[i] -
'0');
201template<
typename T, constevalutil::fixed_
string Path>
202struct path_accessor {
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();
212 template<
typename DocOrValue>
213 static inline simdjson_result<value> access(DocOrValue& doc_or_val)
noexcept {
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");
221 return access_impl<parser.skip_root()>(doc_or_val.get_value());
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");
231 constexpr bool path_valid = validate_path();
232 static_assert(path_valid,
"JSON path does not match struct definition");
235 constexpr auto final_type = get_final_type();
238 static_assert(final_type == ^^FieldType,
"Target type does not match the field type at the path");
241 auto json_value = access_impl<parser.skip_root()>(doc_or_val.get_value());
242 if (json_value.error())
return json_value.error();
244 return json_value.get(target);
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();
254 while (i < path_view.size()) {
255 if (path_view[i] ==
'.') {
258 std::size_t field_start = i;
259 while (i < path_view.size() && path_view[i] !=
'.' && path_view[i] !=
'[') {
263 std::string_view field_name = path_view.substr(field_start, i - field_start);
265 auto members = std::meta::nonstatic_data_members_of(
266 current_type, std::meta::access_context::unchecked()
269 for (
auto mem : members) {
270 if (std::meta::identifier_of(mem) == field_name) {
271 current_type = std::meta::type_of(mem);
276 }
else if (path_view[i] ==
'[') {
278 if (i >= path_view.size())
break;
280 if (path_view[i] ==
'"' || path_view[i] ==
'\'') {
282 char quote = path_view[i];
284 std::size_t field_start = i;
285 while (i < path_view.size() && path_view[i] != quote) {
289 std::string_view field_name = path_view.substr(field_start, i - field_start);
290 if (i < path_view.size()) ++i;
291 if (i < path_view.size() && path_view[i] ==
']') ++i;
293 auto members = std::meta::nonstatic_data_members_of(
294 current_type, std::meta::access_context::unchecked()
297 for (
auto mem : members) {
298 if (std::meta::identifier_of(mem) == field_name) {
299 current_type = std::meta::type_of(mem);
306 while (i < path_view.size() && path_view[i] >=
'0' && path_view[i] <=
'9') {
309 if (i < path_view.size() && path_view[i] ==
']') ++i;
311 current_type = get_element_type_reflected(current_type);
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();
328 if constexpr (PathPos >= path_view.size()) {
329 return current.get(target_ref);
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);
337 constexpr auto member_info = find_member_by_name(CurrentType, field_name);
338 static_assert(member_info != ^^void,
"Field not found in struct");
340 constexpr auto member_type = std::meta::type_of(member_info);
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);
347 if constexpr (next_pos >= path_view.size()) {
348 return field_value.get(target_ref);
350 return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
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);
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);
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);
370 if constexpr (next_pos >= path_view.size()) {
371 return field_value.get(target_ref);
373 return extract_with_reflection<member_type, next_pos>(field_value, target_ref);
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");
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);
385 if constexpr (next_pos >= path_view.size()) {
386 return elem_value.get(target_ref);
388 return extract_with_reflection<elem_type, next_pos>(elem_value, target_ref);
394 return extract_with_reflection<CurrentType, PathPos + 1>(current, target_ref);
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) {
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;
413 if constexpr (PathPos >= path_view.size()) {
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);
420 auto obj_result = current.get_object();
421 if (obj_result.error())
return obj_result.error();
423 auto obj = obj_result.value_unsafe();
424 auto next_value = obj.find_field_unordered(field_name);
426 return access_impl<next_pos>(next_value);
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);
433 if constexpr (is_field) {
434 constexpr std::string_view field_name = std::get<1>(bracket_info);
436 auto obj_result = current.get_object();
437 if (obj_result.error())
return obj_result.error();
439 auto obj = obj_result.value_unsafe();
440 auto next_value = obj.find_field_unordered(field_name);
442 return access_impl<next_pos>(next_value);
445 constexpr std::size_t index = std::get<3>(bracket_info);
447 auto arr_result = current.get_array();
448 if (arr_result.error())
return arr_result.error();
450 auto arr = arr_result.value_unsafe();
451 auto next_value = arr.at(index);
453 return access_impl<next_pos>(next_value);
456 return access_impl<PathPos + 1>(current);
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] !=
'[') {
467 std::string_view field_name = path_view.substr(field_start, i - field_start);
468 return std::make_tuple(field_name, i);
472 static consteval auto parse_bracket(std::size_t start) {
473 std::size_t i = start + 1;
475 if (i < path_view.size() && (path_view[i] ==
'"' || path_view[i] ==
'\'')) {
477 char quote = path_view[i];
479 std::size_t field_start = i;
480 while (i < path_view.size() && path_view[i] != quote) {
483 std::string_view field_name = path_view.substr(field_start, i - field_start);
484 if (i < path_view.size()) ++i;
485 if (i < path_view.size() && path_view[i] ==
']') ++i;
487 return std::make_tuple(
true, field_name, i, std::size_t(0));
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');
495 if (i < path_view.size() && path_view[i] ==
']') ++i;
497 return std::make_tuple(
false, std::string_view{}, i, index);
505 static consteval bool is_array_like_reflected(std::meta::info type_reflection) {
506 if (std::meta::is_array_type(type_reflection)) {
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}));
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);
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") {
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) {
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);
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; }) {
568 if constexpr (std::is_array_v<BaseType>) {
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,
580 std::is_array_v<std::remove_cvref_t<Type>>,
581 std::remove_extent_t<std::remove_cvref_t<Type>>,
587 static consteval bool validate_path() {
588 if constexpr (!std::is_class_v<T>) {
592 auto current_type = ^^T;
593 std::size_t i = parser.skip_root();
595 while (i < path_view.size()) {
596 if (path_view[i] ==
'.') {
598 std::size_t field_start = i;
599 while (i < path_view.size() && path_view[i] !=
'.' && path_view[i] !=
'[') {
603 std::string_view field_name = path_view.substr(field_start, i - field_start);
606 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
608 for (
auto mem : members) {
609 if (std::meta::identifier_of(mem) == field_name) {
610 current_type = std::meta::type_of(mem);
620 }
else if (path_view[i] ==
'[') {
622 if (i >= path_view.size())
return false;
624 if (path_view[i] ==
'"' || path_view[i] ==
'\'') {
625 char quote = path_view[i];
627 std::size_t field_start = i;
628 while (i < path_view.size() && path_view[i] != quote) {
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;
637 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
639 for (
auto mem : members) {
640 if (std::meta::identifier_of(mem) == field_name) {
641 current_type = std::meta::type_of(mem);
652 while (i < path_view.size() && path_view[i] >=
'0' && path_view[i] <=
'9') {
656 if (i < path_view.size() && path_view[i] ==
']') ++i;
658 if (!is_array_like_reflected(current_type)) {
662 auto new_type = get_element_type_reflected(current_type);
664 if (new_type == ^^
void) {
668 current_type = new_type;
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);
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);
698template<constevalutil::fixed_
string Po
inter>
699struct json_pointer_parser {
700 static constexpr std::string_view pointer_str = Pointer.view();
703 static consteval void unescape_token(std::string_view src,
char* dest, std::size_t& out_len) {
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++] =
'~';
710 }
else if (src[i + 1] ==
'1') {
711 dest[out_len++] =
'/';
714 dest[out_len++] = src[i];
717 dest[out_len++] = src[i];
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;
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');
742 static consteval std::size_t count_tokens() {
743 if (pointer_str.empty() || pointer_str ==
"/")
return 0;
745 std::size_t count = 0;
746 std::size_t pos = pointer_str[0] ==
'/' ? 1 : 0;
748 while (pos < pointer_str.size()) {
750 std::size_t next_slash = pointer_str.find(
'/', pos);
751 if (next_slash == std::string_view::npos)
break;
752 pos = next_slash + 1;
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;
763 while (current_token < token_index) {
764 std::size_t next_slash = pointer_str.find(
'/', pos);
765 pos = next_slash + 1;
769 std::size_t token_end = pointer_str.find(
'/', pos);
770 if (token_end == std::string_view::npos) token_end = pointer_str.size();
772 return pointer_str.substr(pos, token_end - pos);
777template<
typename T, constevalutil::fixed_
string Po
inter>
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();
784 static consteval bool validate_pointer() {
785 if constexpr (!std::is_class_v<T>) {
789 auto current_type = ^^T;
790 std::size_t pos = pointer_view[0] ==
'/' ? 1 : 0;
792 while (pos < pointer_view.size()) {
794 std::size_t token_end = pointer_view.find(
'/', pos);
795 if (token_end == std::string_view::npos) token_end = pointer_view.size();
797 std::string_view token = pointer_view.substr(pos, token_end - pos);
799 if (parser::is_numeric(token)) {
800 if (!path_accessor<T, Pointer>::is_array_like_reflected(current_type)) {
803 current_type = path_accessor<T, Pointer>::get_element_type_reflected(current_type);
806 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
808 for (
auto mem : members) {
809 if (std::meta::identifier_of(mem) == token) {
810 current_type = std::meta::type_of(mem);
816 if (!found)
return false;
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) {
831 constexpr std::string_view token = parser::get_token(TokenIndex);
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);
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);
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");
854 if (pointer_view.empty() || pointer_view ==
"/") {
855 if constexpr (
requires { doc_or_val.get_value(); }) {
856 return doc_or_val.get_value();
862 simdjson_result<value> current = doc_or_val.get_value();
863 return access_impl<0>(current);
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");
871 constexpr bool pointer_valid = validate_pointer();
872 static_assert(pointer_valid,
"JSON Pointer does not match struct definition");
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");
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();
881 return json_value.get(target);
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;
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();
895 std::string_view token = pointer_view.substr(pos, token_end - pos);
897 if (parser::is_numeric(token)) {
898 current_type = path_accessor<T,
"">::get_element_type_reflected(current_type);
900 auto members = std::meta::nonstatic_data_members_of(current_type, std::meta::access_context::unchecked());
902 for (
auto mem : members) {
903 if (std::meta::identifier_of(mem) == token) {
904 current_type = std::meta::type_of(mem);
918template<
typename T, constevalutil::fixed_
string Po
inter,
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);
925template<constevalutil::fixed_
string Po
inter,
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);
An ephemeral JSON value returned during iteration.
The top level simdjson namespace, containing everything the library provides.
error_code
All possible errors returned by simdjson.