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:
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user