10#ifndef SIMDJSON_GENERIC_COMPILE_TIME_JSON_INL_H
12#if SIMDJSON_STATIC_REFLECTION
15#include "simdjson/internal/fast_float.h"
32#define simdjson_consteval_error(...) \
38namespace compile_time {
48namespace number_parsing {
58consteval std::pair<double, size_t> parse_double(
const char *src,
61 auto answer = simdjson_fast_float::from_chars_advanced(
64 simdjson_fast_float::chars_format::json});
65 if (answer.ec == std::errc::invalid_argument) {
66 simdjson_consteval_error(
"Invalid float value");
73 if (value > (std::numeric_limits<double>::max)() ||
74 value < std::numeric_limits<double>::lowest()) {
75 simdjson_consteval_error(
"Overflow while computing the float value");
77 return {value, size_t(answer.ptr - src)};
82consteval auto make_data_member_options(
auto&& name_str) {
83 std::meta::data_member_options options{};
84 options.name = std::forward<decltype(name_str)>(name_str);
91template <
char... Vals>
struct fixed_json_string {
93 static constexpr char inner_data[] = {Vals...,
'\0'};
96 static constexpr std::size_t inner_size =
sizeof...(Vals);
100 static constexpr std::string_view view = {inner_data, inner_size};
102 constexpr operator std::string_view() {
return view; }
103 constexpr const char *c_str() {
return view.data(); }
104 constexpr const char *data() {
return view.data(); }
105 constexpr size_t size() {
return view.size(); }
108consteval std::meta::info to_fixed_json_string(std::string_view in) {
109 std::vector<std::meta::info> Args = {};
111 Args.push_back(std::meta::reflect_constant(c));
113 return std::meta::substitute(^^fixed_json_string, Args);
119template <std::meta::info... meta_info>
struct type_builder {
120 struct constructed_type;
122 std::meta::define_aggregate(^^constructed_type, {
133template <std::meta::info... meta_info>
134using class_type = type_builder<meta_info...>::constructed_type;
142template <
typename T,
auto... Vs>
constexpr T construct_from = T{Vs...};
147[[nodiscard]]
constexpr bool is_whitespace(
char c) {
148 return c ==
' ' || c ==
'\n' || c ==
'\t' || c ==
'\r';
151[[nodiscard]]
constexpr std::string_view trim_whitespace(std::string_view str) {
153 size_t end = str.size();
155 while (start < end && is_whitespace(str[start])) {
158 while (end > start && is_whitespace(str[end - 1])) {
161 return str.substr(start, end - start);
165consteval std::pair<std::meta::info, size_t>
166parse_json_object_impl(std::string_view json);
175[[nodiscard]]
consteval size_t
176parse_number(std::string_view json,
177 std::variant<int64_t, uint64_t, double> &out) {
179 simdjson_consteval_error(
"Empty string is not a valid JSON number");
181 if (json[0] ==
'+') {
182 simdjson_consteval_error(
"Invalid number: leading '+' sign is not allowed");
185 auto it = json.begin();
186 if (it != json.end() && (*it ==
'-')) {
189 while (it != json.end() && (*it >=
'0' && *it <=
'9')) {
192 bool is_float =
false;
193 if (it != json.end() && (*it ==
'.' || *it ==
'e' || *it ==
'E')) {
197 while (it != json.end() && (*it >=
'0' && *it <=
'9')) {
201 if (it != json.end() && (*it ==
'e' || *it ==
'E')) {
203 if (it != json.end() && (*it ==
'+' || *it ==
'-')) {
206 while (it != json.end() && (*it >=
'0' && *it <=
'9')) {
211 size_t scope = it - json.begin();
213 bool is_negative = json[0] ==
'-';
217 auto [value, offset] =
218 number_parsing::parse_double(json.data(), json.data() + json.size());
219 if (offset != scope) {
220 simdjson_consteval_error(
221 "Internal error: cannot agree on the character range of the float");
225 }
else if (is_negative) {
226 int64_t int_value = 0;
227 std::from_chars_result res =
228 std::from_chars(json.data(), json.data() + json.size(), int_value);
229 if (res.ec == std::errc()) {
231 if (
static_cast<std::size_t
>(res.ptr - json.data()) != scope) {
232 simdjson_consteval_error(
233 "Internal error: cannot agree on the character range of the float");
235 return (res.ptr - json.data());
237 simdjson_consteval_error(
"Invalid integer value");
240 uint64_t uint_value = 0;
241 std::from_chars_result res =
242 std::from_chars(json.data(), json.data() + json.size(), uint_value);
243 if (res.ec == std::errc()) {
245 if (
static_cast<std::size_t
>(res.ptr - json.data()) != scope) {
246 simdjson_consteval_error(
247 "Internal error: cannot agree on the character range of the float");
249 return (res.ptr - json.data());
251 simdjson_consteval_error(
"Invalid unsigned integer value");
265[[nodiscard]]
consteval std::pair<std::string, size_t>
266parse_string(std::string_view json) {
267 auto cursor = json.begin();
268 auto end = json.end();
272 if (cursor == end || *(cursor++) !=
'"') {
273 simdjson_consteval_error(
"Expected opening quote for string");
279 auto process_escaped_unicode = [&] [[nodiscard]] () ->
bool {
286 auto hex_to_u32 = [&] [[nodiscard]] () -> uint32_t {
287 auto digit = [](uint8_t c) -> uint32_t {
288 if (c >=
'0' && c <=
'9')
290 if (c >=
'A' && c <=
'F')
292 if (c >=
'a' && c <=
'f')
296 if (end - cursor < 4) {
299 uint32_t d0 = digit(*cursor++);
300 uint32_t d1 = digit(*cursor++);
301 uint32_t d2 = digit(*cursor++);
302 uint32_t d3 = digit(*cursor++);
304 return (d0 << 12) | (d1 << 8) | (d2 << 4) | d3;
309 auto codepoint_to_utf8 = [&out] [[nodiscard]] (uint32_t cp) ->
bool {
312 if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) {
316 out.push_back(uint8_t(cp));
317 }
else if (cp <= 0x7FF) {
318 out.push_back(uint8_t((cp >> 6) + 192));
319 out.push_back(uint8_t((cp & 63) + 128));
320 }
else if (cp <= 0xFFFF) {
321 out.push_back(uint8_t((cp >> 12) + 224));
322 out.push_back(uint8_t(((cp >> 6) & 63) + 128));
323 out.push_back(uint8_t((cp & 63) + 128));
324 }
else if (cp <= 0x10FFFF) {
325 out.push_back(uint8_t((cp >> 18) + 240));
326 out.push_back(uint8_t(((cp >> 12) & 63) + 128));
327 out.push_back(uint8_t(((cp >> 6) & 63) + 128));
328 out.push_back(uint8_t((cp & 63) + 128));
337 constexpr uint32_t substitution_code_point = 0xfffd;
339 if (end - cursor < 4) {
343 uint32_t code_point = hex_to_u32();
345 if (code_point > 0xFFFF) {
349 if (code_point >= 0xd800 && code_point < 0xdc00) {
353 if (end - cursor < 6 || *cursor !=
'\\' ||
354 *(cursor + 1) !=
'u') {
355 code_point = substitution_code_point;
358 uint32_t code_point_2 = hex_to_u32();
359 if (code_point_2 > 0xFFFF) {
362 uint32_t low_bit = code_point_2 - 0xdc00;
365 code_point = substitution_code_point;
367 code_point = ((code_point - 0xd800) << 10 | low_bit) + 0x10000;
370 }
else if (code_point >= 0xdc00 && code_point < 0xe000) {
372 code_point = substitution_code_point;
375 return codepoint_to_utf8(code_point);
378 while (cursor != end && *cursor !=
'"') {
381 simdjson_consteval_error(
"Unterminated string");
384 char c = *(cursor++);
390 size_t how_many_backslashes = 1;
391 while (cursor != end && *(cursor) ==
'\\') {
393 how_many_backslashes++;
395 size_t backslashes_to_add = how_many_backslashes / 2;
397 for (
size_t i = 0; i < backslashes_to_add; i++) {
402 if (how_many_backslashes % 2 == 1) {
406 simdjson_consteval_error(
"Truncated escape sequence in string");
408 char next_char = *cursor;
433 if (!process_escaped_unicode()) {
434 simdjson_consteval_error(
435 "Invalid unicode escape sequence in string");
439 simdjson_consteval_error(
"Invalid escape character in string");
447 if (
static_cast<unsigned char>(c) < 0x20) {
448 simdjson_consteval_error(
"Invalid control character in string");
450 if (
static_cast<unsigned char>(c) >= 0x80) {
453 uint8_t first_byte =
static_cast<uint8_t
>(c);
455 if ((first_byte & 0b11100000) == 0b11000000) {
457 simdjson_consteval_error(
"Truncated UTF-8 sequence in string");
460 char second_byte = *cursor;
463 if ((
static_cast<uint8_t
>(second_byte) & 0b11000000) != 0b10000000) {
464 simdjson_consteval_error(
"Invalid UTF-8 continuation byte in string");
467 uint32_t code_point = (first_byte & 0b00011111) << 6 |
468 (
static_cast<uint8_t
>(second_byte) & 0b00111111);
469 if ((code_point < 0x80) || (0x7ff < code_point)) {
470 simdjson_consteval_error(
"Invalid UTF-8 code point in string");
472 }
else if ((first_byte & 0b11110000) == 0b11100000) {
474 simdjson_consteval_error(
"Truncated UTF-8 sequence in string");
476 char second_byte = *cursor;
479 if ((
static_cast<uint8_t
>(second_byte) & 0b11000000) != 0b10000000) {
480 simdjson_consteval_error(
"Invalid UTF-8 continuation byte in string");
483 simdjson_consteval_error(
"Truncated UTF-8 sequence in string");
485 char third_byte = *cursor;
488 if ((
static_cast<uint8_t
>(third_byte) & 0b11000000) != 0b10000000) {
489 simdjson_consteval_error(
"Invalid UTF-8 continuation byte in string");
492 uint32_t code_point = (first_byte & 0b00001111) << 12 |
493 (
static_cast<uint8_t
>(second_byte) & 0b00111111)
495 (
static_cast<uint8_t
>(third_byte) & 0b00111111);
496 if ((code_point < 0x800) || (0xffff < code_point) ||
497 (0xd7ff < code_point && code_point < 0xe000)) {
498 simdjson_consteval_error(
"Invalid UTF-8 code point in string");
500 }
else if ((first_byte & 0b11111000) == 0b11110000) {
502 simdjson_consteval_error(
"Truncated UTF-8 sequence in string");
504 char second_byte = *cursor;
508 simdjson_consteval_error(
"Truncated UTF-8 sequence in string");
510 char third_byte = *cursor;
514 simdjson_consteval_error(
"Truncated UTF-8 sequence in string");
516 char fourth_byte = *cursor;
519 if ((
static_cast<uint8_t
>(second_byte) & 0b11000000) != 0b10000000) {
520 simdjson_consteval_error(
"Invalid UTF-8 continuation byte in string");
522 if ((
static_cast<uint8_t
>(third_byte) & 0b11000000) != 0b10000000) {
523 simdjson_consteval_error(
"Invalid UTF-8 continuation byte in string");
525 if ((
static_cast<uint8_t
>(fourth_byte) & 0b11000000) != 0b10000000) {
526 simdjson_consteval_error(
"Invalid UTF-8 continuation byte in string");
529 uint32_t code_point =
530 (first_byte & 0b00000111) << 18 |
531 (
static_cast<uint8_t
>(second_byte) & 0b00111111) << 12 |
532 (
static_cast<uint8_t
>(third_byte) & 0b00111111) << 6 |
533 (
static_cast<uint8_t
>(fourth_byte) & 0b00111111);
534 if (code_point <= 0xffff || 0x10ffff < code_point) {
535 simdjson_consteval_error(
"Invalid UTF-8 code point in string");
539 simdjson_consteval_error(
"Invalid UTF-8 continuation byte in string");
545 simdjson_consteval_error(
"Unterminated string");
547 if (*cursor !=
'"') {
548 simdjson_consteval_error(
"Internal error: expected closing quote");
553 return {out, size_t(cursor - json.begin())};
562consteval std::pair<std::meta::info, size_t>
563parse_json_array_impl(
const std::string_view json) {
565 auto cursor = json.begin();
566 auto end = json.end();
567 auto is_whitespace = [](
char c) {
568 return c ==
' ' || c ==
'\n' || c ==
'\t' || c ==
'\r';
571 auto skip_whitespace = [&]() ->
void {
572 while (cursor != end && is_whitespace(*cursor))
576 auto expect_consume = [&] [[nodiscard]] (
char c) ->
bool {
578 if (cursor == end || *(cursor++) != c) {
584 if (!expect_consume(
'[')) {
585 simdjson_consteval_error(
"Expected '['");
588 std::vector<std::meta::info> values = {^^
void};
590 if (cursor != end && *cursor ==
']') {
591 if (!expect_consume(
']')) {
592 simdjson_consteval_error(
"Expected ']'");
595 auto array_type = std::meta::substitute(
597 ^^int, std::meta::reflect_constant(0uz)});
598 values[0] = array_type;
599 consumed = size_t(cursor - json.begin());
600 if (json[consumed - 1] !=
']') {
601 simdjson_consteval_error(
"Expected ']'");
603 return {std::meta::substitute(^^construct_from, values), consumed};
605 while (cursor != end && *cursor !=
']') {
609 std::string_view value(cursor, end);
610 auto [parsed, object_size] = parse_json_object_impl(value);
611 if (*(cursor + object_size - 1) !=
'}') {
612 simdjson_consteval_error(
"Expected '}'");
614 values.push_back(parsed);
615 cursor += object_size;
619 std::string_view value(cursor, end);
620 auto [parsed, array_size] = parse_json_array_impl(value);
621 if (*(cursor + array_size - 1) !=
']') {
622 simdjson_consteval_error(
"Expected ']'");
624 values.push_back(parsed);
625 cursor += array_size;
629 auto res = parse_string(std::string_view(cursor, end));
630 cursor += res.second;
631 for (
char ch : res.first) {
633 simdjson_consteval_error(
634 "Field string values cannot contain embedded nulls");
637 values.push_back(std::meta::reflect_constant_string(res.first));
641 if (end - cursor < 4 || std::string_view(cursor, 4) !=
"true") {
642 simdjson_consteval_error(
"Invalid value");
645 values.push_back(std::meta::reflect_constant(
true));
649 if (end - cursor < 5 || std::string_view(cursor, 5) !=
"false") {
650 simdjson_consteval_error(
"Invalid value");
653 values.push_back(std::meta::reflect_constant(
false));
657 if (end - cursor < 4 || std::string_view(cursor, 4) !=
"null") {
658 simdjson_consteval_error(
"Invalid value");
661 values.push_back(std::meta::reflect_constant(
nullptr));
676 std::string_view suffix = std::string_view(cursor, end);
677 std::variant<int64_t, uint64_t, double> out;
678 size_t r = parse_number(suffix, out);
680 if (std::holds_alternative<int64_t>(out)) {
681 int64_t int_value = std::get<int64_t>(out);
682 values.push_back(std::meta::reflect_constant(int_value));
683 }
else if (std::holds_alternative<uint64_t>(out)) {
684 uint64_t uint_value = std::get<uint64_t>(out);
685 values.push_back(std::meta::reflect_constant(uint_value));
687 double float_value = std::get<double>(out);
688 values.push_back(std::meta::reflect_constant(float_value));
693 simdjson_consteval_error(
"Invalid character starting value");
696 if (cursor != end && *cursor ==
',') {
702 if (!expect_consume(
']')) {
703 simdjson_consteval_error(
"Expected ']'");
705 std::size_t count = values.size() - 1;
708 auto elem_type = std::meta::type_of(values[1]);
713 if (std::meta::is_array_type(elem_type) &&
714 std::meta::remove_all_extents(elem_type) == ^^
const char) {
715 elem_type = ^^
const char *;
717 auto array_type = std::meta::substitute(
720 elem_type, std::meta::reflect_constant(count)});
723 values[0] = array_type;
724 consumed = size_t(cursor - json.begin());
725 if (json[consumed - 1] !=
']') {
726 simdjson_consteval_error(
"Expected ']'");
728 return {std::meta::substitute(^^construct_from, values), consumed};
737consteval std::pair<std::meta::info, size_t>
738parse_json_object_impl(std::string_view json) {
740 auto cursor = json.begin();
741 auto end = json.end();
743 auto skip_whitespace = [&]() ->
void {
744 while (cursor != end && is_whitespace(*cursor))
748 auto expect_consume = [&] [[nodiscard]] (
char c) ->
bool {
750 if (cursor == end || *(cursor++) != c) {
756 if (!expect_consume(
'{')) {
757 simdjson_consteval_error(
"Expected '{'");
760 std::vector<std::meta::info> members;
761 std::vector<std::meta::info> values = {^^
void};
763 while (cursor != end && *cursor !=
'}') {
768 auto field = parse_string(std::string_view(cursor, end));
769 std::string field_name = field.first;
770 cursor += field.second;
771 if (!expect_consume(
':')) {
772 simdjson_consteval_error(
"Expected ':'");
776 simdjson_consteval_error(
"Expected value after colon");
781 std::string_view value(cursor, end);
782 auto [parsed, object_size] = parse_json_object_impl(value);
783 if (*(cursor + object_size - 1) !=
'}') {
784 simdjson_consteval_error(
"Expected '}'");
786 cursor += object_size;
787 auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), make_data_member_options(field_name));
788 members.push_back(std::meta::reflect_constant(dms));
789 values.push_back(parsed);
794 std::string_view value(cursor, end);
795 auto [parsed, array_size] = parse_json_array_impl(value);
796 auto dms = std::meta::data_member_spec(std::meta::type_of(parsed), make_data_member_options(field_name));
797 members.push_back(std::meta::reflect_constant(dms));
798 values.push_back(parsed);
799 if (*(cursor + array_size - 1) !=
']') {
800 simdjson_consteval_error(
"Expected ']'");
802 cursor += array_size;
807 auto res = parse_string(std::string_view(cursor, end));
808 std::string_view value = res.first;
809 cursor += res.second;
810 for (
char ch : value) {
812 simdjson_consteval_error(
813 "Field string values cannot contain embedded nulls");
817 std::meta::data_member_spec(^^
const char *, make_data_member_options(field_name));
818 members.push_back(std::meta::reflect_constant(dms));
819 values.push_back(std::meta::reflect_constant_string(value));
823 if (end - cursor < 4 || std::string_view(cursor, 4) !=
"true") {
824 simdjson_consteval_error(
"Invalid value");
828 auto dms = std::meta::data_member_spec(^^
bool, make_data_member_options(field_name));
829 members.push_back(std::meta::reflect_constant(dms));
830 values.push_back(std::meta::reflect_constant(
true));
834 if (end - cursor < 5 || std::string_view(cursor, 5) !=
"false") {
835 simdjson_consteval_error(
"Invalid value");
839 auto dms = std::meta::data_member_spec(^^
bool, make_data_member_options(field_name));
840 members.push_back(std::meta::reflect_constant(dms));
841 values.push_back(std::meta::reflect_constant(
false));
845 if (end - cursor < 4 || std::string_view(cursor, 4) !=
"null") {
846 simdjson_consteval_error(
"Invalid value");
850 auto dms = std::meta::data_member_spec(^^std::nullptr_t, make_data_member_options(field_name));
851 members.push_back(std::meta::reflect_constant(dms));
852 values.push_back(std::meta::reflect_constant(
nullptr));
867 std::string_view suffix = std::string_view(cursor, end);
868 std::variant<int64_t, uint64_t, double> out;
869 size_t r = parse_number(suffix, out);
871 if (std::holds_alternative<int64_t>(out)) {
872 int64_t int_value = std::get<int64_t>(out);
874 std::meta::data_member_spec(^^int64_t, make_data_member_options(field_name));
875 members.push_back(std::meta::reflect_constant(dms));
876 values.push_back(std::meta::reflect_constant(int_value));
877 }
else if (std::holds_alternative<uint64_t>(out)) {
878 uint64_t uint_value = std::get<uint64_t>(out);
880 std::meta::data_member_spec(^^uint64_t, make_data_member_options(field_name));
881 members.push_back(std::meta::reflect_constant(dms));
882 values.push_back(std::meta::reflect_constant(uint_value));
884 double float_value = std::get<double>(out);
886 std::meta::data_member_spec(^^
double, make_data_member_options(field_name));
887 members.push_back(std::meta::reflect_constant(dms));
888 values.push_back(std::meta::reflect_constant(float_value));
893 simdjson_consteval_error(
"Invalid character starting value");
897 simdjson_consteval_error(
"Expected '}' or ','");
899 if (*cursor ==
',') {
902 }
else if (*cursor !=
'}') {
903 simdjson_consteval_error(
"Expected '}'");
907 if (!expect_consume(
'}')) {
908 simdjson_consteval_error(
"Expected '}'");
910 values[0] = std::meta::substitute(^^class_type, members);
911 consumed = size_t(cursor - json.begin());
912 if (json[consumed - 1] !=
'}') {
913 simdjson_consteval_error(
"Expected '}'");
915 return {std::meta::substitute(^^construct_from, values), consumed};
924template <constevalutil::fixed_
string json_str>
consteval auto parse_json() {
925 constexpr std::string_view json = trim_whitespace(json_str.view());
926 static_assert(!json.empty(),
"JSON string cannot be empty");
931 if constexpr (json.front() ==
'[') {
932 return [: parse_json_array_impl(json).first :];
934 return [: parse_json_object_impl(json).first :];
Compile-time JSON parsing using C++26 reflection with std::meta::substitute()
The top level simdjson namespace, containing everything the library provides.