mirror of
https://github.com/nlohmann/json.git
synced 2026-07-25 11:54:55 +00:00
Encode big-endian numbers with a byte swap instead of std::reverse
write_number() reordered multi-byte numbers for the big-endian formats (CBOR/MessagePack/UBJSON) with std::reverse over the byte array. GCC lowered only some sizes to a bswap; clang kept a scalar byte shuffle (0 bswap instructions in the CBOR number path). Replace the reverse with size-dispatched __builtin_bswap16/32/64 helpers (portable shift fallback for other compilers; std::reverse retained for exotic sizes such as a long double number_float_t). Codegen: the CBOR number path now emits bswap on both compilers (gcc 2 -> 16, clang 0 -> 4). Output is byte-for-byte identical to the previous implementation across the binary differential corpus. Throughput (isolated vs the std::reverse version, best of 9): CBOR int64 array gcc +7% clang +10% CBOR uint16 array gcc +27% clang flat Modest but consistent on number-dense encodings; negligible on string/blob-heavy output, as expected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAYM1qhSA2FDaDcGfPW3fG Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7374730aed
commit
ac6b505923
@@ -1768,6 +1768,71 @@ class binary_writer
|
||||
On the other hand, BSON and BJData use little endian and should reorder
|
||||
on big endian systems.
|
||||
*/
|
||||
// single-instruction byte swaps (compilers lower these to bswap/rev/movbe);
|
||||
// used to emit big-endian numbers without a per-byte std::reverse loop
|
||||
static std::uint16_t byte_swap(std::uint16_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap16(x);
|
||||
#else
|
||||
return static_cast<std::uint16_t>((x >> 8) | (x << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::uint32_t byte_swap(std::uint32_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap32(x);
|
||||
#else
|
||||
return ((x & 0x000000FFu) << 24) | ((x & 0x0000FF00u) << 8)
|
||||
| ((x & 0x00FF0000u) >> 8) | ((x & 0xFF000000u) >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::uint64_t byte_swap(std::uint64_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(x);
|
||||
#else
|
||||
x = ((x & 0x00000000FFFFFFFFull) << 32) | ((x & 0xFFFFFFFF00000000ull) >> 32);
|
||||
x = ((x & 0x0000FFFF0000FFFFull) << 16) | ((x & 0xFFFF0000FFFF0000ull) >> 16);
|
||||
x = ((x & 0x00FF00FF00FF00FFull) << 8) | ((x & 0xFF00FF00FF00FF00ull) >> 8);
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
// reverse the bytes of a fixed-size buffer; a single byte_swap() for the
|
||||
// common 2/4/8-byte number payloads, std::reverse for any other size
|
||||
static void reverse_bytes(std::array<CharType, 2>& a) noexcept
|
||||
{
|
||||
std::uint16_t v{};
|
||||
std::memcpy(&v, a.data(), sizeof(v));
|
||||
v = byte_swap(v);
|
||||
std::memcpy(a.data(), &v, sizeof(v));
|
||||
}
|
||||
|
||||
static void reverse_bytes(std::array<CharType, 4>& a) noexcept
|
||||
{
|
||||
std::uint32_t v{};
|
||||
std::memcpy(&v, a.data(), sizeof(v));
|
||||
v = byte_swap(v);
|
||||
std::memcpy(a.data(), &v, sizeof(v));
|
||||
}
|
||||
|
||||
static void reverse_bytes(std::array<CharType, 8>& a) noexcept
|
||||
{
|
||||
std::uint64_t v{};
|
||||
std::memcpy(&v, a.data(), sizeof(v));
|
||||
v = byte_swap(v);
|
||||
std::memcpy(a.data(), &v, sizeof(v));
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
static void reverse_bytes(std::array<CharType, N>& a) noexcept
|
||||
{
|
||||
std::reverse(a.begin(), a.end());
|
||||
}
|
||||
|
||||
template<typename NumberType>
|
||||
void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
|
||||
{
|
||||
@@ -1779,7 +1844,7 @@ class binary_writer
|
||||
if (is_little_endian != OutputIsLittleEndian)
|
||||
{
|
||||
// reverse byte order prior to conversion if necessary
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
reverse_bytes(vec);
|
||||
}
|
||||
|
||||
oa.write_characters(vec.data(), sizeof(NumberType));
|
||||
|
||||
@@ -18582,6 +18582,71 @@ class binary_writer
|
||||
On the other hand, BSON and BJData use little endian and should reorder
|
||||
on big endian systems.
|
||||
*/
|
||||
// single-instruction byte swaps (compilers lower these to bswap/rev/movbe);
|
||||
// used to emit big-endian numbers without a per-byte std::reverse loop
|
||||
static std::uint16_t byte_swap(std::uint16_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap16(x);
|
||||
#else
|
||||
return static_cast<std::uint16_t>((x >> 8) | (x << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::uint32_t byte_swap(std::uint32_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap32(x);
|
||||
#else
|
||||
return ((x & 0x000000FFu) << 24) | ((x & 0x0000FF00u) << 8)
|
||||
| ((x & 0x00FF0000u) >> 8) | ((x & 0xFF000000u) >> 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::uint64_t byte_swap(std::uint64_t x) noexcept
|
||||
{
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(x);
|
||||
#else
|
||||
x = ((x & 0x00000000FFFFFFFFull) << 32) | ((x & 0xFFFFFFFF00000000ull) >> 32);
|
||||
x = ((x & 0x0000FFFF0000FFFFull) << 16) | ((x & 0xFFFF0000FFFF0000ull) >> 16);
|
||||
x = ((x & 0x00FF00FF00FF00FFull) << 8) | ((x & 0xFF00FF00FF00FF00ull) >> 8);
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
|
||||
// reverse the bytes of a fixed-size buffer; a single byte_swap() for the
|
||||
// common 2/4/8-byte number payloads, std::reverse for any other size
|
||||
static void reverse_bytes(std::array<CharType, 2>& a) noexcept
|
||||
{
|
||||
std::uint16_t v{};
|
||||
std::memcpy(&v, a.data(), sizeof(v));
|
||||
v = byte_swap(v);
|
||||
std::memcpy(a.data(), &v, sizeof(v));
|
||||
}
|
||||
|
||||
static void reverse_bytes(std::array<CharType, 4>& a) noexcept
|
||||
{
|
||||
std::uint32_t v{};
|
||||
std::memcpy(&v, a.data(), sizeof(v));
|
||||
v = byte_swap(v);
|
||||
std::memcpy(a.data(), &v, sizeof(v));
|
||||
}
|
||||
|
||||
static void reverse_bytes(std::array<CharType, 8>& a) noexcept
|
||||
{
|
||||
std::uint64_t v{};
|
||||
std::memcpy(&v, a.data(), sizeof(v));
|
||||
v = byte_swap(v);
|
||||
std::memcpy(a.data(), &v, sizeof(v));
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
static void reverse_bytes(std::array<CharType, N>& a) noexcept
|
||||
{
|
||||
std::reverse(a.begin(), a.end());
|
||||
}
|
||||
|
||||
template<typename NumberType>
|
||||
void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
|
||||
{
|
||||
@@ -18593,7 +18658,7 @@ class binary_writer
|
||||
if (is_little_endian != OutputIsLittleEndian)
|
||||
{
|
||||
// reverse byte order prior to conversion if necessary
|
||||
std::reverse(vec.begin(), vec.end());
|
||||
reverse_bytes(vec);
|
||||
}
|
||||
|
||||
oa.write_characters(vec.data(), sizeof(NumberType));
|
||||
|
||||
Reference in New Issue
Block a user