mirror of
https://github.com/nlohmann/json.git
synced 2026-09-26 09:50:30 +00:00
Read BSON keys in bulk from contiguous input
BSON keys (and array indices) are C-style strings, which were read byte by byte. For contiguous input they are now read up to their \x00-byte in one step, using the same bulk_scan flag as BON8 strings: twitter.json is read in 1.46 instead of 2.01 ms, citm_catalog.json in 2.93 instead of 3.33 ms, jeopardy.json in 182 instead of 207 ms. canada.json, whose keys are almost all one-digit array indices, takes 2 % longer. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -411,6 +411,11 @@ class binary_reader
|
||||
*/
|
||||
bool get_bson_cstr(string_t& result)
|
||||
{
|
||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto out = std::back_inserter(result);
|
||||
while (true)
|
||||
{
|
||||
@@ -427,6 +432,46 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read a C-style string from contiguous input in one step
|
||||
|
||||
@param[in,out] result the string to append to
|
||||
@return whether the string was read; if the input has no \x00-byte, nothing
|
||||
is read, and @ref get_bson_cstr reports the end of the input
|
||||
*/
|
||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
||||
{
|
||||
const std::size_t remaining = ia.bulk_remaining();
|
||||
if (remaining == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||
// a plain loop rather than std::memchr: most keys are short (array
|
||||
// indices are keys, too), and the call would cost more than it saves
|
||||
std::size_t length = 0;
|
||||
while (length < remaining && data[length] != 0x00)
|
||||
{
|
||||
++length;
|
||||
}
|
||||
if (length == remaining)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
||||
// consume the string and its \x00-byte, as the byte-wise path does
|
||||
ia.bulk_skip(length + 1);
|
||||
chars_read += length + 1;
|
||||
current = 0x00;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// input that is not contiguous: C-style strings are read byte by byte
|
||||
bool get_bson_cstr_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Parses a zero-terminated string of length @a len from the BSON
|
||||
input.
|
||||
|
||||
@@ -12930,6 +12930,11 @@ class binary_reader
|
||||
*/
|
||||
bool get_bson_cstr(string_t& result)
|
||||
{
|
||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto out = std::back_inserter(result);
|
||||
while (true)
|
||||
{
|
||||
@@ -12946,6 +12951,46 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief read a C-style string from contiguous input in one step
|
||||
|
||||
@param[in,out] result the string to append to
|
||||
@return whether the string was read; if the input has no \x00-byte, nothing
|
||||
is read, and @ref get_bson_cstr reports the end of the input
|
||||
*/
|
||||
bool get_bson_cstr_bulk(string_t& result, std::true_type /*bulk*/)
|
||||
{
|
||||
const std::size_t remaining = ia.bulk_remaining();
|
||||
if (remaining == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const auto* const data = reinterpret_cast<const unsigned char*>(ia.bulk_data());
|
||||
// a plain loop rather than std::memchr: most keys are short (array
|
||||
// indices are keys, too), and the call would cost more than it saves
|
||||
std::size_t length = 0;
|
||||
while (length < remaining && data[length] != 0x00)
|
||||
{
|
||||
++length;
|
||||
}
|
||||
if (length == remaining)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.append(reinterpret_cast<const typename string_t::value_type*>(data), length);
|
||||
// consume the string and its \x00-byte, as the byte-wise path does
|
||||
ia.bulk_skip(length + 1);
|
||||
chars_read += length + 1;
|
||||
current = 0x00;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// input that is not contiguous: C-style strings are read byte by byte
|
||||
bool get_bson_cstr_bulk(string_t& /*result*/, std::false_type /*bulk*/) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Parses a zero-terminated string of length @a len from the BSON
|
||||
input.
|
||||
|
||||
@@ -1066,6 +1066,42 @@ TEST_CASE("Incomplete BSON Input")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("BSON keys from contiguous and stream input")
|
||||
{
|
||||
// contiguous input reads a key up to its \x00-byte in one step, a stream
|
||||
// reads it byte by byte; both must give the same value or error for the
|
||||
// complete document and for every truncation of it
|
||||
const json j = {{"", true}, {"k", {1, 2, 3}}, {std::string(40, 'x'), {{"nested key", "value"}}}};
|
||||
const std::vector<std::uint8_t> bson = json::to_bson(j);
|
||||
CHECK(json::from_bson(bson) == j);
|
||||
|
||||
for (std::size_t length = 0; length <= bson.size(); ++length)
|
||||
{
|
||||
CAPTURE(length)
|
||||
const std::vector<std::uint8_t> input(bson.begin(), bson.begin() + static_cast<std::ptrdiff_t>(length));
|
||||
std::string from_vector;
|
||||
std::string from_stream;
|
||||
try
|
||||
{
|
||||
from_vector = json::from_bson(input).dump();
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
from_vector = e.what();
|
||||
}
|
||||
try
|
||||
{
|
||||
std::istringstream stream(std::string(input.begin(), input.end()));
|
||||
from_stream = json::from_bson(stream).dump();
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
from_stream = e.what();
|
||||
}
|
||||
CHECK(from_vector == from_stream);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Negative size of binary value")
|
||||
{
|
||||
// invalid BSON: the size of the binary value is -1
|
||||
|
||||
Reference in New Issue
Block a user