mirror of
https://github.com/nlohmann/json
synced 2024-11-27 06:10:06 +00:00
Merge branch 'feature/sax2' into develop #971
This commit is contained in:
commit
cf9299d222
3
Makefile
3
Makefile
@ -9,6 +9,7 @@ SRCS = include/nlohmann/json.hpp \
|
||||
include/nlohmann/detail/exceptions.hpp \
|
||||
include/nlohmann/detail/input/binary_reader.hpp \
|
||||
include/nlohmann/detail/input/input_adapters.hpp \
|
||||
include/nlohmann/detail/input/json_sax.hpp \
|
||||
include/nlohmann/detail/input/lexer.hpp \
|
||||
include/nlohmann/detail/input/parser.hpp \
|
||||
include/nlohmann/detail/iterators/internal_iterator.hpp \
|
||||
@ -84,7 +85,7 @@ coverage:
|
||||
mkdir build_coverage
|
||||
cd build_coverage ; CXX=g++-5 cmake .. -GNinja -DJSON_Coverage=ON -DJSON_MultipleHeaders=ON
|
||||
cd build_coverage ; ninja
|
||||
cd build_coverage ; ctest -j10
|
||||
cd build_coverage ; ctest -E '.*_default' -j10
|
||||
cd build_coverage ; ninja lcov_html
|
||||
open build_coverage/test/html/index.html
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // min
|
||||
#include <array> // array
|
||||
#include <cassert> // assert
|
||||
#include <cstddef> // size_t
|
||||
#include <cstring> // strlen
|
||||
#include <ios> // streamsize, streamoff, streampos
|
||||
#include <istream> // istream
|
||||
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
|
||||
#include <memory> // shared_ptr, make_shared, addressof
|
||||
@ -20,6 +17,9 @@ namespace nlohmann
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
/// the supported input formats
|
||||
enum class input_format_t { json, cbor, msgpack, ubjson };
|
||||
|
||||
////////////////////
|
||||
// input adapters //
|
||||
////////////////////
|
||||
@ -28,19 +28,17 @@ namespace detail
|
||||
@brief abstract input adapter interface
|
||||
|
||||
Produces a stream of std::char_traits<char>::int_type characters from a
|
||||
std::istream, a buffer, or some other input type. Accepts the return of exactly
|
||||
one non-EOF character for future input. The int_type characters returned
|
||||
consist of all valid char values as positive values (typically unsigned char),
|
||||
plus an EOF value outside that range, specified by the value of the function
|
||||
std::char_traits<char>::eof(). This value is typically -1, but could be any
|
||||
arbitrary value which is not a valid char value.
|
||||
std::istream, a buffer, or some other input type. Accepts the return of
|
||||
exactly one non-EOF character for future input. The int_type characters
|
||||
returned consist of all valid char values as positive values (typically
|
||||
unsigned char), plus an EOF value outside that range, specified by the value
|
||||
of the function std::char_traits<char>::eof(). This value is typically -1, but
|
||||
could be any arbitrary value which is not a valid char value.
|
||||
*/
|
||||
struct input_adapter_protocol
|
||||
{
|
||||
/// get a character [0,255] or std::char_traits<char>::eof().
|
||||
virtual std::char_traits<char>::int_type get_character() = 0;
|
||||
/// restore the last non-eof() character to input
|
||||
virtual void unget_character() = 0;
|
||||
virtual ~input_adapter_protocol() = default;
|
||||
};
|
||||
|
||||
@ -68,34 +66,7 @@ class input_stream_adapter : public input_adapter_protocol
|
||||
|
||||
explicit input_stream_adapter(std::istream& i)
|
||||
: is(i), sb(*i.rdbuf())
|
||||
{
|
||||
// skip byte order mark
|
||||
std::char_traits<char>::int_type c;
|
||||
if ((c = get_character()) == 0xEF)
|
||||
{
|
||||
if ((c = get_character()) == 0xBB)
|
||||
{
|
||||
if ((c = get_character()) == 0xBF)
|
||||
{
|
||||
return; // Ignore BOM
|
||||
}
|
||||
else if (c != std::char_traits<char>::eof())
|
||||
{
|
||||
is.unget();
|
||||
}
|
||||
is.putback('\xBB');
|
||||
}
|
||||
else if (c != std::char_traits<char>::eof())
|
||||
{
|
||||
is.unget();
|
||||
}
|
||||
is.putback('\xEF');
|
||||
}
|
||||
else if (c != std::char_traits<char>::eof())
|
||||
{
|
||||
is.unget(); // no byte order mark; process as usual
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
// delete because of pointer members
|
||||
input_stream_adapter(const input_stream_adapter&) = delete;
|
||||
@ -109,11 +80,6 @@ class input_stream_adapter : public input_adapter_protocol
|
||||
return sb.sbumpc();
|
||||
}
|
||||
|
||||
void unget_character() override
|
||||
{
|
||||
sb.sungetc(); // is.unget() avoided for performance
|
||||
}
|
||||
|
||||
private:
|
||||
/// the associated input stream
|
||||
std::istream& is;
|
||||
@ -125,14 +91,8 @@ class input_buffer_adapter : public input_adapter_protocol
|
||||
{
|
||||
public:
|
||||
input_buffer_adapter(const char* b, const std::size_t l)
|
||||
: cursor(b), limit(b + l), start(b)
|
||||
{
|
||||
// skip byte order mark
|
||||
if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF')
|
||||
{
|
||||
cursor += 3;
|
||||
}
|
||||
}
|
||||
: cursor(b), limit(b + l)
|
||||
{}
|
||||
|
||||
// delete because of pointer members
|
||||
input_buffer_adapter(const input_buffer_adapter&) = delete;
|
||||
@ -148,21 +108,11 @@ class input_buffer_adapter : public input_adapter_protocol
|
||||
return std::char_traits<char>::eof();
|
||||
}
|
||||
|
||||
void unget_character() noexcept override
|
||||
{
|
||||
if (JSON_LIKELY(cursor > start))
|
||||
{
|
||||
--cursor;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// pointer to the current character
|
||||
const char* cursor;
|
||||
/// pointer past the last character
|
||||
const char* limit;
|
||||
/// pointer to the first character
|
||||
const char* start;
|
||||
const char* const limit;
|
||||
};
|
||||
|
||||
template<typename WideStringType>
|
||||
@ -173,13 +123,6 @@ class wide_string_input_adapter : public input_adapter_protocol
|
||||
|
||||
std::char_traits<char>::int_type get_character() noexcept override
|
||||
{
|
||||
// unget_character() was called previously: return the last character
|
||||
if (next_unget)
|
||||
{
|
||||
next_unget = false;
|
||||
return last_char;
|
||||
}
|
||||
|
||||
// check if buffer needs to be filled
|
||||
if (utf8_bytes_index == utf8_bytes_filled)
|
||||
{
|
||||
@ -199,12 +142,7 @@ class wide_string_input_adapter : public input_adapter_protocol
|
||||
// use buffer
|
||||
assert(utf8_bytes_filled > 0);
|
||||
assert(utf8_bytes_index < utf8_bytes_filled);
|
||||
return (last_char = utf8_bytes[utf8_bytes_index++]);
|
||||
}
|
||||
|
||||
void unget_character() noexcept override
|
||||
{
|
||||
next_unget = true;
|
||||
return utf8_bytes[utf8_bytes_index++];
|
||||
}
|
||||
|
||||
private:
|
||||
@ -328,11 +266,6 @@ class wide_string_input_adapter : public input_adapter_protocol
|
||||
std::size_t utf8_bytes_index = 0;
|
||||
/// number of valid bytes in the utf8_codes array
|
||||
std::size_t utf8_bytes_filled = 0;
|
||||
|
||||
/// the last character (returned after unget_character() is called)
|
||||
std::char_traits<char>::int_type last_char = 0;
|
||||
/// whether get_character() should return last_char
|
||||
bool next_unget = false;
|
||||
};
|
||||
|
||||
class input_adapter
|
||||
|
689
include/nlohmann/detail/input/json_sax.hpp
Normal file
689
include/nlohmann/detail/input/json_sax.hpp
Normal file
@ -0,0 +1,689 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/detail/input/parser.hpp>
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
|
||||
namespace nlohmann
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief SAX interface
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
struct json_sax
|
||||
{
|
||||
/// type for (signed) integers
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
/// type for unsigned integers
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
/// type for floating-point numbers
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
/// type for strings
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
|
||||
/// constant to indicate that no size limit is given for array or object
|
||||
static constexpr auto no_limit = std::size_t(-1);
|
||||
|
||||
/*!
|
||||
@brief a null value was read
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool null() = 0;
|
||||
|
||||
/*!
|
||||
@brief a boolean value was read
|
||||
@param[in] val boolean value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool boolean(bool val) = 0;
|
||||
|
||||
/*!
|
||||
@brief an integer number was read
|
||||
@param[in] val integer value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool number_integer(number_integer_t val) = 0;
|
||||
|
||||
/*!
|
||||
@brief an unsigned integer number was read
|
||||
@param[in] val unsigned integer value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool number_unsigned(number_unsigned_t val) = 0;
|
||||
|
||||
/*!
|
||||
@brief an floating-point number was read
|
||||
@param[in] val floating-point value
|
||||
@param[in] s raw token value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool number_float(number_float_t val, const string_t& s) = 0;
|
||||
|
||||
/*!
|
||||
@brief a string was read
|
||||
@param[in] val string value
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool string(string_t& val) = 0;
|
||||
|
||||
/*!
|
||||
@brief the beginning of an object was read
|
||||
@param[in] elements number of object elements or no_limit if unknown
|
||||
@return whether parsing should proceed
|
||||
@note binary formats may report the number of elements
|
||||
*/
|
||||
virtual bool start_object(std::size_t elements = no_limit) = 0;
|
||||
|
||||
/*!
|
||||
@brief an object key was read
|
||||
@param[in] val object key
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool key(string_t& val) = 0;
|
||||
|
||||
/*!
|
||||
@brief the end of an object was read
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool end_object() = 0;
|
||||
|
||||
/*!
|
||||
@brief the beginning of an array was read
|
||||
@param[in] elements number of array elements or no_limit if unknown
|
||||
@return whether parsing should proceed
|
||||
@note binary formats may report the number of elements
|
||||
*/
|
||||
virtual bool start_array(std::size_t elements = no_limit) = 0;
|
||||
|
||||
/*!
|
||||
@brief the end of an array was read
|
||||
@return whether parsing should proceed
|
||||
*/
|
||||
virtual bool end_array() = 0;
|
||||
|
||||
/*!
|
||||
@brief a parse error occurred
|
||||
@param[in] position the position in the input where the error occurs
|
||||
@param[in] last_token the last read token
|
||||
@param[in] error_msg a detailed error message
|
||||
@return whether parsing should proceed (must return false)
|
||||
*/
|
||||
virtual bool parse_error(std::size_t position,
|
||||
const std::string& last_token,
|
||||
const detail::exception& ex) = 0;
|
||||
|
||||
virtual ~json_sax() = default;
|
||||
};
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
/*!
|
||||
@brief SAX implementation to create a JSON value from SAX events
|
||||
|
||||
This class implements the @ref json_sax interface and processes the SAX events
|
||||
to create a JSON value which makes it basically a DOM parser. The structure or
|
||||
hierarchy of the JSON value is managed by the stack `ref_stack` which contains
|
||||
a pointer to the respective array or object for each recursion depth.
|
||||
|
||||
After successful parsing, the value that is passed by reference to the
|
||||
constructor contains the parsed value.
|
||||
|
||||
@tparam BasicJsonType the JSON type
|
||||
*/
|
||||
template<typename BasicJsonType>
|
||||
class json_sax_dom_parser : public json_sax<BasicJsonType>
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
|
||||
/*!
|
||||
@param[in, out] r reference to a JSON value that is manipulated while
|
||||
parsing
|
||||
@param[in] allow_exceptions_ whether parse errors yield exceptions
|
||||
*/
|
||||
json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
|
||||
: root(r), allow_exceptions(allow_exceptions_)
|
||||
{}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
handle_value(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(number_unsigned_t val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t val, const string_t&) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t& val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t len) override
|
||||
{
|
||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
|
||||
|
||||
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408,
|
||||
"excessive object size: " + std::to_string(len)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(string_t& val) override
|
||||
{
|
||||
// add null at given key and store the reference for later
|
||||
object_element = &(ref_stack.back()->m_value.object->operator[](val));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
ref_stack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t len) override
|
||||
{
|
||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
|
||||
|
||||
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408,
|
||||
"excessive array size: " + std::to_string(len)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
ref_stack.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&,
|
||||
const detail::exception& ex) override
|
||||
{
|
||||
errored = true;
|
||||
if (allow_exceptions)
|
||||
{
|
||||
// determine the proper exception type from the id
|
||||
switch ((ex.id / 100) % 100)
|
||||
{
|
||||
case 1:
|
||||
JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));
|
||||
case 2:
|
||||
JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex)); // LCOV_EXCL_LINE
|
||||
case 3:
|
||||
JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex)); // LCOV_EXCL_LINE
|
||||
case 4:
|
||||
JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));
|
||||
case 5:
|
||||
JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex)); // LCOV_EXCL_LINE
|
||||
default:
|
||||
assert(false); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_errored() const
|
||||
{
|
||||
return errored;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@invariant If the ref stack is empty, then the passed value will be the new
|
||||
root.
|
||||
@invariant If the ref stack contains a value, then it is an array or an
|
||||
object to which we can add elements
|
||||
*/
|
||||
template<typename Value>
|
||||
BasicJsonType* handle_value(Value&& v)
|
||||
{
|
||||
if (ref_stack.empty())
|
||||
{
|
||||
root = BasicJsonType(std::forward<Value>(v));
|
||||
return &root;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
|
||||
if (ref_stack.back()->is_array())
|
||||
{
|
||||
ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
|
||||
return &(ref_stack.back()->m_value.array->back());
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(object_element);
|
||||
*object_element = BasicJsonType(std::forward<Value>(v));
|
||||
return object_element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// the parsed JSON value
|
||||
BasicJsonType& root;
|
||||
/// stack to model hierarchy of values
|
||||
std::vector<BasicJsonType*> ref_stack;
|
||||
/// helper to hold the reference for the next object element
|
||||
BasicJsonType* object_element = nullptr;
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class json_sax_dom_callback_parser : public json_sax<BasicJsonType>
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
using parser_callback_t = typename BasicJsonType::parser_callback_t;
|
||||
using parse_event_t = typename BasicJsonType::parse_event_t;
|
||||
|
||||
json_sax_dom_callback_parser(BasicJsonType& r,
|
||||
const parser_callback_t cb,
|
||||
const bool allow_exceptions_ = true)
|
||||
: root(r), callback(cb), allow_exceptions(allow_exceptions_)
|
||||
{
|
||||
keep_stack.push_back(true);
|
||||
}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
handle_value(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(number_unsigned_t val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t val, const string_t&) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t& val) override
|
||||
{
|
||||
handle_value(val);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t len) override
|
||||
{
|
||||
// check callback for object start
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
|
||||
keep_stack.push_back(keep);
|
||||
|
||||
auto val = handle_value(BasicJsonType::value_t::object, true);
|
||||
ref_stack.push_back(val.second);
|
||||
|
||||
// check object limit
|
||||
if (ref_stack.back())
|
||||
{
|
||||
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408,
|
||||
"excessive object size: " + std::to_string(len)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(string_t& val) override
|
||||
{
|
||||
BasicJsonType k = BasicJsonType(val);
|
||||
|
||||
// check callback for key
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
|
||||
key_keep_stack.push_back(keep);
|
||||
|
||||
// add discarded value at given key and store the reference for later
|
||||
if (keep and ref_stack.back())
|
||||
{
|
||||
object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
bool keep = true;
|
||||
if (ref_stack.back())
|
||||
{
|
||||
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back());
|
||||
if (not keep)
|
||||
{
|
||||
// discard object
|
||||
*ref_stack.back() = discarded;
|
||||
}
|
||||
}
|
||||
|
||||
assert(not ref_stack.empty());
|
||||
assert(not keep_stack.empty());
|
||||
ref_stack.pop_back();
|
||||
keep_stack.pop_back();
|
||||
|
||||
if (not ref_stack.empty() and ref_stack.back())
|
||||
{
|
||||
// remove discarded value
|
||||
if (ref_stack.back()->is_object())
|
||||
{
|
||||
for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
|
||||
{
|
||||
if (it->is_discarded())
|
||||
{
|
||||
ref_stack.back()->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t len) override
|
||||
{
|
||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
|
||||
keep_stack.push_back(keep);
|
||||
|
||||
auto val = handle_value(BasicJsonType::value_t::array, true);
|
||||
ref_stack.push_back(val.second);
|
||||
|
||||
// check array limit
|
||||
if (ref_stack.back())
|
||||
{
|
||||
if (JSON_UNLIKELY(len != json_sax<BasicJsonType>::no_limit and len > ref_stack.back()->max_size()))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(408,
|
||||
"excessive array size: " + std::to_string(len)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
bool keep = true;
|
||||
|
||||
if (ref_stack.back())
|
||||
{
|
||||
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
|
||||
if (not keep)
|
||||
{
|
||||
// discard array
|
||||
*ref_stack.back() = discarded;
|
||||
}
|
||||
}
|
||||
|
||||
assert(not ref_stack.empty());
|
||||
assert(not keep_stack.empty());
|
||||
ref_stack.pop_back();
|
||||
keep_stack.pop_back();
|
||||
|
||||
// remove discarded value
|
||||
if (not keep and not ref_stack.empty())
|
||||
{
|
||||
if (ref_stack.back()->is_array())
|
||||
{
|
||||
ref_stack.back()->m_value.array->pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&,
|
||||
const detail::exception& ex) override
|
||||
{
|
||||
errored = true;
|
||||
if (allow_exceptions)
|
||||
{
|
||||
// determine the proper exception type from the id
|
||||
switch ((ex.id / 100) % 100)
|
||||
{
|
||||
case 1:
|
||||
JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));
|
||||
case 2:
|
||||
JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex)); // LCOV_EXCL_LINE
|
||||
case 3:
|
||||
JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex)); // LCOV_EXCL_LINE
|
||||
case 4:
|
||||
JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));
|
||||
case 5:
|
||||
JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex)); // LCOV_EXCL_LINE
|
||||
default:
|
||||
assert(false); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is_errored() const
|
||||
{
|
||||
return errored;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@param[in] v value to add to the JSON value we build during parsing
|
||||
@param[in] skip_callback whether we should skip calling the callback
|
||||
function; this is required after start_array() and
|
||||
start_object() SAX events, because otherwise we would call the
|
||||
callback function with an empty array or object, respectively.
|
||||
|
||||
@invariant If the ref stack is empty, then the passed value will be the new
|
||||
root.
|
||||
@invariant If the ref stack contains a value, then it is an array or an
|
||||
object to which we can add elements
|
||||
|
||||
@return pair of boolean (whether value should be kept) and pointer (to the
|
||||
passed value in the ref_stack hierarchy; nullptr if not kept)
|
||||
*/
|
||||
template<typename Value>
|
||||
std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
|
||||
{
|
||||
assert(not keep_stack.empty());
|
||||
|
||||
// do not handle this value if we know it would be added to a discarded
|
||||
// container
|
||||
if (not keep_stack.back())
|
||||
{
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
// create value
|
||||
auto value = BasicJsonType(std::forward<Value>(v));
|
||||
|
||||
// check callback
|
||||
const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
|
||||
|
||||
// do not handle this value if we just learnt it shall be discarded
|
||||
if (not keep)
|
||||
{
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
if (ref_stack.empty())
|
||||
{
|
||||
root = std::move(value);
|
||||
return {true, &root};
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());
|
||||
if (ref_stack.back()->is_array())
|
||||
{
|
||||
ref_stack.back()->m_value.array->push_back(std::move(value));
|
||||
return {true, &(ref_stack.back()->m_value.array->back())};
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if we should store an element for the current key
|
||||
assert(not key_keep_stack.empty());
|
||||
const bool store_element = key_keep_stack.back();
|
||||
key_keep_stack.pop_back();
|
||||
|
||||
if (not store_element)
|
||||
{
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
assert(object_element);
|
||||
*object_element = std::move(value);
|
||||
return {true, object_element};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// the parsed JSON value
|
||||
BasicJsonType& root;
|
||||
/// stack to model hierarchy of values
|
||||
std::vector<BasicJsonType*> ref_stack;
|
||||
/// stack to manage which values to keep
|
||||
std::vector<bool> keep_stack;
|
||||
/// stack to manage which object keys to keep
|
||||
std::vector<bool> key_keep_stack;
|
||||
/// helper to hold the reference for the next object element
|
||||
BasicJsonType* object_element = nullptr;
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// callback function
|
||||
const parser_callback_t callback = nullptr;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
/// a discarded value for the callback
|
||||
BasicJsonType discarded = BasicJsonType::value_t::discarded;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType>
|
||||
class json_sax_acceptor : public json_sax<BasicJsonType>
|
||||
{
|
||||
public:
|
||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using number_float_t = typename BasicJsonType::number_float_t;
|
||||
using string_t = typename BasicJsonType::string_t;
|
||||
|
||||
bool null() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(number_integer_t) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(number_unsigned_t) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(number_float_t, const string_t&) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(string_t&) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(string_t&) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&, const detail::exception&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -99,7 +99,7 @@ class lexer
|
||||
}
|
||||
}
|
||||
|
||||
explicit lexer(detail::input_adapter_t adapter)
|
||||
explicit lexer(detail::input_adapter_t&& adapter)
|
||||
: ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {}
|
||||
|
||||
// delete because of pointer members
|
||||
@ -1081,7 +1081,16 @@ scan_number_done:
|
||||
std::char_traits<char>::int_type get()
|
||||
{
|
||||
++chars_read;
|
||||
current = ia->get_character();
|
||||
if (next_unget)
|
||||
{
|
||||
// just reset the next_unget variable and work with current
|
||||
next_unget = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = ia->get_character();
|
||||
}
|
||||
|
||||
if (JSON_LIKELY(current != std::char_traits<char>::eof()))
|
||||
{
|
||||
token_string.push_back(std::char_traits<char>::to_char_type(current));
|
||||
@ -1089,13 +1098,20 @@ scan_number_done:
|
||||
return current;
|
||||
}
|
||||
|
||||
/// unget current character (return it again on next get)
|
||||
/*!
|
||||
@brief unget current character (read it again on next get)
|
||||
|
||||
We implement unget by setting variable next_unget to true. The input is not
|
||||
changed - we just simulate ungetting by modifying chars_read and
|
||||
token_string. The next call to get() will behave as if the unget character
|
||||
is read again.
|
||||
*/
|
||||
void unget()
|
||||
{
|
||||
next_unget = true;
|
||||
--chars_read;
|
||||
if (JSON_LIKELY(current != std::char_traits<char>::eof()))
|
||||
{
|
||||
ia->unget_character();
|
||||
assert(token_string.size() != 0);
|
||||
token_string.pop_back();
|
||||
}
|
||||
@ -1131,9 +1147,9 @@ scan_number_done:
|
||||
}
|
||||
|
||||
/// return current string value (implicitly resets the token; useful only once)
|
||||
string_t&& move_string()
|
||||
string_t& get_string()
|
||||
{
|
||||
return std::move(token_buffer);
|
||||
return token_buffer;
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
@ -1183,8 +1199,43 @@ scan_number_done:
|
||||
// actual scanner
|
||||
/////////////////////
|
||||
|
||||
/*!
|
||||
@brief skip the UTF-8 byte order mark
|
||||
@return true iff there is no BOM or the correct BOM has been skipped
|
||||
*/
|
||||
bool skip_bom()
|
||||
{
|
||||
if (get() == 0xEF)
|
||||
{
|
||||
if (get() == 0xBB and get() == 0xBF)
|
||||
{
|
||||
// we completely parsed the BOM
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// after reading 0xEF, an unexpected character followed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the first character is not the beginning of the BOM; unget it to
|
||||
// process is later
|
||||
unget();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
token_type scan()
|
||||
{
|
||||
// initially, skip the BOM
|
||||
if (chars_read == 0 and not skip_bom())
|
||||
{
|
||||
error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
|
||||
return token_type::parse_error;
|
||||
}
|
||||
|
||||
// read next character and ignore whitespace
|
||||
do
|
||||
{
|
||||
@ -1254,6 +1305,9 @@ scan_number_done:
|
||||
/// the current character
|
||||
std::char_traits<char>::int_type current = std::char_traits<char>::eof();
|
||||
|
||||
/// whether the next get() call should just return current
|
||||
bool next_unget = false;
|
||||
|
||||
/// the number of characters read
|
||||
std::size_t chars_read = 0;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||
#include <nlohmann/detail/input/json_sax.hpp>
|
||||
#include <nlohmann/detail/input/lexer.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
@ -53,15 +54,20 @@ class parser
|
||||
value
|
||||
};
|
||||
|
||||
using json_sax_t = json_sax<BasicJsonType>;
|
||||
|
||||
using parser_callback_t =
|
||||
std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;
|
||||
|
||||
/// a parser reading from an input adapter
|
||||
explicit parser(detail::input_adapter_t adapter,
|
||||
explicit parser(detail::input_adapter_t&& adapter,
|
||||
const parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions_ = true)
|
||||
: callback(cb), m_lexer(adapter), allow_exceptions(allow_exceptions_)
|
||||
{}
|
||||
: callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_)
|
||||
{
|
||||
// read first token
|
||||
get_token();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief public parser interface
|
||||
@ -75,31 +81,54 @@ class parser
|
||||
*/
|
||||
void parse(const bool strict, BasicJsonType& result)
|
||||
{
|
||||
// read first token
|
||||
get_token();
|
||||
|
||||
parse_internal(true, result);
|
||||
result.assert_invariant();
|
||||
|
||||
// in strict mode, input must be completely read
|
||||
if (strict)
|
||||
if (callback)
|
||||
{
|
||||
get_token();
|
||||
expect(token_type::end_of_input);
|
||||
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
|
||||
sax_parse_internal(&sdp);
|
||||
result.assert_invariant();
|
||||
|
||||
// in strict mode, input must be completely read
|
||||
if (strict and (get_token() != token_type::end_of_input))
|
||||
{
|
||||
sdp.parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input)));
|
||||
}
|
||||
|
||||
// in case of an error, return discarded value
|
||||
if (sdp.is_errored())
|
||||
{
|
||||
result = value_t::discarded;
|
||||
return;
|
||||
}
|
||||
|
||||
// set top-level value to null if it was discarded by the callback
|
||||
// function
|
||||
if (result.is_discarded())
|
||||
{
|
||||
result = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// in case of an error, return discarded value
|
||||
if (errored)
|
||||
else
|
||||
{
|
||||
result = value_t::discarded;
|
||||
return;
|
||||
}
|
||||
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
|
||||
sax_parse_internal(&sdp);
|
||||
result.assert_invariant();
|
||||
|
||||
// set top-level value to null if it was discarded by the callback
|
||||
// function
|
||||
if (result.is_discarded())
|
||||
{
|
||||
result = nullptr;
|
||||
// in strict mode, input must be completely read
|
||||
if (strict and (get_token() != token_type::end_of_input))
|
||||
{
|
||||
sdp.parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input)));
|
||||
}
|
||||
|
||||
// in case of an error, return discarded value
|
||||
if (sdp.is_errored())
|
||||
{
|
||||
result = value_t::discarded;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,414 +140,308 @@ class parser
|
||||
*/
|
||||
bool accept(const bool strict = true)
|
||||
{
|
||||
// read first token
|
||||
get_token();
|
||||
json_sax_acceptor<BasicJsonType> sax_acceptor;
|
||||
return sax_parse(&sax_acceptor, strict);
|
||||
}
|
||||
|
||||
if (not accept_internal())
|
||||
bool sax_parse(json_sax_t* sax, const bool strict = true)
|
||||
{
|
||||
const bool result = sax_parse_internal(sax);
|
||||
|
||||
// strict mode: next byte must be EOF
|
||||
if (result and strict and (get_token() != token_type::end_of_input))
|
||||
{
|
||||
return false;
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input)));
|
||||
}
|
||||
|
||||
// strict => last token must be EOF
|
||||
return not strict or (get_token() == token_type::end_of_input);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief the actual parser
|
||||
@throw parse_error.101 in case of an unexpected token
|
||||
@throw parse_error.102 if to_unicode fails or surrogate error
|
||||
@throw parse_error.103 if to_unicode fails
|
||||
*/
|
||||
void parse_internal(bool keep, BasicJsonType& result)
|
||||
bool sax_parse_internal(json_sax_t* sax)
|
||||
{
|
||||
// never parse after a parse error was detected
|
||||
assert(not errored);
|
||||
// stack to remember the hieararchy of structured values we are parsing
|
||||
// true = array; false = object
|
||||
std::vector<bool> states;
|
||||
// value to avoid a goto (see comment where set to true)
|
||||
bool skip_to_state_evaluation = false;
|
||||
|
||||
// start with a discarded value
|
||||
if (not result.is_discarded())
|
||||
while (true)
|
||||
{
|
||||
result.m_value.destroy(result.m_type);
|
||||
result.m_type = value_t::discarded;
|
||||
}
|
||||
|
||||
switch (last_token)
|
||||
{
|
||||
case token_type::begin_object:
|
||||
if (not skip_to_state_evaluation)
|
||||
{
|
||||
if (keep)
|
||||
// invariant: get_token() was called before each iteration
|
||||
switch (last_token)
|
||||
{
|
||||
if (callback)
|
||||
case token_type::begin_object:
|
||||
{
|
||||
keep = callback(depth++, parse_event_t::object_start, result);
|
||||
}
|
||||
|
||||
if (not callback or keep)
|
||||
{
|
||||
// explicitly set result to object to cope with {}
|
||||
result.m_type = value_t::object;
|
||||
result.m_value = value_t::object;
|
||||
}
|
||||
}
|
||||
|
||||
// read next token
|
||||
get_token();
|
||||
|
||||
// closing } -> we are done
|
||||
if (last_token == token_type::end_object)
|
||||
{
|
||||
if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
|
||||
{
|
||||
result.m_value.destroy(result.m_type);
|
||||
result.m_type = value_t::discarded;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// parse values
|
||||
string_t key;
|
||||
BasicJsonType value;
|
||||
while (true)
|
||||
{
|
||||
// store key
|
||||
if (not expect(token_type::value_string))
|
||||
{
|
||||
return;
|
||||
}
|
||||
key = m_lexer.move_string();
|
||||
|
||||
bool keep_tag = false;
|
||||
if (keep)
|
||||
{
|
||||
if (callback)
|
||||
if (JSON_UNLIKELY(not sax->start_object()))
|
||||
{
|
||||
BasicJsonType k(key);
|
||||
keep_tag = callback(depth, parse_event_t::key, k);
|
||||
return false;
|
||||
}
|
||||
|
||||
// closing } -> we are done
|
||||
if (get_token() == token_type::end_object)
|
||||
{
|
||||
if (JSON_UNLIKELY(not sax->end_object()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// parse key
|
||||
if (JSON_UNLIKELY(last_token != token_type::value_string))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string)));
|
||||
}
|
||||
else
|
||||
{
|
||||
keep_tag = true;
|
||||
if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
if (JSON_UNLIKELY(get_token() != token_type::name_separator))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator)));
|
||||
}
|
||||
|
||||
// remember we are now inside an object
|
||||
states.push_back(false);
|
||||
|
||||
// parse values
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
case token_type::begin_array:
|
||||
{
|
||||
if (JSON_UNLIKELY(not sax->start_array()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// closing ] -> we are done
|
||||
if (get_token() == token_type::end_array)
|
||||
{
|
||||
if (JSON_UNLIKELY(not sax->end_array()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// remember we are now inside an array
|
||||
states.push_back(true);
|
||||
|
||||
// parse values (no need to call get_token)
|
||||
continue;
|
||||
}
|
||||
|
||||
case token_type::value_float:
|
||||
{
|
||||
const auto res = m_lexer.get_number_float();
|
||||
|
||||
if (JSON_UNLIKELY(not std::isfinite(res)))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JSON_UNLIKELY(not sax->number_float(res, m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
get_token();
|
||||
if (not expect(token_type::name_separator))
|
||||
case token_type::literal_false:
|
||||
{
|
||||
return;
|
||||
if (JSON_UNLIKELY(not sax->boolean(false)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// parse and add value
|
||||
get_token();
|
||||
value.m_value.destroy(value.m_type);
|
||||
value.m_type = value_t::discarded;
|
||||
parse_internal(keep, value);
|
||||
|
||||
if (JSON_UNLIKELY(errored))
|
||||
case token_type::literal_null:
|
||||
{
|
||||
return;
|
||||
if (JSON_UNLIKELY(not sax->null()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (keep and keep_tag and not value.is_discarded())
|
||||
case token_type::literal_true:
|
||||
{
|
||||
result.m_value.object->emplace(std::move(key), std::move(value));
|
||||
if (JSON_UNLIKELY(not sax->boolean(true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// comma -> next value
|
||||
get_token();
|
||||
if (last_token == token_type::value_separator)
|
||||
case token_type::value_integer:
|
||||
{
|
||||
get_token();
|
||||
continue;
|
||||
if (JSON_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// closing }
|
||||
if (not expect(token_type::end_object))
|
||||
case token_type::value_string:
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (keep and callback and not callback(--depth, parse_event_t::object_end, result))
|
||||
{
|
||||
result.m_value.destroy(result.m_type);
|
||||
result.m_type = value_t::discarded;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::begin_array:
|
||||
{
|
||||
if (keep)
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
keep = callback(depth++, parse_event_t::array_start, result);
|
||||
if (JSON_UNLIKELY(not sax->string(m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (not callback or keep)
|
||||
case token_type::value_unsigned:
|
||||
{
|
||||
// explicitly set result to array to cope with []
|
||||
result.m_type = value_t::array;
|
||||
result.m_value = value_t::array;
|
||||
if (JSON_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::parse_error:
|
||||
{
|
||||
// using "uninitialized" to avoid "expected" message
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized)));
|
||||
}
|
||||
|
||||
default: // the last token was unexpected
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value)));
|
||||
}
|
||||
}
|
||||
|
||||
// read next token
|
||||
get_token();
|
||||
|
||||
// closing ] -> we are done
|
||||
if (last_token == token_type::end_array)
|
||||
{
|
||||
if (callback and not callback(--depth, parse_event_t::array_end, result))
|
||||
{
|
||||
result.m_value.destroy(result.m_type);
|
||||
result.m_type = value_t::discarded;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// parse values
|
||||
BasicJsonType value;
|
||||
while (true)
|
||||
{
|
||||
// parse value
|
||||
value.m_value.destroy(value.m_type);
|
||||
value.m_type = value_t::discarded;
|
||||
parse_internal(keep, value);
|
||||
|
||||
if (JSON_UNLIKELY(errored))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (keep and not value.is_discarded())
|
||||
{
|
||||
result.m_value.array->push_back(std::move(value));
|
||||
}
|
||||
|
||||
// comma -> next value
|
||||
get_token();
|
||||
if (last_token == token_type::value_separator)
|
||||
{
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
// closing ]
|
||||
if (not expect(token_type::end_array))
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (keep and callback and not callback(--depth, parse_event_t::array_end, result))
|
||||
{
|
||||
result.m_value.destroy(result.m_type);
|
||||
result.m_type = value_t::discarded;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::literal_null:
|
||||
else
|
||||
{
|
||||
result.m_type = value_t::null;
|
||||
break;
|
||||
skip_to_state_evaluation = false;
|
||||
}
|
||||
|
||||
case token_type::value_string:
|
||||
// we reached this line after we successfully parsed a value
|
||||
if (states.empty())
|
||||
{
|
||||
result.m_type = value_t::string;
|
||||
result.m_value = m_lexer.move_string();
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::literal_true:
|
||||
{
|
||||
result.m_type = value_t::boolean;
|
||||
result.m_value = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::literal_false:
|
||||
{
|
||||
result.m_type = value_t::boolean;
|
||||
result.m_value = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::value_unsigned:
|
||||
{
|
||||
result.m_type = value_t::number_unsigned;
|
||||
result.m_value = m_lexer.get_number_unsigned();
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::value_integer:
|
||||
{
|
||||
result.m_type = value_t::number_integer;
|
||||
result.m_value = m_lexer.get_number_integer();
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::value_float:
|
||||
{
|
||||
result.m_type = value_t::number_float;
|
||||
result.m_value = m_lexer.get_number_float();
|
||||
|
||||
// throw in case of infinity or NAN
|
||||
if (JSON_UNLIKELY(not std::isfinite(result.m_value.number_float)))
|
||||
{
|
||||
if (allow_exceptions)
|
||||
{
|
||||
JSON_THROW(out_of_range::create(406, "number overflow parsing '" +
|
||||
m_lexer.get_token_string() + "'"));
|
||||
}
|
||||
expect(token_type::uninitialized);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case token_type::parse_error:
|
||||
{
|
||||
// using "uninitialized" to avoid "expected" message
|
||||
if (not expect(token_type::uninitialized))
|
||||
{
|
||||
return;
|
||||
}
|
||||
break; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// the last token was unexpected; we expected a value
|
||||
if (not expect(token_type::literal_or_value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
break; // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
|
||||
if (keep and callback and not callback(depth, parse_event_t::value, result))
|
||||
{
|
||||
result.m_value.destroy(result.m_type);
|
||||
result.m_type = value_t::discarded;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the actual acceptor
|
||||
|
||||
@invariant 1. The last token is not yet processed. Therefore, the caller
|
||||
of this function must make sure a token has been read.
|
||||
2. When this function returns, the last token is processed.
|
||||
That is, the last read character was already considered.
|
||||
|
||||
This invariant makes sure that no token needs to be "unput".
|
||||
*/
|
||||
bool accept_internal()
|
||||
{
|
||||
switch (last_token)
|
||||
{
|
||||
case token_type::begin_object:
|
||||
{
|
||||
// read next token
|
||||
get_token();
|
||||
|
||||
// closing } -> we are done
|
||||
if (last_token == token_type::end_object)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// parse values
|
||||
while (true)
|
||||
{
|
||||
// parse key
|
||||
if (last_token != token_type::value_string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
get_token();
|
||||
if (last_token != token_type::name_separator)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse value
|
||||
get_token();
|
||||
if (not accept_internal())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// comma -> next value
|
||||
get_token();
|
||||
if (last_token == token_type::value_separator)
|
||||
{
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
// closing }
|
||||
return (last_token == token_type::end_object);
|
||||
}
|
||||
}
|
||||
|
||||
case token_type::begin_array:
|
||||
{
|
||||
// read next token
|
||||
get_token();
|
||||
|
||||
// closing ] -> we are done
|
||||
if (last_token == token_type::end_array)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// parse values
|
||||
while (true)
|
||||
{
|
||||
// parse value
|
||||
if (not accept_internal())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// comma -> next value
|
||||
get_token();
|
||||
if (last_token == token_type::value_separator)
|
||||
{
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
// closing ]
|
||||
return (last_token == token_type::end_array);
|
||||
}
|
||||
}
|
||||
|
||||
case token_type::value_float:
|
||||
{
|
||||
// reject infinity or NAN
|
||||
return std::isfinite(m_lexer.get_number_float());
|
||||
}
|
||||
|
||||
case token_type::literal_false:
|
||||
case token_type::literal_null:
|
||||
case token_type::literal_true:
|
||||
case token_type::value_integer:
|
||||
case token_type::value_string:
|
||||
case token_type::value_unsigned:
|
||||
// empty stack: we reached the end of the hieararchy: done
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (states.back()) // array
|
||||
{
|
||||
// comma -> next value
|
||||
if (get_token() == token_type::value_separator)
|
||||
{
|
||||
// parse a new value
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
default: // the last token was unexpected
|
||||
return false;
|
||||
// closing ]
|
||||
if (JSON_LIKELY(last_token == token_type::end_array))
|
||||
{
|
||||
if (JSON_UNLIKELY(not sax->end_array()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are done with this array. Before we can parse a
|
||||
// new value, we need to evaluate the new state first.
|
||||
// By setting skip_to_state_evaluation to false, we
|
||||
// are effectively jumping to the beginning of this if.
|
||||
assert(not states.empty());
|
||||
states.pop_back();
|
||||
skip_to_state_evaluation = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array)));
|
||||
}
|
||||
}
|
||||
else // object
|
||||
{
|
||||
// comma -> next value
|
||||
if (get_token() == token_type::value_separator)
|
||||
{
|
||||
// parse key
|
||||
if (JSON_UNLIKELY(get_token() != token_type::value_string))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string)));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// parse separator (:)
|
||||
if (JSON_UNLIKELY(get_token() != token_type::name_separator))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator)));
|
||||
}
|
||||
|
||||
// parse values
|
||||
get_token();
|
||||
continue;
|
||||
}
|
||||
|
||||
// closing }
|
||||
if (JSON_LIKELY(last_token == token_type::end_object))
|
||||
{
|
||||
if (JSON_UNLIKELY(not sax->end_object()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are done with this object. Before we can parse a
|
||||
// new value, we need to evaluate the new state first.
|
||||
// By setting skip_to_state_evaluation to false, we
|
||||
// are effectively jumping to the beginning of this if.
|
||||
assert(not states.empty());
|
||||
states.pop_back();
|
||||
skip_to_state_evaluation = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,29 +451,7 @@ class parser
|
||||
return (last_token = m_lexer.scan());
|
||||
}
|
||||
|
||||
/*!
|
||||
@throw parse_error.101 if expected token did not occur
|
||||
*/
|
||||
bool expect(token_type t)
|
||||
{
|
||||
if (JSON_UNLIKELY(t != last_token))
|
||||
{
|
||||
errored = true;
|
||||
expected = t;
|
||||
if (allow_exceptions)
|
||||
{
|
||||
throw_exception();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[noreturn]] void throw_exception() const
|
||||
std::string exception_message(const token_type expected)
|
||||
{
|
||||
std::string error_msg = "syntax error - ";
|
||||
if (last_token == token_type::parse_error)
|
||||
@ -568,22 +469,16 @@ class parser
|
||||
error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
|
||||
}
|
||||
|
||||
JSON_THROW(parse_error::create(101, m_lexer.get_position(), error_msg));
|
||||
return error_msg;
|
||||
}
|
||||
|
||||
private:
|
||||
/// current level of recursion
|
||||
int depth = 0;
|
||||
/// callback function
|
||||
const parser_callback_t callback = nullptr;
|
||||
/// the type of the last read token
|
||||
token_type last_token = token_type::uninitialized;
|
||||
/// the lexer
|
||||
lexer_t m_lexer;
|
||||
/// whether a syntax error occurred
|
||||
bool errored = false;
|
||||
/// possible reason for the syntax error
|
||||
token_type expected = token_type::uninitialized;
|
||||
/// whether to throw exceptions in case of errors
|
||||
const bool allow_exceptions = true;
|
||||
};
|
||||
|
@ -172,6 +172,10 @@ class basic_json
|
||||
friend class ::nlohmann::detail::binary_writer;
|
||||
template<typename BasicJsonType>
|
||||
friend class ::nlohmann::detail::binary_reader;
|
||||
template<typename BasicJsonType>
|
||||
friend class ::nlohmann::detail::json_sax_dom_parser;
|
||||
template<typename BasicJsonType>
|
||||
friend class ::nlohmann::detail::json_sax_dom_callback_parser;
|
||||
|
||||
/// workaround type for MSVC
|
||||
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
|
||||
@ -206,6 +210,8 @@ class basic_json
|
||||
/// helper type for initializer lists of basic_json values
|
||||
using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
|
||||
|
||||
using input_format_t = detail::input_format_t;
|
||||
|
||||
////////////////
|
||||
// exceptions //
|
||||
////////////////
|
||||
@ -1106,6 +1112,7 @@ class basic_json
|
||||
*/
|
||||
using parser_callback_t = typename parser::parser_callback_t;
|
||||
|
||||
using json_sax_t = typename parser::json_sax_t;
|
||||
|
||||
//////////////////
|
||||
// constructors //
|
||||
@ -5992,7 +5999,7 @@ class basic_json
|
||||
|
||||
@since version 2.0.3 (contiguous containers)
|
||||
*/
|
||||
static basic_json parse(detail::input_adapter i,
|
||||
static basic_json parse(detail::input_adapter&& i,
|
||||
const parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@ -6001,26 +6008,23 @@ class basic_json
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@copydoc basic_json parse(detail::input_adapter, const parser_callback_t)
|
||||
*/
|
||||
static basic_json parse(detail::input_adapter& i,
|
||||
const parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
basic_json result;
|
||||
parser(i, cb, allow_exceptions).parse(true, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool accept(detail::input_adapter i)
|
||||
static bool accept(detail::input_adapter&& i)
|
||||
{
|
||||
return parser(i).accept(true);
|
||||
}
|
||||
|
||||
static bool accept(detail::input_adapter& i)
|
||||
static bool sax_parse(detail::input_adapter&& i, json_sax_t* sax,
|
||||
input_format_t format = input_format_t::json,
|
||||
const bool strict = true)
|
||||
{
|
||||
return parser(i).accept(true);
|
||||
assert(sax);
|
||||
switch (format)
|
||||
{
|
||||
case input_format_t::json:
|
||||
return parser(std::move(i)).sax_parse(sax, strict);
|
||||
default:
|
||||
return binary_reader(std::move(i)).sax_parse(format, sax, strict);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -6092,6 +6096,15 @@ class basic_json
|
||||
return parser(detail::input_adapter(first, last)).accept(true);
|
||||
}
|
||||
|
||||
template<class IteratorType, typename std::enable_if<
|
||||
std::is_base_of<
|
||||
std::random_access_iterator_tag,
|
||||
typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>
|
||||
static bool sax_parse(IteratorType first, IteratorType last, json_sax_t* sax)
|
||||
{
|
||||
return parser(detail::input_adapter(first, last)).sax_parse(sax);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief deserialize from stream
|
||||
@deprecated This stream operator is deprecated and will be removed in
|
||||
@ -6588,6 +6601,9 @@ class basic_json
|
||||
@param[in] i an input in CBOR format convertible to an input adapter
|
||||
@param[in] strict whether to expect the input to be consumed until EOF
|
||||
(true by default)
|
||||
@param[in] allow_exceptions whether to throw exceptions in case of a
|
||||
parse error (optional, true by default)
|
||||
|
||||
@return deserialized JSON value
|
||||
|
||||
@throw parse_error.110 if the given input ends prematurely or the end of
|
||||
@ -6603,29 +6619,39 @@ class basic_json
|
||||
|
||||
@sa http://cbor.io
|
||||
@sa @ref to_cbor(const basic_json&) for the analogous serialization
|
||||
@sa @ref from_msgpack(detail::input_adapter, const bool) for the
|
||||
@sa @ref from_msgpack(detail::input_adapter, const bool, const bool) for the
|
||||
related MessagePack format
|
||||
@sa @ref from_ubjson(detail::input_adapter, const bool) for the related
|
||||
UBJSON format
|
||||
@sa @ref from_ubjson(detail::input_adapter, const bool, const bool) for the
|
||||
related UBJSON format
|
||||
|
||||
@since version 2.0.9; parameter @a start_index since 2.1.1; changed to
|
||||
consume input adapters, removed start_index parameter, and added
|
||||
@a strict parameter since 3.0.0
|
||||
@a strict parameter since 3.0.0; added @allow_exceptions parameter
|
||||
since 3.2.0
|
||||
*/
|
||||
static basic_json from_cbor(detail::input_adapter i,
|
||||
const bool strict = true)
|
||||
static basic_json from_cbor(detail::input_adapter&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
return binary_reader(i).parse_cbor(strict);
|
||||
basic_json result;
|
||||
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/*!
|
||||
@copydoc from_cbor(detail::input_adapter, const bool)
|
||||
@copydoc from_cbor(detail::input_adapter, const bool, const bool)
|
||||
*/
|
||||
template<typename A1, typename A2,
|
||||
detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
|
||||
static basic_json from_cbor(A1 && a1, A2 && a2, const bool strict = true)
|
||||
static basic_json from_cbor(A1 && a1, A2 && a2,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_cbor(strict);
|
||||
basic_json result;
|
||||
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::cbor, &sdp, strict);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -6678,6 +6704,10 @@ class basic_json
|
||||
adapter
|
||||
@param[in] strict whether to expect the input to be consumed until EOF
|
||||
(true by default)
|
||||
@param[in] allow_exceptions whether to throw exceptions in case of a
|
||||
parse error (optional, true by default)
|
||||
|
||||
@return deserialized JSON value
|
||||
|
||||
@throw parse_error.110 if the given input ends prematurely or the end of
|
||||
file was not reached when @a strict was set to true
|
||||
@ -6692,29 +6722,39 @@ class basic_json
|
||||
|
||||
@sa http://msgpack.org
|
||||
@sa @ref to_msgpack(const basic_json&) for the analogous serialization
|
||||
@sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR
|
||||
format
|
||||
@sa @ref from_ubjson(detail::input_adapter, const bool) for the related
|
||||
UBJSON format
|
||||
@sa @ref from_cbor(detail::input_adapter, const bool, const bool) for the
|
||||
related CBOR format
|
||||
@sa @ref from_ubjson(detail::input_adapter, const bool, const bool) for
|
||||
the related UBJSON format
|
||||
|
||||
@since version 2.0.9; parameter @a start_index since 2.1.1; changed to
|
||||
consume input adapters, removed start_index parameter, and added
|
||||
@a strict parameter since 3.0.0
|
||||
@a strict parameter since 3.0.0; added @allow_exceptions parameter
|
||||
since 3.2.0
|
||||
*/
|
||||
static basic_json from_msgpack(detail::input_adapter i,
|
||||
const bool strict = true)
|
||||
static basic_json from_msgpack(detail::input_adapter&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
return binary_reader(i).parse_msgpack(strict);
|
||||
basic_json result;
|
||||
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/*!
|
||||
@copydoc from_msgpack(detail::input_adapter, const bool)
|
||||
@copydoc from_msgpack(detail::input_adapter, const bool, const bool)
|
||||
*/
|
||||
template<typename A1, typename A2,
|
||||
detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
|
||||
static basic_json from_msgpack(A1 && a1, A2 && a2, const bool strict = true)
|
||||
static basic_json from_msgpack(A1 && a1, A2 && a2,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_msgpack(strict);
|
||||
basic_json result;
|
||||
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::msgpack, &sdp, strict);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -6749,6 +6789,10 @@ class basic_json
|
||||
@param[in] i an input in UBJSON format convertible to an input adapter
|
||||
@param[in] strict whether to expect the input to be consumed until EOF
|
||||
(true by default)
|
||||
@param[in] allow_exceptions whether to throw exceptions in case of a
|
||||
parse error (optional, true by default)
|
||||
|
||||
@return deserialized JSON value
|
||||
|
||||
@throw parse_error.110 if the given input ends prematurely or the end of
|
||||
file was not reached when @a strict was set to true
|
||||
@ -6763,24 +6807,36 @@ class basic_json
|
||||
@sa http://ubjson.org
|
||||
@sa @ref to_ubjson(const basic_json&, const bool, const bool) for the
|
||||
analogous serialization
|
||||
@sa @ref from_cbor(detail::input_adapter, const bool) for the related CBOR
|
||||
format
|
||||
@sa @ref from_msgpack(detail::input_adapter, const bool) for the related
|
||||
MessagePack format
|
||||
@sa @ref from_cbor(detail::input_adapter, const bool, const bool) for the
|
||||
related CBOR format
|
||||
@sa @ref from_msgpack(detail::input_adapter, const bool, const bool) for
|
||||
the related MessagePack format
|
||||
|
||||
@since version 3.1.0
|
||||
@since version 3.1.0; added @allow_exceptions parameter since 3.2.0
|
||||
*/
|
||||
static basic_json from_ubjson(detail::input_adapter i,
|
||||
const bool strict = true)
|
||||
static basic_json from_ubjson(detail::input_adapter&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
return binary_reader(i).parse_ubjson(strict);
|
||||
basic_json result;
|
||||
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/*!
|
||||
@copydoc from_ubjson(detail::input_adapter, const bool, const bool)
|
||||
*/
|
||||
template<typename A1, typename A2,
|
||||
detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>
|
||||
static basic_json from_ubjson(A1 && a1, A2 && a2, const bool strict = true)
|
||||
static basic_json from_ubjson(A1 && a1, A2 && a2,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
return binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).parse_ubjson(strict);
|
||||
basic_json result;
|
||||
detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);
|
||||
const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::ubjson, &sdp, strict);
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -34,6 +34,76 @@ using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
|
||||
class SaxCountdown : public nlohmann::json::json_sax_t
|
||||
{
|
||||
public:
|
||||
explicit SaxCountdown(const int count) : events_left(count)
|
||||
{}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool boolean(bool) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_integer(json::number_integer_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_unsigned(json::number_unsigned_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_float(json::number_float_t, const std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool string(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool key(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&, const json::exception&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int events_left = 0;
|
||||
};
|
||||
|
||||
TEST_CASE("CBOR")
|
||||
{
|
||||
SECTION("individual values")
|
||||
@ -55,6 +125,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
@ -68,6 +139,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("false")
|
||||
@ -79,6 +151,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,6 +216,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +265,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,6 +301,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,6 +318,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("-256..-24")
|
||||
@ -272,6 +349,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,6 +379,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,6 +410,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,6 +443,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -397,6 +478,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -439,6 +521,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -489,6 +572,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -554,6 +638,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,6 +671,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,6 +705,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -660,6 +747,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -709,6 +797,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -729,6 +818,8 @@ TEST_CASE("CBOR")
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result) == v);
|
||||
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -741,12 +832,14 @@ TEST_CASE("CBOR")
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xf9})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xf9})),
|
||||
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xf9}), true, false).is_discarded());
|
||||
}
|
||||
SECTION("only one byte follows")
|
||||
{
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c})),
|
||||
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xf9, 0x7c}), true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
|
||||
@ -867,6 +960,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -898,6 +992,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -930,6 +1025,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -964,6 +1060,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -979,6 +1076,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("[null]")
|
||||
@ -990,6 +1088,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("[1,2,3,4,5]")
|
||||
@ -1001,6 +1100,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("[[[[]]]]")
|
||||
@ -1012,6 +1112,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("array with uint16_t elements")
|
||||
@ -1026,6 +1127,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("array with uint32_t elements")
|
||||
@ -1042,6 +1144,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,6 +1159,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("{\"\":null}")
|
||||
@ -1067,6 +1171,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("{\"a\": {\"b\": {\"c\": {}}}}")
|
||||
@ -1081,6 +1186,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("object with uint8_t elements")
|
||||
@ -1107,6 +1213,7 @@ TEST_CASE("CBOR")
|
||||
CHECK(result[1] == 0xff); // size byte (0xff)
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("object with uint16_t elements")
|
||||
@ -1135,6 +1242,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("object with uint32_t elements")
|
||||
@ -1165,6 +1273,7 @@ TEST_CASE("CBOR")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_cbor(result) == j);
|
||||
CHECK(json::from_cbor(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1206,6 +1315,7 @@ TEST_CASE("CBOR")
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>()), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>()),
|
||||
"[json.exception.parse_error.110] parse error at 1: unexpected end of input");
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>(), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("too short byte vector")
|
||||
@ -1225,6 +1335,15 @@ TEST_CASE("CBOR")
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x62})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x62, 0x60})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x7F})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x7F, 0x60})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x82, 0x01})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x9F, 0x01})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xBF, 0x61, 0x61, 0xF5})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xA1, 0x61, 0X61})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xBF, 0x61, 0X61})), json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x18})),
|
||||
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
@ -1256,6 +1375,49 @@ TEST_CASE("CBOR")
|
||||
"[json.exception.parse_error.110] parse error at 8: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
|
||||
"[json.exception.parse_error.110] parse error at 9: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x62})),
|
||||
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x62, 0x60})),
|
||||
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x7F})),
|
||||
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x7F, 0x60})),
|
||||
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x82, 0x01})),
|
||||
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x9F, 0x01})),
|
||||
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xBF, 0x61, 0x61, 0xF5})),
|
||||
"[json.exception.parse_error.110] parse error at 5: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xA1, 0x61, 0x61})),
|
||||
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xBF, 0x61, 0x61})),
|
||||
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x18}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x19}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x19, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1a}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1a, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x62}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x62, 0x60}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7F, 0x60}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x82, 0x01}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x9F, 0x01}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xBF, 0x61, 0x61, 0xF5}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xA1, 0x61, 0x61}), true, false).is_discarded());
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xBF, 0x61, 0x61}), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("unsupported bytes")
|
||||
@ -1265,9 +1427,12 @@ TEST_CASE("CBOR")
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0x1c})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0x1c})),
|
||||
"[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0x1C");
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x1c}), true, false).is_discarded());
|
||||
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xf8})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xf8})),
|
||||
"[json.exception.parse_error.112] parse error at 1: error reading CBOR; last byte: 0xF8");
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xf8}), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("all unsupported bytes")
|
||||
@ -1317,6 +1482,7 @@ TEST_CASE("CBOR")
|
||||
})
|
||||
{
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({static_cast<uint8_t>(byte)})), json::parse_error&);
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({static_cast<uint8_t>(byte)}), true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1326,6 +1492,7 @@ TEST_CASE("CBOR")
|
||||
CHECK_THROWS_AS(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01})),
|
||||
"[json.exception.parse_error.113] parse error at 2: expected a CBOR string; last byte: 0xFF");
|
||||
CHECK(json::from_cbor(std::vector<uint8_t>({0xa1, 0xff, 0x01}), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("strict mode")
|
||||
@ -1335,6 +1502,7 @@ TEST_CASE("CBOR")
|
||||
{
|
||||
const auto result = json::from_cbor(vec, false);
|
||||
CHECK(result == json());
|
||||
CHECK(not json::from_cbor(vec, false, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("strict mode")
|
||||
@ -1342,9 +1510,34 @@ TEST_CASE("CBOR")
|
||||
CHECK_THROWS_AS(json::from_cbor(vec), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_cbor(vec),
|
||||
"[json.exception.parse_error.110] parse error at 2: expected end of input");
|
||||
CHECK(json::from_cbor(vec, true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SAX aborts")
|
||||
{
|
||||
SECTION("start_array(len)")
|
||||
{
|
||||
std::vector<uint8_t> v = {0x83, 0x01, 0x02, 0x03};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::cbor));
|
||||
}
|
||||
|
||||
SECTION("start_object(len)")
|
||||
{
|
||||
std::vector<uint8_t> v = {0xA1, 0x63, 0x66, 0x6F, 0x6F, 0xF4};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::cbor));
|
||||
}
|
||||
|
||||
SECTION("key()")
|
||||
{
|
||||
std::vector<uint8_t> v = {0xA1, 0x63, 0x66, 0x6F, 0x6F, 0xF4};
|
||||
SaxCountdown scp(1);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::cbor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use this testcase outside [hide] to run it with Valgrind
|
||||
|
@ -35,6 +35,170 @@ using nlohmann::json;
|
||||
|
||||
#include <valarray>
|
||||
|
||||
class SaxEventLogger : public nlohmann::json::json_sax_t
|
||||
{
|
||||
public:
|
||||
bool null() override
|
||||
{
|
||||
events.push_back("null()");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val) override
|
||||
{
|
||||
events.push_back(val ? "boolean(true)" : "boolean(false)");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(json::number_integer_t val) override
|
||||
{
|
||||
events.push_back("number_integer(" + std::to_string(val) + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(json::number_unsigned_t val) override
|
||||
{
|
||||
events.push_back("number_unsigned(" + std::to_string(val) + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(json::number_float_t, const std::string& s) override
|
||||
{
|
||||
events.push_back("number_float(" + s + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(std::string& val) override
|
||||
{
|
||||
events.push_back("string(" + val + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t elements) override
|
||||
{
|
||||
if (elements == no_limit)
|
||||
{
|
||||
events.push_back("start_object()");
|
||||
}
|
||||
else
|
||||
{
|
||||
events.push_back("start_object(" + std::to_string(elements) + ")");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(std::string& val) override
|
||||
{
|
||||
events.push_back("key(" + val + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
events.push_back("end_object()");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t elements) override
|
||||
{
|
||||
if (elements == no_limit)
|
||||
{
|
||||
events.push_back("start_array()");
|
||||
}
|
||||
else
|
||||
{
|
||||
events.push_back("start_array(" + std::to_string(elements) + ")");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
events.push_back("end_array()");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t position, const std::string&, const json::exception&) override
|
||||
{
|
||||
errored = true;
|
||||
events.push_back("parse_error(" + std::to_string(position) + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> events;
|
||||
bool errored = false;
|
||||
};
|
||||
|
||||
class SaxCountdown : public nlohmann::json::json_sax_t
|
||||
{
|
||||
public:
|
||||
explicit SaxCountdown(const int count) : events_left(count)
|
||||
{}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool boolean(bool) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_integer(json::number_integer_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_unsigned(json::number_unsigned_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_float(json::number_float_t, const std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool string(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool key(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&, const json::exception&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int events_left = 0;
|
||||
};
|
||||
|
||||
json parser_helper(const std::string& s);
|
||||
bool accept_helper(const std::string& s);
|
||||
|
||||
@ -49,11 +213,18 @@ json parser_helper(const std::string& s)
|
||||
CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j_nothrow));
|
||||
CHECK(j_nothrow == j);
|
||||
|
||||
json j_sax;
|
||||
nlohmann::detail::json_sax_dom_parser<json> sdp(j_sax);
|
||||
json::sax_parse(s, &sdp);
|
||||
CHECK(j_sax == j);
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
bool accept_helper(const std::string& s)
|
||||
{
|
||||
CAPTURE(s);
|
||||
|
||||
// 1. parse s without exceptions
|
||||
json j;
|
||||
CHECK_NOTHROW(json::parser(nlohmann::detail::input_adapter(s), nullptr, false).parse(true, j));
|
||||
@ -65,7 +236,23 @@ bool accept_helper(const std::string& s)
|
||||
// 3. check if both approaches come to the same result
|
||||
CHECK(ok_noexcept == ok_accept);
|
||||
|
||||
// 4. return result
|
||||
// 4. parse with SAX (compare with relaxed accept result)
|
||||
SaxEventLogger el;
|
||||
CHECK_NOTHROW(json::sax_parse(s, &el, json::input_format_t::json, false));
|
||||
CHECK(json::parser(nlohmann::detail::input_adapter(s)).accept(false) == not el.errored);
|
||||
|
||||
// 5. parse with simple callback
|
||||
json::parser_callback_t cb = [](int, json::parse_event_t, json&)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
json j_cb = json::parse(s, cb, false);
|
||||
const bool ok_noexcept_cb = not j_cb.is_discarded();
|
||||
|
||||
// 6. check if this approach came to the same result
|
||||
CHECK(ok_noexcept == ok_noexcept_cb);
|
||||
|
||||
// 7. return result
|
||||
return ok_accept;
|
||||
}
|
||||
|
||||
@ -1473,4 +1660,100 @@ TEST_CASE("parser class")
|
||||
CHECK(j == json(true));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("improve test coverage")
|
||||
{
|
||||
SECTION("parser with callback")
|
||||
{
|
||||
json::parser_callback_t cb = [](int, json::parse_event_t, json&)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
CHECK(json::parse("{\"foo\": true:", cb, false).is_discarded());
|
||||
|
||||
CHECK_THROWS_AS(json::parse("{\"foo\": true:", cb), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse("{\"foo\": true:", cb),
|
||||
"[json.exception.parse_error.101] parse error at 13: syntax error - unexpected ':'; expected '}'");
|
||||
|
||||
CHECK_THROWS_AS(json::parse("1.18973e+4932", cb), json::out_of_range&);
|
||||
CHECK_THROWS_WITH(json::parse("1.18973e+4932", cb),
|
||||
"[json.exception.out_of_range.406] number overflow parsing '1.18973e+4932'");
|
||||
}
|
||||
|
||||
SECTION("SAX parser")
|
||||
{
|
||||
SECTION("} without value")
|
||||
{
|
||||
SaxCountdown s(1);
|
||||
CHECK(json::sax_parse("{}", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("} with value")
|
||||
{
|
||||
SaxCountdown s(3);
|
||||
CHECK(json::sax_parse("{\"k1\": true}", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("second key")
|
||||
{
|
||||
SaxCountdown s(3);
|
||||
CHECK(json::sax_parse("{\"k1\": true, \"k2\": false}", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("] without value")
|
||||
{
|
||||
SaxCountdown s(1);
|
||||
CHECK(json::sax_parse("[]", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("] with value")
|
||||
{
|
||||
SaxCountdown s(2);
|
||||
CHECK(json::sax_parse("[1]", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("float")
|
||||
{
|
||||
SaxCountdown s(0);
|
||||
CHECK(json::sax_parse("3.14", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("false")
|
||||
{
|
||||
SaxCountdown s(0);
|
||||
CHECK(json::sax_parse("false", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("null")
|
||||
{
|
||||
SaxCountdown s(0);
|
||||
CHECK(json::sax_parse("null", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("true")
|
||||
{
|
||||
SaxCountdown s(0);
|
||||
CHECK(json::sax_parse("true", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("unsigned")
|
||||
{
|
||||
SaxCountdown s(0);
|
||||
CHECK(json::sax_parse("12", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("integer")
|
||||
{
|
||||
SaxCountdown s(0);
|
||||
CHECK(json::sax_parse("-12", &s) == false);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
SaxCountdown s(0);
|
||||
CHECK(json::sax_parse("\"foo\"", &s) == false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,18 +35,162 @@ using nlohmann::json;
|
||||
#include <iostream>
|
||||
#include <valarray>
|
||||
|
||||
struct SaxEventLogger : public nlohmann::json::json_sax_t
|
||||
{
|
||||
bool null() override
|
||||
{
|
||||
events.push_back("null()");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool boolean(bool val) override
|
||||
{
|
||||
events.push_back(val ? "boolean(true)" : "boolean(false)");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_integer(json::number_integer_t val) override
|
||||
{
|
||||
events.push_back("number_integer(" + std::to_string(val) + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_unsigned(json::number_unsigned_t val) override
|
||||
{
|
||||
events.push_back("number_unsigned(" + std::to_string(val) + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool number_float(json::number_float_t, const std::string& s) override
|
||||
{
|
||||
events.push_back("number_float(" + s + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string(std::string& val) override
|
||||
{
|
||||
events.push_back("string(" + val + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t elements) override
|
||||
{
|
||||
if (elements == std::size_t(-1))
|
||||
{
|
||||
events.push_back("start_object()");
|
||||
}
|
||||
else
|
||||
{
|
||||
events.push_back("start_object(" + std::to_string(elements) + ")");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool key(std::string& val) override
|
||||
{
|
||||
events.push_back("key(" + val + ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_object()override
|
||||
{
|
||||
events.push_back("end_object()");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t elements) override
|
||||
{
|
||||
if (elements == std::size_t(-1))
|
||||
{
|
||||
events.push_back("start_array()");
|
||||
}
|
||||
else
|
||||
{
|
||||
events.push_back("start_array(" + std::to_string(elements) + ")");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
events.push_back("end_array()");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t position, const std::string&, const json::exception&) override
|
||||
{
|
||||
events.push_back("parse_error(" + std::to_string(position) + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> events;
|
||||
};
|
||||
|
||||
struct SaxEventLoggerExitAfterStartObject : public SaxEventLogger
|
||||
{
|
||||
bool start_object(std::size_t elements) override
|
||||
{
|
||||
if (elements == no_limit)
|
||||
{
|
||||
events.push_back("start_object()");
|
||||
}
|
||||
else
|
||||
{
|
||||
events.push_back("start_object(" + std::to_string(elements) + ")");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct SaxEventLoggerExitAfterKey : public SaxEventLogger
|
||||
{
|
||||
bool key(std::string& val) override
|
||||
{
|
||||
events.push_back("key(" + val + ")");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger
|
||||
{
|
||||
bool start_array(std::size_t elements) override
|
||||
{
|
||||
if (elements == no_limit)
|
||||
{
|
||||
events.push_back("start_array()");
|
||||
}
|
||||
else
|
||||
{
|
||||
events.push_back("start_array(" + std::to_string(elements) + ")");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("deserialization")
|
||||
{
|
||||
SECTION("successful deserialization")
|
||||
{
|
||||
SECTION("stream")
|
||||
{
|
||||
std::stringstream ss1, ss2;
|
||||
std::stringstream ss1, ss2, ss3;
|
||||
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}]";
|
||||
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}]";
|
||||
ss3 << "[\"foo\",1,2,3,false,{\"one\":1}]";
|
||||
json j = json::parse(ss1);
|
||||
CHECK(json::accept(ss2));
|
||||
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(ss3, &l));
|
||||
CHECK(l.events.size() == 11);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "string(foo)", "number_unsigned(1)",
|
||||
"number_unsigned(2)", "number_unsigned(3)", "boolean(false)",
|
||||
"start_object()", "key(one)", "number_unsigned(1)",
|
||||
"end_object()", "end_array()"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("string literal")
|
||||
@ -55,6 +199,17 @@ TEST_CASE("deserialization")
|
||||
json j = json::parse(s);
|
||||
CHECK(json::accept(s));
|
||||
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(s, &l));
|
||||
CHECK(l.events.size() == 11);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "string(foo)", "number_unsigned(1)",
|
||||
"number_unsigned(2)", "number_unsigned(3)", "boolean(false)",
|
||||
"start_object()", "key(one)", "number_unsigned(1)",
|
||||
"end_object()", "end_array()"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("string_t")
|
||||
@ -63,6 +218,17 @@ TEST_CASE("deserialization")
|
||||
json j = json::parse(s);
|
||||
CHECK(json::accept(s));
|
||||
CHECK(j == json({"foo", 1, 2, 3, false, {{"one", 1}}}));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(s, &l));
|
||||
CHECK(l.events.size() == 11);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "string(foo)", "number_unsigned(1)",
|
||||
"number_unsigned(2)", "number_unsigned(3)", "boolean(false)",
|
||||
"start_object()", "key(one)", "number_unsigned(1)",
|
||||
"end_object()", "end_array()"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("operator<<")
|
||||
@ -93,19 +259,31 @@ TEST_CASE("deserialization")
|
||||
{
|
||||
SECTION("stream")
|
||||
{
|
||||
std::stringstream ss1, ss2, ss3, ss4;
|
||||
std::stringstream ss1, ss2, ss3, ss4, ss5;
|
||||
ss1 << "[\"foo\",1,2,3,false,{\"one\":1}";
|
||||
ss2 << "[\"foo\",1,2,3,false,{\"one\":1}";
|
||||
ss3 << "[\"foo\",1,2,3,false,{\"one\":1}";
|
||||
ss4 << "[\"foo\",1,2,3,false,{\"one\":1}";
|
||||
ss5 << "[\"foo\",1,2,3,false,{\"one\":1}";
|
||||
CHECK_THROWS_AS(json::parse(ss1), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse(ss2),
|
||||
"[json.exception.parse_error.101] parse error at 29: syntax error - unexpected end of input; expected ']'");
|
||||
CHECK(not json::accept(ss3));
|
||||
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(ss1, nullptr, false));
|
||||
CHECK_NOTHROW(j_error = json::parse(ss4, nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(ss5, &l));
|
||||
CHECK(l.events.size() == 11);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "string(foo)", "number_unsigned(1)",
|
||||
"number_unsigned(2)", "number_unsigned(3)", "boolean(false)",
|
||||
"start_object()", "key(one)", "number_unsigned(1)",
|
||||
"end_object()", "parse_error(29)"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
@ -119,6 +297,17 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(s, nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(s, &l));
|
||||
CHECK(l.events.size() == 11);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "string(foo)", "number_unsigned(1)",
|
||||
"number_unsigned(2)", "number_unsigned(3)", "boolean(false)",
|
||||
"start_object()", "key(one)", "number_unsigned(1)",
|
||||
"end_object()", "parse_error(29)"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("operator<<")
|
||||
@ -160,6 +349,11 @@ TEST_CASE("deserialization")
|
||||
std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(v) == json(true));
|
||||
CHECK(json::accept(v));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from std::array")
|
||||
@ -167,6 +361,11 @@ TEST_CASE("deserialization")
|
||||
std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
|
||||
CHECK(json::parse(v) == json(true));
|
||||
CHECK(json::accept(v));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from array")
|
||||
@ -174,6 +373,11 @@ TEST_CASE("deserialization")
|
||||
uint8_t v[] = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(v) == json(true));
|
||||
CHECK(json::accept(v));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from chars")
|
||||
@ -186,6 +390,12 @@ TEST_CASE("deserialization")
|
||||
v[4] = '\0';
|
||||
CHECK(json::parse(v) == json(true));
|
||||
CHECK(json::accept(v));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
|
||||
delete[] v;
|
||||
}
|
||||
|
||||
@ -194,6 +404,11 @@ TEST_CASE("deserialization")
|
||||
std::string v = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(v) == json(true));
|
||||
CHECK(json::accept(v));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from std::initializer_list")
|
||||
@ -201,6 +416,11 @@ TEST_CASE("deserialization")
|
||||
std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(v) == json(true));
|
||||
CHECK(json::accept(v));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("empty container")
|
||||
@ -208,6 +428,11 @@ TEST_CASE("deserialization")
|
||||
std::vector<uint8_t> v;
|
||||
CHECK_THROWS_AS(json::parse(v), json::parse_error&);
|
||||
CHECK(not json::accept(v));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(1)"}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,6 +443,12 @@ TEST_CASE("deserialization")
|
||||
std::vector<uint8_t> v = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
|
||||
CHECK(json::accept(std::begin(v), std::end(v)));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
|
||||
}
|
||||
|
||||
SECTION("from std::array")
|
||||
@ -225,6 +456,11 @@ TEST_CASE("deserialization")
|
||||
std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e'} };
|
||||
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
|
||||
CHECK(json::accept(std::begin(v), std::end(v)));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from array")
|
||||
@ -232,6 +468,11 @@ TEST_CASE("deserialization")
|
||||
uint8_t v[] = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
|
||||
CHECK(json::accept(std::begin(v), std::end(v)));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from std::string")
|
||||
@ -239,6 +480,11 @@ TEST_CASE("deserialization")
|
||||
std::string v = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
|
||||
CHECK(json::accept(std::begin(v), std::end(v)));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from std::initializer_list")
|
||||
@ -246,6 +492,11 @@ TEST_CASE("deserialization")
|
||||
std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
|
||||
CHECK(json::accept(std::begin(v), std::end(v)));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("from std::valarray")
|
||||
@ -253,6 +504,11 @@ TEST_CASE("deserialization")
|
||||
std::valarray<uint8_t> v = {'t', 'r', 'u', 'e'};
|
||||
CHECK(json::parse(std::begin(v), std::end(v)) == json(true));
|
||||
CHECK(json::accept(std::begin(v), std::end(v)));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"boolean(true)"}));
|
||||
}
|
||||
|
||||
SECTION("with empty range")
|
||||
@ -260,6 +516,11 @@ TEST_CASE("deserialization")
|
||||
std::vector<uint8_t> v;
|
||||
CHECK_THROWS_AS(json::parse(std::begin(v), std::end(v)), json::parse_error&);
|
||||
CHECK(not json::accept(std::begin(v), std::end(v)));
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(1)"}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,6 +536,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(10)"}));
|
||||
}
|
||||
|
||||
SECTION("case 2")
|
||||
@ -286,6 +552,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(11)"}));
|
||||
}
|
||||
|
||||
SECTION("case 3")
|
||||
@ -297,6 +568,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(18)"}));
|
||||
}
|
||||
|
||||
SECTION("case 4")
|
||||
@ -308,6 +584,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(18)"}));
|
||||
}
|
||||
|
||||
SECTION("case 5")
|
||||
@ -319,6 +600,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(3)"}));
|
||||
}
|
||||
|
||||
SECTION("case 6")
|
||||
@ -332,6 +618,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 7")
|
||||
@ -343,6 +634,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 8")
|
||||
@ -354,6 +650,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 9")
|
||||
@ -365,6 +666,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 10")
|
||||
@ -376,6 +682,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 11")
|
||||
@ -387,6 +698,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 12")
|
||||
@ -398,6 +714,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 13")
|
||||
@ -409,6 +730,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 14")
|
||||
@ -420,6 +746,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 15")
|
||||
@ -431,6 +762,11 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>({"parse_error(4)"}));
|
||||
}
|
||||
|
||||
SECTION("case 16")
|
||||
@ -442,6 +778,15 @@ TEST_CASE("deserialization")
|
||||
json j_error;
|
||||
CHECK_NOTHROW(j_error = json::parse(std::begin(v), std::end(v), nullptr, false));
|
||||
CHECK(j_error.is_discarded());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(std::begin(v), std::end(v), &l));
|
||||
CHECK(l.events.size() == 4);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"start_object()", "key()", "number_unsigned(11)",
|
||||
"parse_error(7)"
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -454,39 +799,89 @@ TEST_CASE("deserialization")
|
||||
{
|
||||
CHECK_THROWS_AS(json::parse(bom), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse(bom),
|
||||
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
"[json.exception.parse_error.101] parse error at 4: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
|
||||
CHECK_THROWS_AS(json::parse(std::istringstream(bom)), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse(std::istringstream(bom)),
|
||||
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
"[json.exception.parse_error.101] parse error at 4: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(bom, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(4)"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("BOM and content")
|
||||
{
|
||||
CHECK(json::parse(bom + "1") == 1);
|
||||
CHECK(json::parse(std::istringstream(bom + "1")) == 1);
|
||||
|
||||
SaxEventLogger l1, l2;
|
||||
CHECK(json::sax_parse(std::istringstream(bom + "1"), &l1));
|
||||
CHECK(json::sax_parse(bom + "1", &l2));
|
||||
CHECK(l1.events.size() == 1);
|
||||
CHECK(l1.events == std::vector<std::string>(
|
||||
{
|
||||
"number_unsigned(1)"
|
||||
}));
|
||||
CHECK(l2.events.size() == 1);
|
||||
CHECK(l2.events == std::vector<std::string>(
|
||||
{
|
||||
"number_unsigned(1)"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("2 byte of BOM")
|
||||
{
|
||||
CHECK_THROWS_AS(json::parse(bom.substr(0, 2)), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse(bom),
|
||||
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
CHECK_THROWS_WITH(json::parse(bom.substr(0, 2)),
|
||||
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'");
|
||||
|
||||
CHECK_THROWS_AS(json::parse(std::istringstream(bom.substr(0, 2))), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse(std::istringstream(bom)),
|
||||
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
CHECK_THROWS_WITH(json::parse(std::istringstream(bom.substr(0, 2))),
|
||||
"[json.exception.parse_error.101] parse error at 3: syntax error - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF\xBB'");
|
||||
|
||||
SaxEventLogger l1, l2;
|
||||
CHECK(not json::sax_parse(std::istringstream(bom.substr(0, 2)), &l1));
|
||||
CHECK(not json::sax_parse(bom.substr(0, 2), &l2));
|
||||
CHECK(l1.events.size() == 1);
|
||||
CHECK(l1.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(3)"
|
||||
}));
|
||||
CHECK(l2.events.size() == 1);
|
||||
CHECK(l2.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(3)"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("1 byte of BOM")
|
||||
{
|
||||
CHECK_THROWS_AS(json::parse(bom.substr(0, 1)), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse(bom),
|
||||
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
CHECK_THROWS_WITH(json::parse(bom.substr(0, 1)),
|
||||
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'");
|
||||
|
||||
CHECK_THROWS_AS(json::parse(std::istringstream(bom.substr(0, 1))), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::parse(std::istringstream(bom)),
|
||||
"[json.exception.parse_error.101] parse error at 1: syntax error - unexpected end of input; expected '[', '{', or a literal");
|
||||
CHECK_THROWS_WITH(json::parse(std::istringstream(bom.substr(0, 1))),
|
||||
"[json.exception.parse_error.101] parse error at 2: syntax error - invalid BOM; must be 0xEF 0xBB 0xBF if given; last read: '\xEF'");
|
||||
|
||||
SaxEventLogger l1, l2;
|
||||
CHECK(not json::sax_parse(std::istringstream(bom.substr(0, 1)), &l1));
|
||||
CHECK(not json::sax_parse(bom.substr(0, 1), &l2));
|
||||
CHECK(l1.events.size() == 1);
|
||||
CHECK(l1.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(2)"
|
||||
}));
|
||||
CHECK(l2.events.size() == 1);
|
||||
CHECK(l2.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(2)"
|
||||
}));
|
||||
}
|
||||
|
||||
SECTION("variations")
|
||||
@ -514,12 +909,46 @@ TEST_CASE("deserialization")
|
||||
// without any variation, we skip the BOM
|
||||
CHECK(json::parse(s + "null") == json());
|
||||
CHECK(json::parse(std::istringstream(s + "null")) == json());
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(json::sax_parse(s + "null", &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"null()"
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// any variation is an error
|
||||
CHECK_THROWS_AS(json::parse(s + "null"), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::parse(std::istringstream(s + "null")), json::parse_error&);
|
||||
|
||||
SaxEventLogger l;
|
||||
CHECK(not json::sax_parse(s + "null", &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
|
||||
if (i0 != 0)
|
||||
{
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(1)"
|
||||
}));
|
||||
}
|
||||
else if (i1 != 0)
|
||||
{
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(2)"
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(l.events == std::vector<std::string>(
|
||||
{
|
||||
"parse_error(3)"
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -536,4 +965,49 @@ TEST_CASE("deserialization")
|
||||
CHECK(j == 456);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SAX and early abort")
|
||||
{
|
||||
std::string s = "[1, [\"string\", 43.12], null, {\"key1\": true, \"key2\": false}]";
|
||||
|
||||
SaxEventLogger default_logger;
|
||||
SaxEventLoggerExitAfterStartObject exit_after_start_object;
|
||||
SaxEventLoggerExitAfterKey exit_after_key;
|
||||
SaxEventLoggerExitAfterStartArray exit_after_start_array;
|
||||
|
||||
json::sax_parse(s, &default_logger);
|
||||
CHECK(default_logger.events.size() == 14);
|
||||
CHECK(default_logger.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "number_unsigned(1)", "start_array()",
|
||||
"string(string)", "number_float(43.12)", "end_array()", "null()",
|
||||
"start_object()", "key(key1)", "boolean(true)", "key(key2)",
|
||||
"boolean(false)", "end_object()", "end_array()"
|
||||
}));
|
||||
|
||||
json::sax_parse(s, &exit_after_start_object);
|
||||
CHECK(exit_after_start_object.events.size() == 8);
|
||||
CHECK(exit_after_start_object.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "number_unsigned(1)", "start_array()",
|
||||
"string(string)", "number_float(43.12)", "end_array()", "null()",
|
||||
"start_object()"
|
||||
}));
|
||||
|
||||
json::sax_parse(s, &exit_after_key);
|
||||
CHECK(exit_after_key.events.size() == 9);
|
||||
CHECK(exit_after_key.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()", "number_unsigned(1)", "start_array()",
|
||||
"string(string)", "number_float(43.12)", "end_array()", "null()",
|
||||
"start_object()", "key(key1)"
|
||||
}));
|
||||
|
||||
json::sax_parse(s, &exit_after_start_array);
|
||||
CHECK(exit_after_start_array.events.size() == 1);
|
||||
CHECK(exit_after_start_array.events == std::vector<std::string>(
|
||||
{
|
||||
"start_array()"
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,76 @@ using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
|
||||
class SaxCountdown : public nlohmann::json::json_sax_t
|
||||
{
|
||||
public:
|
||||
explicit SaxCountdown(const int count) : events_left(count)
|
||||
{}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool boolean(bool) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_integer(json::number_integer_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_unsigned(json::number_unsigned_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_float(json::number_float_t, const std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool string(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool key(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&, const json::exception&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int events_left = 0;
|
||||
};
|
||||
|
||||
TEST_CASE("MessagePack")
|
||||
{
|
||||
SECTION("individual values")
|
||||
@ -55,6 +125,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
@ -68,6 +139,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("false")
|
||||
@ -79,6 +151,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,6 +185,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,6 +216,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,6 +250,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,6 +285,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +328,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,6 +379,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,6 +411,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,6 +428,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("-32768..-129 (int 16)")
|
||||
@ -380,6 +461,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,6 +506,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,6 +556,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -505,6 +589,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,6 +622,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,6 +656,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -611,6 +698,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -660,6 +748,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -680,6 +769,7 @@ TEST_CASE("MessagePack")
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result) == v);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -728,6 +818,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -759,6 +850,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -791,6 +883,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -825,6 +918,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -840,6 +934,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("[null]")
|
||||
@ -851,6 +946,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("[1,2,3,4,5]")
|
||||
@ -862,6 +958,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("[[[[]]]]")
|
||||
@ -873,6 +970,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("array 16")
|
||||
@ -887,6 +985,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("array 32")
|
||||
@ -910,6 +1009,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -924,6 +1024,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("{\"\":null}")
|
||||
@ -935,6 +1036,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("{\"a\": {\"b\": {\"c\": {}}}}")
|
||||
@ -949,6 +1051,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("map 16")
|
||||
@ -972,6 +1075,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("map 32")
|
||||
@ -1002,6 +1106,7 @@ TEST_CASE("MessagePack")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_msgpack(result) == j);
|
||||
CHECK(json::from_msgpack(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1020,10 +1125,12 @@ TEST_CASE("MessagePack")
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>()), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>()),
|
||||
"[json.exception.parse_error.110] parse error at 1: unexpected end of input");
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>(), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("too short byte vector")
|
||||
{
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0x87})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcc})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcd})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcd, 0x00})), json::parse_error&);
|
||||
@ -1039,7 +1146,12 @@ TEST_CASE("MessagePack")
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xa5, 0x68, 0x65})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0x92, 0x01})), json::parse_error&);
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0x81, 0xa1, 0x61})), json::parse_error&);
|
||||
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0x87})),
|
||||
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcc})),
|
||||
"[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcd})),
|
||||
@ -1070,6 +1182,32 @@ TEST_CASE("MessagePack")
|
||||
"[json.exception.parse_error.110] parse error at 8: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})),
|
||||
"[json.exception.parse_error.110] parse error at 9: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xa5, 0x68, 0x65})),
|
||||
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0x92, 0x01})),
|
||||
"[json.exception.parse_error.110] parse error at 3: unexpected end of input");
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0x81, 0xa1, 0x61})),
|
||||
"[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0x87}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcc}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcd}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcd, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xce}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xce, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xa5, 0x68, 0x65}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0x92, 0x01}), true, false).is_discarded());
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0x81, 0xA1, 0x61}), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("unsupported bytes")
|
||||
@ -1079,9 +1217,12 @@ TEST_CASE("MessagePack")
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xc1})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xc1})),
|
||||
"[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xC1");
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xc6}), true, false).is_discarded());
|
||||
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0xc6})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0xc6})),
|
||||
"[json.exception.parse_error.112] parse error at 1: error reading MessagePack; last byte: 0xC6");
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0xc6}), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("all unsupported bytes")
|
||||
@ -1099,6 +1240,7 @@ TEST_CASE("MessagePack")
|
||||
})
|
||||
{
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({static_cast<uint8_t>(byte)})), json::parse_error&);
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({static_cast<uint8_t>(byte)}), true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1108,6 +1250,7 @@ TEST_CASE("MessagePack")
|
||||
CHECK_THROWS_AS(json::from_msgpack(std::vector<uint8_t>({0x81, 0xff, 0x01})), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_msgpack(std::vector<uint8_t>({0x81, 0xff, 0x01})),
|
||||
"[json.exception.parse_error.113] parse error at 2: expected a MessagePack string; last byte: 0xFF");
|
||||
CHECK(json::from_msgpack(std::vector<uint8_t>({0x81, 0xff, 0x01}), true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("strict mode")
|
||||
@ -1124,9 +1267,34 @@ TEST_CASE("MessagePack")
|
||||
CHECK_THROWS_AS(json::from_msgpack(vec), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_msgpack(vec),
|
||||
"[json.exception.parse_error.110] parse error at 2: expected end of input");
|
||||
CHECK(json::from_msgpack(vec, true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SAX aborts")
|
||||
{
|
||||
SECTION("start_array(len)")
|
||||
{
|
||||
std::vector<uint8_t> v = {0x93, 0x01, 0x02, 0x03};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::msgpack));
|
||||
}
|
||||
|
||||
SECTION("start_object(len)")
|
||||
{
|
||||
std::vector<uint8_t> v = {0x81, 0xa3, 0x66, 0x6F, 0x6F, 0xc2};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::msgpack));
|
||||
}
|
||||
|
||||
SECTION("key()")
|
||||
{
|
||||
std::vector<uint8_t> v = {0x81, 0xa3, 0x66, 0x6F, 0x6F, 0xc2};
|
||||
SaxCountdown scp(1);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::msgpack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1139,7 +1139,6 @@ TEST_CASE("nst's JSONTestSuite (2)")
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_string_unescaped_tab.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_string_unicode_CapitalU.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_string_with_trailing_garbage.json",
|
||||
//"test/data/nst_json_testsuite2/test_parsing/n_structure_100000_opening_arrays.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_U+2060_word_joined.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_UTF8_BOM_no_data.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_angle_bracket_..json",
|
||||
@ -1165,7 +1164,6 @@ TEST_CASE("nst's JSONTestSuite (2)")
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_object_with_trailing_garbage.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_open_array_apostrophe.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_open_array_comma.json",
|
||||
//"test/data/nst_json_testsuite2/test_parsing/n_structure_open_array_object.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_open_array_open_object.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_open_array_open_string.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_open_array_string.json",
|
||||
@ -1199,6 +1197,21 @@ TEST_CASE("nst's JSONTestSuite (2)")
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("n (previously overflowed)")
|
||||
{
|
||||
for (auto filename :
|
||||
{
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_100000_opening_arrays.json",
|
||||
"test/data/nst_json_testsuite2/test_parsing/n_structure_open_array_object.json"
|
||||
}
|
||||
)
|
||||
{
|
||||
CAPTURE(filename);
|
||||
std::ifstream f(filename);
|
||||
CHECK(not json::accept(f));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("i -> y")
|
||||
{
|
||||
for (auto filename :
|
||||
|
@ -34,6 +34,76 @@ using nlohmann::json;
|
||||
|
||||
#include <fstream>
|
||||
|
||||
class SaxCountdown : public nlohmann::json::json_sax_t
|
||||
{
|
||||
public:
|
||||
explicit SaxCountdown(const int count) : events_left(count)
|
||||
{}
|
||||
|
||||
bool null() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool boolean(bool) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_integer(json::number_integer_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_unsigned(json::number_unsigned_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool number_float(json::number_float_t, const std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool string(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_object(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool key(std::string&) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_object() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool start_array(std::size_t) override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool end_array() override
|
||||
{
|
||||
return events_left-- > 0;
|
||||
}
|
||||
|
||||
bool parse_error(std::size_t, const std::string&, const json::exception&) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
int events_left = 0;
|
||||
};
|
||||
|
||||
TEST_CASE("UBJSON")
|
||||
{
|
||||
SECTION("individual values")
|
||||
@ -55,6 +125,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
@ -68,6 +139,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("false")
|
||||
@ -79,6 +151,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +214,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +227,7 @@ TEST_CASE("UBJSON")
|
||||
numbers.push_back(-10000000);
|
||||
numbers.push_back(-100000000);
|
||||
numbers.push_back(-1000000000);
|
||||
numbers.push_back(-2147483648L);
|
||||
numbers.push_back(-2147483647 - 1); // https://stackoverflow.com/a/29356002/266378
|
||||
for (auto i : numbers)
|
||||
{
|
||||
CAPTURE(i);
|
||||
@ -187,6 +261,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,6 +295,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,6 +316,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("-128..-1 (int8)")
|
||||
@ -270,6 +347,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,6 +380,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,6 +413,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,6 +448,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,6 +491,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -458,6 +540,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -493,6 +576,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,6 +609,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -558,6 +643,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,6 +685,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -646,6 +733,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -666,6 +754,7 @@ TEST_CASE("UBJSON")
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result) == v);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -704,6 +793,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -736,6 +826,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -769,6 +860,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -804,6 +896,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -821,6 +914,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -832,6 +926,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -843,6 +938,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -857,6 +953,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -868,6 +965,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -879,6 +977,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -893,6 +992,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -904,6 +1004,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -915,6 +1016,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -929,6 +1031,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -940,6 +1043,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -951,6 +1055,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -967,6 +1072,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -983,6 +1089,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -994,6 +1101,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1010,6 +1118,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -1028,6 +1137,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -1039,6 +1149,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1056,6 +1167,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -1067,6 +1179,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -1078,6 +1191,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1092,6 +1206,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -1103,6 +1218,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -1114,6 +1230,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1131,6 +1248,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=false")
|
||||
@ -1145,6 +1263,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
|
||||
SECTION("size=true type=true")
|
||||
@ -1159,6 +1278,7 @@ TEST_CASE("UBJSON")
|
||||
|
||||
// roundtrip
|
||||
CHECK(json::from_ubjson(result) == j);
|
||||
CHECK(json::from_ubjson(result, true, false) == j);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1190,6 +1310,80 @@ TEST_CASE("UBJSON")
|
||||
CHECK_THROWS_AS(json::to_ubjson(j), json::out_of_range&);
|
||||
CHECK_THROWS_WITH(json::to_ubjson(j), "[json.exception.out_of_range.407] number overflow serializing 9223372036854775808");
|
||||
}
|
||||
|
||||
SECTION("excessive size")
|
||||
{
|
||||
SECTION("array")
|
||||
{
|
||||
std::vector<uint8_t> v_ubjson = {'[', '$', 'Z', '#', 'L', 0x78, 0x28, 0x00, 0x68, 0x28, 0x69, 0x69, 0x17};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v_ubjson), json::out_of_range&);
|
||||
|
||||
json j;
|
||||
nlohmann::detail::json_sax_dom_callback_parser<json> scp(j, [](int, json::parse_event_t, const json&)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
CHECK_THROWS_AS(json::sax_parse(v_ubjson, &scp, json::input_format_t::ubjson), json::out_of_range&);
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
std::vector<uint8_t> v_ubjson = {'{', '$', 'Z', '#', 'L', 0x78, 0x28, 0x00, 0x68, 0x28, 0x69, 0x69, 0x17};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v_ubjson), json::out_of_range&);
|
||||
|
||||
json j;
|
||||
nlohmann::detail::json_sax_dom_callback_parser<json> scp(j, [](int, json::parse_event_t, const json&)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
CHECK_THROWS_AS(json::sax_parse(v_ubjson, &scp, json::input_format_t::ubjson), json::out_of_range&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("SAX aborts")
|
||||
{
|
||||
SECTION("start_array()")
|
||||
{
|
||||
std::vector<uint8_t> v = {'[', 'T', 'F', ']'};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::ubjson));
|
||||
}
|
||||
|
||||
SECTION("start_object()")
|
||||
{
|
||||
std::vector<uint8_t> v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::ubjson));
|
||||
}
|
||||
|
||||
SECTION("key() in object")
|
||||
{
|
||||
std::vector<uint8_t> v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'};
|
||||
SaxCountdown scp(1);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::ubjson));
|
||||
}
|
||||
|
||||
SECTION("start_array(len)")
|
||||
{
|
||||
std::vector<uint8_t> v = {'[', '#', 'i', '2', 'T', 'F'};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::ubjson));
|
||||
}
|
||||
|
||||
SECTION("start_object(len)")
|
||||
{
|
||||
std::vector<uint8_t> v = {'{', '#', 'i', '1', 3, 'f', 'o', 'o', 'F'};
|
||||
SaxCountdown scp(0);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::ubjson));
|
||||
}
|
||||
|
||||
SECTION("key() in object with length")
|
||||
{
|
||||
std::vector<uint8_t> v = {'{', 'i', 3, 'f', 'o', 'o', 'F', '}'};
|
||||
SaxCountdown scp(1);
|
||||
CHECK(not json::sax_parse(v, &scp, json::input_format_t::ubjson));
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("parsing values")
|
||||
@ -1240,6 +1434,7 @@ TEST_CASE("UBJSON")
|
||||
SECTION("optimized version (length only)")
|
||||
{
|
||||
// create vector with two elements of the same type
|
||||
std::vector<uint8_t> v_TU = {'[', '#', 'U', 2, 'T', 'T'};
|
||||
std::vector<uint8_t> v_T = {'[', '#', 'i', 2, 'T', 'T'};
|
||||
std::vector<uint8_t> v_F = {'[', '#', 'i', 2, 'F', 'F'};
|
||||
std::vector<uint8_t> v_Z = {'[', '#', 'i', 2, 'Z', 'Z'};
|
||||
@ -1253,6 +1448,7 @@ TEST_CASE("UBJSON")
|
||||
std::vector<uint8_t> v_C = {'[', '#', 'i', 2, 'C', 'a', 'C', 'a'};
|
||||
|
||||
// check if vector is parsed correctly
|
||||
CHECK(json::from_ubjson(v_TU) == json({true, true}));
|
||||
CHECK(json::from_ubjson(v_T) == json({true, true}));
|
||||
CHECK(json::from_ubjson(v_F) == json({false, false}));
|
||||
CHECK(json::from_ubjson(v_Z) == json({nullptr, nullptr}));
|
||||
@ -1379,6 +1575,136 @@ TEST_CASE("UBJSON")
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v), "[json.exception.parse_error.112] parse error at 4: expected '#' after UBJSON type information; last byte: 0x02");
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("strings")
|
||||
{
|
||||
std::vector<uint8_t> vS = {'S'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vS), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vS, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> v = {'S', 'i', '2', 'a'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v), "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
|
||||
CHECK(json::from_ubjson(v, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vC = {'C'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vC), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vC), "[json.exception.parse_error.110] parse error at 2: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vC, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("sizes")
|
||||
{
|
||||
std::vector<uint8_t> vU = {'[', '#', 'U'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vU), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vU), "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vU, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vi = {'[', '#', 'i'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vi), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vi, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vI = {'[', '#', 'I'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vI), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vI), "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vI, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vl = {'[', '#', 'l'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vl), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vl), "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vl, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vL = {'[', '#', 'L'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vL), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vL), "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vL, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> v0 = {'[', '#', 'T', ']'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v0), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v0), "[json.exception.parse_error.113] parse error at 3: byte after '#' must denote a number type; last byte: 0x54");
|
||||
CHECK(json::from_ubjson(v0, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("types")
|
||||
{
|
||||
std::vector<uint8_t> v0 = {'[', '$'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v0), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v0), "[json.exception.parse_error.110] parse error at 3: unexpected end of input");
|
||||
CHECK(json::from_ubjson(v0, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vi = {'[', '$', '#'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vi), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vi), "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vi, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vT = {'[', '$', 'T'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vT), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vT), "[json.exception.parse_error.110] parse error at 4: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vT, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("arrays")
|
||||
{
|
||||
std::vector<uint8_t> vST = {'[', '$', 'i', '#', 'i', 2, 1};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vST), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vST), "[json.exception.parse_error.110] parse error at 8: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vST, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vS = {'[', '#', 'i', 2, 'i', 1};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vS), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at 7: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vS, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> v = {'[', 'i', 2, 'i', 1};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v), "[json.exception.parse_error.110] parse error at 6: unexpected end of input");
|
||||
CHECK(json::from_ubjson(v, true, false).is_discarded());
|
||||
}
|
||||
|
||||
SECTION("objects")
|
||||
{
|
||||
std::vector<uint8_t> vST = {'{', '$', 'i', '#', 'i', 2, 'i', 1, 'a', 1};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vST), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vST), "[json.exception.parse_error.110] parse error at 11: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vST, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vT = {'{', '$', 'i', 'i', 1, 'a', 1};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vT), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vT), "[json.exception.parse_error.112] parse error at 4: expected '#' after UBJSON type information; last byte: 0x69");
|
||||
CHECK(json::from_ubjson(vT, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vS = {'{', '#', 'i', 2, 'i', 1, 'a', 'i', 1};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vS), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vS), "[json.exception.parse_error.110] parse error at 10: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vS, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> v = {'{', 'i', 1, 'a', 'i', 1};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v), "[json.exception.parse_error.110] parse error at 7: unexpected end of input");
|
||||
CHECK(json::from_ubjson(v, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> v2 = {'{', 'i', 1, 'a', 'i', 1, 'i'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v2), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v2), "[json.exception.parse_error.110] parse error at 8: unexpected end of input");
|
||||
CHECK(json::from_ubjson(v2, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> v3 = {'{', 'i', 1, 'a'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(v3), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(v3), "[json.exception.parse_error.110] parse error at 5: unexpected end of input");
|
||||
CHECK(json::from_ubjson(v3, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vST1 = {'{', '$', 'd', '#', 'i', 2, 'i', 1, 'a'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vST1), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vST1), "[json.exception.parse_error.110] parse error at 10: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vST1, true, false).is_discarded());
|
||||
|
||||
std::vector<uint8_t> vST2 = {'{', '#', 'i', 2, 'i', 1, 'a'};
|
||||
CHECK_THROWS_AS(json::from_ubjson(vST2), json::parse_error&);
|
||||
CHECK_THROWS_WITH(json::from_ubjson(vST2), "[json.exception.parse_error.110] parse error at 8: unexpected end of input");
|
||||
CHECK(json::from_ubjson(vST2, true, false).is_discarded());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("writing optimized values")
|
||||
|
Loading…
Reference in New Issue
Block a user