From 4cab20e7d634c07b36e31948ab1f75d1a538adcb Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Tue, 21 Apr 2026 13:21:22 -0400 Subject: [PATCH] Upgrade Core to `3ea3a54756071232ca017b96bc15f17d8b5be370` Signed-off-by: Juan Cruz Viotti --- CMakeLists.txt | 10 - DEPENDENCIES | 2 +- config.cmake.in | 9 +- src/compiler/CMakeLists.txt | 2 +- src/compiler/compiler.cc | 3 +- src/compiler/mapper/enum_8_bit.h | 2 +- src/compiler/mapper/enum_8_bit_top_level.h | 2 +- src/compiler/mapper/enum_arbitrary.h | 2 +- src/compiler/mapper/integer_bounded_8_bit.h | 4 +- .../integer_bounded_greater_than_8_bit.h | 4 +- .../mapper/integer_bounded_multiplier_8_bit.h | 6 +- ...er_bounded_multiplier_greater_than_8_bit.h | 6 +- src/numeric/CMakeLists.txt | 7 - .../include/sourcemeta/jsonbinpack/numeric.h | 17 - .../sourcemeta/jsonbinpack/numeric_integral.h | 130 -------- .../sourcemeta/jsonbinpack/numeric_real.h | 54 ---- .../sourcemeta/jsonbinpack/numeric_zigzag.h | 31 -- src/runtime/CMakeLists.txt | 2 +- src/runtime/decoder_any.cc | 29 +- src/runtime/decoder_array.cc | 7 +- src/runtime/decoder_integer.cc | 21 +- src/runtime/decoder_string.cc | 4 +- src/runtime/encoder_any.cc | 54 ++-- src/runtime/encoder_array.cc | 7 +- src/runtime/encoder_integer.cc | 23 +- src/runtime/encoder_number.cc | 5 +- src/runtime/encoder_string.cc | 4 +- .../sourcemeta/jsonbinpack/runtime_encoding.h | 50 +-- src/runtime/input_stream.cc | 5 +- src/runtime/output_stream.cc | 5 +- test/numeric/CMakeLists.txt | 11 - .../numeric/closest_smallest_exponent_test.cc | 15 - test/numeric/divide_ceil_test.cc | 79 ----- test/numeric/divide_floor_test.cc | 79 ----- test/numeric/uint_max_test.cc | 20 -- test/numeric/zigzag_test.cc | 149 --------- test/runtime/encode_real_test.cc | 46 +-- vendor/core/src/lang/numeric/CMakeLists.txt | 2 +- .../numeric/include/sourcemeta/core/numeric.h | 2 + .../include/sourcemeta/core/numeric_util.h | 299 ++++++++++++++++++ .../include/sourcemeta/core/numeric_zigzag.h | 59 ++++ 41 files changed, 525 insertions(+), 743 deletions(-) delete mode 100644 src/numeric/CMakeLists.txt delete mode 100644 src/numeric/include/sourcemeta/jsonbinpack/numeric.h delete mode 100644 src/numeric/include/sourcemeta/jsonbinpack/numeric_integral.h delete mode 100644 src/numeric/include/sourcemeta/jsonbinpack/numeric_real.h delete mode 100644 src/numeric/include/sourcemeta/jsonbinpack/numeric_zigzag.h delete mode 100644 test/numeric/CMakeLists.txt delete mode 100644 test/numeric/closest_smallest_exponent_test.cc delete mode 100644 test/numeric/divide_ceil_test.cc delete mode 100644 test/numeric/divide_floor_test.cc delete mode 100644 test/numeric/uint_max_test.cc delete mode 100644 test/numeric/zigzag_test.cc create mode 100644 vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_util.h create mode 100644 vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_zigzag.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a831d78e2..1a5b53ab9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,6 @@ both schema-driven and schema-less support." list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") # Options -option(JSONBINPACK_NUMERIC "Build the JSON BinPack numeric library" ON) option(JSONBINPACK_RUNTIME "Build the JSON BinPack runtime" ON) option(JSONBINPACK_COMPILER "Build the JSON BinPack compiler" ON) option(JSONBINPACK_TESTS "Build the JSON BinPack tests" OFF) @@ -37,11 +36,6 @@ if(JSONBINPACK_INSTALL) COMPONENT sourcemeta_jsonbinpack_dev) endif() -# Numeric -if(JSONBINPACK_NUMERIC) - add_subdirectory(src/numeric) -endif() - # Runtime if(JSONBINPACK_RUNTIME) add_subdirectory(src/runtime) @@ -73,10 +67,6 @@ endif() if(JSONBINPACK_TESTS) enable_testing() - if(JSONBINPACK_NUMERIC) - add_subdirectory(test/numeric) - endif() - if(JSONBINPACK_RUNTIME) add_subdirectory(test/runtime) endif() diff --git a/DEPENDENCIES b/DEPENDENCIES index acd31bcc4..4ddefe511 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -1,4 +1,4 @@ vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02 -core https://github.com/sourcemeta/core efaed8a73fa1ba38f0c2fdcbd11d0b3223c5a70e +core https://github.com/sourcemeta/core 3ea3a54756071232ca017b96bc15f17d8b5be370 blaze https://github.com/sourcemeta/blaze 0ff98cb5e537f571bdf04f0d59a031a0d8634e07 bootstrap https://github.com/twbs/bootstrap 1a6fdfae6be09b09eaced8f0e442ca6f7680a61e diff --git a/config.cmake.in b/config.cmake.in index 6a58ed500..591a57879 100644 --- a/config.cmake.in +++ b/config.cmake.in @@ -4,23 +4,18 @@ list(APPEND JSONBINPACK_COMPONENTS ${JSONBinPack_FIND_COMPONENTS}) list(APPEND JSONBINPACK_COMPONENTS ${jsonbinpack_FIND_COMPONENTS}) if(NOT JSONBINPACK_COMPONENTS) - list(APPEND JSONBINPACK_COMPONENTS numeric) list(APPEND JSONBINPACK_COMPONENTS runtime) list(APPEND JSONBINPACK_COMPONENTS compiler) endif() include(CMakeFindDependencyMacro) -find_dependency(Core COMPONENTS regex uri json jsonpointer jsonschema) +find_dependency(Core COMPONENTS numeric regex uri json jsonpointer jsonschema) find_dependency(Blaze COMPONENTS alterschema) foreach(component ${JSONBINPACK_COMPONENTS}) - if(component STREQUAL "numeric") - include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_jsonbinpack_numeric.cmake") - elseif(component STREQUAL "runtime") - include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_jsonbinpack_numeric.cmake") + if(component STREQUAL "runtime") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_jsonbinpack_runtime.cmake") elseif(component STREQUAL "compiler") - include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_jsonbinpack_numeric.cmake") include("${CMAKE_CURRENT_LIST_DIR}/sourcemeta_jsonbinpack_compiler.cmake") else() message(FATAL_ERROR "Unknown JSON BinPack component: ${component}") diff --git a/src/compiler/CMakeLists.txt b/src/compiler/CMakeLists.txt index 78c3ba86e..01b7364af 100644 --- a/src/compiler/CMakeLists.txt +++ b/src/compiler/CMakeLists.txt @@ -23,7 +23,7 @@ if(JSONBINPACK_INSTALL) endif() target_link_libraries(sourcemeta_jsonbinpack_compiler PRIVATE - sourcemeta::jsonbinpack::numeric) + sourcemeta::core::numeric) target_link_libraries(sourcemeta_jsonbinpack_compiler PUBLIC sourcemeta::core::json) target_link_libraries(sourcemeta_jsonbinpack_compiler PRIVATE diff --git a/src/compiler/compiler.cc b/src/compiler/compiler.cc index f5f49bb23..309298bea 100644 --- a/src/compiler/compiler.cc +++ b/src/compiler/compiler.cc @@ -1,5 +1,6 @@ #include -#include + +#include #include #include diff --git a/src/compiler/mapper/enum_8_bit.h b/src/compiler/mapper/enum_8_bit.h index 20510fab9..85dbe2ea6 100644 --- a/src/compiler/mapper/enum_8_bit.h +++ b/src/compiler/mapper/enum_8_bit.h @@ -20,7 +20,7 @@ class Enum8Bit final : public sourcemeta::blaze::SchemaTransformRule { schema.is_object() && schema.defines("enum") && schema.at("enum").is_array() && !location.pointer.empty() && schema.at("enum").size() > 1 && - is_byte(schema.at("enum").size() - 1); + sourcemeta::core::is_byte(schema.at("enum").size() - 1); } auto transform(sourcemeta::core::JSON &schema, diff --git a/src/compiler/mapper/enum_8_bit_top_level.h b/src/compiler/mapper/enum_8_bit_top_level.h index e9801b03b..6618cd83d 100644 --- a/src/compiler/mapper/enum_8_bit_top_level.h +++ b/src/compiler/mapper/enum_8_bit_top_level.h @@ -20,7 +20,7 @@ class Enum8BitTopLevel final : public sourcemeta::blaze::SchemaTransformRule { schema.is_object() && schema.defines("enum") && schema.at("enum").is_array() && location.pointer.empty() && schema.at("enum").size() > 1 && - is_byte(schema.at("enum").size() - 1); + sourcemeta::core::is_byte(schema.at("enum").size() - 1); } auto transform(sourcemeta::core::JSON &schema, diff --git a/src/compiler/mapper/enum_arbitrary.h b/src/compiler/mapper/enum_arbitrary.h index 159997b38..c3864c21d 100644 --- a/src/compiler/mapper/enum_arbitrary.h +++ b/src/compiler/mapper/enum_arbitrary.h @@ -21,7 +21,7 @@ class EnumArbitrary final : public sourcemeta::blaze::SchemaTransformRule { schema.is_object() && schema.defines("enum") && schema.at("enum").is_array() && !location.pointer.empty() && schema.at("enum").size() > 1 && - !is_byte(schema.at("enum").size() - 1); + !sourcemeta::core::is_byte(schema.at("enum").size() - 1); } auto transform(sourcemeta::core::JSON &schema, diff --git a/src/compiler/mapper/integer_bounded_8_bit.h b/src/compiler/mapper/integer_bounded_8_bit.h index 5c9c71036..fa68accab 100644 --- a/src/compiler/mapper/integer_bounded_8_bit.h +++ b/src/compiler/mapper/integer_bounded_8_bit.h @@ -20,8 +20,8 @@ class IntegerBounded8Bit final : public sourcemeta::blaze::SchemaTransformRule { schema.is_object() && schema.defines("type") && schema.at("type").to_string() == "integer" && schema.defines("minimum") && schema.defines("maximum") && - is_byte(schema.at("maximum").to_integer() - - schema.at("minimum").to_integer()) && + sourcemeta::core::is_byte(schema.at("maximum").to_integer() - + schema.at("minimum").to_integer()) && !schema.defines("multipleOf"); } diff --git a/src/compiler/mapper/integer_bounded_greater_than_8_bit.h b/src/compiler/mapper/integer_bounded_greater_than_8_bit.h index c2186a288..63a5673a8 100644 --- a/src/compiler/mapper/integer_bounded_greater_than_8_bit.h +++ b/src/compiler/mapper/integer_bounded_greater_than_8_bit.h @@ -22,8 +22,8 @@ class IntegerBoundedGreaterThan8Bit final schema.is_object() && schema.defines("type") && schema.at("type").to_string() == "integer" && schema.defines("minimum") && schema.defines("maximum") && - !is_byte(schema.at("maximum").to_integer() - - schema.at("minimum").to_integer()) && + !sourcemeta::core::is_byte(schema.at("maximum").to_integer() - + schema.at("minimum").to_integer()) && !schema.defines("multipleOf"); } diff --git a/src/compiler/mapper/integer_bounded_multiplier_8_bit.h b/src/compiler/mapper/integer_bounded_multiplier_8_bit.h index 9b691d729..508a4f368 100644 --- a/src/compiler/mapper/integer_bounded_multiplier_8_bit.h +++ b/src/compiler/mapper/integer_bounded_multiplier_8_bit.h @@ -28,9 +28,9 @@ class IntegerBoundedMultiplier8Bit final return false; } - return is_byte(count_multiples(schema.at("minimum").to_integer(), - schema.at("maximum").to_integer(), - schema.at("multipleOf").to_integer())); + return sourcemeta::core::is_byte(sourcemeta::core::count_multiples( + schema.at("minimum").to_integer(), schema.at("maximum").to_integer(), + schema.at("multipleOf").to_integer())); } auto transform(sourcemeta::core::JSON &schema, diff --git a/src/compiler/mapper/integer_bounded_multiplier_greater_than_8_bit.h b/src/compiler/mapper/integer_bounded_multiplier_greater_than_8_bit.h index 2ae9a5ff0..e14606a63 100644 --- a/src/compiler/mapper/integer_bounded_multiplier_greater_than_8_bit.h +++ b/src/compiler/mapper/integer_bounded_multiplier_greater_than_8_bit.h @@ -28,9 +28,9 @@ class IntegerBoundedMultiplierGreaterThan8Bit final return false; } - return !is_byte(count_multiples(schema.at("minimum").to_integer(), - schema.at("maximum").to_integer(), - schema.at("multipleOf").to_integer())); + return !sourcemeta::core::is_byte(sourcemeta::core::count_multiples( + schema.at("minimum").to_integer(), schema.at("maximum").to_integer(), + schema.at("multipleOf").to_integer())); } auto transform(sourcemeta::core::JSON &schema, diff --git a/src/numeric/CMakeLists.txt b/src/numeric/CMakeLists.txt deleted file mode 100644 index 55a02a466..000000000 --- a/src/numeric/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -sourcemeta_library(NAMESPACE sourcemeta PROJECT jsonbinpack NAME numeric - FOLDER "JSON BinPack/Numeric" - PRIVATE_HEADERS integral.h real.h zigzag.h) - -if(JSONBINPACK_INSTALL) - sourcemeta_library_install(NAMESPACE sourcemeta PROJECT jsonbinpack NAME numeric) -endif() diff --git a/src/numeric/include/sourcemeta/jsonbinpack/numeric.h b/src/numeric/include/sourcemeta/jsonbinpack/numeric.h deleted file mode 100644 index 4b9801cba..000000000 --- a/src/numeric/include/sourcemeta/jsonbinpack/numeric.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_NUMERIC_H_ -#define SOURCEMETA_JSONBINPACK_NUMERIC_H_ - -/// @defgroup numeric Numeric -/// @brief A comprehensive numeric library for JSON BinPack -/// -/// This functionality is included as follows: -/// -/// ```cpp -/// #include -/// ``` - -#include -#include -#include - -#endif diff --git a/src/numeric/include/sourcemeta/jsonbinpack/numeric_integral.h b/src/numeric/include/sourcemeta/jsonbinpack/numeric_integral.h deleted file mode 100644 index a29b93d65..000000000 --- a/src/numeric/include/sourcemeta/jsonbinpack/numeric_integral.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_NUMERIC_INTEGRAL_H_ -#define SOURCEMETA_JSONBINPACK_NUMERIC_INTEGRAL_H_ - -#include // assert -#include // std::abs -#include // std::uint8_t, std::int64_t, std::uint64_t -#include // std::numeric_limits - -namespace sourcemeta::jsonbinpack { - -/// @ingroup numeric -template constexpr auto is_byte(const T value) noexcept -> bool { - return value <= std::numeric_limits::max(); -} - -/// @ingroup numeric -constexpr auto count_multiples(const std::int64_t minimum, - const std::int64_t maximum, - const std::int64_t multiplier) -> std::uint64_t { - assert(minimum <= maximum); - assert(multiplier > 0); - return static_cast((maximum / multiplier) - - ((minimum - 1) / multiplier)); -} - -/// @ingroup numeric -template constexpr auto uint_max = (2 << (T - 1)) - 1; - -/// @ingroup numeric -template -constexpr auto is_within(const T value, const std::int64_t lower, - const std::int64_t higher) noexcept -> bool { - return value >= lower && value <= higher; -} - -/// @ingroup numeric -template -constexpr auto is_within(const T value, const std::uint64_t lower, - const std::uint64_t higher) noexcept -> bool { - if (value >= 0) { - return static_cast(value) >= lower && - static_cast(value) <= higher; - } else { - return false; - } -} - -/// @ingroup numeric -constexpr auto abs(const std::int64_t value) noexcept -> std::uint64_t { - if (value < 0) { - return static_cast(value * -1); - } else { - return static_cast(value); - } -} - -/// @ingroup numeric -constexpr auto divide_ceil(const std::int64_t dividend, - const std::uint64_t divisor) noexcept - -> std::int64_t { - // Division by zero is invalid - assert(divisor > 0); - - // Avoid std::ceil as it involves casting to IEEE 764 imprecise types - if (divisor == 1) { - return dividend; - } else if (dividend >= 0) { - // This branch guards against overflows - if (static_cast(dividend) + divisor < divisor) { - return static_cast( - (static_cast(dividend) / divisor) + 1 - (1 / divisor)); - } else { - return static_cast( - (static_cast(dividend) + divisor - 1) / divisor); - } - } else { - // `dividend` is negative, so `abs(dividend)` is ensured to be positive - // Then, `divisor` is ensured to be at least 2, which means the - // division result fits in a signed integer. - return -(static_cast( - static_cast(std::abs(dividend)) / divisor)); - } -} - -/// @ingroup numeric -constexpr auto divide_floor(const std::int64_t dividend, - const std::uint64_t divisor) noexcept - -> std::int64_t { - // Division by zero is invalid - assert(divisor > 0); - - // Avoid std::floor as it involves casting to IEEE 764 imprecise types - if (divisor == 1) { - return dividend; - } else if (dividend >= 0) { - return static_cast(static_cast(dividend) / - divisor); - } else { - const std::uint64_t absolute_dividend{ - static_cast(std::abs(dividend))}; - return -( - static_cast(1 + ((absolute_dividend - 1) / divisor))); - } -} - -/// @ingroup numeric -constexpr auto closest_smallest_exponent(const std::uint64_t value, - const std::uint8_t base, - const std::uint8_t exponent_start, - const std::uint8_t exponent_end) - -> std::uint8_t { - assert(exponent_start <= exponent_end); - std::uint64_t result{base}; - for (std::uint8_t exponent = 1; exponent < exponent_end; exponent++) { - // Avoid std::pow, which officially only returns `double` - const std::uint64_t next{result * base}; - if (next > value && exponent >= exponent_start) { - return exponent; - } else { - result = next; - } - } - - assert(result <= value); - return exponent_end; -} - -} // namespace sourcemeta::jsonbinpack - -#endif diff --git a/src/numeric/include/sourcemeta/jsonbinpack/numeric_real.h b/src/numeric/include/sourcemeta/jsonbinpack/numeric_real.h deleted file mode 100644 index fe52879cd..000000000 --- a/src/numeric/include/sourcemeta/jsonbinpack/numeric_real.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_NUMERIC_REAL_H_ -#define SOURCEMETA_JSONBINPACK_NUMERIC_REAL_H_ - -#include // assert -#include // std::modf, std::floor, std::isfinite -#include // std::floating_point, std::integral - -namespace sourcemeta::jsonbinpack { - -// IEEE764 floating-point encoding is not precise. Some real numbers -// cannot be represented directly and thus approximations must be -// used. Here, we abuse those imprecision characteristics for -// space-efficiency by performing rounding on a very, very low -// threshold. -/// @ingroup numeric -template -constexpr auto correct_ieee764(const Real value) -> Real { - assert(std::isfinite(value)); - const Real IEEE764_CORRECTION_THRESHOLD{0.000000001}; - const Real base{std::floor(value)}; - const Real next{base + 1}; - if (next - value <= IEEE764_CORRECTION_THRESHOLD) { - return next; - } else if (value - base <= IEEE764_CORRECTION_THRESHOLD) { - return base; - } else { - return value; - } -} - -/// @ingroup numeric -template -constexpr auto real_digits(Real value, std::uint64_t *point_position) - -> Integer { - assert(std::isfinite(value)); - Real integral; - std::uint64_t shifts{0}; - - Real result = std::modf(value, &integral); - while (result != 0.0) { - value *= 10; - shifts += 1; - result = std::modf(correct_ieee764(value), &integral); - } - - // This is the point position from right to left - *point_position = shifts; - - return static_cast(std::floor(integral)); -} - -} // namespace sourcemeta::jsonbinpack - -#endif diff --git a/src/numeric/include/sourcemeta/jsonbinpack/numeric_zigzag.h b/src/numeric/include/sourcemeta/jsonbinpack/numeric_zigzag.h deleted file mode 100644 index 37d07487f..000000000 --- a/src/numeric/include/sourcemeta/jsonbinpack/numeric_zigzag.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef SOURCEMETA_JSONBINPACK_NUMERIC_ZIGZAG_H_ -#define SOURCEMETA_JSONBINPACK_NUMERIC_ZIGZAG_H_ - -#include // std::abs -#include // std::uint64_t, std::int64_t - -namespace sourcemeta::jsonbinpack { - -/// @ingroup numeric -constexpr auto zigzag_encode(const std::int64_t value) noexcept - -> std::uint64_t { - if (value >= 0) { - return static_cast(value * 2); - } - - return (static_cast(std::abs(value)) * 2) - 1; -} - -/// @ingroup numeric -constexpr auto zigzag_decode(const std::uint64_t value) noexcept - -> std::int64_t { - if (value % 2 == 0) { - return static_cast(value / 2); - } - - return -(static_cast((value + 1) / 2)); -} - -} // namespace sourcemeta::jsonbinpack - -#endif diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index 510ed693b..dd53bf5e9 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -43,4 +43,4 @@ endif() target_link_libraries(sourcemeta_jsonbinpack_runtime PUBLIC sourcemeta::core::json) target_link_libraries(sourcemeta_jsonbinpack_runtime PUBLIC - sourcemeta::jsonbinpack::numeric) + sourcemeta::core::numeric) diff --git a/src/runtime/decoder_any.cc b/src/runtime/decoder_any.cc index 623d0929d..4cd6f5690 100644 --- a/src/runtime/decoder_any.cc +++ b/src/runtime/decoder_any.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include "unreachable.h" #include // assert @@ -12,7 +13,7 @@ namespace sourcemeta::jsonbinpack { auto Decoder::BYTE_CHOICE_INDEX(const struct BYTE_CHOICE_INDEX &options) -> sourcemeta::core::JSON { assert(!options.choices.empty()); - assert(is_byte(options.choices.size())); + assert(sourcemeta::core::is_byte(options.choices.size())); const std::uint8_t index{this->get_byte()}; assert(options.choices.size() > index); return options.choices[index]; @@ -30,7 +31,7 @@ auto Decoder::TOP_LEVEL_BYTE_CHOICE_INDEX( const struct TOP_LEVEL_BYTE_CHOICE_INDEX &options) -> sourcemeta::core::JSON { assert(!options.choices.empty()); - assert(is_byte(options.choices.size())); + assert(sourcemeta::core::is_byte(options.choices.size())); if (!this->has_more_data()) { return options.choices.front(); } else { @@ -96,10 +97,12 @@ auto Decoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( subtype > 0 ? static_cast(-subtype) : static_cast(-this->get_byte() - 1)}; case TYPE_SHARED_STRING: { - const auto length = - subtype == 0 ? this->get_varint() - 1 + - static_cast(uint_max<5>) * 2 - : subtype - 1; + const auto length = subtype == 0 + ? this->get_varint() - 1 + + static_cast( + sourcemeta::core::uint_max<5>) * + 2 + : subtype - 1; const std::uint64_t position{this->position()}; const std::uint64_t current{this->rewind(this->get_varint(), position)}; const sourcemeta::core::JSON value{this->get_string_utf8(length)}; @@ -109,15 +112,18 @@ auto Decoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( case TYPE_STRING: return subtype == 0 ? this->FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( - {static_cast(uint_max<5>) * 2}) + {static_cast( + sourcemeta::core::uint_max<5>) * + 2}) : sourcemeta::core::JSON{this->get_string_utf8(subtype - 1)}; case TYPE_LONG_STRING: return sourcemeta::core::JSON{ - this->get_string_utf8(subtype + uint_max<5>)}; + this->get_string_utf8(subtype + sourcemeta::core::uint_max<5>)}; case TYPE_ARRAY: return subtype == 0 ? this->FIXED_TYPED_ARRAY( - {.size = this->get_varint() + uint_max<5>, + {.size = this->get_varint() + + sourcemeta::core::uint_max<5>, .encoding = std::make_shared( sourcemeta::jsonbinpack:: ANY_PACKED_TYPE_TAG_BYTE_PREFIX{}), @@ -131,7 +137,8 @@ auto Decoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( case TYPE_OBJECT: return subtype == 0 ? this->FIXED_TYPED_ARBITRARY_OBJECT( - {.size = this->get_varint() + uint_max<5>, + {.size = this->get_varint() + + sourcemeta::core::uint_max<5>, .key_encoding = std::make_shared( sourcemeta::jsonbinpack:: PREFIX_VARINT_LENGTH_STRING_SHARED{}), diff --git a/src/runtime/decoder_array.cc b/src/runtime/decoder_array.cc index d8945a05d..0d5f1c3cc 100644 --- a/src/runtime/decoder_array.cc +++ b/src/runtime/decoder_array.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include // assert #include // std::uint8_t, std::uint64_t #include // std::move @@ -25,10 +26,10 @@ auto Decoder::FIXED_TYPED_ARRAY(const struct FIXED_TYPED_ARRAY &options) auto Decoder::BOUNDED_8BITS_TYPED_ARRAY( const struct BOUNDED_8BITS_TYPED_ARRAY &options) -> sourcemeta::core::JSON { assert(options.maximum >= options.minimum); - assert(is_byte(options.maximum - options.minimum)); + assert(sourcemeta::core::is_byte(options.maximum - options.minimum)); const std::uint8_t byte{this->get_byte()}; const std::uint64_t size{byte + options.minimum}; - assert(is_within(size, options.minimum, options.maximum)); + assert(sourcemeta::core::is_within(size, options.minimum, options.maximum)); return this->FIXED_TYPED_ARRAY( {.size = size, .encoding = options.encoding, diff --git a/src/runtime/decoder_integer.cc b/src/runtime/decoder_integer.cc index 03695187e..d4c15bf70 100644 --- a/src/runtime/decoder_integer.cc +++ b/src/runtime/decoder_integer.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include // assert #include // std::uint8_t, std::uint32_t, std::int64_t, std::uint64_t @@ -12,7 +13,7 @@ auto Decoder::BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( assert(options.multiplier > 0); const std::uint8_t byte{this->get_byte()}; const std::int64_t closest_minimum{ - divide_ceil(options.minimum, options.multiplier)}; + sourcemeta::core::divide_ceil(options.minimum, options.multiplier)}; if (closest_minimum >= 0) { const std::uint64_t closest_minimum_multiple{ static_cast(closest_minimum) * options.multiplier}; @@ -21,8 +22,8 @@ auto Decoder::BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( return sourcemeta::core::JSON{static_cast( (byte * options.multiplier) + closest_minimum_multiple)}; } else { - const std::uint64_t closest_minimum_multiple{abs(closest_minimum) * - options.multiplier}; + const std::uint64_t closest_minimum_multiple{ + sourcemeta::core::abs(closest_minimum) * options.multiplier}; // We trust the encoder that the data we are seeing // corresponds to a valid 64-bit signed integer. return sourcemeta::core::JSON{static_cast( @@ -35,7 +36,7 @@ auto Decoder::FLOOR_MULTIPLE_ENUM_VARINT( -> sourcemeta::core::JSON { assert(options.multiplier > 0); const std::int64_t closest_minimum{ - divide_ceil(options.minimum, options.multiplier)}; + sourcemeta::core::divide_ceil(options.minimum, options.multiplier)}; if (closest_minimum >= 0) { const std::uint64_t closest_minimum_multiple{ static_cast(closest_minimum) * options.multiplier}; @@ -44,8 +45,8 @@ auto Decoder::FLOOR_MULTIPLE_ENUM_VARINT( return sourcemeta::core::JSON{static_cast( (this->get_varint() * options.multiplier) + closest_minimum_multiple)}; } else { - const std::uint64_t closest_minimum_multiple{abs(closest_minimum) * - options.multiplier}; + const std::uint64_t closest_minimum_multiple{ + sourcemeta::core::abs(closest_minimum) * options.multiplier}; // We trust the encoder that the data we are seeing // corresponds to a valid 64-bit signed integer. return sourcemeta::core::JSON{static_cast( @@ -58,7 +59,7 @@ auto Decoder::ROOF_MULTIPLE_MIRROR_ENUM_VARINT( -> sourcemeta::core::JSON { assert(options.multiplier > 0); const std::int64_t closest_maximum{ - divide_floor(options.maximum, options.multiplier)}; + sourcemeta::core::divide_floor(options.maximum, options.multiplier)}; if (closest_maximum >= 0) { const std::uint64_t closest_maximum_multiple{ static_cast(closest_maximum) * options.multiplier}; @@ -68,8 +69,8 @@ auto Decoder::ROOF_MULTIPLE_MIRROR_ENUM_VARINT( -(static_cast(this->get_varint() * options.multiplier)) + static_cast(closest_maximum_multiple))}; } else { - const std::uint64_t closest_maximum_multiple{abs(closest_maximum) * - options.multiplier}; + const std::uint64_t closest_maximum_multiple{ + sourcemeta::core::abs(closest_maximum) * options.multiplier}; // We trust the encoder that the data we are seeing // corresponds to a valid 64-bit signed integer. return sourcemeta::core::JSON{static_cast( diff --git a/src/runtime/decoder_string.cc b/src/runtime/decoder_string.cc index 838711ea9..76661deb5 100644 --- a/src/runtime/decoder_string.cc +++ b/src/runtime/decoder_string.cc @@ -56,12 +56,12 @@ auto Decoder::BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( const struct BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED &options) -> sourcemeta::core::JSON { assert(options.minimum <= options.maximum); - assert(is_byte(options.maximum - options.minimum)); + assert(sourcemeta::core::is_byte(options.maximum - options.minimum)); const std::uint8_t prefix{this->get_byte()}; const bool is_shared{prefix == 0}; const std::uint64_t length{(is_shared ? this->get_byte() : prefix) + options.minimum - 1}; - assert(is_within(length, options.minimum, options.maximum)); + assert(sourcemeta::core::is_within(length, options.minimum, options.maximum)); if (is_shared) { const std::uint64_t position{this->position()}; diff --git a/src/runtime/encoder_any.cc b/src/runtime/encoder_any.cc index b5fc3133d..43c907555 100644 --- a/src/runtime/encoder_any.cc +++ b/src/runtime/encoder_any.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include "unreachable.h" #include // std::find_if @@ -17,12 +18,12 @@ auto Encoder::BYTE_CHOICE_INDEX(const sourcemeta::core::JSON &document, const struct BYTE_CHOICE_INDEX &options) -> void { assert(!options.choices.empty()); - assert(is_byte(options.choices.size())); + assert(sourcemeta::core::is_byte(options.choices.size())); const auto iterator{std::ranges::find(options.choices, document)}; assert(iterator != std::cend(options.choices)); const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; - assert( - is_within(cursor, 0, static_cast(options.choices.size()))); + assert(sourcemeta::core::is_within( + cursor, 0, static_cast(options.choices.size()))); this->put_byte(static_cast(cursor)); } @@ -36,8 +37,8 @@ auto Encoder::LARGE_CHOICE_INDEX(const sourcemeta::core::JSON &document, })}; assert(iterator != std::cend(options.choices)); const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; - assert(is_within(cursor, static_cast(0), - options.choices.size() - 1)); + assert(sourcemeta::core::is_within(cursor, static_cast(0), + options.choices.size() - 1)); this->put_varint(static_cast(cursor)); } @@ -45,15 +46,15 @@ auto Encoder::TOP_LEVEL_BYTE_CHOICE_INDEX( const sourcemeta::core::JSON &document, const struct TOP_LEVEL_BYTE_CHOICE_INDEX &options) -> void { assert(options.choices.size() > 0); - assert(is_byte(options.choices.size())); + assert(sourcemeta::core::is_byte(options.choices.size())); const auto iterator{ std::ranges::find_if(options.choices, [&document](auto const &choice) { return choice == document; })}; assert(iterator != std::cend(options.choices)); const auto cursor{std::distance(std::cbegin(options.choices), iterator)}; - assert(is_within(cursor, 0, - static_cast(options.choices.size()) - 1)); + assert(sourcemeta::core::is_within( + cursor, 0, static_cast(options.choices.size()) - 1)); // This encoding encodes the first option of the enum as "no data" if (cursor > 0) { this->put_byte(static_cast(cursor - 1)); @@ -83,7 +84,7 @@ auto Encoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( static_cast(subtype << type_size)); } else if (document.is_real() && document.is_integral()) { const auto value{document.as_integer()}; - if (value >= 0 && is_byte(value)) { + if (value >= 0 && sourcemeta::core::is_byte(value)) { this->put_byte(TYPE_OTHER | SUBTYPE_POSITIVE_REAL_INTEGER_BYTE << type_size); this->put_byte(static_cast(value)); @@ -97,13 +98,14 @@ auto Encoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( } else if (document.is_integer()) { const std::int64_t value{document.to_integer()}; const bool is_positive{value >= 0}; - const std::uint64_t absolute{is_positive ? static_cast(value) - : abs(value) - 1}; - if (is_byte(absolute)) { + const std::uint64_t absolute{is_positive + ? static_cast(value) + : sourcemeta::core::abs(value) - 1}; + if (sourcemeta::core::is_byte(absolute)) { const std::uint8_t type{is_positive ? TYPE_POSITIVE_INTEGER_BYTE : TYPE_NEGATIVE_INTEGER_BYTE}; const std::uint8_t absolute_byte{static_cast(absolute)}; - if (absolute < uint_max<5>) { + if (absolute < sourcemeta::core::uint_max<5>) { this->put_byte( type | static_cast((absolute_byte + 1) << type_size)); } else { @@ -121,7 +123,7 @@ auto Encoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( const sourcemeta::core::JSON::String &value{document.to_string()}; const auto size{document.byte_size()}; const auto shared{this->cache_.find(value, Cache::Type::Standalone)}; - if (size < uint_max<5>) { + if (size < sourcemeta::core::uint_max<5>) { const std::uint8_t type{shared.has_value() ? TYPE_SHARED_STRING : TYPE_STRING}; this->put_byte( @@ -132,15 +134,18 @@ auto Encoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( this->cache_.record(value, this->position(), Cache::Type::Standalone); this->put_string_utf8(value, size); } - } else if (size >= uint_max<5> && - size < static_cast(uint_max<5>) * 2 && + } else if (size >= sourcemeta::core::uint_max<5> && + size < + static_cast(sourcemeta::core::uint_max<5>) * + 2 && !shared.has_value()) { this->put_byte(static_cast( - TYPE_LONG_STRING | ((size - uint_max<5>) << type_size))); + TYPE_LONG_STRING | + ((size - sourcemeta::core::uint_max<5>) << type_size))); this->put_string_utf8(value, size); } else if (size >= 2 << (SUBTYPE_LONG_STRING_BASE_EXPONENT_7 - 1) && !shared.has_value()) { - const std::uint8_t exponent{closest_smallest_exponent( + const std::uint8_t exponent{sourcemeta::core::closest_smallest_exponent( size, 2, SUBTYPE_LONG_STRING_BASE_EXPONENT_7, SUBTYPE_LONG_STRING_BASE_EXPONENT_10)}; this->put_byte( @@ -157,13 +162,14 @@ auto Encoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( // If we got this far, the string is at least a certain length return FLOOR_VARINT_PREFIX_UTF8_STRING_SHARED( - document, {static_cast(uint_max<5> * 2)}); + document, + {static_cast(sourcemeta::core::uint_max<5> * 2)}); } } else if (document.is_array()) { const auto size{document.size()}; - if (size >= uint_max<5>) { + if (size >= sourcemeta::core::uint_max<5>) { this->put_byte(TYPE_ARRAY); - this->put_varint(size - uint_max<5>); + this->put_varint(size - sourcemeta::core::uint_max<5>); } else { this->put_byte( static_cast(TYPE_ARRAY | ((size + 1) << type_size))); @@ -177,9 +183,9 @@ auto Encoder::ANY_PACKED_TYPE_TAG_BYTE_PREFIX( .prefix_encodings = {}}); } else if (document.is_object()) { const auto size{document.size()}; - if (size >= uint_max<5>) { + if (size >= sourcemeta::core::uint_max<5>) { this->put_byte(TYPE_OBJECT); - this->put_varint(size - uint_max<5>); + this->put_varint(size - sourcemeta::core::uint_max<5>); } else { this->put_byte( static_cast(TYPE_OBJECT | ((size + 1) << type_size))); diff --git a/src/runtime/encoder_array.cc b/src/runtime/encoder_array.cc index 4af491f3d..e9e3f0258 100644 --- a/src/runtime/encoder_array.cc +++ b/src/runtime/encoder_array.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include // assert #include // std::uint8_t #include // std::move @@ -27,8 +28,8 @@ auto Encoder::BOUNDED_8BITS_TYPED_ARRAY( const struct BOUNDED_8BITS_TYPED_ARRAY &options) -> void { assert(options.maximum >= options.minimum); const auto size{document.size()}; - assert(is_within(size, options.minimum, options.maximum)); - assert(is_byte(options.maximum - options.minimum)); + assert(sourcemeta::core::is_within(size, options.minimum, options.maximum)); + assert(sourcemeta::core::is_byte(options.maximum - options.minimum)); this->put_byte(static_cast(size - options.minimum)); this->FIXED_TYPED_ARRAY(document, {.size = size, diff --git a/src/runtime/encoder_integer.cc b/src/runtime/encoder_integer.cc index e7b4043de..c229f255c 100644 --- a/src/runtime/encoder_integer.cc +++ b/src/runtime/encoder_integer.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include // assert #include // std::uint8_t, std::int64_t, std::uint64_t @@ -11,16 +12,16 @@ auto Encoder::BOUNDED_MULTIPLE_8BITS_ENUM_FIXED( const struct BOUNDED_MULTIPLE_8BITS_ENUM_FIXED &options) -> void { assert(document.is_integer()); const std::int64_t value{document.to_integer()}; - assert(is_within(value, options.minimum, options.maximum)); + assert(sourcemeta::core::is_within(value, options.minimum, options.maximum)); assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); + assert(sourcemeta::core::abs(value) % options.multiplier == 0); const std::int64_t enum_minimum{ - divide_ceil(options.minimum, options.multiplier)}; + sourcemeta::core::divide_ceil(options.minimum, options.multiplier)}; #ifndef NDEBUG const std::int64_t enum_maximum{ - divide_floor(options.maximum, options.multiplier)}; + sourcemeta::core::divide_floor(options.maximum, options.multiplier)}; #endif - assert(is_byte(enum_maximum - enum_minimum)); + assert(sourcemeta::core::is_byte(enum_maximum - enum_minimum)); this->put_byte(static_cast( (value / static_cast(options.multiplier)) - enum_minimum)); } @@ -32,7 +33,7 @@ auto Encoder::FLOOR_MULTIPLE_ENUM_VARINT( const std::int64_t value{document.to_integer()}; assert(options.minimum <= value); assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); + assert(sourcemeta::core::abs(value) % options.multiplier == 0); if (options.multiplier == 1) { return this->put_varint( static_cast(value - options.minimum)); @@ -40,7 +41,7 @@ auto Encoder::FLOOR_MULTIPLE_ENUM_VARINT( return this->put_varint( (static_cast(value) / options.multiplier) - - static_cast(divide_ceil( + static_cast(sourcemeta::core::divide_ceil( options.minimum, static_cast(options.multiplier)))); } @@ -51,7 +52,7 @@ auto Encoder::ROOF_MULTIPLE_MIRROR_ENUM_VARINT( const std::int64_t value{document.to_integer()}; assert(value <= options.maximum); assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); + assert(sourcemeta::core::abs(value) % options.multiplier == 0); if (options.multiplier == 1) { return this->put_varint( static_cast(options.maximum - value)); @@ -59,7 +60,7 @@ auto Encoder::ROOF_MULTIPLE_MIRROR_ENUM_VARINT( return this->put_varint( static_cast( - divide_floor(options.maximum, options.multiplier)) - + sourcemeta::core::divide_floor(options.maximum, options.multiplier)) - (static_cast(value) / options.multiplier)); } @@ -69,7 +70,7 @@ auto Encoder::ARBITRARY_MULTIPLE_ZIGZAG_VARINT( assert(document.is_integer()); const std::int64_t value{document.to_integer()}; assert(options.multiplier > 0); - assert(abs(value) % options.multiplier == 0); + assert(sourcemeta::core::abs(value) % options.multiplier == 0); this->put_varint_zigzag(value / static_cast(options.multiplier)); } diff --git a/src/runtime/encoder_number.cc b/src/runtime/encoder_number.cc index 9c9f3f269..2812f6d49 100644 --- a/src/runtime/encoder_number.cc +++ b/src/runtime/encoder_number.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include // assert #include // std::uint64_t @@ -12,7 +13,7 @@ auto Encoder::DOUBLE_VARINT_TUPLE(const sourcemeta::core::JSON &document, const auto value{document.to_real()}; std::uint64_t point_position; const std::int64_t integral{ - real_digits(value, &point_position)}; + sourcemeta::core::real_digits(value, point_position)}; this->put_varint_zigzag(integral); this->put_varint(point_position); } diff --git a/src/runtime/encoder_string.cc b/src/runtime/encoder_string.cc index ea569d6d3..ffcf81c42 100644 --- a/src/runtime/encoder_string.cc +++ b/src/runtime/encoder_string.cc @@ -75,8 +75,8 @@ auto Encoder::BOUNDED_8BIT_PREFIX_UTF8_STRING_SHARED( const auto size{value.size()}; assert(document.byte_size() == size); assert(options.minimum <= options.maximum); - assert(is_byte(options.maximum - options.minimum + 1)); - assert(is_within(size, options.minimum, options.maximum)); + assert(sourcemeta::core::is_byte(options.maximum - options.minimum + 1)); + assert(sourcemeta::core::is_within(size, options.minimum, options.maximum)); const auto shared{this->cache_.find(value, Cache::Type::Standalone)}; // (1) Write 0x00 if shared, else do nothing diff --git a/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoding.h b/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoding.h index bcfe385db..f239e8243 100644 --- a/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoding.h +++ b/src/runtime/include/sourcemeta/jsonbinpack/runtime_encoding.h @@ -1,9 +1,8 @@ #ifndef SOURCEMETA_JSONBINPACK_RUNTIME_ENCODING_H_ #define SOURCEMETA_JSONBINPACK_RUNTIME_ENCODING_H_ -#include - #include +#include #include // std::int64_t, std::uint64_t #include // std::shared_ptr @@ -413,14 +412,16 @@ constexpr std::uint8_t TYPE_ARRAY = 0b00000100; constexpr std::uint8_t TYPE_POSITIVE_INTEGER_BYTE = 0b00000101; constexpr std::uint8_t TYPE_NEGATIVE_INTEGER_BYTE = 0b00000110; constexpr std::uint8_t TYPE_OTHER = 0b00000111; -static_assert(TYPE_SHARED_STRING <= uint_max); -static_assert(TYPE_STRING <= uint_max); -static_assert(TYPE_LONG_STRING <= uint_max); -static_assert(TYPE_OBJECT <= uint_max); -static_assert(TYPE_ARRAY <= uint_max); -static_assert(TYPE_POSITIVE_INTEGER_BYTE <= uint_max); -static_assert(TYPE_NEGATIVE_INTEGER_BYTE <= uint_max); -static_assert(TYPE_OTHER <= uint_max); +static_assert(TYPE_SHARED_STRING <= sourcemeta::core::uint_max); +static_assert(TYPE_STRING <= sourcemeta::core::uint_max); +static_assert(TYPE_LONG_STRING <= sourcemeta::core::uint_max); +static_assert(TYPE_OBJECT <= sourcemeta::core::uint_max); +static_assert(TYPE_ARRAY <= sourcemeta::core::uint_max); +static_assert(TYPE_POSITIVE_INTEGER_BYTE <= + sourcemeta::core::uint_max); +static_assert(TYPE_NEGATIVE_INTEGER_BYTE <= + sourcemeta::core::uint_max); +static_assert(TYPE_OTHER <= sourcemeta::core::uint_max); constexpr auto subtype_size = 5; constexpr std::uint8_t SUBTYPE_FALSE = 0b00000000; @@ -435,17 +436,24 @@ constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_8 = 0b00001000; constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_9 = 0b00001001; constexpr std::uint8_t SUBTYPE_LONG_STRING_BASE_EXPONENT_10 = 0b00001010; -static_assert(SUBTYPE_FALSE <= uint_max); -static_assert(SUBTYPE_TRUE <= uint_max); -static_assert(SUBTYPE_NULL <= uint_max); -static_assert(SUBTYPE_POSITIVE_INTEGER <= uint_max); -static_assert(SUBTYPE_NEGATIVE_INTEGER <= uint_max); -static_assert(SUBTYPE_NUMBER <= uint_max); -static_assert(SUBTYPE_POSITIVE_REAL_INTEGER_BYTE <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_7 <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_8 <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_9 <= uint_max); -static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_10 <= uint_max); +static_assert(SUBTYPE_FALSE <= sourcemeta::core::uint_max); +static_assert(SUBTYPE_TRUE <= sourcemeta::core::uint_max); +static_assert(SUBTYPE_NULL <= sourcemeta::core::uint_max); +static_assert(SUBTYPE_POSITIVE_INTEGER <= + sourcemeta::core::uint_max); +static_assert(SUBTYPE_NEGATIVE_INTEGER <= + sourcemeta::core::uint_max); +static_assert(SUBTYPE_NUMBER <= sourcemeta::core::uint_max); +static_assert(SUBTYPE_POSITIVE_REAL_INTEGER_BYTE <= + sourcemeta::core::uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_7 <= + sourcemeta::core::uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_8 <= + sourcemeta::core::uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_9 <= + sourcemeta::core::uint_max); +static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_10 <= + sourcemeta::core::uint_max); // Note that the binary values actually match the declared exponents static_assert(SUBTYPE_LONG_STRING_BASE_EXPONENT_7 == 7); diff --git a/src/runtime/input_stream.cc b/src/runtime/input_stream.cc index 57303dad3..76efff45f 100644 --- a/src/runtime/input_stream.cc +++ b/src/runtime/input_stream.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include "varint.h" #include // assert @@ -47,7 +48,7 @@ auto InputStream::get_varint() -> std::uint64_t { auto InputStream::get_varint_zigzag() -> std::int64_t { const std::uint64_t value = varint_decode(this->stream); - return zigzag_decode(value); + return sourcemeta::core::zigzag_decode(value); } auto InputStream::has_more_data() const noexcept -> bool { diff --git a/src/runtime/output_stream.cc b/src/runtime/output_stream.cc index 07b7530c3..8ab03a4a7 100644 --- a/src/runtime/output_stream.cc +++ b/src/runtime/output_stream.cc @@ -1,6 +1,7 @@ -#include #include +#include + #include "varint.h" #include // assert @@ -32,7 +33,7 @@ auto OutputStream::put_varint(const std::uint64_t value) -> void { } auto OutputStream::put_varint_zigzag(const std::int64_t value) -> void { - varint_encode(this->stream, zigzag_encode(value)); + varint_encode(this->stream, sourcemeta::core::zigzag_encode(value)); } auto OutputStream::put_string_utf8(const sourcemeta::core::JSON::String &string, diff --git a/test/numeric/CMakeLists.txt b/test/numeric/CMakeLists.txt deleted file mode 100644 index e5c14a7b0..000000000 --- a/test/numeric/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -sourcemeta_googletest(NAMESPACE sourcemeta PROJECT jsonbinpack NAME numeric - FOLDER "JSON BinPack/Numeric" - SOURCES - zigzag_test.cc - closest_smallest_exponent_test.cc - divide_ceil_test.cc - divide_floor_test.cc - uint_max_test.cc) - -target_link_libraries(sourcemeta_jsonbinpack_numeric_unit - PRIVATE sourcemeta::jsonbinpack::numeric) diff --git a/test/numeric/closest_smallest_exponent_test.cc b/test/numeric/closest_smallest_exponent_test.cc deleted file mode 100644 index 8c310a0dc..000000000 --- a/test/numeric/closest_smallest_exponent_test.cc +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include - -TEST(JSONBinPack_numeric, closest_smallest_exponent_2_2_1_2) { - EXPECT_EQ(sourcemeta::jsonbinpack::closest_smallest_exponent(2, 2, 1, 2), 1); -} - -TEST(JSONBinPack_numeric, closest_smallest_exponent_20_2_1_6) { - EXPECT_EQ(sourcemeta::jsonbinpack::closest_smallest_exponent(20, 2, 1, 6), 4); -} - -TEST(JSONBinPack_numeric, closest_smallest_exponent_20_3_1_6) { - EXPECT_EQ(sourcemeta::jsonbinpack::closest_smallest_exponent(20, 3, 1, 6), 2); -} diff --git a/test/numeric/divide_ceil_test.cc b/test/numeric/divide_ceil_test.cc deleted file mode 100644 index e1216c9c7..000000000 --- a/test/numeric/divide_ceil_test.cc +++ /dev/null @@ -1,79 +0,0 @@ -#include - -#include - -TEST(JSONBinPack_numeric, divide_ceil_simple_positive) { - const std::int64_t dividend{10}; - const std::uint64_t divisor{5}; - const std::int64_t result{2}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_simple_negative) { - const std::int64_t dividend{-10}; - const std::uint64_t divisor{5}; - const std::int64_t result{-2}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_small_positive) { - const std::int64_t dividend{11}; - const std::uint64_t divisor{5}; - const std::int64_t result{3}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_small_negative) { - const std::int64_t dividend{-11}; - const std::uint64_t divisor{5}; - const std::int64_t result{-2}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_positive_large_divisor) { - const std::int64_t dividend{11}; - const std::uint64_t divisor{20}; - const std::int64_t result{1}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_negative_large_divisor) { - const std::int64_t dividend{-11}; - const std::uint64_t divisor{20}; - const std::int64_t result{0}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_max_dividend_min_divisor) { - const std::int64_t dividend{std::numeric_limits::max()}; - const std::uint64_t divisor{1}; - const std::int64_t result{std::numeric_limits::max()}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_min_dividend_min_divisor) { - const std::int64_t dividend{std::numeric_limits::min()}; - const std::uint64_t divisor{1}; - const std::int64_t result{std::numeric_limits::min()}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_max_dividend_max_divisor) { - // This is 9223372036854775807 - const std::int64_t dividend{std::numeric_limits::max()}; - // This is 18446744073709551615 - const std::uint64_t divisor{std::numeric_limits::max()}; - // The division result is 0.5, so the ceil must be 1 - const std::int64_t result{1}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_ceil_min_dividend_max_divisor) { - // This is -9223372036854775808 - const std::int64_t dividend{std::numeric_limits::min()}; - // This is 18446744073709551615 - const std::uint64_t divisor{std::numeric_limits::max()}; - // The division result is -0.5, so the ceil must be 0 - const std::int64_t result{0}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_ceil(dividend, divisor), result); -} diff --git a/test/numeric/divide_floor_test.cc b/test/numeric/divide_floor_test.cc deleted file mode 100644 index 1827ac1cb..000000000 --- a/test/numeric/divide_floor_test.cc +++ /dev/null @@ -1,79 +0,0 @@ -#include - -#include - -TEST(JSONBinPack_numeric, divide_floor_simple_positive) { - const std::int64_t dividend{10}; - const std::uint64_t divisor{5}; - const std::int64_t result{2}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_simple_negative) { - const std::int64_t dividend{-10}; - const std::uint64_t divisor{5}; - const std::int64_t result{-2}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_small_positive) { - const std::int64_t dividend{11}; - const std::uint64_t divisor{5}; - const std::int64_t result{2}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_small_negative) { - const std::int64_t dividend{-11}; - const std::uint64_t divisor{5}; - const std::int64_t result{-3}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_positive_large_divisor) { - const std::int64_t dividend{11}; - const std::uint64_t divisor{20}; - const std::int64_t result{0}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_negative_large_divisor) { - const std::int64_t dividend{-11}; - const std::uint64_t divisor{20}; - const std::int64_t result{-1}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_max_dividend_min_divisor) { - const std::int64_t dividend{std::numeric_limits::max()}; - const std::uint64_t divisor{1}; - const std::int64_t result{std::numeric_limits::max()}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_min_dividend_min_divisor) { - const std::int64_t dividend{std::numeric_limits::min()}; - const std::uint64_t divisor{1}; - const std::int64_t result{std::numeric_limits::min()}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_max_dividend_max_divisor) { - // This is 9223372036854775807 - const std::int64_t dividend{std::numeric_limits::max()}; - // This is 18446744073709551615 - const std::uint64_t divisor{std::numeric_limits::max()}; - // The division result is 0.5, so the floor must be 0 - const std::int64_t result{0}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} - -TEST(JSONBinPack_numeric, divide_floor_min_dividend_max_divisor) { - // This is -9223372036854775808 - const std::int64_t dividend{std::numeric_limits::min()}; - // This is 18446744073709551615 - const std::uint64_t divisor{std::numeric_limits::max()}; - // The division result is -0.5, so the floor must be -1 - const std::int64_t result{-1}; - EXPECT_EQ(sourcemeta::jsonbinpack::divide_floor(dividend, divisor), result); -} diff --git a/test/numeric/uint_max_test.cc b/test/numeric/uint_max_test.cc deleted file mode 100644 index 189c8d4f8..000000000 --- a/test/numeric/uint_max_test.cc +++ /dev/null @@ -1,20 +0,0 @@ -#include - -#include - -TEST(JSONBinPack_numeric, uint_max_8) { - EXPECT_EQ(sourcemeta::jsonbinpack::uint_max<8>, - std::numeric_limits::max()); -} - -TEST(JSONBinPack_numeric, uint_max_5) { - EXPECT_EQ(sourcemeta::jsonbinpack::uint_max<5>, 31); -} - -TEST(JSONBinPack_numeric, uint_max_3) { - EXPECT_EQ(sourcemeta::jsonbinpack::uint_max<3>, 7); -} - -TEST(JSONBinPack_numeric, uint_max_2) { - EXPECT_EQ(sourcemeta::jsonbinpack::uint_max<2>, 3); -} diff --git a/test/numeric/zigzag_test.cc b/test/numeric/zigzag_test.cc deleted file mode 100644 index 73301c69f..000000000 --- a/test/numeric/zigzag_test.cc +++ /dev/null @@ -1,149 +0,0 @@ -#include - -#include - -TEST(JSONBinPack_numeric, encode_zigzag_int_0_0) { - const int value = 0; - const std::uint64_t expected = 0; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -TEST(JSONBinPack_numeric, encode_zigzag_int_minus_1_1) { - const int value = -1; - const std::uint64_t expected = 1; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -TEST(JSONBinPack_numeric, encode_zigzag_int_1_2) { - const int value = 1; - const std::uint64_t expected = 2; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -TEST(JSONBinPack_numeric, encode_zigzag_int_minus_2_3) { - const int value = -2; - const std::uint64_t expected = 3; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -TEST(JSONBinPack_numeric, encode_zigzag_int64_0_0) { - const std::int64_t value = 0; - const std::uint64_t expected = 0; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -TEST(JSONBinPack_numeric, encode_zigzag_int64_minus_1_1) { - const std::int64_t value = -1; - const std::uint64_t expected = 1; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -TEST(JSONBinPack_numeric, encode_zigzag_int64_1_2) { - const std::int64_t value = 1; - const std::uint64_t expected = 2; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -TEST(JSONBinPack_numeric, encode_zigzag_int64_minus_2_3) { - const std::int64_t value = -2; - const std::uint64_t expected = 3; - const std::uint64_t current = sourcemeta::jsonbinpack::zigzag_encode(value); - EXPECT_EQ(current, expected); -} - -// Max 64-bit signed integer value -TEST(JSONBinPack_numeric, encode_zigzag_int64_9223372036854775807) { - const std::int64_t value = 9223372036854775807; - EXPECT_EQ(value, std::numeric_limits::max()); - const std::uint64_t expected = 18446744073709551614U; - EXPECT_EQ(sourcemeta::jsonbinpack::zigzag_encode(value), expected); -} - -// Min 64-bit signed integer value -TEST(JSONBinPack_numeric, encode_zigzag_int64_minus_9223372036854775807) { - const std::int64_t value = -9223372036854775807; - EXPECT_EQ(value, std::numeric_limits::min() + 1); - const std::uint64_t expected = 18446744073709551613U; - EXPECT_EQ(sourcemeta::jsonbinpack::zigzag_encode(value), expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int_0_0) { - const unsigned int value = 0; - const std::int64_t expected = 0; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int_1_minus_1) { - const unsigned int value = 1; - const std::int64_t expected = -1; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int_2_1) { - const unsigned int value = 2; - const std::int64_t expected = 1; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int_3_minus_2) { - const unsigned int value = 3; - const std::int64_t expected = -2; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int64_0_0) { - const std::uint64_t value = 0; - const std::int64_t expected = 0; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int64_1_minus_1) { - const std::uint64_t value = 1; - const std::int64_t expected = -1; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int64_2_1) { - const std::uint64_t value = 2; - const std::int64_t expected = 1; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -TEST(JSONBinPack_numeric, decode_zigzag_int64_3_minus_2) { - const std::uint64_t value = 3; - const std::int64_t expected = -2; - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -// Max 64-bit signed integer value -TEST(JSONBinPack_numeric, decode_zigzag_int64_9223372036854775807) { - const std::uint64_t value = 18446744073709551614U; - const std::int64_t expected = 9223372036854775807; - EXPECT_EQ(expected, std::numeric_limits::max()); - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} - -// Min 64-bit signed integer value -TEST(JSONBinPack_numeric, decode_zigzag_int64_minus_9223372036854775807) { - const std::uint64_t value = 18446744073709551613U; - const std::int64_t expected = -9223372036854775807; - EXPECT_EQ(expected, std::numeric_limits::min() + 1); - const std::int64_t result = sourcemeta::jsonbinpack::zigzag_decode(value); - EXPECT_EQ(result, expected); -} diff --git a/test/runtime/encode_real_test.cc b/test/runtime/encode_real_test.cc index 6dd6130c5..0f7d0b243 100644 --- a/test/runtime/encode_real_test.cc +++ b/test/runtime/encode_real_test.cc @@ -1,12 +1,12 @@ #include -#include +#include TEST(JSONBinPack_Encoder, real_digits_1) { const double input{3.14}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 314); EXPECT_EQ(point_position, 2); } @@ -14,8 +14,8 @@ TEST(JSONBinPack_Encoder, real_digits_1) { TEST(JSONBinPack_Encoder, real_digits_2) { const double input{123.456}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 123456); EXPECT_EQ(point_position, 3); } @@ -23,8 +23,8 @@ TEST(JSONBinPack_Encoder, real_digits_2) { TEST(JSONBinPack_Encoder, real_digits_3) { const double input{-3.14}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, -314); EXPECT_EQ(point_position, 2); } @@ -32,8 +32,8 @@ TEST(JSONBinPack_Encoder, real_digits_3) { TEST(JSONBinPack_Encoder, real_digits_4) { const double input{0}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 0); EXPECT_EQ(point_position, 0); } @@ -41,8 +41,8 @@ TEST(JSONBinPack_Encoder, real_digits_4) { TEST(JSONBinPack_Encoder, real_digits_5) { const double input{-0}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 0); EXPECT_EQ(point_position, 0); } @@ -50,8 +50,8 @@ TEST(JSONBinPack_Encoder, real_digits_5) { TEST(JSONBinPack_Encoder, real_digits_6) { const double input{1}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 1); EXPECT_EQ(point_position, 0); } @@ -59,8 +59,8 @@ TEST(JSONBinPack_Encoder, real_digits_6) { TEST(JSONBinPack_Encoder, real_digits_7) { const double input{-1}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, -1); EXPECT_EQ(point_position, 0); } @@ -68,8 +68,8 @@ TEST(JSONBinPack_Encoder, real_digits_7) { TEST(JSONBinPack_Encoder, real_digits_8) { const double input{0.0001}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 1); EXPECT_EQ(point_position, 4); } @@ -77,8 +77,8 @@ TEST(JSONBinPack_Encoder, real_digits_8) { TEST(JSONBinPack_Encoder, real_digits_9) { const double input{0.000123}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 123); EXPECT_EQ(point_position, 6); } @@ -86,8 +86,8 @@ TEST(JSONBinPack_Encoder, real_digits_9) { TEST(JSONBinPack_Encoder, real_digits_10) { const double input{-0.000123}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, -123); EXPECT_EQ(point_position, 6); } @@ -95,8 +95,8 @@ TEST(JSONBinPack_Encoder, real_digits_10) { TEST(JSONBinPack_Encoder, integer_5) { const double input{5}; std::uint64_t point_position; - const std::int64_t result{sourcemeta::jsonbinpack::real_digits( - input, &point_position)}; + const std::int64_t result{ + sourcemeta::core::real_digits(input, point_position)}; EXPECT_EQ(result, 5); EXPECT_EQ(point_position, 0); } diff --git a/vendor/core/src/lang/numeric/CMakeLists.txt b/vendor/core/src/lang/numeric/CMakeLists.txt index 14ac639f5..dca302238 100644 --- a/vendor/core/src/lang/numeric/CMakeLists.txt +++ b/vendor/core/src/lang/numeric/CMakeLists.txt @@ -1,5 +1,5 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME numeric - PRIVATE_HEADERS error.h decimal.h parse.h uint128.h + PRIVATE_HEADERS error.h decimal.h parse.h uint128.h util.h zigzag.h SOURCES decimal.cc parse.cc big_coefficient.h) if(SOURCEMETA_CORE_INSTALL) diff --git a/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric.h b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric.h index b06addc62..b93f92a97 100644 --- a/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric.h +++ b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include // NOLINTEND(misc-include-cleaner) #endif diff --git a/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_util.h b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_util.h new file mode 100644 index 000000000..66eafc774 --- /dev/null +++ b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_util.h @@ -0,0 +1,299 @@ +#ifndef SOURCEMETA_CORE_NUMERIC_UTIL_H_ +#define SOURCEMETA_CORE_NUMERIC_UTIL_H_ + +#include + +#include // assert +#include // std::modf, std::floor, std::isfinite +#include // std::floating_point, std::integral, std::same_as +#include // std::uint8_t, std::int64_t, std::uint64_t +#include // std::numeric_limits + +namespace sourcemeta::core { + +/// @ingroup numeric +/// Convert a value to a Decimal, returning a copy if it is already one +template auto to_decimal(const T &value) -> Decimal { + if constexpr (std::same_as) { + return value; + } else { + return Decimal{value}; + } +} + +/// @ingroup numeric +/// A type that is either a Decimal or a built-in integral type +template +concept decimal_or_integral = std::same_as || std::integral; + +/// @ingroup numeric +/// True when at least one of the given types is a Decimal +template +concept any_decimal = (std::same_as || ...); + +/// @ingroup numeric +/// Check whether a value fits in an unsigned 8-bit byte +template constexpr auto is_byte(const T &value) -> bool { + if constexpr (std::same_as) { + return value.is_finite() && value.is_integral() && value >= Decimal{0} && + value <= Decimal{255}; + } else { + return value >= 0 && value <= std::numeric_limits::max(); + } +} + +/// @ingroup numeric +/// Compute the floor of the division of two integral values. Supports +/// primitive integers, Decimal operands, and mixed combinations. +template + requires decimal_or_integral && decimal_or_integral +auto divide_floor(const Dividend ÷nd, const Divisor &divisor) { + if constexpr (any_decimal) { + Decimal decimal_dividend{to_decimal(dividend)}; + const Decimal decimal_divisor{to_decimal(divisor)}; + assert(decimal_dividend.is_integral()); + assert(decimal_divisor.is_integral()); + assert(decimal_divisor > Decimal{0}); + if (decimal_divisor == Decimal{1}) { + return decimal_dividend; + } else if (decimal_dividend >= Decimal{0}) { + return decimal_dividend.divide_integer(decimal_divisor); + } else { + const Decimal absolute_dividend{ + decimal_dividend.is_signed() ? -decimal_dividend : decimal_dividend}; + const Decimal quotient{absolute_dividend.divide_integer(decimal_divisor)}; + if (absolute_dividend % decimal_divisor == Decimal{0}) { + return -quotient; + } else { + return -(quotient + Decimal{1}); + } + } + } else { + const auto signed_dividend{static_cast(dividend)}; + const auto unsigned_divisor{static_cast(divisor)}; + assert(unsigned_divisor > 0); + if (unsigned_divisor == 1) { + return signed_dividend; + } else if (signed_dividend >= 0) { + return static_cast( + static_cast(signed_dividend) / unsigned_divisor); + } else { + // Negate in unsigned to avoid UB for INT64_MIN + const std::uint64_t absolute_dividend{ + static_cast(0) - + static_cast(signed_dividend)}; + return -(static_cast( + 1 + ((absolute_dividend - 1) / unsigned_divisor))); + } + } +} + +/// @ingroup numeric +/// Compute the ceiling of the division of two integral values. Supports +/// primitive integers, Decimal operands, and mixed combinations. +template + requires decimal_or_integral && decimal_or_integral +auto divide_ceil(const Dividend ÷nd, const Divisor &divisor) { + if constexpr (any_decimal) { + Decimal decimal_dividend{to_decimal(dividend)}; + const Decimal decimal_divisor{to_decimal(divisor)}; + assert(decimal_dividend.is_integral()); + assert(decimal_divisor.is_integral()); + assert(decimal_divisor > Decimal{0}); + if (decimal_divisor == Decimal{1}) { + return decimal_dividend; + } else if (decimal_dividend >= Decimal{0}) { + const Decimal quotient{decimal_dividend.divide_integer(decimal_divisor)}; + if (decimal_dividend % decimal_divisor == Decimal{0}) { + return quotient; + } else { + return quotient + Decimal{1}; + } + } else { + const Decimal absolute_dividend{ + decimal_dividend.is_signed() ? -decimal_dividend : decimal_dividend}; + return -(absolute_dividend.divide_integer(decimal_divisor)); + } + } else { + const auto signed_dividend{static_cast(dividend)}; + const auto unsigned_divisor{static_cast(divisor)}; + assert(unsigned_divisor > 0); + if (unsigned_divisor == 1) { + return signed_dividend; + } else if (signed_dividend >= 0) { + if (static_cast(signed_dividend) + unsigned_divisor < + unsigned_divisor) { + return static_cast( + (static_cast(signed_dividend) / unsigned_divisor) + + 1 - (1 / unsigned_divisor)); + } else { + return static_cast( + (static_cast(signed_dividend) + unsigned_divisor - + 1) / + unsigned_divisor); + } + } else { + // Negate in unsigned to avoid UB for INT64_MIN + return -(static_cast( + (static_cast(0) - + static_cast(signed_dividend)) / + unsigned_divisor)); + } + } +} + +/// @ingroup numeric +/// Count the number of multiples of a given multiplier that fall within +/// the closed range [minimum, maximum]. Supports primitive integers, +/// Decimal operands, and mixed combinations. +template + requires decimal_or_integral && decimal_or_integral && + decimal_or_integral +auto count_multiples(const Minimum &minimum, const Maximum &maximum, + const Multiplier &multiplier) { + if constexpr (any_decimal) { + const Decimal decimal_minimum{to_decimal(minimum)}; + const Decimal decimal_maximum{to_decimal(maximum)}; + const Decimal decimal_multiplier{to_decimal(multiplier)}; + assert(decimal_minimum.is_integral()); + assert(decimal_maximum.is_integral()); + assert(decimal_multiplier.is_integral()); + assert(decimal_minimum <= decimal_maximum); + assert(decimal_multiplier > Decimal{0}); + return divide_floor(decimal_maximum, decimal_multiplier) - + divide_floor(decimal_minimum - Decimal{1}, decimal_multiplier); + } else { + const auto signed_minimum{static_cast(minimum)}; + const auto signed_maximum{static_cast(maximum)}; + const auto signed_multiplier{static_cast(multiplier)}; + assert(signed_minimum <= signed_maximum); + assert(signed_multiplier > 0); + return static_cast( + divide_floor(signed_maximum, + static_cast(signed_multiplier)) - + divide_floor(signed_minimum - 1, + static_cast(signed_multiplier))); + } +} + +/// @ingroup numeric +/// The maximum value representable by an unsigned integer of T bits +template +constexpr auto uint_max = [] { + static_assert(T > 0 && T < 64, "uint_max requires 0 < T < 64"); + return (std::uint64_t{1} << T) - 1; +}(); + +/// @ingroup numeric +/// Check whether a value falls within the closed range [lower, higher] +/// using signed 64-bit bounds +template +constexpr auto is_within(const T &value, const std::int64_t lower, + const std::int64_t higher) noexcept -> bool { + return value >= lower && value <= higher; +} + +/// @ingroup numeric +/// Check whether a value falls within the closed range [lower, higher] +/// using unsigned 64-bit bounds +template +constexpr auto is_within(const T &value, const std::uint64_t lower, + const std::uint64_t higher) noexcept -> bool { + if (value >= 0) { + return static_cast(value) >= lower && + static_cast(value) <= higher; + } else { + return false; + } +} + +/// @ingroup numeric +/// Check whether a Decimal value falls within the closed range +/// [lower, higher] +inline auto is_within(const Decimal &value, const Decimal &lower, + const Decimal &higher) -> bool { + return value >= lower && value <= higher; +} + +/// @ingroup numeric +/// Compute the absolute value of an integer or Decimal +template auto abs(const T &value) { + if constexpr (std::same_as) { + return value.is_signed() ? -value : value; + } else { + if (value < 0) { + // Negate in unsigned to avoid UB for INT64_MIN + return static_cast(0) - static_cast(value); + } else { + return static_cast(value); + } + } +} + +/// @ingroup numeric +/// Find the smallest exponent in [exponent_start, exponent_end] such that +/// base raised to the next power exceeds the given value, i.e. +/// `(base ^ exponent) <= value < (base ^ (exponent + 1))` +constexpr auto closest_smallest_exponent(const std::uint64_t value, + const std::uint8_t base, + const std::uint8_t exponent_start, + const std::uint8_t exponent_end) + -> std::uint8_t { + assert(exponent_start <= exponent_end); + std::uint64_t result{base}; + for (std::uint8_t exponent{1}; exponent < exponent_end; exponent++) { + const std::uint64_t next{result * base}; + if (next > value && exponent >= exponent_start) { + return exponent; + } else { + result = next; + } + } + + assert(result <= value); + return exponent_end; +} + +/// @ingroup numeric +/// Correct IEEE 754 floating-point imprecision by rounding values that +/// are extremely close to the nearest integer +template +constexpr auto correct_ieee754(const Real value) -> Real { + assert(std::isfinite(value)); + const Real threshold{static_cast(0.000000001)}; + const Real base{std::floor(value)}; + const Real next{base + 1}; + if (next - value <= threshold) { + return next; + } else if (value - base <= threshold) { + return base; + } else { + return value; + } +} + +/// @ingroup numeric +/// Extract the integer digits and decimal point position from a +/// floating-point value. The point_position output indicates how many +/// digits from the right the decimal point sits. +template +constexpr auto real_digits(Real value, std::uint64_t &point_position) + -> Integer { + assert(std::isfinite(value)); + Real integral_part; + std::uint64_t shifts{0}; + + Real fractional_part{std::modf(value, &integral_part)}; + while (fractional_part != 0.0) { + value *= 10; + shifts += 1; + fractional_part = std::modf(correct_ieee754(value), &integral_part); + } + + point_position = shifts; + return static_cast(std::floor(integral_part)); +} + +} // namespace sourcemeta::core + +#endif diff --git a/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_zigzag.h b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_zigzag.h new file mode 100644 index 000000000..9a59bfad6 --- /dev/null +++ b/vendor/core/src/lang/numeric/include/sourcemeta/core/numeric_zigzag.h @@ -0,0 +1,59 @@ +#ifndef SOURCEMETA_CORE_NUMERIC_ZIGZAG_H_ +#define SOURCEMETA_CORE_NUMERIC_ZIGZAG_H_ + +#include + +#include // assert +#include // std::same_as +#include // std::uint64_t, std::int64_t + +namespace sourcemeta::core { + +/// @ingroup numeric +/// Encode a signed integer into an unsigned integer using zigzag encoding, +/// where the sign information is stored in the least significant bit +template auto zigzag_encode(const T &value) { + if constexpr (std::same_as) { + assert(value.is_integral()); + if (value >= Decimal{0}) { + return value * Decimal{2}; + } + const Decimal absolute{value.is_signed() ? -value : value}; + return (absolute * Decimal{2}) - Decimal{1}; + } else { + const auto signed_value{static_cast(value)}; + if (signed_value >= 0) { + return static_cast(signed_value) * 2; + } + // Negate in unsigned to avoid UB for INT64_MIN + return (static_cast(0) - + static_cast(signed_value)) * + 2 - + 1; + } +} + +/// @ingroup numeric +/// Decode a zigzag-encoded unsigned integer back into a signed integer +template auto zigzag_decode(const T &value) { + if constexpr (std::same_as) { + assert(value.is_integral()); + assert(value >= Decimal{0}); + if (value % Decimal{2} == Decimal{0}) { + return value.divide_integer(Decimal{2}); + } + return -((value + Decimal{1}).divide_integer(Decimal{2})); + } else { + const auto unsigned_value{static_cast(value)}; + if (unsigned_value % 2 == 0) { + return static_cast(unsigned_value / 2); + } + // Use bitwise complement to avoid overflow for UINT64_MAX + // `~(x / 2)` == `-(x / 2 + 1)` in two's complement + return static_cast(~(unsigned_value / 2)); + } +} + +} // namespace sourcemeta::core + +#endif