mirror of
https://github.com/nlohmann/json.git
synced 2026-10-16 19:18:12 +00:00
Changes promised in the replies to the review of #5620: - read() reuses the owned copy: a copied input (an rvalue byte container, a stream, FILE*, wide characters, an iterator range) is appended to the cleared owned buffer instead of a fresh std::string being move-assigned over it. A moved-in std::string still replaces it. - The error replay runs the library parser with a SAX handler that stores nothing and throws the reported exception, instead of BasicJsonType::parse(), which built a DOM up to the error. - Pointers to and arrays of wide characters, and FILE*, take the input adapter path (converted to UTF-8 into an owned buffer) instead of failing a static_assert. Wide pointers are NUL-terminated. - Streams are read in blocks through rdbuf() into the buffer, instead of character by character through the input adapter; eofbit is set at the end, as by the library's stream adapter. - tape/tape_size/tape_cap are now nodes/node_count/node_capacity. - The builder's members get descriptive, m_-prefixed names. - The shallow frame stack is a std::array (in a wrapper with a user-provided constructor: -Weffc++ wants the member initialized, and value-initializing the 64 frames costs up to 30 % on small documents). - The value dispatch macro has no NEXT parameter; the switch is left with break, and the macro is undefined after its last use. - literal() and digits_exceed() deduce the length from the string literal; each literal has one helper; the two `if (!is_float)` blocks are merged. - arena_ensure() sits right before the writes; the decode arena is a std::vector<char>. - The builder records the deepest nesting, and materialize() sizes its stack from it once (growing it only if a subtree is deeper). - Comment fixes (hex4, slow_string, the NUL rule, put_utf8, 0 - m, the arena resize, grow()) and formatting (macro backslashes, digit cases, one power of ten per line, value_t assertion next to is_container()). - build_with's template parameters follow the builder's order. Greg's idea of keeping the source offset in `off` and the arena offset in `next` for escaped strings was measured and not adopted: string access got 7-18 % slower with a select, and 3.5-18 % slower with a branch-free field pick (Apple M-series, clang -O3), parsing was unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
168 lines
4.7 KiB
C++
168 lines
4.7 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_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));
|
|
}
|
|
|
|
/// 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
|