11#include <system_error>
17constexpr std::size_t kMaxJsonDepth = 64;
19void appendEscapedString(std::string_view
value, std::string& out) {
20 constexpr char hex[] =
"0123456789abcdef";
22 for (
const unsigned char byte :
value) {
24 case '"': out +=
"\\\"";
break;
25 case '\\': out +=
"\\\\";
break;
26 case '\b': out +=
"\\b";
break;
27 case '\f': out +=
"\\f";
break;
28 case '\n': out +=
"\\n";
break;
29 case '\r': out +=
"\\r";
break;
30 case '\t': out +=
"\\t";
break;
34 out.push_back(hex[
byte >> 4]);
35 out.push_back(hex[
byte & 0x0f]);
37 out.push_back(
static_cast<char>(
byte));
48 using T = std::decay_t<
decltype(
current)>;
49 if constexpr (std::is_same_v<T, std::monostate>) {
51 }
else if constexpr (std::is_same_v<T, bool>) {
52 out +=
current ?
"true" :
"false";
53 }
else if constexpr (std::is_same_v<T, std::int64_t>) {
57 }
else if constexpr (std::is_same_v<T, double>) {
61 std::ostringstream stream;
62 stream.imbue(std::locale::classic());
63 stream << std::setprecision(std::numeric_limits<double>::max_digits10) <<
current;
66 }
else if constexpr (std::is_same_v<T, std::string>) {
67 appendEscapedString(
current, out);
68 }
else if constexpr (std::is_same_v<T, EditorValue::Array>) {
72 if (!
first) out.push_back(
',');
74 appendJson(entry, out);
77 }
else if constexpr (std::is_same_v<T, EditorValue::Object>) {
81 if (!
first) out.push_back(
',');
83 appendEscapedString(
key, out);
85 appendJson(entry, out);
93void appendUtf8(std::uint32_t codePoint, std::string& out) {
94 if (codePoint <= 0x7f) {
95 out.push_back(
static_cast<char>(codePoint));
96 }
else if (codePoint <= 0x7ff) {
97 out.push_back(
static_cast<char>(0xc0 | (codePoint >> 6)));
98 out.push_back(
static_cast<char>(0x80 | (codePoint & 0x3f)));
99 }
else if (codePoint <= 0xffff) {
100 out.push_back(
static_cast<char>(0xe0 | (codePoint >> 12)));
101 out.push_back(
static_cast<char>(0x80 | ((codePoint >> 6) & 0x3f)));
102 out.push_back(
static_cast<char>(0x80 | (codePoint & 0x3f)));
104 out.push_back(
static_cast<char>(0xf0 | (codePoint >> 18)));
105 out.push_back(
static_cast<char>(0x80 | ((codePoint >> 12) & 0x3f)));
106 out.push_back(
static_cast<char>(0x80 | ((codePoint >> 6) & 0x3f)));
107 out.push_back(
static_cast<char>(0x80 | (codePoint & 0x3f)));
113 explicit JsonParser(std::string_view
input) : input_(
input) {}
117 if (!parseValue(out, 0))
return false;
119 if (position_ != input_.size())
return fail(
"unexpected trailing JSON content");
123 const std::string&
error()
const {
return error_; }
127 if (
depth > kMaxJsonDepth)
return fail(
"JSON nesting exceeds 64 levels");
128 if (position_ >= input_.size())
return fail(
"expected a JSON value");
129 switch (input_[position_]) {
130 case 'n':
return parseLiteral(
"null",
EditorValue{}, out);
131 case 't':
return parseLiteral(
"true",
EditorValue(
true), out);
132 case 'f':
return parseLiteral(
"false",
EditorValue(
false), out);
135 if (!parseString(
value))
return false;
139 case '[':
return parseArray(out,
depth + 1);
140 case '{':
return parseObject(out,
depth + 1);
142 if (input_[position_] ==
'-' || isDigit(input_[position_]))
return parseNumber(out);
143 return fail(
"expected a JSON value");
148 if (input_.substr(position_,
literal.size()) !=
literal)
return fail(
"invalid JSON literal");
150 out = std::move(
value);
164 if (!parseValue(entry,
depth))
return false;
165 result.push_back(std::move(entry));
167 if (consume(
']'))
break;
168 if (!consume(
','))
return fail(
"expected ',' or ']' in JSON array");
185 if (!parseString(
key))
return false;
187 if (!consume(
':'))
return fail(
"expected ':' after JSON object key");
190 if (!parseValue(entry,
depth))
return false;
191 result[std::move(
key)] = std::move(entry);
193 if (consume(
'}'))
break;
194 if (!consume(
','))
return fail(
"expected ',' or '}' in JSON object");
201 bool parseString(std::string& out) {
202 if (!consume(
'"'))
return fail(
"expected a JSON string");
203 while (position_ < input_.size()) {
204 const unsigned char byte =
static_cast<unsigned char>(input_[position_++]);
205 if (
byte ==
'"')
return true;
206 if (
byte < 0x20)
return fail(
"unescaped control character in JSON string");
208 out.push_back(
static_cast<char>(
byte));
211 if (position_ >= input_.size())
return fail(
"unterminated JSON escape");
212 switch (input_[position_++]) {
213 case '"': out.push_back(
'"');
break;
214 case '\\': out.push_back(
'\\');
break;
215 case '/': out.push_back(
'/');
break;
216 case 'b': out.push_back(
'\b');
break;
217 case 'f': out.push_back(
'\f');
break;
218 case 'n': out.push_back(
'\n');
break;
219 case 'r': out.push_back(
'\r');
break;
220 case 't': out.push_back(
'\t');
break;
222 if (!parseUnicodeEscape(out))
return false;
224 default:
return fail(
"invalid JSON escape");
227 return fail(
"unterminated JSON string");
230 bool parseUnicodeEscape(std::string& out) {
231 std::uint32_t codePoint = 0;
232 if (!parseHexQuad(codePoint))
return false;
233 if (codePoint >= 0xd800 && codePoint <= 0xdbff) {
234 if (position_ + 2 > input_.size() || input_[position_] !=
'\\' || input_[position_ + 1] !=
'u')
235 return fail(
"missing low surrogate in JSON string");
237 std::uint32_t low = 0;
238 if (!parseHexQuad(low))
return false;
239 if (low < 0xdc00 || low > 0xdfff)
return fail(
"invalid low surrogate in JSON string");
240 codePoint = 0x10000 + ((codePoint - 0xd800) << 10) + (low - 0xdc00);
241 }
else if (codePoint >= 0xdc00 && codePoint <= 0xdfff) {
242 return fail(
"unexpected low surrogate in JSON string");
244 appendUtf8(codePoint, out);
248 bool parseHexQuad(std::uint32_t&
value) {
249 if (position_ + 4 > input_.size())
return fail(
"incomplete Unicode escape in JSON string");
251 for (
int i = 0; i < 4; ++i) {
252 const char digit = input_[position_++];
254 if (digit >=
'0' && digit <=
'9')
255 value |=
static_cast<std::uint32_t
>(digit -
'0');
256 else if (digit >=
'a' && digit <=
'f')
257 value |=
static_cast<std::uint32_t
>(digit -
'a' + 10);
258 else if (digit >=
'A' && digit <=
'F')
259 value |=
static_cast<std::uint32_t
>(digit -
'A' + 10);
261 return fail(
"invalid Unicode escape in JSON string");
267 const std::size_t
start = position_;
270 if (position_ < input_.size() && isDigit(input_[position_]))
return fail(
"leading zero in JSON number");
272 if (position_ >= input_.size() || !isDigit(input_[position_]))
return fail(
"invalid JSON number");
273 while (position_ < input_.size() && isDigit(input_[position_])) ++position_;
279 if (position_ >= input_.size() || !isDigit(input_[position_]))
return fail(
"invalid JSON fraction");
280 while (position_ < input_.size() && isDigit(input_[position_])) ++position_;
282 if (position_ < input_.size() && (input_[position_] ==
'e' || input_[position_] ==
'E')) {
285 if (position_ < input_.size() && (input_[position_] ==
'+' || input_[position_] ==
'-')) ++position_;
286 if (position_ >= input_.size() || !isDigit(input_[position_]))
return fail(
"invalid JSON exponent");
287 while (position_ < input_.size() && isDigit(input_[position_])) ++position_;
290 const std::string_view
token = input_.substr(
start, position_ -
start);
292 std::int64_t
value = 0;
299 std::istringstream stream{std::string(
token)};
300 stream.imbue(std::locale::classic());
302 stream >> std::noskipws >>
value;
303 if (stream.fail() || !stream.eof() || !std::isfinite(
value))
304 return fail(
"JSON number is outside the supported range");
309 void skipWhitespace() {
310 while (position_ < input_.size()) {
311 const char value = input_[position_];
317 bool consume(
char expected) {
318 if (position_ >= input_.size() || input_[position_] != expected)
return false;
323 bool fail(std::string_view
message) {
324 if (error_.empty()) error_ = std::string(
message) +
" at byte " + std::to_string(position_);
328 static bool isDigit(
char value) {
return value >=
'0' &&
value <=
'9'; }
330 std::string_view input_;
331 std::size_t position_ = 0;
335std::uint64_t fnv1a(
const std::string&
text) {
336 std::uint64_t
hash = 1469598103934665603ull;
337 for (
const unsigned char byte :
text) {
339 hash *= 1099511628211ull;
348 appendJson(
value, json);
353 JsonParser parser(json);
355 if (!parser.parse(
value))
356 return eve::editing::failed<EditorValue>(EditorStatus::Rejected,
RuleId(
"editor.value.invalid-json"),
358 return eve::editing::applied<EditorValue>(std::move(
value));
362 std::ostringstream stream;
std::array< std::uint8_t, 32 > hash
std::unique_ptr< gpgpu::GpuBuffer > buffer
Move-only operation result carrying either a value or Status.
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
std::string editorValueContentHash(const EditorValue &value)
Stable content hash derived from deterministic JSON serialization.
std::string editorValueToJson(const EditorValue &value)
Serialize an EditorValue to deterministic compact JSON.
Result< EditorValue > editorValueFromJson(const std::string &json)
Parse JSON into the pointer-free EditorValue protocol tree.
StrongId< RuleIdTag > RuleId
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
ModelData parse(std::span< const uint8_t > bytes)
Parse.