Use standard library helpers where available, with C++11 fallbacks (#5876)
* Use standard library helpers where available, with C++11 fallbacks Add detail helpers that call the standard library function when the library provides it (checked with the __cpp_lib_* feature-test macros, so no new ABI-tag macros) and a C++11 implementation otherwise: - detail::clamp (std::clamp): by value, so that static constexpr members need no out-of-line definition before C++17; used in json_view's dump() buffer sizing, where step() can now be a static constexpr member. - detail::bit_cast (std::bit_cast): replaces dtoa_impl::reinterpret_bits and the memcpy bit copies in the number parser, to_chars, the json_view serializer, and binary_writer::to_char_type. - detail::byteswap (std::byteswap): one implementation instead of the binary writer's own byte_swap() overloads and the bswap chain in to_chars' store_msb_first(). - count_leading_zeros/count_trailing_zeros: two 32-bit _BitScanReverse/ _BitScanForward calls for MSVC x86/ARM32 instead of the loop, and std::countl_zero/countr_zero for compilers without builtins. - conjunction, disjunction, negation, bool_constant, and uncvref_t map to their std counterparts in C++17/C++20. Builtins and intrinsics stay ahead of the <bit> functions, as MSVC's std::countl_zero checks for LZCNT at run time unless AVX2 is enabled. The generated code is unchanged with Clang (C++11/20/23, arm64 and x86-64) and GCC 16 for parsing, dumping, and the binary formats. The binary reader keeps its own byte_swap() and the writer its memcpy calls: the smaller code from detail::byteswap/std::bit_cast there made GCC 16 inline get_number()/write_number() differently, which slowed reading by 10-18 % and writing by about 1 %. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Name the target byte order once (JSON_LITTLE_ENDIAN_TARGET, JSON_BIG_ENDIAN_TARGET) to_chars.hpp and bit_ops.hpp repeated the long __BYTE_ORDER__/_MSC_VER conditions; macro_scope.hpp now defines them once (undefined again in macro_unscope.hpp). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Explain why the serializer growth bound is not a clamp Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
1 parent
7c26e99ac5
commit
3e031b06a5
13 files changed
+496
-202
No files matched your search
@@ -13,7 +13,7 @@
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::detail::dtoa_impl::reinterpret_bits;
|
||||
using nlohmann::detail::bit_cast;
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -44,7 +44,7 @@ float make_float(uint32_t sign_bit, uint32_t biased_exponent, uint32_t significa
|
||||
bits |= biased_exponent << 23;
|
||||
bits |= significand;
|
||||
|
||||
return reinterpret_bits<float>(bits);
|
||||
return bit_cast<float>(bits);
|
||||
}
|
||||
|
||||
// ldexp -- convert f * 2^e to IEEE single precision
|
||||
@@ -81,7 +81,7 @@ float make_float(uint64_t f, int e)
|
||||
: static_cast<uint64_t>(e + kExponentBias);
|
||||
|
||||
const uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
|
||||
return reinterpret_bits<float>(static_cast<uint32_t>(bits));
|
||||
return bit_cast<float>(static_cast<uint32_t>(bits));
|
||||
}
|
||||
|
||||
double make_double(uint64_t sign_bit, uint64_t biased_exponent, uint64_t significand)
|
||||
@@ -96,7 +96,7 @@ double make_double(uint64_t sign_bit, uint64_t biased_exponent, uint64_t signifi
|
||||
bits |= biased_exponent << 52;
|
||||
bits |= significand;
|
||||
|
||||
return reinterpret_bits<double>(bits);
|
||||
return bit_cast<double>(bits);
|
||||
}
|
||||
|
||||
// ldexp -- convert f * 2^e to IEEE double precision
|
||||
@@ -133,7 +133,7 @@ double make_double(uint64_t f, int e)
|
||||
: static_cast<uint64_t>(e + kExponentBias);
|
||||
|
||||
const uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize);
|
||||
return reinterpret_bits<double>(bits);
|
||||
return bit_cast<double>(bits);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -652,7 +652,7 @@ bool reads_back(const std::string& digits, int e, double v)
|
||||
{
|
||||
const std::string text = digits + "e" + std::to_string(e);
|
||||
// (compared bit for bit: v is positive and finite, and -Wfloat-equal)
|
||||
return reinterpret_bits<std::uint64_t>(parse_double(text)) == reinterpret_bits<std::uint64_t>(v);
|
||||
return bit_cast<std::uint64_t>(parse_double(text)) == bit_cast<std::uint64_t>(v);
|
||||
}
|
||||
|
||||
/// Check the representation of a positive finite double: it reads back as
|
||||
@@ -665,7 +665,7 @@ void check_shortest(double v)
|
||||
CAPTURE(text)
|
||||
CHECK(parse_double(text) == v);
|
||||
// the layout is that of format_buffer() for the digits of Zmij
|
||||
const auto sd = nlohmann::detail::zmij::to_shortest(reinterpret_bits<std::uint64_t>(v));
|
||||
const auto sd = nlohmann::detail::zmij::to_shortest(bit_cast<std::uint64_t>(v));
|
||||
const std::uint64_t significand = sd.has_digit ? (sd.integral * 10) + sd.digit : sd.integral;
|
||||
int exponent = sd.has_digit ? sd.exponent : sd.exponent + 1;
|
||||
std::string significand_digits = std::to_string(significand);
|
||||
@@ -779,7 +779,7 @@ TEST_CASE("shortest digits of doubles")
|
||||
for (int i = 0; i < 100000; ++i)
|
||||
{
|
||||
const std::uint64_t bits = rng() & 0x7FFFFFFFFFFFFFFFu;
|
||||
const auto v = reinterpret_bits<double>(bits);
|
||||
const auto v = bit_cast<double>(bits);
|
||||
if (std::isfinite(v) && bits != 0)
|
||||
{
|
||||
check_shortest(v);
|
||||
|
||||
Reference in new issue
Block a user