mirror of
https://github.com/nlohmann/json.git
synced 2026-10-16 19:18:12 +00:00
basic_json_document gets a second template parameter, Editable (false by default), plus the aliases json_editable_document, json_editable_view, ordered_json_editable_document and ordered_json_editable_view. Editable documents can change values and structure without rewriting the source text: set()/push_back() on values, keys, array indices and JSON pointers; insert() before an array element; erase() of an object key, array index or JSON pointer. New values and element sequences go into edit storage that the document owns and never moves, so views keep referring to their value across edits and a parsed node never moves. Read-only documents walk the plain node array and are unaffected. Strings are checked for UTF-8 on entry, so dump() of an editable document never throws type_error.316. Binary values cannot be stored (type_error.319). A seeded differential test applies random edits to an editable document and to the equivalent ordered_json and compares both after every step. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
179 lines
5.1 KiB
C++
179 lines
5.1 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm> // min
|
|
#include <cstddef> // size_t
|
|
#include <string> // string
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <nlohmann/detail/view/builder.hpp>
|
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
namespace view
|
|
{
|
|
|
|
// Exceptions are thrown out of line, so that the accessors that may throw stay
|
|
// small enough to be inlined.
|
|
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const char* prefix, const char* type)
|
|
{
|
|
NLOHMANN_VIEW_THROW(type_error::create(id, concat(prefix, type), nullptr));
|
|
}
|
|
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(int id, const std::string& msg)
|
|
{
|
|
NLOHMANN_VIEW_THROW(type_error::create(id, msg, nullptr));
|
|
}
|
|
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_out_of_range(int id, const std::string& msg)
|
|
{
|
|
NLOHMANN_VIEW_THROW(out_of_range::create(id, msg, nullptr));
|
|
}
|
|
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_invalid_iterator(int id, const char* msg)
|
|
{
|
|
NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr));
|
|
}
|
|
|
|
/// a parse error without a position (as those of json_pointer)
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(int id, const std::string& msg)
|
|
{
|
|
NLOHMANN_VIEW_THROW(parse_error::create(id, 0, msg, nullptr));
|
|
}
|
|
|
|
/// SAX handler of the error replay: accepts every event without storing it,
|
|
/// and throws the exception the parser reports
|
|
template<typename BasicJsonType>
|
|
struct throwing_sax
|
|
{
|
|
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 binary_t = typename BasicJsonType::binary_t;
|
|
|
|
bool null()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool boolean(bool /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool number_integer(number_integer_t /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool number_unsigned(number_unsigned_t /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool string(string_t& /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool binary(binary_t& /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool start_object(std::size_t /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool key(string_t& /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool end_object()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool start_array(std::size_t /*unused*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool end_array()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/// (a template, as in json_sax_dom_parser: the parser passes the concrete
|
|
/// exception type, which is thrown as is)
|
|
template<class Exception>
|
|
[[noreturn]] bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const Exception& ex)
|
|
{
|
|
NLOHMANN_VIEW_THROW(ex);
|
|
}
|
|
};
|
|
|
|
/*!
|
|
@brief throw the exception BasicJsonType::parse would throw for this input
|
|
|
|
The view accepts exactly the inputs parse() accepts, so on a failure the
|
|
library parser is run on the same bytes: it throws the exception parse() would
|
|
throw, with the same message, position, and "last read" token. The replay
|
|
uses a SAX handler that stores nothing, so it only lexes and parses up to the
|
|
error, without building values. The error path is cold, so this costs nothing
|
|
on valid input. Should parse() accept the input nevertheless (a bug), the
|
|
view's own failure is reported.
|
|
*/
|
|
template<typename BasicJsonType>
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_parse_failure(const parse_failure& f, const char* src, std::size_t size,
|
|
bool ignore_comments, bool ignore_trailing_commas)
|
|
{
|
|
if (f.code == error_code::input_too_large)
|
|
{
|
|
// (the limit is detail::view::max_input_size(): 4 GiB minus 16 bytes)
|
|
NLOHMANN_VIEW_THROW(out_of_range::create(416, "input of 4294967280 bytes or more is not supported by json_document", nullptr));
|
|
}
|
|
throwing_sax<BasicJsonType> sax;
|
|
const bool accepted = BasicJsonType::sax_parse(src, src + size, &sax, input_format_t::json, true, ignore_comments, ignore_trailing_commas);
|
|
// LCOV_EXCL_START (only if parse() accepts what the view rejects: a bug)
|
|
static_cast<void>(accepted);
|
|
|
|
position_t pos;
|
|
const std::size_t off = (std::min)(f.offset, size);
|
|
pos.chars_read_total = off + 1;
|
|
std::size_t line_start = 0;
|
|
for (std::size_t i = 0; i < off; ++i)
|
|
{
|
|
if (src[i] == '\n')
|
|
{
|
|
++pos.lines_read;
|
|
line_start = i + 1;
|
|
}
|
|
}
|
|
pos.chars_read_current_line = off + 1 - line_start;
|
|
NLOHMANN_VIEW_THROW(parse_error::create(101, pos, "syntax error while parsing value", nullptr));
|
|
// LCOV_EXCL_STOP
|
|
}
|
|
|
|
} // namespace view
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|