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 <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-20 10:09:42 +02:00
1 parent aa9f5ff2c8
commit 2020807e85
6 files changed
+95 -103

No files matched your search

@@ -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<std::uint64_t, 17>& powers_of_ten_16() noexcept
/// the powers of ten up to 10^19 (all that fit in std::uint64_t)
inline const std::array<std::uint64_t, 20>& powers_of_ten() noexcept
{
static const std::array<std::uint64_t, 17> powers =
static const std::array<std::uint64_t, 20> 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<std::size_t>(count - 1)] && count <= 15);
JSON_ASSERT(digits >= powers_of_ten()[static_cast<std::size_t>(count - 1)] && count <= 15);
const int scale = 16 - count;
return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten_16()[static_cast<std::size_t>(scale)], exp - scale - 1, 0, false});
return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten()[static_cast<std::size_t>(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<std::size_t>(log2_bound)] ? 1 : 0);
const int count = log2_bound + (digits >= powers_of_ten()[static_cast<std::size_t>(log2_bound)] ? 1 : 0);
return write_short_decimal(first, digits, count, exp);
}
+1 -26
View File
@@ -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<std::uint64_t, 20> 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
+18 -15
View File
@@ -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<T>& 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<std::size_t>(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<T>* m_heap;
std::vector<T>& m_heap;
};
/// a string literal at w, without its terminating NUL (after room())
/// @return a pointer past it
template<std::size_t N>
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<int>(count), static_cast<int>(d.exponent))
+26 -8
View File
@@ -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<std::uint64_t, 17>& powers_of_ten_16() noexcept
/// the powers of ten up to 10^19 (all that fit in std::uint64_t)
inline const std::array<std::uint64_t, 20>& powers_of_ten() noexcept
{
static const std::array<std::uint64_t, 17> powers =
static const std::array<std::uint64_t, 20> 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<std::size_t>(count - 1)] && count <= 15);
JSON_ASSERT(digits >= powers_of_ten()[static_cast<std::size_t>(count - 1)] && count <= 15);
const int scale = 16 - count;
return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten_16()[static_cast<std::size_t>(scale)], exp - scale - 1, 0, false});
return write_shortest(first, zmij::shortest_decimal{digits * powers_of_ten()[static_cast<std::size_t>(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<std::size_t>(log2_bound)] ? 1 : 0);
const int count = log2_bound + (digits >= powers_of_ten()[static_cast<std::size_t>(log2_bound)] ? 1 : 0);
return write_short_decimal(first, digits, count, exp);
}
+19 -41
View File
@@ -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<std::uint64_t, 20> 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<T>& 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<std::size_t>(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<T>* m_heap;
std::vector<T>& m_heap;
};
/// a string literal at w, without its terminating NUL (after room())
/// @return a pointer past it
template<std::size_t N>
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<int>(count), static_cast<int>(d.exponent))
+5 -5
View File
@@ -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")