1
0
mirror of https://github.com/nlohmann/json.git synced 2025-08-07 18:02:57 +03:00

Optimize binary get_number implementation by reading multiple bytes at once (#4391)

* multibyte binary reader

* wide_string_input_adapter fallback to get_character

Update input_adapters.hpp

* Update json.hpp

* Add from msgpack test

* Test for broken msgpack with stream, address some warnings

* Reading binary number from wchar as an error, address warnings

* Not casting float to int, it violates strict aliasing rule
This commit is contained in:
Tianyi Chen
2024-11-29 00:19:58 -08:00
committed by GitHub
parent e41905fcb0
commit 935c6eeb5a
5 changed files with 258 additions and 42 deletions

View File

@@ -20,6 +20,9 @@
#include <string> // char_traits, string
#include <utility> // make_pair, move
#include <vector> // vector
#ifdef __cpp_lib_byteswap
#include <bit> //byteswap
#endif
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/input/input_adapters.hpp>
@@ -2754,6 +2757,29 @@ class binary_reader
return current = ia.get_character();
}
/*!
@brief get_to read into a primitive type
This function provides the interface to the used input adapter. It does
not throw in case the input reached EOF, but returns false instead
@return bool, whether the read was successful
*/
template<class T>
bool get_to(T& dest, const input_format_t format, const char* context)
{
auto new_chars_read = ia.get_elements(&dest);
chars_read += new_chars_read;
if (JSON_HEDLEY_UNLIKELY(new_chars_read < sizeof(T)))
{
// in case of failure, advance position by 1 to report failing location
++chars_read;
sax->parse_error(chars_read, "<end of file>", parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
return false;
}
return true;
}
/*!
@return character read from the input after ignoring all 'N' entries
*/
@@ -2768,6 +2794,28 @@ class binary_reader
return current;
}
template<class NumberType>
static void byte_swap(NumberType& number)
{
constexpr std::size_t sz = sizeof(number);
#ifdef __cpp_lib_byteswap
if constexpr (sz == 1)
{
return;
}
if constexpr(std::is_integral_v<NumberType>)
{
number = std::byteswap(number);
return;
}
#endif
auto* ptr = reinterpret_cast<std::uint8_t*>(&number);
for (std::size_t i = 0; i < sz / 2; ++i)
{
std::swap(ptr[i], ptr[sz - i - 1]);
}
}
/*
@brief read a number from the input
@@ -2786,29 +2834,16 @@ class binary_reader
template<typename NumberType, bool InputIsLittleEndian = false>
bool get_number(const input_format_t format, NumberType& result)
{
// step 1: read input into array with system's byte order
std::array<std::uint8_t, sizeof(NumberType)> vec{};
for (std::size_t i = 0; i < sizeof(NumberType); ++i)
// read in the original format
if (JSON_HEDLEY_UNLIKELY(!get_to(result, format, "number")))
{
get();
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number")))
{
return false;
}
// reverse byte order prior to conversion if necessary
if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))
{
vec[sizeof(NumberType) - i - 1] = static_cast<std::uint8_t>(current);
}
else
{
vec[i] = static_cast<std::uint8_t>(current); // LCOV_EXCL_LINE
}
return false;
}
if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))
{
byte_swap(result);
}
// step 2: convert array into number of type T and return
std::memcpy(&result, vec.data(), sizeof(NumberType));
return true;
}