diff --git a/deps/simdjson/simdjson.cpp b/deps/simdjson/simdjson.cpp index 980bb83cefa4..e8a2a24a04b4 100644 --- a/deps/simdjson/simdjson.cpp +++ b/deps/simdjson/simdjson.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2026-10-07 22:43:29 -0400. version 5.0.3 Do not edit! */ +/* auto-generated on 2026-10-14 15:49:28 -0400. version 5.0.5 Do not edit! */ /* including simdjson.cpp: */ /* begin file simdjson.cpp */ #define SIMDJSON_SRC_SIMDJSON_CPP @@ -3355,6 +3355,10 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input) { out.push_back('"'); return out; } + +consteval std::size_t padded_key_length(std::size_t length) { + return (length + 15) / 16 * 16; +} #endif // SIMDJSON_CONSTEVAL @@ -3391,10 +3395,45 @@ struct fixed_string { template fixed_string(const char (&)[N]) -> fixed_string; +template +inline constexpr bool is_fixed_string = false; +template +inline constexpr bool is_fixed_string> = true; + template struct string_constant { static constexpr std::string_view value = str.view(); }; + +#if SIMDJSON_CONSTEVAL +template +consteval std::size_t static_string_size() { + return Text::text().size(); +} +template +consteval std::size_t padded_static_string_size() { + return padded_key_length(static_string_size()); +} + +template +consteval auto make_static_string() { + std::array()> out{}; + std::string text = Text::text(); + // The tail past text.size() stays zero: it is the padding. + for (std::size_t i = 0; i < text.size(); ++i) { + out[i] = text[i]; + } + return out; +} + +template +inline constexpr auto static_string = make_static_string(); + +template +struct quoted_escaped_text { + static consteval std::string text() { return consteval_to_quoted_escaped(str.view()); } +}; +#endif // SIMDJSON_CONSTEVAL #endif // SIMDJSON_SUPPORTS_CONCEPTS } // namespace constevalutil @@ -9436,10 +9475,10 @@ simdjson_fastfloat_really_inline bool rounds_to_nearest() noexcept { // https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013 #elif defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wfloat-equal" +SIMDJSON_DISABLE_GCC_WARNING(-Wfloat-equal) #elif defined(__GNUC__) #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" +SIMDJSON_DISABLE_GCC_WARNING(-Wfloat-equal) #endif return (fmini + 1.0f == 1.0f - fmini); #ifdef SIMDJSON_FASTFLOAT_VISUAL_STUDIO @@ -9680,7 +9719,7 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value, #ifdef __clang__ #pragma clang diagnostic push #if (!defined(__APPLE_CC__) && __clang_major__ >= 10) || (__clang_major__ >= 13) -#pragma clang diagnostic ignored "-Wc++20-extensions" +SIMDJSON_DISABLE_GCC_WARNING(-Wc++20-extensions) #endif #endif if simdjson_fastfloat_unlikely (pns.too_many_digits) { diff --git a/deps/simdjson/simdjson.h b/deps/simdjson/simdjson.h index 58041ff68a79..faf21bdc2320 100644 --- a/deps/simdjson/simdjson.h +++ b/deps/simdjson/simdjson.h @@ -1,4 +1,4 @@ -/* auto-generated on 2026-10-07 22:43:29 -0400. version 5.0.3 Do not edit! */ +/* auto-generated on 2026-10-14 15:49:28 -0400. version 5.0.5 Do not edit! */ /* including simdjson.h: */ /* begin file simdjson.h */ #ifndef SIMDJSON_H @@ -2715,7 +2715,7 @@ namespace std { #define SIMDJSON_SIMDJSON_VERSION_H /** The version of simdjson being used (major.minor.revision) */ -#define SIMDJSON_VERSION "5.0.3" +#define SIMDJSON_VERSION "5.0.5" namespace simdjson { enum { @@ -2730,7 +2730,7 @@ enum { /** * The revision (major.minor.REVISION) of simdjson being used. */ - SIMDJSON_VERSION_REVISION = 3 + SIMDJSON_VERSION_REVISION = 5 }; } // namespace simdjson @@ -3418,6 +3418,10 @@ consteval std::string consteval_to_quoted_escaped(std::string_view input) { out.push_back('"'); return out; } + +consteval std::size_t padded_key_length(std::size_t length) { + return (length + 15) / 16 * 16; +} #endif // SIMDJSON_CONSTEVAL @@ -3454,10 +3458,45 @@ struct fixed_string { template fixed_string(const char (&)[N]) -> fixed_string; +template +inline constexpr bool is_fixed_string = false; +template +inline constexpr bool is_fixed_string> = true; + template struct string_constant { static constexpr std::string_view value = str.view(); }; + +#if SIMDJSON_CONSTEVAL +template +consteval std::size_t static_string_size() { + return Text::text().size(); +} +template +consteval std::size_t padded_static_string_size() { + return padded_key_length(static_string_size()); +} + +template +consteval auto make_static_string() { + std::array()> out{}; + std::string text = Text::text(); + // The tail past text.size() stays zero: it is the padding. + for (std::size_t i = 0; i < text.size(); ++i) { + out[i] = text[i]; + } + return out; +} + +template +inline constexpr auto static_string = make_static_string(); + +template +struct quoted_escaped_text { + static consteval std::string text() { return consteval_to_quoted_escaped(str.view()); } +}; +#endif // SIMDJSON_CONSTEVAL #endif // SIMDJSON_SUPPORTS_CONCEPTS } // namespace constevalutil @@ -8621,7 +8660,7 @@ template std::string prettify(simdjson_result x) { if (x.error()) { throw simdjson_error(x.error()); } - return to_string(x.value()); + return prettify(x.value()); } #endif @@ -8852,6 +8891,7 @@ inline std::string fractured_json_string(std::string_view json_str, #include #include +#include namespace simdjson { namespace internal { @@ -8893,6 +8933,28 @@ simdjson_inline error_code parse_json_pointer_array_index(std::string_view json_ return SUCCESS; } + +/** + * Returns true if the error means that a JSONPath segment matched nothing + * (missing field, index out of bounds, value of the wrong type, or a scalar + * reached while path segments remain), as opposed to a document error. + * A wildcard walk skips such branches, as RFC 9535 and DOM's + * at_path_with_wildcard do. + */ +simdjson_inline bool is_matching_failure_error(error_code error) noexcept { + return error == NO_SUCH_FIELD || error == INDEX_OUT_OF_BOUNDS || + error == INCORRECT_TYPE || error == INVALID_JSON_POINTER; +} + +/** + * Returns true if a wildcard follows in the remaining JSONPath. A segment that + * matches nothing before a wildcard yields an empty result instead of an + * error, as in DOM's at_path_with_wildcard. Like DOM, this treats a '*' inside + * a quoted key as a wildcard. + */ +simdjson_inline bool json_path_has_wildcard(std::string_view json_path) noexcept { + return json_path.find('*') != std::string_view::npos; +} } // namespace internal /** @@ -8999,12 +9061,100 @@ inline std::pair get_next_key_and_json_path( } else if ((i+2 < json_path.size()) && json_path[i] == '[' && json_path[i+1] == '*' && json_path[i+2] == ']') { // i.e [*].additional_keys or [*]["additional_keys"] key = "*"; i += 3; + } else if (i < json_path.size() && json_path[i] == '[') { // i.e. [0] or [123] + size_t j = i + 1; + while (j < json_path.size() && json_path[j] >= '0' && json_path[j] <= '9') { + ++j; + } + if (j > i + 1 && j < json_path.size() && json_path[j] == ']') { + key = json_path.substr(i + 1, j - i - 1); + i = j + 1; + } } return std::make_pair(key, json_path.substr(i)); } +namespace internal { +/** + * A JSONPath as the DOM at_path_with_wildcard() consumes it, parsed once. + * + * Every node visited at a given depth sees the same remaining path, so we + * parse it up front rather than at each node: re-scanning (and converting) + * the remaining path at every node costs O(nodes * path length), which + * made a long path after a couple of wildcards take minutes on a large + * document. + */ +struct wildcard_json_path { + enum class tail_type { invalid, all_values, pointer }; + // One JSON pointer ("/key") per leading segment, "/*" being a wildcard. + std::vector steps{}; + // What to do once the steps are exhausted. + tail_type tail{tail_type::invalid}; + // The rest of the path, as a JSON pointer, when tail == pointer. + std::string tail_pointer{}; +}; + +/** + * Splits json_path the same way the recursive DOM at_path_with_wildcard() + * historically did, one level at a time, but in a single pass. + */ +inline wildcard_json_path parse_wildcard_json_path(std::string_view json_path) { + wildcard_json_path result; + // The remaining path (always a suffix of json_path) contains a '*' if and + // only if it starts at or before the last one. + const size_t last_star = json_path.rfind('*'); + std::string_view path = json_path; + while (true) { + size_t i = 0; + if (!path.empty() && path.front() == '$') { + i = 1; + } + if (i >= path.size() || (path[i] != '.' && path[i] != '[')) { + result.tail = wildcard_json_path::tail_type::invalid; + return result; + } + const size_t offset = json_path.size() - path.size(); + if (last_star == std::string_view::npos || last_star < offset) { + result.tail_pointer = json_path_to_pointer_conversion(path); + result.tail = (result.tail_pointer == "-1") + ? wildcard_json_path::tail_type::invalid + : wildcard_json_path::tail_type::pointer; + return result; + } + if ((path.compare(i, 3, "[*]") == 0 && path.size() == i + 3) || + (path.compare(i, 2, ".*") == 0 && path.size() == i + 2)) { + result.tail = wildcard_json_path::tail_type::all_values; + return result; + } + std::pair key_and_json_path = get_next_key_and_json_path(path); + if (key_and_json_path.first.empty()) { + result.tail = wildcard_json_path::tail_type::invalid; + return result; + } + std::string step; + step.reserve(key_and_json_path.first.size() + 1); + step += '/'; + // The step is consumed by at_pointer, which unescapes ~0/~1, so '~' and + // '/' in the key must be escaped as json_path_to_pointer_conversion does: + // a raw '/' splits the step into two pointer tokens and a raw '~' + // invalidates it, either way addressing the wrong element. + for (const char c : key_and_json_path.first) { + if (c == '~') { + step += "~0"; + } else if (c == '/') { + step += "~1"; + } else { + step += c; + } + } + result.steps.push_back(std::move(step)); + path = key_and_json_path.second; + } +} +} // namespace internal + } // namespace simdjson #endif // SIMDJSON_JSONPATHUTIL_H /* end file simdjson/jsonpathutil.h */ @@ -9383,68 +9533,7 @@ inline void array::process_json_path_of_child_elements(std::vector::ite inline simdjson_result> array::at_path_with_wildcard(std::string_view json_path) const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 - - size_t i = 0; - // json_path.starts_with('$') requires C++20. - if (!json_path.empty() && json_path.front() == '$') { - i = 1; - } - - if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) { - return INVALID_JSON_POINTER; - } - - if (json_path.find("*") != std::string::npos) { - std::vector child_values; - - if ( - (json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) || - (json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2) - ) { - get_values(child_values); - return child_values; - } - - std::pair key_and_json_path = get_next_key_and_json_path(json_path); - - std::string_view key = key_and_json_path.first; - json_path = key_and_json_path.second; - - if (key.size() > 0) { - if (key == "*") { - get_values(child_values); - } else { - element pointer_result; - std::string json_pointer = std::string("/") + std::string(key); - auto error = at_pointer(json_pointer).get(pointer_result); - - if (!error) { - child_values.emplace_back(pointer_result); - } - } - - std::vector result = {}; - - if (child_values.size() > 0) { - std::vector::iterator child_values_begin = child_values.begin(); - std::vector::iterator child_values_end = child_values.end(); - - process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result); - } - - return result; - } else { - return INVALID_JSON_POINTER; - } - } else { - element result; - auto error = at_path(json_path).get(result); - if (error) { - return error; - } - - return std::vector{std::move(result)}; - } + return element(*this).at_path_with_wildcard(json_path); } inline simdjson_result array::at(size_t index) const noexcept { @@ -9723,73 +9812,7 @@ inline void object::process_json_path_of_child_elements(std::vector::it inline simdjson_result> object::at_path_with_wildcard(std::string_view json_path) const noexcept { SIMDJSON_DEVELOPMENT_ASSERT(tape.usable()); // https://github.com/simdjson/simdjson/issues/1914 - - size_t i = 0; - if (json_path.empty()) { - return INVALID_JSON_POINTER; - } - // if JSONPath starts with $, skip it - // json_path.starts_with('$') requires C++20. - if (json_path.front() == '$') { - i = 1; - } - - if (i >= json_path.size() || (json_path[i] != '.' && json_path[i] != '[')) { - // expect JSONPath expressions to always start with $ but this isn't currently - // expected in jsonpathutil.h. - return INVALID_JSON_POINTER; - } - - if (json_path.find("*") != std::string::npos) { - - std::vector child_values; - - if ( - (json_path.compare(i, 3, "[*]") == 0 && json_path.size() == i + 3) || - (json_path.compare(i, 2,".*") == 0 && json_path.size() == i + 2) - ) { - get_values(child_values); - return child_values; - } - - std::pair key_and_json_path = get_next_key_and_json_path(json_path); - - std::string_view key = key_and_json_path.first; - json_path = key_and_json_path.second; - - if (key.size() > 0) { - if (key == "*") { - get_values(child_values); - } else { - element pointer_result; - auto error = at_pointer(std::string("/") + std::string(key)).get(pointer_result); - - if (!error) { - child_values.emplace_back(pointer_result); - } - } - - std::vector result = {}; - if (child_values.size() > 0) { - - std::vector::iterator child_values_begin = child_values.begin(); - std::vector::iterator child_values_end = child_values.end(); - - process_json_path_of_child_elements(child_values_begin, child_values_end, json_path, result); - } - - return result; - } else { - return INVALID_JSON_POINTER; - } - } else { - element result; - auto error = this->at_path(json_path).get(result); - if (error) { - return error; - } - return std::vector{std::move(result)}; - } + return element(*this).at_path_with_wildcard(json_path); } inline simdjson_result object::at_key(std::string_view key) const noexcept { @@ -10136,6 +10159,68 @@ simdjson_inline dom::array::iterator simdjson_result::end() const #endif // SIMDJSON_EXCEPTIONS +namespace internal { +// Appends the children of e, which must be an array or an object. +inline void get_container_values(const dom::element &e, std::vector &out) noexcept { + dom::array a; + if (!e.get_array().get(a)) { + a.get_values(out); + return; + } + dom::object o; + if (!e.get_object().get(o)) { + o.get_values(out); + } +} + +// Walks a pre-parsed wildcard path from e, which is at depth `step` of the +// path. Errors below the starting node are skipped, not reported. +inline simdjson_result> at_wildcard_json_path(const dom::element &e, const wildcard_json_path &path, size_t step) noexcept { + const dom::element_type t = e.type(); + if (t != dom::element_type::ARRAY && t != dom::element_type::OBJECT) { + return std::vector{}; + } + std::vector children; + if (step == path.steps.size()) { + switch (path.tail) { + case wildcard_json_path::tail_type::all_values: + get_container_values(e, children); + return children; + case wildcard_json_path::tail_type::pointer: { + dom::element result; + auto error = e.at_pointer(path.tail_pointer).get(result); + if (error) { + return error; + } + return std::vector{std::move(result)}; + } + default: + return INVALID_JSON_POINTER; + } + } + const std::string &key_pointer = path.steps[step]; + if (key_pointer == "/*") { + get_container_values(e, children); + } else { + dom::element child; + if (!e.at_pointer(key_pointer).get(child)) { + children.emplace_back(child); + } + } + std::vector result; + for (const dom::element &child : children) { + std::vector child_result; + if (at_wildcard_json_path(child, path, step + 1).get(child_result)) { + continue; + } + result.insert(result.end(), + std::make_move_iterator(child_result.begin()), + std::make_move_iterator(child_result.end())); + } + return result; +} +} // namespace internal + namespace dom { // @@ -10394,9 +10479,8 @@ inline simdjson_result> element::at_path_with_wildcard(std: switch (tape.tape_ref_type()) { case internal::tape_type::START_OBJECT: - return object(tape).at_path_with_wildcard(json_path); case internal::tape_type::START_ARRAY: - return array(tape).at_path_with_wildcard(json_path); + return internal::at_wildcard_json_path(*this, internal::parse_wildcard_json_path(json_path), 0); default: return std::vector{}; } @@ -12711,8 +12795,18 @@ inline element_metrics structure_analyzer::analyze_object(const dom::object& obj inline size_t structure_analyzer::estimate_string_length(std::string_view s) const { size_t len = 2; // quotes for (char c : s) { - if (c == '"' || c == '\\' || static_cast(c) < 32) { - len += 2; // escape sequence (at least) + unsigned char uc = static_cast(c); + if (c == '"' || c == '\\') { + len += 2; // \" and \\ are two-character escapes + } else if (uc < 0x20) { + switch (c) { + case '\b': case '\f': case '\n': case '\r': case '\t': + len += 2; // two-character escapes: \b \f \n \r \t + break; + default: + len += 6; // \u00XX -- six-character escape for other control chars + break; + } } else { len += 1; } @@ -12803,7 +12897,9 @@ inline void structure_analyzer::build_table_columns( table_column_type common = table_column_type::unknown; for (const auto& v : values) { table_column_type t = classify_table_value(v.first.type()); - if (t == table_column_type::unknown) continue; + // Null cannot be projected into object or array columns. Keep these + // values intact by falling back to ordinary value formatting. + if (t == table_column_type::unknown) return; if (common == table_column_type::unknown) common = t; else if (t != common) { common = table_column_type::mixed; break; } } @@ -46559,6 +46655,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -46600,6 +46703,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -46679,6 +46783,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -46872,10 +46985,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -46965,16 +47074,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -47706,6 +47815,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for arm64 */ #include #include +#include #include #include #include @@ -48746,7 +48856,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -48780,6 +48894,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -48946,11 +49072,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -48968,6 +49107,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -49397,6 +49586,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -49438,6 +49634,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -49517,6 +49714,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -49710,10 +49916,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -49803,16 +50005,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -50544,6 +50746,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for fallback */ #include #include +#include #include #include #include @@ -51584,7 +51787,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -51618,6 +51825,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -51784,11 +52003,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -51806,6 +52038,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -52712,6 +52994,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -52753,6 +53042,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -52832,6 +53122,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -53025,10 +53324,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -53118,16 +53413,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -53859,6 +54154,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for haswell */ #include #include +#include #include #include #include @@ -54899,7 +55195,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -54933,6 +55233,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -55099,11 +55411,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -55121,6 +55446,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -56027,6 +56402,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -56068,6 +56450,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -56147,6 +56530,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -56340,10 +56732,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -56433,16 +56821,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -57174,6 +57562,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for icelake */ #include #include +#include #include #include #include @@ -58214,7 +58603,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -58248,6 +58641,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -58414,11 +58819,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -58436,6 +58854,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -59457,6 +59925,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -59498,6 +59973,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -59577,6 +60053,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -59770,10 +60255,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -59863,16 +60344,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -60604,6 +61085,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for ppc64 */ #include #include +#include #include #include #include @@ -61644,7 +62126,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -61678,6 +62164,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -61844,11 +62342,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -61866,6 +62377,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -63194,6 +63755,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -63235,6 +63803,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -63314,6 +63883,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -63507,10 +64085,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -63600,16 +64174,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -64341,6 +64915,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for westmere */ #include #include +#include #include #include #include @@ -65381,7 +65956,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -65415,6 +65994,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -65581,11 +66172,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -65603,6 +66207,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -66421,6 +67075,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -66462,6 +67123,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -66541,6 +67203,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -66734,10 +67405,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -66827,16 +67494,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -67568,6 +68235,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for lsx */ #include #include +#include #include #include #include @@ -68608,7 +69276,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -68642,6 +69314,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -68808,11 +69492,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -68830,6 +69527,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -69671,6 +70418,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -69712,6 +70466,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -69791,6 +70546,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -69984,10 +70748,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -70077,16 +70837,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -70818,6 +71578,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for lasx */ #include #include +#include #include #include #include @@ -71858,7 +72619,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -71892,6 +72657,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -72058,11 +72835,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -72080,6 +72870,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -72924,6 +73764,13 @@ class string_builder { template simdjson_inline void append_key_value(value_type value) noexcept; + template + simdjson_inline void append_key_value() noexcept; + + template + requires (!constevalutil::is_fixed_string>) + simdjson_inline void append_key_value() noexcept; + // Support for optional types (std::optional, etc.) template requires(!require_custom_serialization) @@ -72965,6 +73812,7 @@ requires (!std::is_convertible::value && !concepts::optiona */ template simdjson_inline void append_raw_n(const char *str) noexcept; + #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer. @@ -73044,6 +73892,15 @@ requires (!std::is_convertible::value && !concepts::optiona */ simdjson_inline bool capacity_check(size_t upcoming_bytes); +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL + /** + * Append constevalutil::static_string. The whole zero-padded array is + * copied in 16-byte blocks, but position only advances by the text length. + */ + template + simdjson_inline void append_static_string() noexcept; +#endif + /** * Grow the buffer to at least desired_capacity bytes. * If the allocation fails, is_valid is set to false. We expect @@ -73237,10 +74094,6 @@ using unchecked_writer = basic_writer; // 16-byte blocks). inline constexpr size_t unchecked_slack = 64; -consteval size_t padded_key_length(size_t length) { - return (length + 15) / 16 * 16; -} - // === Helper: invoke a string_builder member that writes variable-length // content (escape_and_append_with_quotes etc), syncing the writer's local // state before the call and reloading after. Used for string fields where @@ -73330,16 +74183,16 @@ simdjson_really_inline void atom_fields(W &w, const T &t, bool &first) { constexpr size_t rest_key_len = first_key_len + 1; constexpr auto first_key = std::define_static_string( constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(first_key_len) - first_key_len, '\0')); + std::string(constevalutil::padded_key_length(first_key_len) - first_key_len, '\0')); constexpr auto rest_key = std::define_static_string( std::string(",") + constevalutil::consteval_to_quoted_escaped(key_name) + ":" + - std::string(padded_key_length(rest_key_len) - rest_key_len, '\0')); - if (!w.ensure(padded_key_length(rest_key_len))) { return; } + std::string(constevalutil::padded_key_length(rest_key_len) - rest_key_len, '\0')); + if (!w.ensure(constevalutil::padded_key_length(rest_key_len))) { return; } if (first) { - std::memcpy(w.ptr + w.pos, first_key, padded_key_length(first_key_len)); + std::memcpy(w.ptr + w.pos, first_key, constevalutil::padded_key_length(first_key_len)); w.pos += first_key_len; } else { - std::memcpy(w.ptr + w.pos, rest_key, padded_key_length(rest_key_len)); + std::memcpy(w.ptr + w.pos, rest_key, constevalutil::padded_key_length(rest_key_len)); w.pos += rest_key_len; } first = false; @@ -74071,6 +74924,7 @@ simdjson_warn_unused simdjson_result extract_fractured_json( /* begin file simdjson/generic/builder/json_string_builder-inl.h for rvv_vls */ #include #include +#include #include #include #include @@ -75111,7 +75965,11 @@ string_builder::escape_and_append_with_quotes(const char *input) noexcept { #if SIMDJSON_SUPPORTS_CONCEPTS template simdjson_inline void string_builder::escape_and_append_with_quotes() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(constevalutil::string_constant::value); +#endif } #endif @@ -75145,6 +76003,18 @@ simdjson_inline void string_builder::append_raw_n(const char *str) noexcept { position += N; } } + +#if SIMDJSON_SUPPORTS_CONCEPTS && SIMDJSON_CONSTEVAL +template +simdjson_inline void string_builder::append_static_string() noexcept { + constexpr auto& padded = constevalutil::static_string; + if (capacity_check(padded.size())) { + std::memcpy(buffer.get() + position, padded.data(), padded.size()); + position += constevalutil::static_string_size(); + } +} +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS // Support for optional types (std::optional, etc.) template @@ -75311,11 +76181,24 @@ string_builder::append_key_value(key_type key, value_type value) noexcept { } #if SIMDJSON_SUPPORTS_CONCEPTS +#if SIMDJSON_CONSTEVAL +template +struct quoted_key_text { + static consteval std::string text() { + return constevalutil::consteval_to_quoted_escaped(key.view()) + ":"; + } +}; +#endif + template simdjson_inline void string_builder::append_key_value(value_type value) noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else escape_and_append_with_quotes(); append_colon(); +#endif SIMDJSON_IF_CONSTEXPR(std::is_same::value) { append_null(); } @@ -75333,6 +76216,56 @@ string_builder::append_key_value(value_type value) noexcept { append(value); } } + +#if SIMDJSON_CONSTEVAL +template +concept compile_time_formattable = + constevalutil::is_fixed_string> || + std::same_as, bool> || + std::same_as, std::nullptr_t>; + +template +requires compile_time_formattable +struct key_value_text { + static consteval std::string text() { + using value_type = std::remove_cvref_t; + std::string out = constevalutil::consteval_to_quoted_escaped(key.view()) + ':'; + SIMDJSON_IF_CONSTEXPR(constevalutil::is_fixed_string) + out += constevalutil::consteval_to_quoted_escaped(value.view()); + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += value ? "true" : "false"; + else SIMDJSON_IF_CONSTEXPR(std::same_as) + out += "null"; + return out; + } +}; +#endif + +template +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + append_static_string>(); +#else + escape_and_append_with_quotes(); + append_colon(); + escape_and_append_with_quotes(); +#endif +} + +template +requires (!constevalutil::is_fixed_string>) +simdjson_inline void +string_builder::append_key_value() noexcept { +#if SIMDJSON_CONSTEVAL + SIMDJSON_IF_CONSTEXPR(compile_time_formattable) { + append_static_string>(); + } else +#endif + { + append_key_value(value); + } +} #endif } // namespace builder @@ -81909,6 +82842,20 @@ struct simdjson_result : public arm64::implementation_si #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -81963,7 +82910,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -81986,7 +82933,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -81996,7 +82943,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -82020,7 +82967,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -82030,7 +82977,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -82081,7 +83028,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -82160,7 +83107,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -82308,7 +83255,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -82327,7 +83274,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -82362,7 +83309,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -82462,7 +83409,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -82481,7 +83428,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -82497,7 +83444,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -82534,7 +83481,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -82843,7 +83790,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -82928,7 +83875,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -82938,7 +83885,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -85779,30 +86726,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -90113,13 +91066,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -99628,6 +100589,20 @@ struct simdjson_result : public fallback::implementat #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -99682,7 +100657,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -99705,7 +100680,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -99715,7 +100690,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -99739,7 +100714,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -99749,7 +100724,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -99800,7 +100775,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -99879,7 +100854,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -100027,7 +101002,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -100046,7 +101021,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -100081,7 +101056,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -100181,7 +101156,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -100200,7 +101175,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -100216,7 +101191,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -100253,7 +101228,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -100562,7 +101537,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -100647,7 +101622,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -100657,7 +101632,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -103498,30 +104473,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -107832,13 +108813,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -117824,6 +118813,20 @@ struct simdjson_result : public haswell::implementatio #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -117878,7 +118881,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -117901,7 +118904,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -117911,7 +118914,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -117935,7 +118938,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -117945,7 +118948,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -117996,7 +118999,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -118075,7 +119078,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -118223,7 +119226,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -118242,7 +119245,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -118277,7 +119280,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -118377,7 +119380,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -118396,7 +119399,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -118412,7 +119415,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -118449,7 +119452,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -118758,7 +119761,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -118843,7 +119846,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -118853,7 +119856,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -121694,30 +122697,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -126028,13 +127037,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -136020,6 +137037,20 @@ struct simdjson_result : public icelake::implementatio #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -136074,7 +137105,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -136097,7 +137128,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -136107,7 +137138,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -136131,7 +137162,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -136141,7 +137172,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -136192,7 +137223,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -136271,7 +137302,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -136419,7 +137450,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -136438,7 +137469,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -136473,7 +137504,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -136573,7 +137604,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -136592,7 +137623,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -136608,7 +137639,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -136645,7 +137676,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -136954,7 +137985,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -137039,7 +138070,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -137049,7 +138080,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -139890,30 +140921,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -144224,13 +145261,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -154331,6 +155376,20 @@ struct simdjson_result : public ppc64::implementation_si #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -154385,7 +155444,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -154408,7 +155467,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -154418,7 +155477,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -154442,7 +155501,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -154452,7 +155511,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -154503,7 +155562,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -154582,7 +155641,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -154730,7 +155789,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -154749,7 +155808,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -154784,7 +155843,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -154884,7 +155943,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -154903,7 +155962,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -154919,7 +155978,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -154956,7 +156015,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -155265,7 +156324,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -155350,7 +156409,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -155360,7 +156419,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -158201,30 +159260,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -162535,13 +163600,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -172949,6 +174022,20 @@ struct simdjson_result : public westmere::implementat #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -173003,7 +174090,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -173026,7 +174113,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -173036,7 +174123,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -173060,7 +174147,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -173070,7 +174157,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -173121,7 +174208,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -173200,7 +174287,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -173348,7 +174435,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -173367,7 +174454,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -173402,7 +174489,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -173502,7 +174589,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -173521,7 +174608,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -173537,7 +174624,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -173574,7 +174661,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -173883,7 +174970,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -173968,7 +175055,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -173978,7 +175065,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -176819,30 +177906,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -181153,13 +182246,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -191057,6 +192158,20 @@ struct simdjson_result : public lsx::implementation_simdjs #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -191111,7 +192226,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -191134,7 +192249,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -191144,7 +192259,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -191168,7 +192283,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -191178,7 +192293,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -191229,7 +192344,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -191308,7 +192423,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -191456,7 +192571,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -191475,7 +192590,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -191510,7 +192625,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -191610,7 +192725,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -191629,7 +192744,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -191645,7 +192760,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -191682,7 +192797,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -191991,7 +193106,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -192076,7 +193191,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -192086,7 +193201,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -194927,30 +196042,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -199261,13 +200382,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -209188,6 +210317,20 @@ struct simdjson_result : public lasx::implementation_simd #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -209242,7 +210385,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -209265,7 +210408,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -209275,7 +210418,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -209299,7 +210442,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -209309,7 +210452,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -209360,7 +210503,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -209439,7 +210582,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -209587,7 +210730,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -209606,7 +210749,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -209641,7 +210784,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -209741,7 +210884,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -209760,7 +210903,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -209776,7 +210919,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -209813,7 +210956,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -210122,7 +211265,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -210207,7 +211350,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -210217,7 +211360,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -213058,30 +214201,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -217392,13 +218541,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -227322,6 +228479,20 @@ struct simdjson_result : public rvv_vls::implementatio #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0 #endif +// Clang 15 and earlier reject a call to a consteval function from within a +// consteval function template when an argument refers to one of the caller's +// reference parameters ("function parameter 'keys' with unknown value cannot +// be used in a constant expression"). With those compilers, the helpers below +// are merely constexpr: the perfect-hash tables are still computed at compile +// time because they initialize static constexpr data members. +#ifndef SIMDJSON_KEY_SELECTOR_CONSTEVAL +#if defined(__clang__) && ((defined(__apple_build_version__) && __apple_build_version__ < 15000000) || (!defined(__apple_build_version__) && __clang_major__ < 16)) + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL constexpr +#else + #define SIMDJSON_KEY_SELECTOR_CONSTEVAL consteval +#endif +#endif + #if SIMDJSON_SUPPORTS_CONCEPTS namespace simdjson { @@ -227376,7 +228547,7 @@ constexpr std::size_t char_at(std::string_view key, std::size_t pos) noexcept { // lengths differ modulo the table size are separated by the length term in the // hash, so they need no position coverage. template -consteval std::size_t count_undistinguished_pairs( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t count_undistinguished_pairs( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -227399,7 +228570,7 @@ consteval std::size_t count_undistinguished_pairs( } template -consteval bool positions_distinguish( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool positions_distinguish( const std::array& keys, const std::size_t* positions, std::size_t num_positions, @@ -227409,7 +228580,7 @@ consteval bool positions_distinguish( // Number of distinct (length % modulus, char_at(key, pos)) pairs at a position. template -consteval std::size_t discriminating_power( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t discriminating_power( const std::array& keys, std::size_t pos, std::size_t modulus) { @@ -227433,7 +228604,7 @@ consteval std::size_t discriminating_power( } template -consteval std::size_t max_key_length(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t max_key_length(const std::array& keys) { std::size_t m = 0; for (std::size_t i = 0; i < N; ++i) { if (keys[i].size() > m) { m = keys[i].size(); } @@ -227443,7 +228614,7 @@ consteval std::size_t max_key_length(const std::array& keys // Bounded backtracking DFS for a minimal set of distinguishing positions. template -consteval bool backtracking_search( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool backtracking_search( const std::array& keys, const std::size_t* candidates, std::size_t num_candidates, @@ -227494,7 +228665,7 @@ consteval bool backtracking_search( // Phase 1: select character positions that distinguish all colliding pairs. template -consteval std::size_t select_positions( +SIMDJSON_KEY_SELECTOR_CONSTEVAL std::size_t select_positions( const std::array& keys, std::array& positions, std::size_t modulus) { @@ -227573,7 +228744,7 @@ struct phf_result { // (position, character) symbol at a time; never revisits a value. Equivalence // classes (keys sharing the same undetermined symbols) keep the search cheap. template -consteval bool try_generate_gperf( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_generate_gperf( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -227721,7 +228892,7 @@ consteval bool try_generate_gperf( } template -consteval bool try_compute_phf(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -227740,7 +228911,7 @@ consteval bool try_compute_phf(const std::array& keys, phf_ } template -consteval bool try_gperf_po2(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_gperf_po2(const std::array& keys, phf_result& result) { if (try_compute_phf(keys, result)) { return true; } constexpr std::size_t NextM = M * 2; if constexpr (NextM <= MaxM) { return try_gperf_po2(keys, result); } @@ -227775,7 +228946,7 @@ constexpr std::size_t hd_key_hash_4(std::string_view key) noexcept { } template -consteval bool try_hash_and_displace( +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_hash_and_displace( const std::array& keys, std::array, MAX_POSITIONS>& asso_values, std::size_t& num_positions, @@ -227875,7 +229046,7 @@ consteval bool try_hash_and_displace( } template -consteval bool try_compute_phf_hd(const std::array& keys, phf_result& result) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL bool try_compute_phf_hd(const std::array& keys, phf_result& result) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); std::array, MAX_POSITIONS> asso{}; std::size_t npos{}; @@ -227894,7 +229065,7 @@ consteval bool try_compute_phf_hd(const std::array& keys, p } template -consteval phf_result compute_phf_hd_po2(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf_hd_po2(const std::array& keys) { static_assert(M <= phf_result::MAX_TABLE_SIZE, "Table size M exceeds maximum"); phf_result result{}; if (try_compute_phf_hd(keys, result)) { return result; } @@ -227910,7 +229081,7 @@ consteval phf_result compute_phf_hd_po2(const std::array // Compute a perfect hash for `keys`: try gperf at power-of-two sizes (capped so // the runtime tables stay within uint8 indices), then fall back to H&D. template -consteval phf_result compute_phf(const std::array& keys) { +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_result compute_phf(const std::array& keys) { constexpr std::size_t StartM = next_power_of_2(N); constexpr std::size_t GPERF_MAX_TABLE = phf_result::MAX_TABLE_SIZE < 256 ? phf_result::MAX_TABLE_SIZE : 256; @@ -227947,7 +229118,7 @@ constexpr std::size_t compute_max_key_len(const std::array& // Validate keys and build the runtime tables from the computed perfect hash. template -consteval phf_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL phf_data build_phf_data(const std::array& keys, const phf_result& result) { for (std::size_t i = 0; i < N; ++i) { if (keys[i].empty()) { compile_time_error("empty keys are not allowed in key_selector"); } @@ -228256,7 +229427,7 @@ constexpr unsigned window_value(const std::array& keys, } template -consteval window_data +SIMDJSON_KEY_SELECTOR_CONSTEVAL window_data compute_window(const std::array& keys) { window_data out{}; @@ -228341,7 +229512,7 @@ match_window_candidate(const char* p, std::uint8_t ki, // --- describe() string helpers (constexpr; no std::to_string, which is not) --- // Append the decimal form of v to s. -SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { +inline SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { if (v == 0) { s.push_back('0'); return; } char buf[20]; std::size_t n = 0; @@ -228351,7 +229522,7 @@ SIMDJSON_CONSTEXPR_STRING void append_uint(std::string& s, std::size_t v) { // Append a byte as its decimal value, plus the printable character in quotes // when it is in the printable ASCII range (e.g. "110 ('n')"). -SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { +inline SIMDJSON_CONSTEXPR_STRING void append_byte(std::string& s, unsigned b) { append_uint(s, b); if (b >= 0x20 && b < 0x7f) { s += " ('"; @@ -231192,30 +232363,36 @@ inline error_code array::for_each_at_path_with_wildcard(std::string_view json_pa if (remaining_path.empty()) { callback(val); } else { - error_code err = element.for_each_at_path_with_wildcard(remaining_path, callback); - if(err) { return err; } + // An element that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { // Specific index case in which we access the element at the given index - size_t idx = 0; - for (char c : key) { if(c < '0' || c > '9'){ return INVALID_JSON_POINTER; } - idx = idx*10 + (c - '0'); } - - auto element = at(idx); + // Rejects leading zeros and indexes that overflow size_t, as DOM does. + size_t idx; + size_t idx_length; + error_code error = internal::parse_json_pointer_array_index(key, idx, idx_length); value val; - SIMDJSON_TRY(element.get(val)); + if (!error) { error = at(idx).get(val); } + if (error) { + if (error == INDEX_OUT_OF_BOUNDS && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()){ callback(val); return SUCCESS; } else { - return element.for_each_at_path_with_wildcard(remaining_path, callback); + return val.for_each_at_path_with_wildcard(remaining_path, callback); } } } @@ -235526,13 +236703,21 @@ inline error_code object::for_each_at_path_with_wildcard(std::string_view json_p if (remaining_path.empty()) { callback(val); } else { - SIMDJSON_TRY(val.for_each_at_path_with_wildcard(remaining_path, callback)); + // A field that does not match the rest of the path is skipped. + error_code err = val.for_each_at_path_with_wildcard(remaining_path, callback); + if (err && !internal::is_matching_failure_error(err)) { return err; } } } return SUCCESS; } else { value val; - SIMDJSON_TRY(find_field(key).get(val)); + error_code error = find_field(key).get(val); + if (error) { + if (error == NO_SUCH_FIELD && internal::json_path_has_wildcard(remaining_path)) { + return SUCCESS; + } + return error; + } if (remaining_path.empty()) { callback(val); @@ -244286,10 +245471,10 @@ simdjson_fastfloat_really_inline bool rounds_to_nearest() noexcept { // https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013 #elif defined(__clang__) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wfloat-equal" +SIMDJSON_DISABLE_GCC_WARNING(-Wfloat-equal) #elif defined(__GNUC__) #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" +SIMDJSON_DISABLE_GCC_WARNING(-Wfloat-equal) #endif return (fmini + 1.0f == 1.0f - fmini); #ifdef SIMDJSON_FASTFLOAT_VISUAL_STUDIO @@ -244530,7 +245715,7 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value, #ifdef __clang__ #pragma clang diagnostic push #if (!defined(__APPLE_CC__) && __clang_major__ >= 10) || (__clang_major__ >= 13) -#pragma clang diagnostic ignored "-Wc++20-extensions" +SIMDJSON_DISABLE_GCC_WARNING(-Wc++20-extensions) #endif #endif if simdjson_fastfloat_unlikely (pns.too_many_digits) {