From 0905602724c1b1c12a83f13d497fcbe0cf996c46 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Thu, 15 Oct 2026 13:03:41 +0200 Subject: [PATCH] Test parse_float_native() at the guard page parse_float_eisel_lemire() was removed by #5738, so the test no longer compiled. The lexer converts floats from its token buffer, so the JSON section never places a float token at the page boundary; the section now calls parse_float_native() for float and double on every complete number token directly and compares the result bit for bit with converting the same token from a std::string. Signed-off-by: Niels Lohmann --- tests/src/unit-guard-page.cpp | 43 ++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/tests/src/unit-guard-page.cpp b/tests/src/unit-guard-page.cpp index b99da7c76..5dc0189e8 100644 --- a/tests/src/unit-guard-page.cpp +++ b/tests/src/unit-guard-page.cpp @@ -309,6 +309,23 @@ void check_binary(guarded_buffer& guard, const std::vector& bytes, } } } + +// parse_float_native() at both ends of the guarded page, compared bit for bit +// with converting the same token from a std::string +template +void check_float_native(guarded_buffer& guard, const std::string& token) +{ + const std::size_t decimal_point = token.find('.'); + const std::size_t exponent = token.find_first_of("eE"); + const std::size_t mantissa_end = exponent == std::string::npos ? token.size() : exponent; + const FloatType expected = nlohmann::detail::parse_float_native(token.data(), token.data() + token.size(), decimal_point, mantissa_end); + for (int placement = 0; placement < 2; ++placement) + { + const char* const first = reinterpret_cast(placement == 0 ? guard.at_end(token) : guard.at_start(token)); + const FloatType value = nlohmann::detail::parse_float_native(first, first + token.size(), decimal_point, mantissa_end); + CHECK(std::memcmp(&value, &expected, sizeof(value)) == 0); + } +} } // namespace TEST_CASE("guard page") @@ -371,22 +388,32 @@ TEST_CASE("guard page") } } - SECTION("Eisel-Lemire") + SECTION("float conversion") { - const std::vector numbers = all_prefixes( + // the lexer converts floats from its token buffer, so the JSON section + // below never places a float token at the page boundary; this calls the + // conversion directly: the 8-digit loops of parse_float_significand() + // and digit_comparison() read up to the end of the token + const std::vector tokens = all_prefixes( { "1234567812345678123456781234567812345678", "-1.2345678901234567890123456789e-123", "0.00000000000000000000000000000000000001", "98765432.10987654", + "123456789012345678901234567890e-30", + // halfway between 1 and the next double, then just above it + "1.00000000000000011102230246251565404236316680908203125", + "1.000000000000000111022302462515654042363166809082031250000000001", }); - for (const auto& number : numbers) + for (const auto& token : tokens) { - double out = 0; - const char* const end = reinterpret_cast(guard.at_end(number)); - static_cast(nlohmann::detail::parse_float_eisel_lemire(end, end + number.size(), out)); - const char* const start = reinterpret_cast(guard.at_start(number)); - static_cast(nlohmann::detail::parse_float_eisel_lemire(start, start + number.size(), out)); + // only complete number tokens: the conversion expects validated input + if (!token.empty() && json::accept(token)) + { + CAPTURE(token) + check_float_native(guard, token); + check_float_native(guard, token); + } } }