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` |
|
| `#!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`) |
|
| [`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()` |
|
| 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
|
### Compatible types
|
||||||
|
|
||||||
|
|||||||
@@ -1856,14 +1856,18 @@ class binary_reader
|
|||||||
|
|
||||||
Only the start of an array is tried, which covers arrays of numbers
|
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
|
(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
|
@param[in] len the number of elements
|
||||||
@return whether the SAX parser accepted the array and the values read
|
@return whether the SAX parser accepted the array and the values read
|
||||||
*/
|
*/
|
||||||
bool enter_cbor_array(const std::size_t len)
|
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
|
/// 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
|
@brief open a MessagePack array and read the scalars it begins with (see
|
||||||
@ref parse_msgpack_scalar_run)
|
@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
|
@param[in] len the number of elements
|
||||||
@return whether the SAX parser accepted the array and the values read
|
@return whether the SAX parser accepted the array and the values read
|
||||||
*/
|
*/
|
||||||
bool enter_msgpack_array(const std::size_t len)
|
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
|
/// 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_out(sized(out, estimate))
|
||||||
, m_pos(&m_out[0])
|
, m_pos(&m_out[0])
|
||||||
, m_end(m_pos + m_out.size())
|
, m_end(m_pos + m_out.size())
|
||||||
, m_reserved((std::max)(estimate, m_out.size()))
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void finish()
|
void finish()
|
||||||
@@ -117,12 +116,11 @@ class output_buffer
|
|||||||
const auto used = static_cast<std::size_t>(m_pos - m_out.data());
|
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
|
// (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
|
// 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)
|
// 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_reserved), used + n + 256);
|
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_out.capacity()), used + n + 256);
|
||||||
if (size > m_reserved)
|
if (size > m_out.capacity())
|
||||||
{
|
{
|
||||||
m_reserved = (std::max)(m_reserved * 2, size);
|
m_out.reserve((std::max)(m_out.capacity() * 2, size));
|
||||||
m_out.reserve(m_reserved);
|
|
||||||
}
|
}
|
||||||
m_out.resize(size);
|
m_out.resize(size);
|
||||||
m_pos = &m_out[0] + used;
|
m_pos = &m_out[0] + used;
|
||||||
@@ -132,9 +130,6 @@ class output_buffer
|
|||||||
StringType& m_out;
|
StringType& m_out;
|
||||||
char* m_pos;
|
char* m_pos;
|
||||||
char* m_end;
|
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
|
/// 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
|
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
|
(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
|
@param[in] len the number of elements
|
||||||
@return whether the SAX parser accepted the array and the values read
|
@return whether the SAX parser accepted the array and the values read
|
||||||
*/
|
*/
|
||||||
bool enter_cbor_array(const std::size_t len)
|
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
|
/// 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
|
@brief open a MessagePack array and read the scalars it begins with (see
|
||||||
@ref parse_msgpack_scalar_run)
|
@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
|
@param[in] len the number of elements
|
||||||
@return whether the SAX parser accepted the array and the values read
|
@return whether the SAX parser accepted the array and the values read
|
||||||
*/
|
*/
|
||||||
bool enter_msgpack_array(const std::size_t len)
|
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
|
/// 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_out(sized(out, estimate))
|
||||||
, m_pos(&m_out[0])
|
, m_pos(&m_out[0])
|
||||||
, m_end(m_pos + m_out.size())
|
, m_end(m_pos + m_out.size())
|
||||||
, m_reserved((std::max)(estimate, m_out.size()))
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void finish()
|
void finish()
|
||||||
@@ -8581,12 +8580,11 @@ class output_buffer
|
|||||||
const auto used = static_cast<std::size_t>(m_pos - m_out.data());
|
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
|
// (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
|
// 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)
|
// 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_reserved), used + n + 256);
|
const std::size_t size = (std::max)((std::min)(m_out.size() + step, m_out.capacity()), used + n + 256);
|
||||||
if (size > m_reserved)
|
if (size > m_out.capacity())
|
||||||
{
|
{
|
||||||
m_reserved = (std::max)(m_reserved * 2, size);
|
m_out.reserve((std::max)(m_out.capacity() * 2, size));
|
||||||
m_out.reserve(m_reserved);
|
|
||||||
}
|
}
|
||||||
m_out.resize(size);
|
m_out.resize(size);
|
||||||
m_pos = &m_out[0] + used;
|
m_pos = &m_out[0] + used;
|
||||||
@@ -8596,9 +8594,6 @@ class output_buffer
|
|||||||
StringType& m_out;
|
StringType& m_out;
|
||||||
char* m_pos;
|
char* m_pos;
|
||||||
char* m_end;
|
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
|
/// The length of the run at s that dump() writes unchanged without
|
||||||
|
|||||||
@@ -629,6 +629,22 @@ static json make_containers()
|
|||||||
return j;
|
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
|
/// one flat array of numbers: the scalar decoding path, which must not move
|
||||||
static json make_scalars()
|
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 / bjdata, make_containers, binary_format::bjdata);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bson, make_containers, binary_format::bson);
|
BENCHMARK_CAPTURE(FromBinaryShape, containers / bson, make_containers, binary_format::bson);
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, containers / bon8, make_containers, binary_format::bon8);
|
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
|
// BSON names every array element, so a large array measures key generation
|
||||||
// rather than scalar decoding and is left out here
|
// rather than scalar decoding and is left out here
|
||||||
BENCHMARK_CAPTURE(FromBinaryShape, scalars / cbor, make_scalars, binary_format::cbor);
|
BENCHMARK_CAPTURE(FromBinaryShape, scalars / cbor, make_scalars, binary_format::cbor);
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "doctest_compatibility.h"
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <nlohmann/json_view.hpp>
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#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
|
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")
|
TEST_CASE("CBOR indefinite-length strings do not recurse per chunk")
|
||||||
{
|
{
|
||||||
// Reading an indefinite-length string or byte array used to call itself
|
// 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));
|
== 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