Merge branch 'develop' into claude/fix-issue-3989-db7e45

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-25 06:24:39 +01:00
commit a6c5d970f3
164 files changed
+5089 -4296

No files matched your search

+91 -76
View File
@@ -331,6 +331,26 @@ class binary_reader
return is_object ? sax->end_object() : sax->end_array();
}
/*!
@brief close the innermost open container and report whether that
completed the value being read
The value begun by the driver is complete once its container is not
inside another one.
@param[out] value_complete whether no container is left open
@return whether the SAX parser accepted the end event
*/
bool leave_container_and_check(bool& value_complete)
{
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
{
return false;
}
value_complete = container_stack.empty();
return true;
}
//////////
// BSON //
//////////
@@ -1268,6 +1288,34 @@ class binary_reader
return last_byte_error(exception_id::invalid_string_or_size, concat("indefinite-length ", type_name, " is not allowed inside indefinite-length ", type_name, "; last byte: 0x", get_token_string()), context);
}
/*!
@brief report an object key that is not a string
@param[in] found description of what was found instead of a string key
@return false
*/
bool unsupported_key_type(const char* found)
{
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
}
/*!
@brief read the length of a CBOR string or byte array and then its bytes
@tparam NumberType the type of the length field
@tparam ContainerType the destination container (string_t or binary_t)
@param[in] context further context information (for diagnostics)
@param[out] result container the bytes are appended to
@return whether reading completed
*/
template<typename NumberType, typename ContainerType>
bool get_cbor_sized_bytes(const char* context, ContainerType& result)
{
NumberType len{};
return get_number(len) && get_bytes(len, context, result);
}
/*!
@brief reads a definite-length CBOR string
@@ -1318,26 +1366,22 @@ class binary_reader
case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
{
std::uint8_t len{};
return get_number(len) && get_string(len, result);
return get_cbor_sized_bytes<std::uint8_t>("string", result);
}
case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
{
std::uint16_t len{};
return get_number(len) && get_string(len, result);
return get_cbor_sized_bytes<std::uint16_t>("string", result);
}
case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
{
std::uint32_t len{};
return get_number(len) && get_string(len, result);
return get_cbor_sized_bytes<std::uint32_t>("string", result);
}
case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
{
std::uint64_t len{};
return get_number(len) && get_string(len, result);
return get_cbor_sized_bytes<std::uint64_t>("string", result);
}
default:
@@ -1476,7 +1520,7 @@ class binary_reader
// a key that begins an item is skipped with its value when recovering
// (see resync); a break stop code or a reserved byte begins none
skip_requested = is_cbor_item_head(static_cast<unsigned int>(current));
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
return unsupported_key_type(found);
}
/*!
@@ -1529,30 +1573,22 @@ class binary_reader
case 0x58: // Binary data (one-byte uint8_t for n follows)
{
std::uint8_t len{};
return get_number(len) &&
get_binary(len, result);
return get_cbor_sized_bytes<std::uint8_t>("binary", result);
}
case 0x59: // Binary data (two-byte uint16_t for n follow)
{
std::uint16_t len{};
return get_number(len) &&
get_binary(len, result);
return get_cbor_sized_bytes<std::uint16_t>("binary", result);
}
case 0x5A: // Binary data (four-byte uint32_t for n follow)
{
std::uint32_t len{};
return get_number(len) &&
get_binary(len, result);
return get_cbor_sized_bytes<std::uint32_t>("binary", result);
}
case 0x5B: // Binary data (eight-byte uint64_t for n follow)
{
std::uint64_t len{};
return get_number(len) &&
get_binary(len, result);
return get_cbor_sized_bytes<std::uint64_t>("binary", result);
}
default:
@@ -1767,12 +1803,12 @@ class binary_reader
if (at_end)
{
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
bool value_complete = false;
if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete)))
{
return false;
}
// the value begun here is complete once its container is
if (container_stack.empty())
if (value_complete)
{
return true;
}
@@ -2373,20 +2409,17 @@ class binary_reader
case 0xD9: // str 8
{
std::uint8_t len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
return get_string_with_length<std::uint8_t>(result, context);
}
case 0xDA: // str 16
{
std::uint16_t len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
return get_string_with_length<std::uint16_t>(result, context);
}
case 0xDB: // str 32
{
std::uint32_t len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
return get_string_with_length<std::uint32_t>(result, context);
}
default:
@@ -2486,7 +2519,7 @@ class binary_reader
// the key is skipped with its value when recovering (see resync)
skip_requested = true;
return last_byte_error(exception_id::invalid_string_or_size, "only string keys are supported, but found ", "object key", found);
return unsupported_key_type(found);
}
/*!
@@ -2631,12 +2664,12 @@ class binary_reader
if (container_stack.back().remaining == 0)
{
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
bool value_complete = false;
if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete)))
{
return false;
}
// the value begun here is complete once its container is
if (container_stack.empty())
if (value_complete)
{
return true;
}
@@ -2981,8 +3014,7 @@ class binary_reader
{
case 'U':
{
std::uint8_t len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
return get_string_with_length<std::uint8_t>(result, context);
}
case 'i':
@@ -3015,8 +3047,7 @@ class binary_reader
{
break;
}
std::uint16_t len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
return get_string_with_length<std::uint16_t>(result, context);
}
case 'm':
@@ -3025,8 +3056,7 @@ class binary_reader
{
break;
}
std::uint32_t len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
return get_string_with_length<std::uint32_t>(result, context);
}
case 'M':
@@ -3035,8 +3065,7 @@ class binary_reader
{
break;
}
std::uint64_t len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
return get_string_with_length<std::uint64_t>(result, context);
}
default:
@@ -3942,12 +3971,12 @@ class binary_reader
if (at_end)
{
if (JSON_HEDLEY_UNLIKELY(!leave_container()))
bool value_complete = false;
if (JSON_HEDLEY_UNLIKELY(!leave_container_and_check(value_complete)))
{
return false;
}
// the value begun here is complete once its container is
if (container_stack.empty())
if (value_complete)
{
return true;
}
@@ -4833,6 +4862,22 @@ class binary_reader
return true;
}
/*!
@brief read a length of type @a NumberType, then a string of that many bytes
@tparam NumberType the type of the length field
@param[out] result string the bytes are appended to
@param[in] context further context information (for diagnostics)
@return whether reading and validating the string succeeded
*/
template<typename NumberType>
bool get_string_with_length(string_t& result, const char* context)
{
NumberType len{};
return get_number(len) && get_string(len, result) && check_string_utf8(result, context);
}
/*!
@brief create a byte array by reading bytes from the input
@@ -5334,37 +5379,7 @@ class binary_reader
const std::string& context) const
{
std::string error_msg = "syntax error while parsing ";
switch (input_format)
{
case input_format_t::cbor:
error_msg += "CBOR";
break;
case input_format_t::msgpack:
error_msg += "MessagePack";
break;
case input_format_t::ubjson:
error_msg += "UBJSON";
break;
case input_format_t::bson:
error_msg += "BSON";
break;
case input_format_t::bjdata:
error_msg += "BJData";
break;
case input_format_t::bon8:
error_msg += "BON8";
break;
case input_format_t::json: // LCOV_EXCL_LINE
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
error_msg += input_format_name(input_format);
return concat(error_msg, ' ', context, ": ", detail);
}
@@ -67,6 +67,36 @@ namespace detail
/// the supported input formats
enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata, bon8 };
/// @return the name of a binary input format, as used in exception messages
inline const char* input_format_name(const input_format_t format) noexcept
{
switch (format)
{
case input_format_t::cbor:
return "CBOR";
case input_format_t::msgpack:
return "MessagePack";
case input_format_t::ubjson:
return "UBJSON";
case input_format_t::bson:
return "BSON";
case input_format_t::bjdata:
return "BJData";
case input_format_t::bon8:
return "BON8";
case input_format_t::json: // LCOV_EXCL_LINE
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
return ""; // LCOV_EXCL_LINE
}
}
/// @return the message of the parse_error thrown or reported for an empty input
inline const char* empty_input_message() noexcept
{
return "attempting to parse an empty input; check that your input string or stream contains the expected JSON";
}
////////////////////
// input adapters //
////////////////////
@@ -1273,7 +1303,7 @@ inline file_input_adapter input_adapter(std::FILE* file)
{
if (file == nullptr)
{
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr));
}
return file_input_adapter(file);
}
@@ -1282,7 +1312,7 @@ inline input_stream_adapter input_adapter(std::istream& stream)
{
if (stream.rdbuf() == nullptr)
{
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr));
}
return input_stream_adapter(stream);
}
@@ -1307,7 +1337,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b)
{
if (b == nullptr)
{
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, empty_input_message(), nullptr));
}
auto length = std::strlen(reinterpret_cast<const char*>(b));
const auto* ptr = reinterpret_cast<const char*>(b);
+53 -73
View File
@@ -323,6 +323,52 @@ struct diagnostic_positions
};
#endif
/*!
@brief error state shared by the DOM-building SAX parsers
Holds whether a parse error occurred and whether errors are thrown, and
implements the parse_error() and is_errored() members of the SAX interface for
json_sax_dom_parser and json_sax_dom_callback_parser.
*/
class json_sax_dom_error_state
{
public:
explicit json_sax_dom_error_state(const bool allow_exceptions_) noexcept
: allow_exceptions(allow_exceptions_)
{}
// make class move-only
json_sax_dom_error_state(const json_sax_dom_error_state&) = delete;
json_sax_dom_error_state(json_sax_dom_error_state&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
json_sax_dom_error_state& operator=(const json_sax_dom_error_state&) = delete;
json_sax_dom_error_state& operator=(json_sax_dom_error_state&&) = delete; // the const member prevents assignment
~json_sax_dom_error_state() = default;
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
bool is_errored() const
{
return errored;
}
private:
/// whether a syntax error occurred
bool errored = false;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
};
/*!
@brief a hash table that finds the position of a key in an object whose
elements are kept in a vector (ordered_map)
@@ -434,7 +480,7 @@ read up to it.
not use the value after a parse error)
*/
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type, bool Collect = false>
class json_sax_dom_parser
class json_sax_dom_parser : public json_sax_dom_error_state
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
@@ -450,7 +496,7 @@ class json_sax_dom_parser
@param[in] allow_exceptions_ whether parse errors yield exceptions
*/
explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr)
: root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)
: json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_)
{}
// make class move-only
@@ -601,24 +647,6 @@ class json_sax_dom_parser
return true;
}
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
using object_t = typename BasicJsonType::object_t;
using array_t = typename BasicJsonType::array_t;
@@ -916,7 +944,7 @@ class json_sax_dom_parser
/// directly if the value was the one too many.
void place_container(const frame& f, frame* enclosing, BasicJsonType&& value, const bool enclosing_is_innermost)
{
// a container without a slot of its own is collected for an enclosing one
// only a container with a slot may have no enclosing container
JSON_ASSERT(f.slot != nullptr || enclosing != nullptr);
if (f.slot != nullptr)
{
@@ -1334,10 +1362,6 @@ class json_sax_dom_parser
/// the array that stores the elements of the innermost array as they
/// arrive (frames.back().array), or nullptr
array_t* stored_array = nullptr;
/// whether a syntax error occurred
bool errored = false;
/// whether to throw exceptions in case of errors
const bool allow_exceptions = true;
/// the lexer reference to obtain the current position
lexer_t* m_lexer_ref = nullptr;
};
@@ -1362,7 +1386,7 @@ constructor contains the parsed value.
@tparam BasicJsonType the JSON type
*/
template<typename BasicJsonType, typename InputAdapterType>
class json_sax_dom_binary_parser
class json_sax_dom_binary_parser : public json_sax_dom_error_state
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
@@ -1378,7 +1402,7 @@ class json_sax_dom_binary_parser
@param[in] allow_exceptions_ whether parse errors yield exceptions
*/
explicit json_sax_dom_binary_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr)
: root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)
: json_sax_dom_error_state(allow_exceptions_), root(r), m_lexer_ref(lexer_)
{}
// make class move-only
@@ -1507,24 +1531,6 @@ class json_sax_dom_binary_parser
return true;
}
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@@ -1578,16 +1584,12 @@ class json_sax_dom_binary_parser
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;
/// the lexer reference to obtain the current position
lexer_t* m_lexer_ref = nullptr;
};
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type>
class json_sax_dom_callback_parser
class json_sax_dom_callback_parser : public json_sax_dom_error_state
{
public:
using number_integer_t = typename BasicJsonType::number_integer_t;
@@ -1603,7 +1605,7 @@ class json_sax_dom_callback_parser
parser_callback_t cb,
const bool allow_exceptions_ = true,
lexer_t* lexer_ = nullptr)
: root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)
: json_sax_dom_error_state(allow_exceptions_), root(r), callback(std::move(cb)), m_lexer_ref(lexer_)
{
keep_stack.push_back(true);
}
@@ -1867,24 +1869,6 @@ class json_sax_dom_callback_parser
return true;
}
template<class Exception>
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
const Exception& ex)
{
errored = true;
static_cast<void>(ex);
if (allow_exceptions)
{
JSON_THROW(ex);
}
return false;
}
constexpr bool is_errored() const
{
return errored;
}
private:
/*!
@@ -2151,12 +2135,8 @@ class json_sax_dom_callback_parser
/// value is later rejected by the callback, instead of erasing the
/// member entirely
std::vector<std::pair<BasicJsonType*, BasicJsonType>> duplicate_key_stash {};
/// 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;
/// the value of the key event for the callback (see key_value())
+36 -187
View File
@@ -683,225 +683,74 @@ class lexer : public lexer_base<BasicJsonType>
// invalid control characters
case 0x00:
{
error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x01:
{
error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x02:
{
error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x03:
{
error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x04:
{
error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x05:
{
error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x06:
{
error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x07:
{
error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x08:
{
error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x09:
{
error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x0A:
{
error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x0B:
{
error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x0C:
{
error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x0D:
{
error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x0E:
{
error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x0F:
{
error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x10:
{
error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x11:
{
error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x12:
{
error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x13:
{
error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x14:
{
error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x15:
{
error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x16:
{
error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x17:
{
error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x18:
{
error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x19:
{
error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x1A:
{
error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x1B:
{
error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x1C:
{
error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x1D:
{
error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x1E:
{
error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
error_cause = error_kind::string;
return token_type::parse_error;
}
case 0x1F:
{
error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
static const std::array<const char*, 32> control_character_messages = {{
"invalid string: control character U+0000 (NUL) must be escaped to \\u0000",
"invalid string: control character U+0001 (SOH) must be escaped to \\u0001",
"invalid string: control character U+0002 (STX) must be escaped to \\u0002",
"invalid string: control character U+0003 (ETX) must be escaped to \\u0003",
"invalid string: control character U+0004 (EOT) must be escaped to \\u0004",
"invalid string: control character U+0005 (ENQ) must be escaped to \\u0005",
"invalid string: control character U+0006 (ACK) must be escaped to \\u0006",
"invalid string: control character U+0007 (BEL) must be escaped to \\u0007",
"invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b",
"invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t",
"invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n",
"invalid string: control character U+000B (VT) must be escaped to \\u000B",
"invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f",
"invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r",
"invalid string: control character U+000E (SO) must be escaped to \\u000E",
"invalid string: control character U+000F (SI) must be escaped to \\u000F",
"invalid string: control character U+0010 (DLE) must be escaped to \\u0010",
"invalid string: control character U+0011 (DC1) must be escaped to \\u0011",
"invalid string: control character U+0012 (DC2) must be escaped to \\u0012",
"invalid string: control character U+0013 (DC3) must be escaped to \\u0013",
"invalid string: control character U+0014 (DC4) must be escaped to \\u0014",
"invalid string: control character U+0015 (NAK) must be escaped to \\u0015",
"invalid string: control character U+0016 (SYN) must be escaped to \\u0016",
"invalid string: control character U+0017 (ETB) must be escaped to \\u0017",
"invalid string: control character U+0018 (CAN) must be escaped to \\u0018",
"invalid string: control character U+0019 (EM) must be escaped to \\u0019",
"invalid string: control character U+001A (SUB) must be escaped to \\u001A",
"invalid string: control character U+001B (ESC) must be escaped to \\u001B",
"invalid string: control character U+001C (FS) must be escaped to \\u001C",
"invalid string: control character U+001D (GS) must be escaped to \\u001D",
"invalid string: control character U+001E (RS) must be escaped to \\u001E",
"invalid string: control character U+001F (US) must be escaped to \\u001F"
}
};
error_message = control_character_messages[static_cast<std::size_t>(current)];
error_cause = error_kind::string;
return token_type::parse_error;
}
+35 -15
View File
@@ -236,6 +236,39 @@ class parser
return m_lexer.get_string();
}
/*!
@brief apply the end-of-input handling after a value was parsed
In strict mode, the next token must be the end of the input; otherwise
parse_error.101 is reported through @a sax. In non-strict mode, the
lookahead is released so the caller can keep reading the input right after
the parsed value.
@param[in,out] sax the SAX parser to report an error to
@param[in] strict whether to expect the last token to be EOF
@return true, or what @a sax returned for the error
*/
template<typename SAX>
bool finish_input(SAX& sax, const bool strict)
{
if (strict)
{
// strict mode: next byte must be EOF
if (get_token() != token_type::end_of_input)
{
return syntax_error(sax, exception_message(token_type::end_of_input, "value"));
}
}
else
{
// the caller keeps using the input: position it right after
// the value by leaving the character that terminated it
m_lexer.release_lookahead();
}
return true;
}
/*!
@brief run a DOM SAX parser to completion and position the lexer
@@ -255,20 +288,7 @@ class parser
{
sax_parse_internal<false>(&sdp);
if (strict)
{
// in strict mode, input must be completely read
if (get_token() != token_type::end_of_input)
{
syntax_error(sdp, exception_message(token_type::end_of_input, "value"));
}
}
else
{
// the caller keeps using the input: position it right after
// the value by leaving the character that terminated it
m_lexer.release_lookahead();
}
static_cast<void>(finish_input(sdp, strict));
return !sdp.is_errored();
}
@@ -493,7 +513,7 @@ class parser
{
// there is nothing to recover
static_cast<void>(report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr), allow_recovery));
empty_input_message(), nullptr), allow_recovery));
return false;
}
@@ -96,7 +96,7 @@ JSON_HEDLEY_ALWAYS_INLINE std::size_t find_string_special(const unsigned char* d
// printable ASCII except '"' and '\\' - is emitted unchanged. Note this differs
// from is_string_special() only in that 0x7F is also a stop (it is escaped as
// \u007f under ensure_ascii).
inline bool is_ascii_copyable(unsigned char c) noexcept
constexpr bool is_ascii_copyable(unsigned char c) noexcept
{
return c >= 0x20u && c < 0x7Fu && c != '"' && c != '\\';
}