diff --git a/DEPENDENCIES b/DEPENDENCIES index e6b1b9388..bdc3067a4 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,3 +1,3 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core 56eab6ef118e9731df539d3a507db1c23241f911 +core https://github.com/sourcemeta/core 57e8c91ed68e3ee903526fd2f45cb16ca46759d8 bootstrap https://github.com/twbs/bootstrap 1a6fdfae6be09b09eaced8f0e442ca6f7680a61e diff --git a/test/packaging/find_package/CMakeLists.txt b/test/packaging/find_package/CMakeLists.txt index d40a5e990..6194ebcd4 100644 --- a/test/packaging/find_package/CMakeLists.txt +++ b/test/packaging/find_package/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.18) project(jsonbinpack_hello VERSION 0.0.1 LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) find_package(JSONBinPack REQUIRED) diff --git a/vendor/core/CMakeLists.txt b/vendor/core/CMakeLists.txt index fdbd3feee..c12369e28 100644 --- a/vendor/core/CMakeLists.txt +++ b/vendor/core/CMakeLists.txt @@ -16,6 +16,7 @@ option(SOURCEMETA_CORE_TIME "Build the Sourcemeta Core time library" ON) option(SOURCEMETA_CORE_CRYPTO "Build the Sourcemeta Core Crypto library" ON) option(SOURCEMETA_CORE_CRYPTO_USE_SYSTEM_OPENSSL "Use system OpenSSL for the Sourcemeta Core Crypto library" OFF) option(SOURCEMETA_CORE_REGEX "Build the Sourcemeta Core Regex library" ON) +option(SOURCEMETA_CORE_IP "Build the Sourcemeta Core IP library" ON) option(SOURCEMETA_CORE_URI "Build the Sourcemeta Core URI library" ON) option(SOURCEMETA_CORE_URITEMPLATE "Build the Sourcemeta Core URI Template library" ON) option(SOURCEMETA_CORE_JSON "Build the Sourcemeta Core JSON library" ON) @@ -114,6 +115,10 @@ if(SOURCEMETA_CORE_REGEX) add_subdirectory(src/core/regex) endif() +if(SOURCEMETA_CORE_IP) + add_subdirectory(src/core/ip) +endif() + if(SOURCEMETA_CORE_URI) add_subdirectory(src/core/uri) endif() @@ -233,6 +238,10 @@ if(SOURCEMETA_CORE_TESTS) add_subdirectory(test/regex) endif() + if(SOURCEMETA_CORE_IP) + add_subdirectory(test/ip) + endif() + if(SOURCEMETA_CORE_URI) add_subdirectory(test/uri) endif() diff --git a/vendor/core/cmake/common/defaults.cmake b/vendor/core/cmake/common/defaults.cmake index 93c8bc140..bb2ce6f7b 100644 --- a/vendor/core/cmake/common/defaults.cmake +++ b/vendor/core/cmake/common/defaults.cmake @@ -1,6 +1,6 @@ # Standards (sane modern defaults) if("CXX" IN_LIST SOURCEMETA_LANGUAGES) - set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD 23) endif() if("C" IN_LIST SOURCEMETA_LANGUAGES) set(CMAKE_C_STANDARD 11) diff --git a/vendor/core/cmake/common/targets/executable.cmake b/vendor/core/cmake/common/targets/executable.cmake index 2066d39a1..63f61cc2e 100644 --- a/vendor/core/cmake/common/targets/executable.cmake +++ b/vendor/core/cmake/common/targets/executable.cmake @@ -51,8 +51,12 @@ function(sourcemeta_executable) target_link_options(${TARGET_NAME} PRIVATE /guard:cf /CETCOMPAT) endif() - # Linux-specific ELF linker hardening options + # Linux-specific ELF linker hardening and compatibility options if(SOURCEMETA_OS_LINUX AND (SOURCEMETA_COMPILER_LLVM OR SOURCEMETA_COMPILER_GCC)) + # Maximize compatibility of pre-built binaries across Linux distros + if(NOT BUILD_SHARED_LIBS) + target_link_options(${TARGET_NAME} PRIVATE -static-libstdc++ -static-libgcc) + endif() target_link_options(${TARGET_NAME} PRIVATE "LINKER:-z,nodlopen" "LINKER:-z,noexecstack" diff --git a/vendor/core/config.cmake.in b/vendor/core/config.cmake.in index e311a1b50..0f93f6c68 100644 --- a/vendor/core/config.cmake.in +++ b/vendor/core/config.cmake.in @@ -14,6 +14,7 @@ if(NOT SOURCEMETA_CORE_COMPONENTS) list(APPEND SOURCEMETA_CORE_COMPONENTS time) list(APPEND SOURCEMETA_CORE_COMPONENTS crypto) list(APPEND SOURCEMETA_CORE_COMPONENTS regex) + list(APPEND SOURCEMETA_CORE_COMPONENTS ip) list(APPEND SOURCEMETA_CORE_COMPONENTS uri) list(APPEND SOURCEMETA_CORE_COMPONENTS uritemplate) list(APPEND SOURCEMETA_CORE_COMPONENTS json) @@ -59,7 +60,10 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS}) elseif(component STREQUAL "regex") find_dependency(PCRE2 CONFIG) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_regex.cmake") + elseif(component STREQUAL "ip") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_ip.cmake") elseif(component STREQUAL "uri") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_ip.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake") elseif(component STREQUAL "uritemplate") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake") @@ -78,6 +82,7 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS}) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonl.cmake") elseif(component STREQUAL "jsonpointer") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_regex.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_ip.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake") @@ -85,6 +90,7 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS}) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_json.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonpointer.cmake") elseif(component STREQUAL "jsonschema") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_ip.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake") @@ -95,6 +101,7 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS}) elseif(component STREQUAL "yaml") find_dependency(PCRE2 CONFIG) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_regex.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_ip.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake") @@ -109,6 +116,7 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS}) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_preprocessor.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_html.cmake") elseif(component STREQUAL "alterschema") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_ip.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake") @@ -120,6 +128,7 @@ foreach(component ${SOURCEMETA_CORE_COMPONENTS}) include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_jsonschema.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_alterschema.cmake") elseif(component STREQUAL "editorschema") + include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_ip.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_uri.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_numeric.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_core_io.cmake") diff --git a/vendor/core/src/core/crypto/crypto_sha256.cc b/vendor/core/src/core/crypto/crypto_sha256.cc index e4be5eb31..08f4e62c5 100644 --- a/vendor/core/src/core/crypto/crypto_sha256.cc +++ b/vendor/core/src/core/crypto/crypto_sha256.cc @@ -41,6 +41,8 @@ auto sha256(const std::string_view input, std::ostream &output) -> void { EVP_MD_CTX_free(context); + // TODO: Use std::views::enumerate once libc++ supports it + // (__cpp_lib_ranges_enumerate) for (std::uint64_t index = 0; index < 32u; ++index) { output.put(HEX_DIGITS[(digest[index] >> 4u) & 0x0fu]); output.put(HEX_DIGITS[digest[index] & 0x0fu]); diff --git a/vendor/core/src/core/html/escape.cc b/vendor/core/src/core/html/escape.cc index 6b3097a1a..57ffbc822 100644 --- a/vendor/core/src/core/html/escape.cc +++ b/vendor/core/src/core/html/escape.cc @@ -5,8 +5,7 @@ namespace sourcemeta::core { auto html_escape(std::string &text) -> void { - std::size_t write_position{0}; - std::size_t original_size{text.size()}; + const std::size_t original_size{text.size()}; // First pass: count how much space we need std::size_t required_size{0}; @@ -35,62 +34,64 @@ auto html_escape(std::string &text) -> void { return; } - // Resize string to accommodate escaped characters - text.resize(required_size); - - // Second pass: work backwards to avoid overwriting data - std::size_t read_position{original_size}; - write_position = required_size; - - while (read_position > 0) { - --read_position; - char character = text[read_position]; - - switch (character) { - case '&': - write_position -= 5; - text[write_position] = '&'; - text[write_position + 1] = 'a'; - text[write_position + 2] = 'm'; - text[write_position + 3] = 'p'; - text[write_position + 4] = ';'; - break; - case '<': - write_position -= 4; - text[write_position] = '&'; - text[write_position + 1] = 'l'; - text[write_position + 2] = 't'; - text[write_position + 3] = ';'; - break; - case '>': - write_position -= 4; - text[write_position] = '&'; - text[write_position + 1] = 'g'; - text[write_position + 2] = 't'; - text[write_position + 3] = ';'; - break; - case '"': - write_position -= 6; - text[write_position] = '&'; - text[write_position + 1] = 'q'; - text[write_position + 2] = 'u'; - text[write_position + 3] = 'o'; - text[write_position + 4] = 't'; - text[write_position + 5] = ';'; - break; - case '\'': - write_position -= 5; - text[write_position] = '&'; - text[write_position + 1] = '#'; - text[write_position + 2] = '3'; - text[write_position + 3] = '9'; - text[write_position + 4] = ';'; - break; - default: - --write_position; - text[write_position] = character; - } - } + // Write escaped characters backwards to avoid overwriting unprocessed data + text.resize_and_overwrite(required_size, + [original_size](char *buffer, std::size_t count) { + auto read_position = original_size; + auto write_position = count; + + while (read_position > 0) { + --read_position; + const auto character = buffer[read_position]; + + switch (character) { + case '&': + write_position -= 5; + buffer[write_position] = '&'; + buffer[write_position + 1] = 'a'; + buffer[write_position + 2] = 'm'; + buffer[write_position + 3] = 'p'; + buffer[write_position + 4] = ';'; + break; + case '<': + write_position -= 4; + buffer[write_position] = '&'; + buffer[write_position + 1] = 'l'; + buffer[write_position + 2] = 't'; + buffer[write_position + 3] = ';'; + break; + case '>': + write_position -= 4; + buffer[write_position] = '&'; + buffer[write_position + 1] = 'g'; + buffer[write_position + 2] = 't'; + buffer[write_position + 3] = ';'; + break; + case '"': + write_position -= 6; + buffer[write_position] = '&'; + buffer[write_position + 1] = 'q'; + buffer[write_position + 2] = 'u'; + buffer[write_position + 3] = 'o'; + buffer[write_position + 4] = 't'; + buffer[write_position + 5] = ';'; + break; + case '\'': + write_position -= 5; + buffer[write_position] = '&'; + buffer[write_position + 1] = '#'; + buffer[write_position + 2] = '3'; + buffer[write_position + 3] = '9'; + buffer[write_position + 4] = ';'; + break; + default: + --write_position; + buffer[write_position] = character; + } + } + + return count; + }); } static auto needs_escape(const std::string_view input) -> bool { diff --git a/vendor/core/src/core/html/include/sourcemeta/core/html_buffer.h b/vendor/core/src/core/html/include/sourcemeta/core/html_buffer.h index 7c0c9f3ca..ae8a4fa14 100644 --- a/vendor/core/src/core/html/include/sourcemeta/core/html_buffer.h +++ b/vendor/core/src/core/html/include/sourcemeta/core/html_buffer.h @@ -48,7 +48,7 @@ class SOURCEMETA_CORE_HTML_EXPORT HTMLBuffer { const auto remaining{ this->cursor_ ? static_cast(this->end_ - this->cursor_) - : std::size_t{0}}; + : 0uz}; if (remaining < length) [[unlikely]] { this->grow(length); } diff --git a/vendor/core/src/core/html/writer.cc b/vendor/core/src/core/html/writer.cc index 23087b79d..ee6775a40 100644 --- a/vendor/core/src/core/html/writer.cc +++ b/vendor/core/src/core/html/writer.cc @@ -9,8 +9,7 @@ auto HTMLBuffer::grow(const std::size_t needed) -> void { this->cursor_ ? static_cast(this->cursor_ - this->buffer_.data()) : 0}; - auto new_capacity{this->buffer_.empty() ? std::size_t{1024} - : this->buffer_.size() * 2}; + auto new_capacity{this->buffer_.empty() ? 1024uz : this->buffer_.size() * 2}; while (new_capacity < current_size + needed) { new_capacity *= 2; } diff --git a/vendor/core/src/core/ip/CMakeLists.txt b/vendor/core/src/core/ip/CMakeLists.txt new file mode 100644 index 000000000..3c2fdd7e6 --- /dev/null +++ b/vendor/core/src/core/ip/CMakeLists.txt @@ -0,0 +1,6 @@ +sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME ip + SOURCES ipv4.cc ipv6.cc) + +if(SOURCEMETA_CORE_INSTALL) + sourcemeta_library_install(NAMESPACE sourcemeta PROJECT core NAME ip) +endif() diff --git a/vendor/core/src/core/ip/include/sourcemeta/core/ip.h b/vendor/core/src/core/ip/include/sourcemeta/core/ip.h new file mode 100644 index 000000000..72860f6b9 --- /dev/null +++ b/vendor/core/src/core/ip/include/sourcemeta/core/ip.h @@ -0,0 +1,54 @@ +#ifndef SOURCEMETA_CORE_IP_H_ +#define SOURCEMETA_CORE_IP_H_ + +#ifndef SOURCEMETA_CORE_IP_EXPORT +#include +#endif + +#include // std::string_view + +/// @defgroup ip IP +/// @brief IPv4 (RFC 3986) and IPv6 (RFC 3986, RFC 4291) address validation. +/// +/// This functionality is included as follows: +/// +/// ```cpp +/// #include +/// ``` + +namespace sourcemeta::core { + +/// @ingroup ip +/// Check whether the given string is a valid IPv4 address per RFC 3986 +/// Section 3.2.2. For example: +/// +/// ```cpp +/// #include +/// +/// #include +/// +/// assert(sourcemeta::core::is_ipv4("192.168.1.1")); +/// assert(!sourcemeta::core::is_ipv4("999.0.0.1")); +/// ``` +SOURCEMETA_CORE_IP_EXPORT +auto is_ipv4(std::string_view address) -> bool; + +/// @ingroup ip +/// Check whether the given string is a valid IPv6 address per RFC 3986 +/// Section 3.2.2 and RFC 4291 Section 2.2. The input must not include +/// surrounding brackets. For example: +/// +/// ```cpp +/// #include +/// +/// #include +/// +/// assert(sourcemeta::core::is_ipv6("2001:db8::1")); +/// assert(!sourcemeta::core::is_ipv6("not an address")); +/// ``` +SOURCEMETA_CORE_IP_EXPORT +auto is_ipv6(std::string_view address) -> bool; + +} // namespace sourcemeta::core + +#endif diff --git a/vendor/core/src/core/ip/ipv4.cc b/vendor/core/src/core/ip/ipv4.cc new file mode 100644 index 000000000..656935a4c --- /dev/null +++ b/vendor/core/src/core/ip/ipv4.cc @@ -0,0 +1,56 @@ +#include + +namespace sourcemeta::core { + +static constexpr auto is_digit(const char character) -> bool { + return character >= '0' && character <= '9'; +} + +auto is_ipv4(const std::string_view address) -> bool { + if (address.empty()) { + return false; + } + + std::string_view::size_type position{0}; + unsigned int octet_count{0}; + + while (octet_count < 4) { + if (position >= address.size()) { + return false; + } + + if (!is_digit(address[position])) { + return false; + } + + const auto octet_start{position}; + unsigned int value{0}; + while (position < address.size() && is_digit(address[position])) { + value = value * 10 + static_cast(address[position] - '0'); + position += 1; + } + + const auto octet_length{position - octet_start}; + + if (octet_length > 1 && address[octet_start] == '0') { + return false; + } + + if (octet_length > 3 || value > 255) { + return false; + } + + octet_count += 1; + + if (octet_count < 4) { + if (position >= address.size() || address[position] != '.') { + return false; + } + position += 1; + } + } + + return position == address.size(); +} + +} // namespace sourcemeta::core diff --git a/vendor/core/src/core/ip/ipv6.cc b/vendor/core/src/core/ip/ipv6.cc new file mode 100644 index 000000000..099be9bdb --- /dev/null +++ b/vendor/core/src/core/ip/ipv6.cc @@ -0,0 +1,112 @@ +#include + +#include // std::array +#include // std::uint8_t + +namespace sourcemeta::core { + +static constexpr auto make_hex_table() -> std::array { + std::array table{}; + for (auto index{0u}; index < 256; index++) { + table[index] = (index >= '0' && index <= '9') || + (index >= 'a' && index <= 'f') || + (index >= 'A' && index <= 'F'); + } + return table; +} + +static constexpr auto HEX_TABLE{make_hex_table()}; + +static constexpr auto is_hex_digit(const char character) -> bool { + return HEX_TABLE[static_cast(character)]; +} + +auto is_ipv6(const std::string_view address) -> bool { + if (address.empty()) { + return false; + } + + const auto size{address.size()}; + + if (address.front() == '[' || address.back() == ']') { + return false; + } + + const auto double_colon{address.find("::")}; + const bool has_compression{double_colon != std::string_view::npos}; + + if (has_compression && + address.find("::", double_colon + 2) != std::string_view::npos) { + return false; + } + + if (address.front() == ':' && (!has_compression || double_colon != 0)) { + return false; + } + if (address.back() == ':' && + (!has_compression || double_colon + 1 != size - 1)) { + return false; + } + + unsigned int group_count{0}; + std::string_view::size_type position{0}; + + while (position < size) { + if (has_compression && position == double_colon) { + position += 2; + continue; + } + + const auto group_start{position}; + unsigned int hex_count{0}; + bool found_dot{false}; + + while (position < size) { + const auto character{address[position]}; + if (character == ':') { + break; + } + if (character == '.') { + found_dot = true; + break; + } + if (!is_hex_digit(character)) { + return false; + } + hex_count += 1; + position += 1; + } + + if (found_dot) { + if (!is_ipv4(address.substr(group_start))) { + return false; + } + group_count += 2; + break; + } + + if (hex_count == 0 || hex_count > 4) { + return false; + } + + group_count += 1; + + if (position < size && address[position] == ':') { + if (has_compression && position == double_colon) { + continue; + } + position += 1; + if (position >= size) { + return false; + } + } + } + + if (has_compression) { + return group_count < 8; + } + + return group_count == 8; +} + +} // namespace sourcemeta::core diff --git a/vendor/core/src/core/json/include/sourcemeta/core/json.h b/vendor/core/src/core/json/include/sourcemeta/core/json.h index 98c7231c4..99148b140 100644 --- a/vendor/core/src/core/json/include/sourcemeta/core/json.h +++ b/vendor/core/src/core/json/include/sourcemeta/core/json.h @@ -13,12 +13,14 @@ #include -#include // std::uint64_t -#include // std::filesystem +#include // std::uint64_t +#include // std::filesystem +#include // std::formatter, std::format_context, std::format_parse_context, std::format_to #include // std::basic_ifstream #include // std::initializer_list #include // std::basic_istream #include // std::basic_ostream +#include // std::ostringstream #include // std::basic_string /// @defgroup json JSON @@ -303,4 +305,32 @@ make_set(std::initializer_list types) -> JSON::TypeSet { } // namespace sourcemeta::core +template <> struct std::formatter { + constexpr auto parse(std::format_parse_context &context) + -> decltype(context.begin()) { + return context.begin(); + } + + auto format(const sourcemeta::core::JSON &value, + std::format_context &context) const -> decltype(context.out()) { + std::ostringstream stream; + stream << value; + return std::format_to(context.out(), "{}", stream.str()); + } +}; + +template <> struct std::formatter { + constexpr auto parse(std::format_parse_context &context) + -> decltype(context.begin()) { + return context.begin(); + } + + auto format(const sourcemeta::core::JSON::Type value, + std::format_context &context) const -> decltype(context.out()) { + std::ostringstream stream; + stream << value; + return std::format_to(context.out(), "{}", stream.str()); + } +}; + #endif diff --git a/vendor/core/src/core/json/include/sourcemeta/core/json_auto.h b/vendor/core/src/core/json/include/sourcemeta/core/json_auto.h index 813c829d1..1e2bb6179 100644 --- a/vendor/core/src/core/json/include/sourcemeta/core/json_auto.h +++ b/vendor/core/src/core/json/include/sourcemeta/core/json_auto.h @@ -3,27 +3,24 @@ #include -#include // std::sort -#include // std::bitset -#include // assert -#include // std::chrono -#include // std::same_as, std::constructible_from +#include // std::sort +#include // std::bitset +#include // assert +#include // std::chrono +#include // std::same_as, std::constructible_from, std::invocable, std::invocable #include // std::filesystem #include // std::function #include // std::optional, std::nullopt, std::bad_optional_access #include // std::tuple, std::apply, std::tuple_element_t, std::tuple_size, std::tuple_size_v -#include // std::false_type, std::true_type, std::void_t, std::is_enum_v, std::underlying_type_t, std::is_same_v, std::is_base_of_v, std::remove_cvref_t +#include // std::false_type, std::true_type, std::is_enum_v, std::underlying_type_t, std::is_same_v, std::is_base_of_v, std::remove_cvref_t #include // std::pair, std:::make_index_sequence, std::index_sequence #include // std::variant, std::variant_size_v, std::variant_alternative_t, std::visit namespace sourcemeta::core { /// @ingroup json -template -struct json_auto_has_mapped_type : std::false_type {}; template -struct json_auto_has_mapped_type> - : std::true_type {}; +concept json_auto_has_mapped_type = requires { typename T::mapped_type; }; /// @ingroup json template struct json_auto_is_basic_string : std::false_type {}; @@ -57,14 +54,10 @@ concept json_auto_has_method_to = requires(const T value) { /// @ingroup json /// Container-like classes can opt-out from automatic JSON /// serialisation by setting `using json_auto = std::false_type;` -template -struct json_auto_supports_auto_impl : std::true_type {}; -template -struct json_auto_supports_auto_impl> - : std::bool_constant< - !std::is_same_v> {}; template -concept json_auto_supports_auto = json_auto_supports_auto_impl::value; +concept json_auto_supports_auto = !requires { + typename T::json_auto; +} || !std::is_same_v; /// @ingroup json template @@ -74,7 +67,7 @@ concept json_auto_list_like = typename T::const_iterator; { type.cbegin() } -> std::same_as; { type.cend() } -> std::same_as; - } && json_auto_supports_auto && !json_auto_has_mapped_type::value && + } && json_auto_supports_auto && !json_auto_has_mapped_type && !json_auto_has_method_from && !json_auto_has_method_to && !json_auto_is_basic_string::value; @@ -87,19 +80,14 @@ concept json_auto_map_like = typename T::key_type; { type.cbegin() } -> std::same_as; { type.cend() } -> std::same_as; - } && json_auto_supports_auto && json_auto_has_mapped_type::value && + } && json_auto_supports_auto && json_auto_has_mapped_type && !json_auto_has_method_from && !json_auto_has_method_to && std::is_same_v; -/// @ingroup json -template -struct json_auto_has_reverse_iterator : std::false_type {}; - /// @ingroup json template -struct json_auto_has_reverse_iterator> - : std::true_type {}; +concept json_auto_has_reverse_iterator = + requires { typename T::reverse_iterator; }; /// @ingroup json template struct json_auto_is_pair : std::false_type {}; @@ -393,7 +381,7 @@ auto to_json(typename T::const_iterator begin, typename T::const_iterator end) } // To guarantee ordering across implementations - if constexpr (!json_auto_has_reverse_iterator::value) { + if constexpr (!json_auto_has_reverse_iterator) { std::sort(result.as_array().begin(), result.as_array().end()); } @@ -401,11 +389,10 @@ auto to_json(typename T::const_iterator begin, typename T::const_iterator end) } /// @ingroup json -template -auto to_json( - typename T::const_iterator begin, typename T::const_iterator end, - const std::function &callback) - -> JSON { +template F> +auto to_json(typename T::const_iterator begin, typename T::const_iterator end, + const F &callback) -> JSON { // TODO: Extend `make_array` to optionally take iterators, etc auto result{JSON::make_array()}; for (auto iterator = begin; iterator != end; ++iterator) { @@ -413,7 +400,7 @@ auto to_json( } // To guarantee ordering across implementations - if constexpr (!json_auto_has_reverse_iterator::value) { + if constexpr (!json_auto_has_reverse_iterator) { std::sort(result.as_array().begin(), result.as_array().end()); } @@ -426,11 +413,9 @@ template auto to_json(const T &value) -> JSON { } /// @ingroup json -template -auto to_json( - const T &value, - const std::function &callback) - -> JSON { +template F> +auto to_json(const T &value, const F &callback) -> JSON { return to_json(value.cbegin(), value.cend(), callback); } @@ -513,11 +498,10 @@ template auto to_json(const T &value) -> JSON { } /// @ingroup json -template -auto to_json( - typename T::const_iterator begin, typename T::const_iterator end, - const std::function &callback) - -> JSON { +template F> +auto to_json(typename T::const_iterator begin, typename T::const_iterator end, + const F &callback) -> JSON { auto result{JSON::make_object()}; for (auto iterator = begin; iterator != end; ++iterator) { result.assign(iterator->first, callback(iterator->second)); @@ -570,11 +554,9 @@ auto from_json( } /// @ingroup json -template -auto to_json( - const T &value, - const std::function &callback) - -> JSON { +template F> +auto to_json(const T &value, const F &callback) -> JSON { return to_json(value.cbegin(), value.cend(), callback); } diff --git a/vendor/core/src/core/json/include/sourcemeta/core/json_object.h b/vendor/core/src/core/json/include/sourcemeta/core/json_object.h index 14e692c6f..c364faaa1 100644 --- a/vendor/core/src/core/json/include/sourcemeta/core/json_object.h +++ b/vendor/core/src/core/json/include/sourcemeta/core/json_object.h @@ -6,7 +6,7 @@ #include // std::size_t #include // std::initializer_list #include // std::advance -#include // std::pair, std::move +#include // std::pair, std::move, std::unreachable #include // std::vector namespace sourcemeta::core { @@ -220,12 +220,7 @@ template class JSONObject { } } -// See https://en.cppreference.com/w/cpp/utility/unreachable -#if defined(_MSC_VER) && !defined(__clang__) - __assume(false); -#else - __builtin_unreachable(); -#endif + std::unreachable(); } /// Access an object entry by its key name @@ -247,12 +242,7 @@ template class JSONObject { } } -// See https://en.cppreference.com/w/cpp/utility/unreachable -#if defined(_MSC_VER) && !defined(__clang__) - __assume(false); -#else - __builtin_unreachable(); -#endif + std::unreachable(); } /// Try to access an object entry by its underlying positional index diff --git a/vendor/core/src/core/json/include/sourcemeta/core/json_value.h b/vendor/core/src/core/json/include/sourcemeta/core/json_value.h index cb9045c59..47ffa2463 100644 --- a/vendor/core/src/core/json/include/sourcemeta/core/json_value.h +++ b/vendor/core/src/core/json/include/sourcemeta/core/json_value.h @@ -25,7 +25,7 @@ #include // std::basic_istringstream #include // std::basic_string, std::char_traits #include // std::basic_string_view -#include // std::enable_if_t, std::is_same_v +#include // std::is_same_v #include // std::pair namespace sourcemeta::core { @@ -683,6 +683,9 @@ class SOURCEMETA_CORE_JSON_EXPORT JSON { /// << "\n"; /// }); /// ``` + // TODO: Merge const/non-const overloads of as_array, as_object, at, front, + // back using deducing this once Apple Clang supports it + // (__cpp_explicit_this_parameter) [[nodiscard]] SOURCEMETA_FORCEINLINE inline auto as_array() const noexcept -> const Array & { assert(this->is_array()); diff --git a/vendor/core/src/core/json/json_value.cc b/vendor/core/src/core/json/json_value.cc index 1d5bf2a9c..0feab582a 100644 --- a/vendor/core/src/core/json/json_value.cc +++ b/vendor/core/src/core/json/json_value.cc @@ -1,19 +1,19 @@ #include #include -#include // std::find +#include // std::ranges::contains, std::ranges::fold_left #include // assert #include // std::isinf, std::isnan, std::modf #include // std::size_t #include // std::int64_t #include // std::reference_wrapper #include // std::initializer_list -#include // std::transform +#include // std::construct_at #include // std::basic_istringstream #include // std::invalid_argument #include // std::to_string #include // std::basic_string_view -#include // std::move +#include // std::exchange, std::move #include // std::vector namespace sourcemeta::core { @@ -52,16 +52,16 @@ JSON::JSON(const bool value) : current_type{Type::Boolean} { JSON::JSON(const std::nullptr_t) {} JSON::JSON(const String &value) : current_type{Type::String} { - new (&this->data_string) String{value}; + std::construct_at(&this->data_string, value); } JSON::JSON(const std::basic_string_view &value) : current_type{Type::String} { - new (&this->data_string) String{value}; + std::construct_at(&this->data_string, value); } JSON::JSON(const Char *const value) : current_type{Type::String} { - new (&this->data_string) String{value}; + std::construct_at(&this->data_string, value); } JSON::JSON(std::initializer_list values) : current_type{Type::Array} { @@ -77,20 +77,20 @@ JSON::JSON(std::initializer_list values) : current_type{Type::Array} { return; } #endif - new (&this->data_array) Array{values}; + std::construct_at(&this->data_array, values); } JSON::JSON(const Array &value) : current_type{Type::Array} { - new (&this->data_array) Array{value}; + std::construct_at(&this->data_array, value); } JSON::JSON(std::initializer_list values) : current_type{Type::Object} { - new (&this->data_object) Object{values}; + std::construct_at(&this->data_object, values); } JSON::JSON(const Object &value) : current_type{Type::Object} { - new (&this->data_object) Object{value}; + std::construct_at(&this->data_object, value); } JSON::JSON(const Decimal &value) : current_type{Type::Decimal} { @@ -121,13 +121,13 @@ JSON::JSON(const JSON &other) : current_type{other.current_type} { this->data_real = other.data_real; break; case Type::String: - new (&this->data_string) String{other.data_string}; + std::construct_at(&this->data_string, other.data_string); break; case Type::Array: - new (&this->data_array) Array{other.data_array}; + std::construct_at(&this->data_array, other.data_array); break; case Type::Object: - new (&this->data_object) Object{other.data_object}; + std::construct_at(&this->data_object, other.data_object); break; case Type::Decimal: this->data_decimal = new Decimal{*other.data_decimal}; @@ -149,20 +149,19 @@ JSON::JSON(JSON &&other) noexcept : current_type{other.current_type} { this->data_real = other.data_real; break; case Type::String: - new (&this->data_string) String{std::move(other.data_string)}; + std::construct_at(&this->data_string, std::move(other.data_string)); other.current_type = Type::Null; break; case Type::Array: - new (&this->data_array) Array{std::move(other.data_array)}; + std::construct_at(&this->data_array, std::move(other.data_array)); other.current_type = Type::Null; break; case Type::Object: - new (&this->data_object) Object{std::move(other.data_object)}; + std::construct_at(&this->data_object, std::move(other.data_object)); other.current_type = Type::Null; break; case Type::Decimal: - this->data_decimal = other.data_decimal; - other.data_decimal = nullptr; + this->data_decimal = std::exchange(other.data_decimal, nullptr); other.current_type = Type::Null; break; default: @@ -184,13 +183,13 @@ auto JSON::operator=(const JSON &other) -> JSON & { this->data_real = other.data_real; break; case Type::String: - new (&this->data_string) String{other.data_string}; + std::construct_at(&this->data_string, other.data_string); break; case Type::Array: - new (&this->data_array) Array{other.data_array}; + std::construct_at(&this->data_array, other.data_array); break; case Type::Object: - new (&this->data_object) Object{other.data_object}; + std::construct_at(&this->data_object, other.data_object); break; case Type::Decimal: this->data_decimal = new Decimal{*other.data_decimal}; @@ -216,20 +215,19 @@ auto JSON::operator=(JSON &&other) noexcept -> JSON & { this->data_real = other.data_real; break; case Type::String: - new (&this->data_string) String{std::move(other.data_string)}; + std::construct_at(&this->data_string, std::move(other.data_string)); other.current_type = Type::Null; break; case Type::Array: - new (&this->data_array) Array{std::move(other.data_array)}; + std::construct_at(&this->data_array, std::move(other.data_array)); other.current_type = Type::Null; break; case Type::Object: - new (&this->data_object) Object{std::move(other.data_object)}; + std::construct_at(&this->data_object, std::move(other.data_object)); other.current_type = Type::Null; break; case Type::Decimal: - this->data_decimal = other.data_decimal; - other.data_decimal = nullptr; + this->data_decimal = std::exchange(other.data_decimal, nullptr); other.current_type = Type::Null; break; default: @@ -455,18 +453,17 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { // which we are not taking into account here, as its typically // implementation dependent. This function is just a rough estimate. if (this->is_object()) { - return std::accumulate(this->as_object().cbegin(), this->as_object().cend(), - static_cast(0), - [](const std::uint64_t accumulator, - const typename Object::value_type &pair) { - return accumulator + - (pair.first.size() * sizeof(Char)) + - pair.second.estimated_byte_size(); - }); + return std::ranges::fold_left(this->as_object(), + static_cast(0), + [](const std::uint64_t accumulator, + const typename Object::value_type &pair) { + return accumulator + + (pair.first.size() * sizeof(Char)) + + pair.second.estimated_byte_size(); + }); } else if (this->is_array()) { - return std::accumulate( - this->as_array().cbegin(), this->as_array().cend(), - static_cast(0), + return std::ranges::fold_left( + this->as_array(), static_cast(0), [](const std::uint64_t accumulator, const JSON &item) { return accumulator + item.estimated_byte_size(); }); @@ -499,21 +496,19 @@ auto JSON::operator-=(const JSON &substractive) -> JSON & { case Type::String: return 3 + this->byte_size(); case Type::Array: - return std::accumulate( - this->as_array().cbegin(), this->as_array().cend(), - static_cast(6), + return std::ranges::fold_left( + this->as_array(), static_cast(6), [](const std::uint64_t accumulator, const JSON &item) { return accumulator + 1 + item.fast_hash(); }); case Type::Object: - return std::accumulate(this->as_object().cbegin(), - this->as_object().cend(), - static_cast(7), - [](const std::uint64_t accumulator, - const typename Object::value_type &pair) { - return accumulator + 1 + pair.first.size() + - pair.second.fast_hash(); - }); + return std::ranges::fold_left( + this->as_object(), static_cast(7), + [](const std::uint64_t accumulator, + const typename Object::value_type &pair) { + return accumulator + 1 + pair.first.size() + + pair.second.fast_hash(); + }); case Type::Decimal: return 8; default: @@ -582,8 +577,7 @@ JSON::defines_any(std::initializer_list keys) const -> bool { [[nodiscard]] auto JSON::contains(const JSON &element) const -> bool { assert(this->is_array()); - return std::find(this->as_array().cbegin(), this->as_array().cend(), - element) != this->as_array().cend(); + return std::ranges::contains(this->as_array(), element); } [[nodiscard]] auto JSON::contains(const JSON::StringView element) const @@ -600,13 +594,13 @@ JSON::defines_any(std::initializer_list keys) const -> bool { [[nodiscard]] auto JSON::includes(const JSON::String &input) const -> bool { assert(this->is_string()); - return this->to_string().find(input) != JSON::String::npos; + return this->to_string().contains(input); } [[nodiscard]] auto JSON::includes(const JSON::String::value_type input) const -> bool { assert(this->is_string()); - return this->to_string().find(input) != JSON::String::npos; + return this->to_string().contains(input); } [[nodiscard]] auto JSON::unique() const -> bool { @@ -653,12 +647,11 @@ auto JSON::push_back_if_unique(const JSON &value) -> std::pair, bool> { assert(this->is_array()); auto &array_data{this->as_array().data}; - const auto match{std::ranges::find(array_data, value)}; - if (match == array_data.cend()) { + if (!std::ranges::contains(array_data, value)) { array_data.push_back(value); return {array_data.back(), true}; } else { - return {*match, false}; + return {*std::ranges::find(array_data, value), false}; } } @@ -666,12 +659,11 @@ auto JSON::push_back_if_unique(JSON &&value) -> std::pair, bool> { assert(this->is_array()); auto &array_data{this->as_array().data}; - const auto match{std::ranges::find(array_data, value)}; - if (match == array_data.cend()) { + if (!std::ranges::contains(array_data, value)) { array_data.push_back(std::move(value)); return {array_data.back(), true}; } else { - return {*match, false}; + return {*std::ranges::find(array_data, value), false}; } } diff --git a/vendor/core/src/core/json/parser.h b/vendor/core/src/core/json/parser.h index 5dae84137..b5f8eed1e 100644 --- a/vendor/core/src/core/json/parser.h +++ b/vendor/core/src/core/json/parser.h @@ -73,10 +73,10 @@ inline auto scan_null(const std::uint64_t line, std::uint64_t &column, if constexpr (TrackPositions) { column += 1; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { throw JSONParseError(line, column); } - if (*cursor != character) { + if (*cursor != character) [[unlikely]] { throw JSONParseError(line, column); } cursor++; @@ -93,10 +93,10 @@ inline auto scan_true(const std::uint64_t line, std::uint64_t &column, if constexpr (TrackPositions) { column += 1; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { throw JSONParseError(line, column); } - if (*cursor != character) { + if (*cursor != character) [[unlikely]] { throw JSONParseError(line, column); } cursor++; @@ -113,10 +113,10 @@ inline auto scan_false(const std::uint64_t line, std::uint64_t &column, if constexpr (TrackPositions) { column += 1; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { throw JSONParseError(line, column); } - if (*cursor != character) { + if (*cursor != character) [[unlikely]] { throw JSONParseError(line, column); } cursor++; @@ -133,7 +133,7 @@ inline auto scan_string_unicode_code_point(const std::uint64_t line, if constexpr (TrackPositions) { column += 1; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { throw JSONParseError(line, column); } const char hex_char{*cursor++}; @@ -144,7 +144,7 @@ inline auto scan_string_unicode_code_point(const std::uint64_t line, digit = static_cast(hex_char - 'a') + 10; } else if (hex_char >= 'A' && hex_char <= 'F') { digit = static_cast(hex_char - 'A') + 10; - } else { + } else [[unlikely]] { throw JSONParseError(line, column); } result = (result << 4) | digit; @@ -161,7 +161,7 @@ inline auto scan_string_unicode(const std::uint64_t line, std::uint64_t &column, cursor, end)}; using CharT = typename JSON::Char; - if (code_point >= 0xDC00 && code_point <= 0xDFFF) { + if (code_point >= 0xDC00 && code_point <= 0xDFFF) [[unlikely]] { throw JSONParseError(line, column); } @@ -169,10 +169,10 @@ inline auto scan_string_unicode(const std::uint64_t line, std::uint64_t &column, if constexpr (TrackPositions) { column += 1; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { throw JSONParseError(line, column); } - if (*cursor != internal::token_string_escape) { + if (*cursor != internal::token_string_escape) [[unlikely]] { throw JSONParseError(line, column); } cursor++; @@ -180,10 +180,10 @@ inline auto scan_string_unicode(const std::uint64_t line, std::uint64_t &column, if constexpr (TrackPositions) { column += 1; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { throw JSONParseError(line, column); } - if (*cursor != internal::token_string_escape_unicode) { + if (*cursor != internal::token_string_escape_unicode) [[unlikely]] { throw JSONParseError(line, column); } cursor++; @@ -193,7 +193,7 @@ inline auto scan_string_unicode(const std::uint64_t line, std::uint64_t &column, // See // https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF - if (low_code_point < 0xDC00 || low_code_point > 0xDFFF) { + if (low_code_point < 0xDC00 || low_code_point > 0xDFFF) [[unlikely]] { throw JSONParseError(line, column); } } @@ -205,7 +205,7 @@ inline auto scan_string_escape(const std::uint64_t line, std::uint64_t &column, if constexpr (TrackPositions) { column += 1; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { throw JSONParseError(line, column); } switch (*cursor++) { @@ -222,7 +222,7 @@ inline auto scan_string_escape(const std::uint64_t line, std::uint64_t &column, scan_string_unicode(line, column, cursor, end); return; default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } } @@ -245,7 +245,7 @@ inline auto scan_string(const std::uint64_t line, std::uint64_t &column, cursor = scan; } - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -264,7 +264,7 @@ inline auto scan_string(const std::uint64_t line, std::uint64_t &column, scan_string_escape(line, column, cursor, end); break; default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } } @@ -288,7 +288,7 @@ inline auto scan_digits(const std::uint64_t line, std::uint64_t &column, } cursor++; } - if (at_least_one && !found) { + if (at_least_one && !found) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -303,7 +303,7 @@ inline auto scan_number(const std::uint64_t line, std::uint64_t &column, using CharT = typename JSON::Char; if (first == internal::token_number_minus) { if (cursor >= end || *cursor < internal::token_number_zero || - *cursor > internal::token_number_nine) { + *cursor > internal::token_number_nine) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -322,7 +322,7 @@ inline auto scan_number(const std::uint64_t line, std::uint64_t &column, if (int_start == internal::token_number_zero) { if (cursor < end && *cursor >= internal::token_number_zero && - *cursor <= internal::token_number_nine) { + *cursor <= internal::token_number_nine) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -378,7 +378,7 @@ inline auto scan_json(const char *&cursor, const char *end, container_stack.reserve(32); internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -441,7 +441,7 @@ inline auto scan_json(const char *&cursor, const char *end, return; } default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } } @@ -455,7 +455,7 @@ do_scan_array: { container_stack.push_back({start_index, 0}); internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -481,7 +481,7 @@ do_scan_array: { container_stack.back().child_count++; internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -544,13 +544,13 @@ do_scan_array: { goto do_scan_array_item_separator; } default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } } do_scan_array_item_separator: internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -572,7 +572,7 @@ do_scan_array: { goto do_scan_container_end; } default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } /* @@ -585,7 +585,7 @@ do_scan_object: { container_stack.push_back({start_index, 0}); internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -611,7 +611,7 @@ do_scan_object: { container_stack.back().child_count++; internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -634,12 +634,12 @@ do_scan_object: { goto do_scan_object_separator; } default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } do_scan_object_separator: internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -653,12 +653,12 @@ do_scan_object: { case internal::token_object_key_delimiter: goto do_scan_object_value; default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } do_scan_object_value: internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -721,13 +721,13 @@ do_scan_object: { goto do_scan_object_property_end; } default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } } do_scan_object_property_end: internal::skip_whitespace(cursor, end, line, column); - if (cursor >= end) { + if (cursor >= end) [[unlikely]] { if constexpr (TrackPositions) { column += 1; } @@ -749,7 +749,7 @@ do_scan_object: { goto do_scan_container_end; } default: - throw JSONParseError(line, column); + [[unlikely]] throw JSONParseError(line, column); } do_scan_container_end: diff --git a/vendor/core/src/core/json/stringify.h b/vendor/core/src/core/json/stringify.h index 77c91a2eb..c32dcca28 100644 --- a/vendor/core/src/core/json/stringify.h +++ b/vendor/core/src/core/json/stringify.h @@ -16,7 +16,7 @@ #include // std::next, std::cbegin, std::cend, std::back_inserter #include // std::basic_ostream #include // std::ostringstream -#include // std::to_string +#include // std::basic_string #include // std::vector namespace sourcemeta::core::internal { @@ -64,11 +64,13 @@ auto stringify( const std::int64_t value, std::basic_ostream &stream) -> void { - const auto string{std::to_string(value)}; - stream.write(string.c_str(), + std::array buffer{}; + const auto [end_pointer, error_code] = + std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); + stream.write(buffer.data(), static_cast::int_type>( - string.size())); + end_pointer - buffer.data())); } template