mirror of
https://github.com/nlohmann/json.git
synced 2026-10-14 10:17:12 +00:00
Derive the lexer position lazily for seekable input
The lexer counted the read position on every character: two increments and a newline check per get(), all on members kept in memory. For seekable input (pointers, strings, other random-access byte ranges) the position can be derived instead: chars_read_total is the adapter's read count (including reads at EOF) minus a pending unget, and line and column are recomputed from the consumed input only when get_position() is called, i.e. on parse errors. JSON_DIAGNOSTIC_POSITIONS uses the new O(1) get_chars_read_total(). Stream, file, wide-string and user-defined adapters keep the eager counters. Parsing pretty-printed input gets 8-12% faster and accept() 12-27% (canada, citm_catalog, twitter; clang 21 arm64 and GCC 13 x86-64). Error messages, byte offsets, SAX positions and diagnostic positions are unchanged (differential comparison against develop on 548k inputs). Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
7 files changed
+328
-57
No files matched your search
@@ -35,6 +35,7 @@ using nlohmann::json;
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <valarray>
|
||||
#include <vector>
|
||||
|
||||
#include "sax_event_loggers.hpp"
|
||||
|
||||
@@ -1250,3 +1251,71 @@ TEST_CASE_TEMPLATE("deserialization of different character types (UTF-32)", T, c
|
||||
CHECK(json::sax_parse(v, &l));
|
||||
CHECK(l.events.size() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("parse error positions are the same for contiguous and stream input")
|
||||
{
|
||||
// contiguous input derives the position from the input on error, stream
|
||||
// input counts it while reading (see lexer::lazy_position); the cases
|
||||
// cover newlines before the error, ungetting a newline after a number,
|
||||
// newlines inside a string, a comment, and reads at the end of input
|
||||
struct test_case
|
||||
{
|
||||
std::string input;
|
||||
std::size_t byte;
|
||||
std::string message;
|
||||
};
|
||||
const std::vector<test_case> cases =
|
||||
{
|
||||
{"[\n 1,\n 2\n", 12, "[json.exception.parse_error.101] parse error at line 4, column 1: syntax error while parsing array - unexpected end of input; expected ']'"},
|
||||
{"[1\n,2,\n]", 8, "[json.exception.parse_error.101] parse error at line 3, column 1: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal"},
|
||||
{"[1\n x]", 5, "[json.exception.parse_error.101] parse error at line 2, column 2: syntax error while parsing array - invalid literal; last read: '1<U+000A> x'; expected ']'"},
|
||||
{"1\n2", 3, "[json.exception.parse_error.101] parse error at line 2, column 1: syntax error while parsing value - unexpected number literal; expected end of input"},
|
||||
{
|
||||
"{\"a\":\n\"b\nc\"}", 9, R"([json.exception.parse_error.101] parse error at line 3, column 0: syntax error while parsing value - invalid string: control character U+000A (LF) must be escaped to \u000A or \n; last read: '"b<U+000A>')"
|
||||
},
|
||||
{"/* a\n b */\n x", 13, "[json.exception.parse_error.101] parse error at line 3, column 2: syntax error while parsing value - invalid literal; last read: '/* a<U+000A> b */<U+000A> x'"},
|
||||
{"\n\n", 3, "[json.exception.parse_error.101] parse error at line 3, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal"},
|
||||
{"[\"a\",\n 1e\n]", 10, "[json.exception.parse_error.101] parse error at line 3, column 0: syntax error while parsing value - invalid number; expected '+', '-', or digit after exponent; last read: '1e<U+000A>'"},
|
||||
{"[tru\n]", 5, "[json.exception.parse_error.101] parse error at line 2, column 0: syntax error while parsing value - invalid literal; last read: '[tru<U+000A>'"},
|
||||
};
|
||||
|
||||
for (const auto& c : cases)
|
||||
{
|
||||
CAPTURE(c.input)
|
||||
const std::string expected = std::to_string(c.byte) + " " + c.message;
|
||||
std::string string_result;
|
||||
std::string pointer_result;
|
||||
std::string stream_result;
|
||||
try
|
||||
{
|
||||
const json j = json::parse(c.input, nullptr, true, true);
|
||||
static_cast<void>(j);
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
string_result = std::to_string(e.byte) + " " + e.what();
|
||||
}
|
||||
try
|
||||
{
|
||||
const json j = json::parse(c.input.data(), c.input.data() + c.input.size(), nullptr, true, true);
|
||||
static_cast<void>(j);
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
pointer_result = std::to_string(e.byte) + " " + e.what();
|
||||
}
|
||||
try
|
||||
{
|
||||
std::istringstream is(c.input);
|
||||
const json j = json::parse(is, nullptr, true, true);
|
||||
static_cast<void>(j);
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
stream_result = std::to_string(e.byte) + " " + e.what();
|
||||
}
|
||||
CHECK(string_result == expected);
|
||||
CHECK(pointer_result == expected);
|
||||
CHECK(stream_result == expected);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user