Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ed3feca3 | ||
|
|
eb2a092d70 | ||
|
|
2a2c7d30f0 |
No files matched your search
@@ -86,7 +86,7 @@ JSON_INLINE_VARIABLE constexpr std::size_t max_valueless_container_size = 1 << 2
|
|||||||
/*!
|
/*!
|
||||||
@brief deserialization of BJData, BON8, BSON, CBOR, MessagePack, and UBJSON values
|
@brief deserialization of BJData, BON8, BSON, CBOR, MessagePack, and UBJSON values
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_binary_parser<BasicJsonType, InputAdapterType>>
|
template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType, InputAdapterType>>
|
||||||
class binary_reader
|
class binary_reader
|
||||||
{
|
{
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -14,7 +14,6 @@
|
|||||||
#include <cstdio> // snprintf
|
#include <cstdio> // snprintf
|
||||||
#include <cstring> // memcmp
|
#include <cstring> // memcmp
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <limits> // numeric_limits
|
|
||||||
#include <string> // char_traits, string
|
#include <string> // char_traits, string
|
||||||
#include <utility> // move
|
#include <utility> // move
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
@@ -1817,15 +1816,6 @@ scan_number_done:
|
|||||||
/// significand (see scan_number_bulk_contiguous())
|
/// significand (see scan_number_bulk_contiguous())
|
||||||
bool convert_accumulated_float(const float_significand& s, std::true_type /*native*/) noexcept
|
bool convert_accumulated_float(const float_significand& s, std::true_type /*native*/) noexcept
|
||||||
{
|
{
|
||||||
// accept() only needs to know that the value is finite (see the parser):
|
|
||||||
// w < 10^19, so a value below 10^max_exponent10 cannot overflow, and a
|
|
||||||
// value that underflows is a finite number as well. Only a token that
|
|
||||||
// may overflow has to be converted to find out.
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(discard_number_values && s.exponent + 19 <= std::numeric_limits<number_float_t>::max_exponent10))
|
|
||||||
{
|
|
||||||
value_float = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
value_float = decimal_to_float<number_float_t>(s);
|
value_float = decimal_to_float<number_float_t>(s);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,13 +250,6 @@ class parser
|
|||||||
// value to avoid a goto (see comment where set to true)
|
// value to avoid a goto (see comment where set to true)
|
||||||
bool skip_to_state_evaluation = false;
|
bool skip_to_state_evaluation = false;
|
||||||
|
|
||||||
// a document that starts with a container pushes onto the stack at
|
|
||||||
// once: allocate it in one step instead of growing it from 1 byte
|
|
||||||
if (last_token == token_type::begin_object || last_token == token_type::begin_array)
|
|
||||||
{
|
|
||||||
states.reserve(32);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!skip_to_state_evaluation)
|
if (!skip_to_state_evaluation)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm> // all_of
|
#include <algorithm> // all_of, copy
|
||||||
#include <array> // array
|
#include <array> // array
|
||||||
#include <cctype> // isdigit
|
#include <cctype> // isdigit
|
||||||
#include <cerrno> // errno, ERANGE
|
#include <cerrno> // errno, ERANGE
|
||||||
@@ -82,12 +82,34 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
// one buffer: every token adds its escaped form (at least its length) and a slash
|
// one buffer: every token adds its escaped form (at least its length) and a slash
|
||||||
std::size_t length = 0;
|
std::size_t length = 0;
|
||||||
|
bool plain = true;
|
||||||
for (const auto& reference_token : reference_tokens)
|
for (const auto& reference_token : reference_tokens)
|
||||||
{
|
{
|
||||||
length += reference_token.size() + 1;
|
length += reference_token.size() + 1;
|
||||||
|
const char* const data = reference_token.data();
|
||||||
|
for (std::size_t i = 0, size = reference_token.size(); plain && i < size; ++i)
|
||||||
|
{
|
||||||
|
plain = data[i] != '~' && data[i] != '/';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t result;
|
string_t result;
|
||||||
|
if (JSON_HEDLEY_LIKELY(plain))
|
||||||
|
{
|
||||||
|
// nothing to escape: the tokens are copied into the string
|
||||||
|
result.resize(length);
|
||||||
|
if (length != 0)
|
||||||
|
{
|
||||||
|
char* out = &result[0];
|
||||||
|
for (const auto& reference_token : reference_tokens)
|
||||||
|
{
|
||||||
|
*out++ = '/';
|
||||||
|
out = std::copy(reference_token.data(), reference_token.data() + reference_token.size(), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
result.reserve(length);
|
result.reserve(length);
|
||||||
for (const auto& reference_token : reference_tokens)
|
for (const auto& reference_token : reference_tokens)
|
||||||
{
|
{
|
||||||
@@ -1198,33 +1220,32 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
result.reserve(token_count);
|
result.reserve(token_count);
|
||||||
|
|
||||||
// extract the reference tokens:
|
// extract the reference tokens: every one runs from after a slash to
|
||||||
// - slash: position of the last read slash (or end of string)
|
// the next slash or the end of the string; a token without a '~' (the
|
||||||
// - start: position after the previous slash
|
// common case) is stored as it is, others are checked and unescaped
|
||||||
for (
|
const char* const data = reference_string.data();
|
||||||
// search for the first slash after the first character
|
const char* const end = data + size;
|
||||||
std::size_t slash = reference_string.find_first_of('/', 1),
|
for (const char* start = data + 1;; ++start)
|
||||||
// set the beginning of the first reference token
|
|
||||||
start = 1;
|
|
||||||
// we can stop if start == 0 (if slash == string_t::npos)
|
|
||||||
start != 0;
|
|
||||||
// set the beginning of the next reference token
|
|
||||||
// (will eventually be 0 if slash == string_t::npos)
|
|
||||||
start = (slash == string_t::npos) ? 0 : slash + 1,
|
|
||||||
// find next slash
|
|
||||||
slash = reference_string.find_first_of('/', start))
|
|
||||||
{
|
{
|
||||||
// use the text between the beginning of the reference token
|
const char* slash = start;
|
||||||
// (start) and the last slash (slash).
|
bool escaped = false;
|
||||||
const auto count = (slash == string_t::npos ? reference_string.size() : slash) - start;
|
while (slash != end && *slash != '/')
|
||||||
auto reference_token = string_t(reference_string.data() + start, count);
|
|
||||||
|
|
||||||
// check reference tokens are properly escaped; a token without
|
|
||||||
// a '~' (the common case) needs neither check nor unescaping
|
|
||||||
std::size_t pos = reference_token.find_first_of('~');
|
|
||||||
if (pos != string_t::npos)
|
|
||||||
{
|
{
|
||||||
for (; pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1))
|
escaped = escaped || *slash == '~';
|
||||||
|
++slash;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto count = static_cast<std::size_t>(slash - start);
|
||||||
|
if (JSON_HEDLEY_LIKELY(!escaped))
|
||||||
|
{
|
||||||
|
result.emplace_back(start, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto reference_token = string_t(start, count);
|
||||||
|
|
||||||
|
// check reference tokens are properly escaped
|
||||||
|
for (std::size_t pos = reference_token.find_first_of('~'); pos != string_t::npos; pos = reference_token.find_first_of('~', pos + 1))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(reference_token[pos] == '~');
|
JSON_ASSERT(reference_token[pos] == '~');
|
||||||
|
|
||||||
@@ -1238,10 +1259,14 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
|
|
||||||
detail::unescape(reference_token);
|
detail::unescape(reference_token);
|
||||||
|
result.push_back(std::move(reference_token));
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally, store the reference token
|
if (slash == end)
|
||||||
result.push_back(std::move(reference_token));
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = slash;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -161,8 +161,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
template<typename BasicJsonType, typename InputAdapterType>
|
template<typename BasicJsonType, typename InputAdapterType>
|
||||||
friend class ::nlohmann::detail::json_sax_dom_parser;
|
friend class ::nlohmann::detail::json_sax_dom_parser;
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
template<typename BasicJsonType, typename InputAdapterType>
|
||||||
friend class ::nlohmann::detail::json_sax_dom_binary_parser;
|
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
|
||||||
friend class ::nlohmann::detail::json_sax_dom_callback_parser;
|
friend class ::nlohmann::detail::json_sax_dom_callback_parser;
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
friend struct ::nlohmann::detail::diagnostic_positions;
|
friend struct ::nlohmann::detail::diagnostic_positions;
|
||||||
@@ -6289,7 +6287,7 @@ public:
|
|||||||
from_ubjson/from_bjdata/from_bon8/from_bson overloads
|
from_ubjson/from_bjdata/from_bon8/from_bson overloads
|
||||||
|
|
||||||
Building the @ref binary_reader as a named local, rather than as a
|
Building the @ref binary_reader as a named local, rather than as a
|
||||||
temporary that @ref detail::json_sax_dom_binary_parser::parse is called on in
|
temporary that @ref detail::json_sax_dom_parser::parse is called on in
|
||||||
the same expression, avoids a false-positive cppcheck accessMoved
|
the same expression, avoids a false-positive cppcheck accessMoved
|
||||||
warning in each of the 16 callers.
|
warning in each of the 16 callers.
|
||||||
*/
|
*/
|
||||||
@@ -6300,7 +6298,7 @@ public:
|
|||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
detail::json_sax_dom_binary_parser<basic_json, InputAdapterType> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, InputAdapterType> sdp(result, allow_exceptions);
|
||||||
binary_reader<InputAdapterType> reader(std::move(ia), format, error_handler);
|
binary_reader<InputAdapterType> reader(std::move(ia), format, error_handler);
|
||||||
if (!reader.sax_parse(&sdp, strict, tag_handler))
|
if (!reader.sax_parse(&sdp, strict, tag_handler))
|
||||||
{
|
{
|
||||||
|
|||||||
Generated
+117
-1122
File diff suppressed because it is too large.
Load diff
@@ -2952,32 +2952,3 @@ TEST_CASE("diagnostic positions: value lifetime, input adapters, and SAX")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST_CASE("accept() agrees with parse() on floats around the overflow limit")
|
|
||||||
{
|
|
||||||
// accept() does not convert a float that cannot overflow; the tokens
|
|
||||||
// around the limit (and beyond it) must still be rejected exactly as
|
|
||||||
// parse() rejects them
|
|
||||||
const char* const numbers[] =
|
|
||||||
{
|
|
||||||
"1e0", "123.456e-5", "1e-400", "-1e-400", "1e307", "1e308", "1e309", "-1e309",
|
|
||||||
"10e307", "100e307", "0.1e309", "0.1e310", "1E+308", "1E+309",
|
|
||||||
"1.7976931348623157e308", "1.7976931348623159e308", "1.8e308", "-1.7976931348623157e308",
|
|
||||||
"12345678901234567e291", "12345678901234567e292", "123456789012345678e290", "123456789012345678e291",
|
|
||||||
"1234567890123456789e289", "1234567890123456789e290", "9999999999999999999e289", "9999999999999999999e290",
|
|
||||||
"0.00000000000000000001e328", "0.00000000000000000001e329"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const char* number : numbers)
|
|
||||||
{
|
|
||||||
for (const std::string text :
|
|
||||||
{
|
|
||||||
std::string(number), "[" + std::string(number) + "]", "{\"a\":" + std::string(number) + "}"
|
|
||||||
})
|
|
||||||
{
|
|
||||||
INFO(text);
|
|
||||||
const bool parses = !json::parse(text, nullptr, false).is_discarded();
|
|
||||||
CHECK(json::accept(text) == parses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -237,274 +237,3 @@ TEST_CASE("regression test for issue #5673 - ordered_json::emplace with a non-rv
|
|||||||
CHECK(oj.dump() == "{\"a\":1}");
|
CHECK(oj.dump() == "{\"a\":1}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// the sizes around which the DOM parser changes how it stores the elements of
|
|
||||||
// an array (16) and the members of an object in a vector (32, and 64 for the
|
|
||||||
// key index)
|
|
||||||
const std::size_t container_sizes[] = {0, 1, 2, 3, 15, 16, 17, 18, 31, 32, 33, 34, 63, 64, 65, 66, 200};
|
|
||||||
|
|
||||||
template<typename Json>
|
|
||||||
std::string array_text(const std::size_t n, const std::size_t depth)
|
|
||||||
{
|
|
||||||
std::string text = "[";
|
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
text += (i == 0 ? "" : ",");
|
|
||||||
// alternate scalars, empty containers and nested containers
|
|
||||||
switch (i % 4)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
text += std::to_string(i);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
text += "\"s" + std::to_string(i) + "\"";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
text += depth == 0 ? "[]" : array_text<Json>(n / 2, depth - 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text += depth == 0 ? "{}" : "{\"a\":" + array_text<Json>(n / 3, depth - 1) + ",\"b\":null}";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return text + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Json>
|
|
||||||
std::string object_text(const std::size_t n, const std::size_t depth, const bool duplicates)
|
|
||||||
{
|
|
||||||
std::string text = "{";
|
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
text += (i == 0 ? "" : ",");
|
|
||||||
text += "\"key" + std::to_string(i) + "\":";
|
|
||||||
switch (i % 4)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
text += std::to_string(i);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
text += depth == 0 ? "[]" : array_text<Json>(n / 2, depth - 1);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
text += depth == 0 ? "{}" : object_text<Json>(n / 2, depth - 1, duplicates);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text += "\"value\"";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (duplicates)
|
|
||||||
{
|
|
||||||
// keys that are there already, the first and the last one, with other values
|
|
||||||
text += n == 0 ? "" : ",";
|
|
||||||
text += "\"key0\":[1,2],\"key" + std::to_string(n - 1) + "\":{\"again\":true}";
|
|
||||||
}
|
|
||||||
return text + "}";
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Json>
|
|
||||||
void check_same_dump(const std::string& text)
|
|
||||||
{
|
|
||||||
// the text is what dump() writes for the parsed value, and the value
|
|
||||||
// survives a second round trip: arrays keep their elements in order, an
|
|
||||||
// ordered_json keeps its members in order of appearance
|
|
||||||
const Json parsed = Json::parse(text);
|
|
||||||
const Json again = Json::parse(parsed.dump());
|
|
||||||
CHECK(parsed == again);
|
|
||||||
CHECK(parsed.dump() == again.dump());
|
|
||||||
CHECK(Json::accept(text));
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("parsing containers of sizes around the limits of the DOM parser")
|
|
||||||
{
|
|
||||||
SECTION("arrays")
|
|
||||||
{
|
|
||||||
for (const std::size_t n : container_sizes)
|
|
||||||
{
|
|
||||||
const std::string text = array_text<json>(n, 2);
|
|
||||||
INFO("size ", n);
|
|
||||||
|
|
||||||
const json parsed = json::parse(text);
|
|
||||||
CHECK(parsed.is_array());
|
|
||||||
CHECK(parsed.size() == n);
|
|
||||||
for (std::size_t i = 0; i < n; i += 4)
|
|
||||||
{
|
|
||||||
CHECK(parsed[i] == i);
|
|
||||||
}
|
|
||||||
check_same_dump<json>(text);
|
|
||||||
check_same_dump<ordered_json>(text);
|
|
||||||
CHECK(ordered_json::parse(text).dump() == parsed.dump());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("objects")
|
|
||||||
{
|
|
||||||
for (const bool duplicates :
|
|
||||||
{
|
|
||||||
false, true
|
|
||||||
})
|
|
||||||
{
|
|
||||||
for (const std::size_t n : container_sizes)
|
|
||||||
{
|
|
||||||
const std::string text = object_text<json>(n, 2, duplicates);
|
|
||||||
INFO("size ", n, " duplicates ", duplicates);
|
|
||||||
|
|
||||||
const json parsed = json::parse(text);
|
|
||||||
const ordered_json ordered = ordered_json::parse(text);
|
|
||||||
CHECK(parsed.is_object());
|
|
||||||
CHECK(ordered.is_object());
|
|
||||||
CHECK(parsed.size() == ordered.size());
|
|
||||||
// the same members, in the order of their first appearance
|
|
||||||
std::size_t i = 0;
|
|
||||||
for (auto it = ordered.begin(); it != ordered.end(); ++it, ++i)
|
|
||||||
{
|
|
||||||
if (i < n)
|
|
||||||
{
|
|
||||||
CHECK(it.key() == "key" + std::to_string(i));
|
|
||||||
}
|
|
||||||
CHECK(parsed.at(it.key()) == json(it.value()));
|
|
||||||
}
|
|
||||||
if (duplicates && n > 1)
|
|
||||||
{
|
|
||||||
// the later value replaced the earlier one in its place
|
|
||||||
CHECK(ordered.at("key0") == ordered_json::parse("[1,2]"));
|
|
||||||
CHECK(ordered.at("key" + std::to_string(n - 1)) == ordered_json::parse("{\"again\":true}"));
|
|
||||||
CHECK(ordered.begin().key() == "key0");
|
|
||||||
CHECK(ordered.size() == n);
|
|
||||||
}
|
|
||||||
check_same_dump<json>(text);
|
|
||||||
check_same_dump<ordered_json>(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("objects with many different and repeated keys")
|
|
||||||
{
|
|
||||||
// 300 members of which a third repeat an earlier key
|
|
||||||
std::string text = "{";
|
|
||||||
for (std::size_t i = 0; i < 300; ++i)
|
|
||||||
{
|
|
||||||
text += (i == 0 ? "" : ",");
|
|
||||||
text += "\"k" + std::to_string(i % 200) + "\":" + std::to_string(i);
|
|
||||||
}
|
|
||||||
text += "}";
|
|
||||||
|
|
||||||
const ordered_json ordered = ordered_json::parse(text);
|
|
||||||
const json parsed = json::parse(text);
|
|
||||||
CHECK(ordered.size() == 200);
|
|
||||||
CHECK(parsed.size() == 200);
|
|
||||||
std::size_t i = 0;
|
|
||||||
for (auto it = ordered.begin(); it != ordered.end(); ++it, ++i)
|
|
||||||
{
|
|
||||||
CHECK(it.key() == "k" + std::to_string(i));
|
|
||||||
// the last value written
|
|
||||||
CHECK(it.value() == (i < 100 ? i + 200 : i));
|
|
||||||
CHECK(parsed.at(it.key()) == json(it.value()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// a deterministic source of structure for parsing tests: builds a JSON text
|
|
||||||
// and, at the same time, the value it has to parse to
|
|
||||||
class tree_generator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit tree_generator(std::uint32_t seed) : state(seed) {}
|
|
||||||
|
|
||||||
template<typename Json>
|
|
||||||
Json make(std::string& text, const std::size_t depth)
|
|
||||||
{
|
|
||||||
switch (next(depth == 0 ? 5 : 9))
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
text += "null";
|
|
||||||
return Json(nullptr);
|
|
||||||
case 1:
|
|
||||||
text += "true";
|
|
||||||
return Json(true);
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
const int n = static_cast<int>(next(2000)) - 1000;
|
|
||||||
text += std::to_string(n);
|
|
||||||
return Json(n);
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
text += "\"text\"";
|
|
||||||
return Json("text");
|
|
||||||
case 4:
|
|
||||||
text += "2.5";
|
|
||||||
return Json(2.5);
|
|
||||||
case 5:
|
|
||||||
case 6:
|
|
||||||
case 7:
|
|
||||||
{
|
|
||||||
Json array = Json::array();
|
|
||||||
text += "[";
|
|
||||||
const std::size_t n = size();
|
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
text += (i == 0 ? "" : ",");
|
|
||||||
array.push_back(make<Json>(text, depth - 1));
|
|
||||||
}
|
|
||||||
text += "]";
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
Json object = Json::object();
|
|
||||||
text += "{";
|
|
||||||
const std::size_t n = size();
|
|
||||||
for (std::size_t i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
const std::string key = "k" + std::to_string(i);
|
|
||||||
text += (i == 0 ? "\"" : ",\"") + key + "\":";
|
|
||||||
object[key] = make<Json>(text, depth - 1);
|
|
||||||
}
|
|
||||||
text += "}";
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::uint32_t next(const std::uint32_t bound)
|
|
||||||
{
|
|
||||||
state = state * 1664525U + 1013904223U;
|
|
||||||
return (state >> 8) % bound;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t size()
|
|
||||||
{
|
|
||||||
return container_sizes[next(sizeof(container_sizes) / sizeof(*container_sizes))];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint32_t state;
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("parsing random trees of arrays and objects")
|
|
||||||
{
|
|
||||||
for (std::uint32_t seed = 1; seed <= 150; ++seed)
|
|
||||||
{
|
|
||||||
INFO("seed ", seed);
|
|
||||||
|
|
||||||
std::string text;
|
|
||||||
std::string ordered_text;
|
|
||||||
const json expected = tree_generator(seed).make<json>(text, 3);
|
|
||||||
const ordered_json ordered_expected = tree_generator(seed).make<ordered_json>(ordered_text, 3);
|
|
||||||
CHECK(text == ordered_text);
|
|
||||||
|
|
||||||
CHECK(json::parse(text) == expected);
|
|
||||||
CHECK(json::accept(text));
|
|
||||||
const ordered_json ordered = ordered_json::parse(text);
|
|
||||||
CHECK(ordered == ordered_expected);
|
|
||||||
CHECK(ordered.dump() == ordered_expected.dump());
|
|
||||||
CHECK(json::parse(text).dump() == expected.dump());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user