Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16ed40b2d4 |
No files matched your search
@@ -406,7 +406,6 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
| `#!cpp operator<<(std::ostream&, const json_pointer&)` | streamability to `#!cpp std::ostream` |
|
||||
| [`to_string`](../../api/basic_json/to_string.md) | conversion of `StringType` to `#!cpp std::string` (the function returns a `#!cpp std::string`) |
|
||||
| exception messages | `data()` and `size()`, or `begin()` and `end()` |
|
||||
| [`basic_json_view::dump`](../../api/basic_json_view/dump.md) | `reserve(n)` and `resize(n)` (one argument); `capacity()` is **not** required |
|
||||
|
||||
### Compatible types
|
||||
|
||||
|
||||
@@ -1856,14 +1856,18 @@ class binary_reader
|
||||
|
||||
Only the start of an array is tried, which covers arrays of numbers
|
||||
(e.g. coordinates) but costs arrays of other values only one call, not one
|
||||
per element.
|
||||
per element. An array of one element is not tried at all: that element is
|
||||
mostly an object or an array (`[[...]]`, the single ring of a GeoJSON
|
||||
polygon), for which the call would find no scalar, while a single scalar,
|
||||
which the run would read faster, is rare. The benchmarks
|
||||
`FromBinaryShape/nested` and `FromBinaryShape/singles` show both sides.
|
||||
|
||||
@param[in] len the number of elements
|
||||
@return whether the SAX parser accepted the array and the values read
|
||||
*/
|
||||
bool enter_cbor_array(const std::size_t len)
|
||||
{
|
||||
return enter_array(len) && (len == 0 || parse_cbor_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
return enter_array(len) && (len <= 1 || parse_cbor_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
}
|
||||
|
||||
/// input that is not contiguous: every value is read by parse_cbor_value
|
||||
@@ -2764,12 +2768,16 @@ class binary_reader
|
||||
/*!
|
||||
@brief open a MessagePack array and read the scalars it begins with (see
|
||||
@ref parse_msgpack_scalar_run)
|
||||
|
||||
An array of one element is not tried, for the reason given at
|
||||
@ref enter_cbor_array.
|
||||
|
||||
@param[in] len the number of elements
|
||||
@return whether the SAX parser accepted the array and the values read
|
||||
*/
|
||||
bool enter_msgpack_array(const std::size_t len)
|
||||
{
|
||||
return enter_array(len) && (len == 0 || parse_msgpack_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
return enter_array(len) && (len <= 1 || parse_msgpack_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
}
|
||||
|
||||
/// input that is not contiguous: every value is read by parse_msgpack_value
|
||||
|
||||
@@ -47,7 +47,6 @@ class output_buffer
|
||||
: m_out(sized(out, estimate))
|
||||
, m_pos(&m_out[0])
|
||||
, m_end(m_pos + m_out.size())
|
||||
, m_reserved((std::max)(estimate, m_out.size()))
|
||||
{}
|
||||
|
||||
void finish()
|
||||
@@ -117,12 +116,11 @@ class output_buffer
|
||||
const auto used = static_cast<std::size_t>(m_pos - m_out.data());
|
||||
// (a step does not go beyond the reserved estimate, so that a good
|
||||
// estimate is never copied to a larger allocation; not a clamp: the
|
||||
// lower bound used + n + 256 can exceed m_reserved, and then it must win)
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_reserved), used + n + 256);
|
||||
if (size > m_reserved)
|
||||
// lower bound used + n + 256 can exceed capacity(), and then it must win)
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_out.capacity()), used + n + 256);
|
||||
if (size > m_out.capacity())
|
||||
{
|
||||
m_reserved = (std::max)(m_reserved * 2, size);
|
||||
m_out.reserve(m_reserved);
|
||||
m_out.reserve((std::max)(m_out.capacity() * 2, size));
|
||||
}
|
||||
m_out.resize(size);
|
||||
m_pos = &m_out[0] + used;
|
||||
@@ -132,9 +130,6 @@ class output_buffer
|
||||
StringType& m_out;
|
||||
char* m_pos;
|
||||
char* m_end;
|
||||
/// what was last passed to reserve(), a lower bound of the capacity
|
||||
/// (StringType need not have a capacity() member)
|
||||
std::size_t m_reserved;
|
||||
};
|
||||
|
||||
/// The length of the run at s that dump() writes unchanged without
|
||||
|
||||
Generated
+11
-3
@@ -19863,14 +19863,18 @@ class binary_reader
|
||||
|
||||
Only the start of an array is tried, which covers arrays of numbers
|
||||
(e.g. coordinates) but costs arrays of other values only one call, not one
|
||||
per element.
|
||||
per element. An array of one element is not tried at all: that element is
|
||||
mostly an object or an array (`[[...]]`, the single ring of a GeoJSON
|
||||
polygon), for which the call would find no scalar, while a single scalar,
|
||||
which the run would read faster, is rare. The benchmarks
|
||||
`FromBinaryShape/nested` and `FromBinaryShape/singles` show both sides.
|
||||
|
||||
@param[in] len the number of elements
|
||||
@return whether the SAX parser accepted the array and the values read
|
||||
*/
|
||||
bool enter_cbor_array(const std::size_t len)
|
||||
{
|
||||
return enter_array(len) && (len == 0 || parse_cbor_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
return enter_array(len) && (len <= 1 || parse_cbor_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
}
|
||||
|
||||
/// input that is not contiguous: every value is read by parse_cbor_value
|
||||
@@ -20771,12 +20775,16 @@ class binary_reader
|
||||
/*!
|
||||
@brief open a MessagePack array and read the scalars it begins with (see
|
||||
@ref parse_msgpack_scalar_run)
|
||||
|
||||
An array of one element is not tried, for the reason given at
|
||||
@ref enter_cbor_array.
|
||||
|
||||
@param[in] len the number of elements
|
||||
@return whether the SAX parser accepted the array and the values read
|
||||
*/
|
||||
bool enter_msgpack_array(const std::size_t len)
|
||||
{
|
||||
return enter_array(len) && (len == 0 || parse_msgpack_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
return enter_array(len) && (len <= 1 || parse_msgpack_scalar_run(std::integral_constant<bool, bulk_scan> {}));
|
||||
}
|
||||
|
||||
/// input that is not contiguous: every value is read by parse_msgpack_value
|
||||
|
||||
Generated
+4
-9
@@ -8511,7 +8511,6 @@ class output_buffer
|
||||
: m_out(sized(out, estimate))
|
||||
, m_pos(&m_out[0])
|
||||
, m_end(m_pos + m_out.size())
|
||||
, m_reserved((std::max)(estimate, m_out.size()))
|
||||
{}
|
||||
|
||||
void finish()
|
||||
@@ -8581,12 +8580,11 @@ class output_buffer
|
||||
const auto used = static_cast<std::size_t>(m_pos - m_out.data());
|
||||
// (a step does not go beyond the reserved estimate, so that a good
|
||||
// estimate is never copied to a larger allocation; not a clamp: the
|
||||
// lower bound used + n + 256 can exceed m_reserved, and then it must win)
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_reserved), used + n + 256);
|
||||
if (size > m_reserved)
|
||||
// lower bound used + n + 256 can exceed capacity(), and then it must win)
|
||||
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_out.capacity()), used + n + 256);
|
||||
if (size > m_out.capacity())
|
||||
{
|
||||
m_reserved = (std::max)(m_reserved * 2, size);
|
||||
m_out.reserve(m_reserved);
|
||||
m_out.reserve((std::max)(m_out.capacity() * 2, size));
|
||||
}
|
||||
m_out.resize(size);
|
||||
m_pos = &m_out[0] + used;
|
||||
@@ -8596,9 +8594,6 @@ class output_buffer
|
||||
StringType& m_out;
|
||||
char* m_pos;
|
||||
char* m_end;
|
||||
/// what was last passed to reserve(), a lower bound of the capacity
|
||||
/// (StringType need not have a capacity() member)
|
||||
std::size_t m_reserved;
|
||||
};
|
||||
|
||||
/// The length of the run at s that dump() writes unchanged without
|
||||
|
||||
@@ -629,6 +629,22 @@ static json make_containers()
|
||||
return j;
|
||||
}
|
||||
|
||||
/// many arrays of one number each: one-element arrays are left to the general
|
||||
/// path (CBOR and MessagePack), which reads a lone number slower than the loop
|
||||
/// for arrays of scalars would
|
||||
static json make_singles()
|
||||
{
|
||||
json data = json::array();
|
||||
for (std::size_t i = 0; i < 100000; ++i)
|
||||
{
|
||||
data.push_back(json::array({i}));
|
||||
}
|
||||
|
||||
json j = json::object();
|
||||
j["data"] = std::move(data);
|
||||
return j;
|
||||
}
|
||||
|
||||
/// one flat array of numbers: the scalar decoding path, which must not move
|
||||
static json make_scalars()
|
||||
{
|
||||
@@ -676,6 +692,8 @@ BENCHMARK_CAPTURE(FromBinaryShape, containers / ubjson_optimized, make_container
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bjdata, make_containers, binary_format::bjdata);
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bson, make_containers, binary_format::bson);
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bon8, make_containers, binary_format::bon8);
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, singles / cbor, make_singles, binary_format::cbor);
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, singles / msgpack, make_singles, binary_format::msgpack);
|
||||
// BSON names every array element, so a large array measures key generation
|
||||
// rather than scalar decoding and is left out here
|
||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / cbor, make_scalars, binary_format::cbor);
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -484,56 +483,4 @@ TEST_CASE("alternative string type")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("alternative string type with json_view")
|
||||
{
|
||||
// regression test for #5994: basic_json_view::dump() called
|
||||
// StringType::capacity(), which alt_string (and the documented StringType
|
||||
// requirements) do not provide
|
||||
using alt_document = nlohmann::basic_json_document<alt_json, nlohmann::view_mode::read_only>;
|
||||
using alt_editable_document = nlohmann::basic_json_document<alt_json, nlohmann::view_mode::editable>;
|
||||
|
||||
const char* text = R"({"a":[1,"x\u00e4"],"b":{"c":true,"d":null},"e":2.5})";
|
||||
const alt_json j = alt_json::parse(text);
|
||||
|
||||
SECTION("read-only document")
|
||||
{
|
||||
const alt_document d = alt_document::parse(text);
|
||||
CHECK(d.root().dump() == j.dump());
|
||||
CHECK(d.root().dump(2) == j.dump(2));
|
||||
CHECK(d.root().dump(-1, ' ', true) == j.dump(-1, ' ', true));
|
||||
CHECK(d.root().materialize() == j);
|
||||
CHECK(d.root().contains(alt_string("b")));
|
||||
CHECK(d.root()[alt_string("b")][alt_string("c")].dump() == "true");
|
||||
}
|
||||
|
||||
SECTION("long output")
|
||||
{
|
||||
// more than one growth step of the output buffer
|
||||
alt_json big = alt_json::array();
|
||||
for (int i = 0; i < 20000; ++i)
|
||||
{
|
||||
big.push_back(alt_json::parse(text));
|
||||
}
|
||||
const alt_string big_text = big.dump();
|
||||
const alt_document d = alt_document::parse(big_text);
|
||||
CHECK(d.root().dump() == big_text);
|
||||
CHECK(d.root().dump(4) == big.dump(4));
|
||||
}
|
||||
|
||||
SECTION("editable document")
|
||||
{
|
||||
alt_editable_document d = alt_editable_document::parse(text);
|
||||
d.set(d.root(), "f", "new");
|
||||
d.push_back(d.root()[alt_string("a")], 3);
|
||||
alt_json expected = j;
|
||||
expected["f"] = "new";
|
||||
expected["a"].push_back(3);
|
||||
CHECK(d.root().materialize() == expected);
|
||||
CHECK(alt_json::parse(d.root().dump()) == expected);
|
||||
|
||||
const alt_editable_document loaded = alt_editable_document::load(d.save());
|
||||
CHECK(loaded.root().materialize() == expected);
|
||||
}
|
||||
}
|
||||
|
||||
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||
@@ -2520,6 +2520,34 @@ TEST_CASE("CBOR arrays of scalars from contiguous and non-contiguous input")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR arrays of one element from contiguous and non-contiguous input")
|
||||
{
|
||||
// contiguous input leaves an array of one element to the general path
|
||||
// rather than to the loop that reads arrays of scalars; both inputs must
|
||||
// still give the same events, value, or error for the complete document
|
||||
// and for every truncation of it, with enough bytes after each array that
|
||||
// the loop could have been entered
|
||||
const json j = json::parse(R"({"a": [[[[1.5]]]], "b": [7], "c": [{"x": 2.5}], "d": ["s"], "e": [[1, 2]], "f": [null], "g": 1.25})");
|
||||
const std::vector<std::uint8_t> bytes = json::to_cbor(j);
|
||||
|
||||
CHECK(json::from_cbor(bytes) == j);
|
||||
|
||||
for (std::size_t length = 0; length <= bytes.size(); ++length)
|
||||
{
|
||||
CAPTURE(length)
|
||||
const std::vector<std::uint8_t> contiguous(bytes.begin(), bytes.begin() + static_cast<std::ptrdiff_t>(length));
|
||||
const std::list<std::uint8_t> bytewise(contiguous.begin(), contiguous.end());
|
||||
|
||||
CHECK(json::from_cbor(contiguous, true, false).dump() == json::from_cbor(bytewise.begin(), bytewise.end(), true, false).dump());
|
||||
|
||||
SaxEventLogger from_contiguous;
|
||||
SaxEventLogger from_bytewise;
|
||||
CHECK(json::sax_parse(contiguous, &from_contiguous, json::input_format_t::cbor, true, false)
|
||||
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::cbor, true, false));
|
||||
CHECK(from_contiguous.events == from_bytewise.events);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
||||
{
|
||||
// Reading an indefinite-length string or byte array used to call itself
|
||||
|
||||
@@ -2311,3 +2311,31 @@ TEST_CASE("MessagePack arrays of scalars from contiguous and non-contiguous inpu
|
||||
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::msgpack));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("MessagePack arrays of one element from contiguous and non-contiguous input")
|
||||
{
|
||||
// contiguous input leaves an array of one element to the general path
|
||||
// rather than to the loop that reads arrays of scalars; both inputs must
|
||||
// still give the same events, value, or error for the complete document
|
||||
// and for every truncation of it, with enough bytes after each array that
|
||||
// the loop could have been entered
|
||||
const json j = json::parse(R"({"a": [[[[1.5]]]], "b": [7], "c": [{"x": 2.5}], "d": ["s"], "e": [[1, 2]], "f": [null], "g": 1.25})");
|
||||
const std::vector<std::uint8_t> bytes = json::to_msgpack(j);
|
||||
|
||||
CHECK(json::from_msgpack(bytes) == j);
|
||||
|
||||
for (std::size_t length = 0; length <= bytes.size(); ++length)
|
||||
{
|
||||
CAPTURE(length)
|
||||
const std::vector<std::uint8_t> contiguous(bytes.begin(), bytes.begin() + static_cast<std::ptrdiff_t>(length));
|
||||
const std::list<std::uint8_t> bytewise(contiguous.begin(), contiguous.end());
|
||||
|
||||
CHECK(json::from_msgpack(contiguous, true, false).dump() == json::from_msgpack(bytewise.begin(), bytewise.end(), true, false).dump());
|
||||
|
||||
SaxEventLogger from_contiguous;
|
||||
SaxEventLogger from_bytewise;
|
||||
CHECK(json::sax_parse(contiguous, &from_contiguous, json::input_format_t::msgpack, true, false)
|
||||
== json::sax_parse(bytewise.begin(), bytewise.end(), &from_bytewise, json::input_format_t::msgpack, true, false));
|
||||
CHECK(from_contiguous.events == from_bytewise.events);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user