载入中...
搜索中...
未找到
EditingValueJson.cpp
浏览该文件的文档.
2
3#include <charconv>
4#include <cmath>
5#include <cstdint>
6#include <iomanip>
7#include <limits>
8#include <locale>
9#include <sstream>
10#include <string_view>
11#include <system_error>
12#include <type_traits>
13
14namespace eve::editing {
15namespace {
16
17constexpr std::size_t kMaxJsonDepth = 64;
18
19void appendEscapedString(std::string_view value, std::string& out) {
20 constexpr char hex[] = "0123456789abcdef";
21 out.push_back('"');
22 for (const unsigned char byte : value) {
23 switch (byte) {
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;
31 default:
32 if (byte < 0x20) {
33 out += "\\u00";
34 out.push_back(hex[byte >> 4]);
35 out.push_back(hex[byte & 0x0f]);
36 } else {
37 out.push_back(static_cast<char>(byte));
38 }
39 break;
40 }
41 }
42 out.push_back('"');
43}
44
45void appendJson(const EditorValue& value, std::string& out) {
46 std::visit(
47 [&out](const auto& current) {
48 using T = std::decay_t<decltype(current)>;
49 if constexpr (std::is_same_v<T, std::monostate>) {
50 out += "null";
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>) {
54 char buffer[32];
55 const auto [end, error] = std::to_chars(buffer, buffer + sizeof(buffer), current);
56 if (error == std::errc()) out.append(buffer, end);
57 } else if constexpr (std::is_same_v<T, double>) {
58 if (!std::isfinite(current)) {
59 out += "null";
60 } else {
61 std::ostringstream stream;
62 stream.imbue(std::locale::classic());
63 stream << std::setprecision(std::numeric_limits<double>::max_digits10) << current;
64 out += stream.str();
65 }
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>) {
69 out.push_back('[');
70 bool first = true;
71 for (const EditorValue& entry : current) {
72 if (!first) out.push_back(',');
73 first = false;
74 appendJson(entry, out);
75 }
76 out.push_back(']');
77 } else if constexpr (std::is_same_v<T, EditorValue::Object>) {
78 out.push_back('{');
79 bool first = true;
80 for (const auto& [key, entry] : current) {
81 if (!first) out.push_back(',');
82 first = false;
83 appendEscapedString(key, out);
84 out.push_back(':');
85 appendJson(entry, out);
86 }
87 out.push_back('}');
88 }
89 },
90 value.storage());
91}
92
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)));
103 } else {
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)));
108 }
109}
110
111class JsonParser {
112public:
113 explicit JsonParser(std::string_view input) : input_(input) {}
114
115 bool parse(EditorValue& out) {
116 skipWhitespace();
117 if (!parseValue(out, 0)) return false;
118 skipWhitespace();
119 if (position_ != input_.size()) return fail("unexpected trailing JSON content");
120 return true;
121 }
122
123 const std::string& error() const { return error_; }
124
125private:
126 bool parseValue(EditorValue& out, std::size_t depth) {
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);
133 case '"': {
134 std::string value;
135 if (!parseString(value)) return false;
136 out = EditorValue(std::move(value));
137 return true;
138 }
139 case '[': return parseArray(out, depth + 1);
140 case '{': return parseObject(out, depth + 1);
141 default:
142 if (input_[position_] == '-' || isDigit(input_[position_])) return parseNumber(out);
143 return fail("expected a JSON value");
144 }
145 }
146
147 bool parseLiteral(std::string_view literal, EditorValue value, EditorValue& out) {
148 if (input_.substr(position_, literal.size()) != literal) return fail("invalid JSON literal");
149 position_ += literal.size();
150 out = std::move(value);
151 return true;
152 }
153
154 bool parseArray(EditorValue& out, std::size_t depth) {
155 ++position_;
156 skipWhitespace();
157 EditorValue::Array result;
158 if (consume(']')) {
159 out = EditorValue(std::move(result));
160 return true;
161 }
162 while (true) {
163 EditorValue entry;
164 if (!parseValue(entry, depth)) return false;
165 result.push_back(std::move(entry));
166 skipWhitespace();
167 if (consume(']')) break;
168 if (!consume(',')) return fail("expected ',' or ']' in JSON array");
169 skipWhitespace();
170 }
171 out = EditorValue(std::move(result));
172 return true;
173 }
174
175 bool parseObject(EditorValue& out, std::size_t depth) {
176 ++position_;
177 skipWhitespace();
178 EditorValue::Object result;
179 if (consume('}')) {
180 out = EditorValue(std::move(result));
181 return true;
182 }
183 while (true) {
184 std::string key;
185 if (!parseString(key)) return false;
186 skipWhitespace();
187 if (!consume(':')) return fail("expected ':' after JSON object key");
188 skipWhitespace();
189 EditorValue entry;
190 if (!parseValue(entry, depth)) return false;
191 result[std::move(key)] = std::move(entry);
192 skipWhitespace();
193 if (consume('}')) break;
194 if (!consume(',')) return fail("expected ',' or '}' in JSON object");
195 skipWhitespace();
196 }
197 out = EditorValue(std::move(result));
198 return true;
199 }
200
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");
207 if (byte != '\\') {
208 out.push_back(static_cast<char>(byte));
209 continue;
210 }
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;
221 case 'u':
222 if (!parseUnicodeEscape(out)) return false;
223 break;
224 default: return fail("invalid JSON escape");
225 }
226 }
227 return fail("unterminated JSON string");
228 }
229
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");
236 position_ += 2;
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");
243 }
244 appendUtf8(codePoint, out);
245 return true;
246 }
247
248 bool parseHexQuad(std::uint32_t& value) {
249 if (position_ + 4 > input_.size()) return fail("incomplete Unicode escape in JSON string");
250 value = 0;
251 for (int i = 0; i < 4; ++i) {
252 const char digit = input_[position_++];
253 value <<= 4;
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);
260 else
261 return fail("invalid Unicode escape in JSON string");
262 }
263 return true;
264 }
265
266 bool parseNumber(EditorValue& out) {
267 const std::size_t start = position_;
268 consume('-');
269 if (consume('0')) {
270 if (position_ < input_.size() && isDigit(input_[position_])) return fail("leading zero in JSON number");
271 } else {
272 if (position_ >= input_.size() || !isDigit(input_[position_])) return fail("invalid JSON number");
273 while (position_ < input_.size() && isDigit(input_[position_])) ++position_;
274 }
275
276 bool integer = true;
277 if (consume('.')) {
278 integer = false;
279 if (position_ >= input_.size() || !isDigit(input_[position_])) return fail("invalid JSON fraction");
280 while (position_ < input_.size() && isDigit(input_[position_])) ++position_;
281 }
282 if (position_ < input_.size() && (input_[position_] == 'e' || input_[position_] == 'E')) {
283 integer = false;
284 ++position_;
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_;
288 }
289
290 const std::string_view token = input_.substr(start, position_ - start);
291 if (integer) {
292 std::int64_t value = 0;
293 const auto [end, error] = std::from_chars(token.data(), token.data() + token.size(), value);
294 if (error == std::errc() && end == token.data() + token.size()) {
295 out = EditorValue(value);
296 return true;
297 }
298 }
299 std::istringstream stream{std::string(token)};
300 stream.imbue(std::locale::classic());
301 double value = 0.0;
302 stream >> std::noskipws >> value;
303 if (stream.fail() || !stream.eof() || !std::isfinite(value))
304 return fail("JSON number is outside the supported range");
305 out = EditorValue(value);
306 return true;
307 }
308
309 void skipWhitespace() {
310 while (position_ < input_.size()) {
311 const char value = input_[position_];
312 if (value != ' ' && value != '\t' && value != '\n' && value != '\r') break;
313 ++position_;
314 }
315 }
316
317 bool consume(char expected) {
318 if (position_ >= input_.size() || input_[position_] != expected) return false;
319 ++position_;
320 return true;
321 }
322
323 bool fail(std::string_view message) {
324 if (error_.empty()) error_ = std::string(message) + " at byte " + std::to_string(position_);
325 return false;
326 }
327
328 static bool isDigit(char value) { return value >= '0' && value <= '9'; }
329
330 std::string_view input_;
331 std::size_t position_ = 0;
332 std::string error_;
333};
334
335std::uint64_t fnv1a(const std::string& text) {
336 std::uint64_t hash = 1469598103934665603ull;
337 for (const unsigned char byte : text) {
338 hash ^= byte;
339 hash *= 1099511628211ull;
340 }
341 return hash;
342}
343
344} // namespace
345
347 std::string json;
348 appendJson(value, json);
349 return json;
350}
351
352Result<EditorValue> editorValueFromJson(const std::string& json) {
353 JsonParser parser(json);
355 if (!parser.parse(value))
356 return eve::editing::failed<EditorValue>(EditorStatus::Rejected, RuleId("editor.value.invalid-json"),
357 parser.error());
358 return eve::editing::applied<EditorValue>(std::move(value));
359}
360
362 std::ostringstream stream;
363 stream << std::hex << std::setfill('0') << std::setw(16) << fnv1a(editorValueToJson(value));
364 return stream.str();
365}
366
367} // namespace eve::editing
double value
Duration start
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
EvpackChunkInput input
Definition Evpack.cpp:170
std::string message
std::uint32_t key
std::int32_t first
std::string text
std::unique_ptr< gpgpu::GpuBuffer > buffer
Definition OnnxGpgpu.cpp:26
std::string error
Definition Package.cpp:60
uint64_t token
double current
eve::Value literal
std::uint32_t depth
Move-only operation result carrying either a value or Status.
Definition Result.h:155
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
Definition EditingIds.h:60
int64_t integer(const RuntimeTensor &v, size_t i=0)
Integer.
ModelData parse(std::span< const uint8_t > bytes)
Parse.