From 2020807e85bcb85fef008781ad9451ade622ed78 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Tue, 20 Oct 2026 10:09:42 +0200 Subject: [PATCH] Address review: one powers-of-ten table, literal sizes from the type - dtoa_impl::powers_of_ten() (10^0 to 10^19, one entry per line) replaces powers_of_ten_16() and the table of int_pow10() - put_literal() takes the length of a string literal from its type - small_stack holds the heap vector by reference - the growth step of the output buffer is written in hex Signed-off-by: Niels Lohmann --- .../nlohmann/detail/conversions/to_chars.hpp | 34 ++++++++--- include/nlohmann/detail/view/scan.hpp | 27 +-------- include/nlohmann/detail/view/serializer.hpp | 33 +++++----- single_include/nlohmann/json.hpp | 34 ++++++++--- single_include/nlohmann/json_view.hpp | 60 ++++++------------- tests/src/unit-to_chars.cpp | 10 ++-- 6 files changed, 95 insertions(+), 103 deletions(-) diff --git a/include/nlohmann/detail/conversions/to_chars.hpp b/include/nlohmann/detail/conversions/to_chars.hpp index 82b946a5a..48f86cf91 100644 --- a/include/nlohmann/detail/conversions/to_chars.hpp +++ b/include/nlohmann/detail/conversions/to_chars.hpp @@ -1342,14 +1342,32 @@ inline char* write_shortest(char* first, const zmij::shortest_decimal d) noexcep return end + (three ? 5 : 4); } -/// the powers of ten up to 10^16 -inline const std::array& powers_of_ten_16() noexcept +/// the powers of ten up to 10^19 (all that fit in std::uint64_t) +inline const std::array& powers_of_ten() noexcept { - static const std::array powers = + static const std::array powers = { { - 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u, 10000000000u, - 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, 10000000000000000u + 1u, + 10u, + 100u, + 1000u, + 10000u, + 100000u, + 1000000u, + 10000000u, + 100000000u, + 1000000000u, + 10000000000u, + 100000000000u, + 1000000000000u, + 10000000000000u, + 100000000000000u, + 1000000000000000u, + 10000000000000000u, + 100000000000000000u, + 1000000000000000000u, + 10000000000000000000u } }; return powers; @@ -1367,9 +1385,9 @@ JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* write_short_decimal(char* first, std::uint64_t digits, int count, int exp) noexcept { - JSON_ASSERT(digits >= powers_of_ten_16()[static_cast(count - 1)] && count <= 15); + JSON_ASSERT(digits >= powers_of_ten()[static_cast(count - 1)] && count <= 15); const int scale = 16 - count; - return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten_16()[static_cast(scale)], exp - scale - 1, 0, false}); + return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten()[static_cast(scale)], exp - scale - 1, 0, false}); } /// as write_short_decimal(), counting the digits (not 0, less than 10^15) @@ -1380,7 +1398,7 @@ inline char* write_short_decimal(char* first, std::uint64_t digits, int exp) noe JSON_ASSERT(digits != 0 && digits < 1000000000000000u); // floor(log10(2^bits)) + 1 digits, or one less const int log2_bound = ((64 - count_leading_zeros(digits)) * 1233) >> 12; - const int count = log2_bound + (digits >= powers_of_ten_16()[static_cast(log2_bound)] ? 1 : 0); + const int count = log2_bound + (digits >= powers_of_ten()[static_cast(log2_bound)] ? 1 : 0); return write_short_decimal(first, digits, count, exp); } diff --git a/include/nlohmann/detail/view/scan.hpp b/include/nlohmann/detail/view/scan.hpp index c6139c8e5..e230afbdb 100644 --- a/include/nlohmann/detail/view/scan.hpp +++ b/include/nlohmann/detail/view/scan.hpp @@ -175,32 +175,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits(const unsigned char /// powers of ten up to 10^19 as integers inline std::uint64_t int_pow10(unsigned k) noexcept { - static const std::array table = - { - { - 1u, - 10u, - 100u, - 1000u, - 10000u, - 100000u, - 1000000u, - 10000000u, - 100000000u, - 1000000000u, - 10000000000u, - 100000000000u, - 1000000000000u, - 10000000000000u, - 100000000000000u, - 1000000000000000u, - 10000000000000000u, - 100000000000000000u, - 1000000000000000000u, - 10000000000000000000u - } - }; - return table[k]; + return ::nlohmann::detail::dtoa_impl::powers_of_ten()[k]; } /// value of 0 < k < 8 digits at p in one step if [p, p + 8) lies below diff --git a/include/nlohmann/detail/view/serializer.hpp b/include/nlohmann/detail/view/serializer.hpp index 340207309..b52f21987 100644 --- a/include/nlohmann/detail/view/serializer.hpp +++ b/include/nlohmann/detail/view/serializer.hpp @@ -109,7 +109,7 @@ class output_buffer /// which a static constexpr member does not have before C++17) static constexpr std::size_t step() noexcept { - return 65536; + return 0x10000; } static StringType& sized(StringType& out, std::size_t estimate) @@ -181,7 +181,7 @@ class small_stack { public: small_stack(T* buffer, std::size_t capacity, std::vector& heap) noexcept - : m_begin(buffer), m_top(buffer), m_end(buffer + capacity), m_heap(&heap) + : m_begin(buffer), m_top(buffer), m_end(buffer + capacity), m_heap(heap) {} small_stack(const small_stack&) = delete; small_stack(small_stack&&) = delete; @@ -195,7 +195,7 @@ class small_stack { const std::size_t used = size(); const std::size_t capacity = 2 * static_cast(m_end - m_begin); - m_begin = grow(*m_heap, m_begin, used, capacity); + m_begin = grow(m_heap, m_begin, used, capacity); m_top = m_begin + used; m_end = m_begin + capacity; } @@ -235,9 +235,18 @@ class small_stack T* m_begin; T* m_top; T* m_end; - std::vector* m_heap; + std::vector& m_heap; }; +/// a string literal at w, without its terminating NUL (after room()) +/// @return a pointer past it +template +NLOHMANN_VIEW_ALWAYS_INLINE char* put_literal(char* w, const char (&s)[N]) noexcept // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + std::memcpy(w, s, N - 1); + return w + N - 1; +} + /// how the view's dump() writes a value struct dump_style { @@ -461,12 +470,6 @@ class view_serializer } w += n; }; - // a literal of n bytes (after room(n)) - const auto literal = [&](const char* text, std::size_t n) - { - std::memcpy(w, text, n); - w += n; - }; // a string that is not a plain string of the source (out of line, so // that the cursor stays in a register here) const auto escaped = [&](const node & n) @@ -552,11 +555,11 @@ class view_serializer room(8); if ((n->flags & node_flags::is_true) != 0) { - literal("true", 4); + w = put_literal(w, "true"); } else { - literal("false", 5); + w = put_literal(w, "false"); } break; case value_t::object: @@ -566,7 +569,7 @@ class view_serializer room(8); if (n->len == 0) { - literal(object ? "{}" : "[]", 2); + w = object ? put_literal(w, "{}") : put_literal(w, "[]"); } else { @@ -586,7 +589,7 @@ class view_serializer } case value_t::null: room(8); - literal("null", 4); + w = put_literal(w, "null"); break; case value_t::binary: // LCOV_EXCL_LINE (not in a document) case value_t::discarded: // LCOV_EXCL_LINE @@ -766,7 +769,7 @@ class view_serializer // check also keeps an image that was only checked for bounds, // whose token may not be made of digits, from the counted // overload) - const auto& powers = ::nlohmann::detail::dtoa_impl::powers_of_ten_16(); + const auto& powers = ::nlohmann::detail::dtoa_impl::powers_of_ten(); const unsigned count = int_digits + frac_digits; return count - 1u < 15u && d.w >= powers[count - 1u] && d.w < powers[count] ? ::nlohmann::detail::dtoa_impl::write_short_decimal(w, d.w, static_cast(count), static_cast(d.exponent)) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 81bf6403a..0df0fa83d 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -28382,14 +28382,32 @@ inline char* write_shortest(char* first, const zmij::shortest_decimal d) noexcep return end + (three ? 5 : 4); } -/// the powers of ten up to 10^16 -inline const std::array& powers_of_ten_16() noexcept +/// the powers of ten up to 10^19 (all that fit in std::uint64_t) +inline const std::array& powers_of_ten() noexcept { - static const std::array powers = + static const std::array powers = { { - 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u, 10000000000u, - 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, 10000000000000000u + 1u, + 10u, + 100u, + 1000u, + 10000u, + 100000u, + 1000000u, + 10000000u, + 100000000u, + 1000000000u, + 10000000000u, + 100000000000u, + 1000000000000u, + 10000000000000u, + 100000000000000u, + 1000000000000000u, + 10000000000000000u, + 100000000000000000u, + 1000000000000000000u, + 10000000000000000000u } }; return powers; @@ -28407,9 +28425,9 @@ JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_RETURNS_NON_NULL inline char* write_short_decimal(char* first, std::uint64_t digits, int count, int exp) noexcept { - JSON_ASSERT(digits >= powers_of_ten_16()[static_cast(count - 1)] && count <= 15); + JSON_ASSERT(digits >= powers_of_ten()[static_cast(count - 1)] && count <= 15); const int scale = 16 - count; - return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten_16()[static_cast(scale)], exp - scale - 1, 0, false}); + return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten()[static_cast(scale)], exp - scale - 1, 0, false}); } /// as write_short_decimal(), counting the digits (not 0, less than 10^15) @@ -28420,7 +28438,7 @@ inline char* write_short_decimal(char* first, std::uint64_t digits, int exp) noe JSON_ASSERT(digits != 0 && digits < 1000000000000000u); // floor(log10(2^bits)) + 1 digits, or one less const int log2_bound = ((64 - count_leading_zeros(digits)) * 1233) >> 12; - const int count = log2_bound + (digits >= powers_of_ten_16()[static_cast(log2_bound)] ? 1 : 0); + const int count = log2_bound + (digits >= powers_of_ten()[static_cast(log2_bound)] ? 1 : 0); return write_short_decimal(first, digits, count, exp); } diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index 3342d7e6e..fadc775db 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -1136,32 +1136,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits(const unsigned char /// powers of ten up to 10^19 as integers inline std::uint64_t int_pow10(unsigned k) noexcept { - static const std::array table = - { - { - 1u, - 10u, - 100u, - 1000u, - 10000u, - 100000u, - 1000000u, - 10000000u, - 100000000u, - 1000000000u, - 10000000000u, - 100000000000u, - 1000000000000u, - 10000000000000u, - 100000000000000u, - 1000000000000000u, - 10000000000000000u, - 100000000000000000u, - 1000000000000000000u, - 10000000000000000000u - } - }; - return table[k]; + return ::nlohmann::detail::dtoa_impl::powers_of_ten()[k]; } /// value of 0 < k < 8 digits at p in one step if [p, p + 8) lies below @@ -6203,7 +6178,7 @@ class output_buffer /// which a static constexpr member does not have before C++17) static constexpr std::size_t step() noexcept { - return 65536; + return 0x10000; } static StringType& sized(StringType& out, std::size_t estimate) @@ -6275,7 +6250,7 @@ class small_stack { public: small_stack(T* buffer, std::size_t capacity, std::vector& heap) noexcept - : m_begin(buffer), m_top(buffer), m_end(buffer + capacity), m_heap(&heap) + : m_begin(buffer), m_top(buffer), m_end(buffer + capacity), m_heap(heap) {} small_stack(const small_stack&) = delete; small_stack(small_stack&&) = delete; @@ -6289,7 +6264,7 @@ class small_stack { const std::size_t used = size(); const std::size_t capacity = 2 * static_cast(m_end - m_begin); - m_begin = grow(*m_heap, m_begin, used, capacity); + m_begin = grow(m_heap, m_begin, used, capacity); m_top = m_begin + used; m_end = m_begin + capacity; } @@ -6329,9 +6304,18 @@ class small_stack T* m_begin; T* m_top; T* m_end; - std::vector* m_heap; + std::vector& m_heap; }; +/// a string literal at w, without its terminating NUL (after room()) +/// @return a pointer past it +template +NLOHMANN_VIEW_ALWAYS_INLINE char* put_literal(char* w, const char (&s)[N]) noexcept // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + std::memcpy(w, s, N - 1); + return w + N - 1; +} + /// how the view's dump() writes a value struct dump_style { @@ -6555,12 +6539,6 @@ class view_serializer } w += n; }; - // a literal of n bytes (after room(n)) - const auto literal = [&](const char* text, std::size_t n) - { - std::memcpy(w, text, n); - w += n; - }; // a string that is not a plain string of the source (out of line, so // that the cursor stays in a register here) const auto escaped = [&](const node & n) @@ -6646,11 +6624,11 @@ class view_serializer room(8); if ((n->flags & node_flags::is_true) != 0) { - literal("true", 4); + w = put_literal(w, "true"); } else { - literal("false", 5); + w = put_literal(w, "false"); } break; case value_t::object: @@ -6660,7 +6638,7 @@ class view_serializer room(8); if (n->len == 0) { - literal(object ? "{}" : "[]", 2); + w = object ? put_literal(w, "{}") : put_literal(w, "[]"); } else { @@ -6680,7 +6658,7 @@ class view_serializer } case value_t::null: room(8); - literal("null", 4); + w = put_literal(w, "null"); break; case value_t::binary: // LCOV_EXCL_LINE (not in a document) case value_t::discarded: // LCOV_EXCL_LINE @@ -6860,7 +6838,7 @@ class view_serializer // check also keeps an image that was only checked for bounds, // whose token may not be made of digits, from the counted // overload) - const auto& powers = ::nlohmann::detail::dtoa_impl::powers_of_ten_16(); + const auto& powers = ::nlohmann::detail::dtoa_impl::powers_of_ten(); const unsigned count = int_digits + frac_digits; return count - 1u < 15u && d.w >= powers[count - 1u] && d.w < powers[count] ? ::nlohmann::detail::dtoa_impl::write_short_decimal(w, d.w, static_cast(count), static_cast(d.exponent)) diff --git a/tests/src/unit-to_chars.cpp b/tests/src/unit-to_chars.cpp index b0cdea71e..fa24477e0 100644 --- a/tests/src/unit-to_chars.cpp +++ b/tests/src/unit-to_chars.cpp @@ -874,13 +874,13 @@ TEST_CASE("short decimals") SECTION("powers of ten") { - const auto& powers = nlohmann::detail::dtoa_impl::powers_of_ten_16(); - std::uint64_t power = 1; - for (const std::uint64_t p : powers) + const auto& powers = nlohmann::detail::dtoa_impl::powers_of_ten(); + CHECK(powers.front() == 1u); + for (std::size_t i = 1; i < powers.size(); ++i) { - CHECK(p == power); - power *= 10; + CHECK(powers[i] == powers[i - 1] * 10u); } + CHECK(powers.back() == 10000000000000000000u); } SECTION("examples")