blob: 8f7d67d3c581f82bf79501a9be6c1cfffddb7423 [file] [log] [blame]
Florin Malita7796f002018-06-08 12:25:38 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkJSON.h"
9
Florin Malitad7bfcaf2018-06-14 18:03:26 -040010#include "SkMalloc.h"
Florin Malita7796f002018-06-08 12:25:38 -040011#include "SkStream.h"
Hal Canary8b681102018-09-05 22:32:41 -040012#include "SkStreamPriv.h"
Florin Malita7796f002018-06-08 12:25:38 -040013#include "SkString.h"
Florin Malita7796f002018-06-08 12:25:38 -040014
15#include <cmath>
Florin Malitafedfd542018-06-14 15:03:21 -040016#include <tuple>
Florin Malita7796f002018-06-08 12:25:38 -040017#include <vector>
18
19namespace skjson {
20
Florin Malitafedfd542018-06-14 15:03:21 -040021// #define SK_JSON_REPORT_ERRORS
Florin Malita7796f002018-06-08 12:25:38 -040022
Florin Malita7796f002018-06-08 12:25:38 -040023static_assert( sizeof(Value) == 8, "");
24static_assert(alignof(Value) == 8, "");
25
26static constexpr size_t kRecAlign = alignof(Value);
27
Florin Malitaae252792018-06-14 11:24:50 -040028void Value::init_tagged(Tag t) {
29 memset(fData8, 0, sizeof(fData8));
30 fData8[Value::kTagOffset] = SkTo<uint8_t>(t);
31 SkASSERT(this->getTag() == t);
32}
Florin Malita7796f002018-06-08 12:25:38 -040033
Florin Malitaae252792018-06-14 11:24:50 -040034// Pointer values store a type (in the upper kTagBits bits) and a pointer.
35void Value::init_tagged_pointer(Tag t, void* p) {
36 *this->cast<uintptr_t>() = reinterpret_cast<uintptr_t>(p);
Florin Malita7796f002018-06-08 12:25:38 -040037
Florin Malitaae252792018-06-14 11:24:50 -040038 if (sizeof(Value) == sizeof(uintptr_t)) {
39 // For 64-bit, we rely on the pointer upper bits being unused/zero.
40 SkASSERT(!(fData8[kTagOffset] & kTagMask));
41 fData8[kTagOffset] |= SkTo<uint8_t>(t);
42 } else {
43 // For 32-bit, we need to zero-initialize the upper 32 bits
44 SkASSERT(sizeof(Value) == sizeof(uintptr_t) * 2);
45 this->cast<uintptr_t>()[kTagOffset >> 2] = 0;
46 fData8[kTagOffset] = SkTo<uint8_t>(t);
Florin Malita7796f002018-06-08 12:25:38 -040047 }
48
Florin Malitaae252792018-06-14 11:24:50 -040049 SkASSERT(this->getTag() == t);
50 SkASSERT(this->ptr<void>() == p);
51}
52
53NullValue::NullValue() {
54 this->init_tagged(Tag::kNull);
55 SkASSERT(this->getTag() == Tag::kNull);
56}
57
58BoolValue::BoolValue(bool b) {
59 this->init_tagged(Tag::kBool);
60 *this->cast<bool>() = b;
61 SkASSERT(this->getTag() == Tag::kBool);
62}
63
64NumberValue::NumberValue(int32_t i) {
65 this->init_tagged(Tag::kInt);
66 *this->cast<int32_t>() = i;
67 SkASSERT(this->getTag() == Tag::kInt);
68}
69
70NumberValue::NumberValue(float f) {
71 this->init_tagged(Tag::kFloat);
72 *this->cast<float>() = f;
73 SkASSERT(this->getTag() == Tag::kFloat);
74}
75
76// Vector recs point to externally allocated slabs with the following layout:
77//
78// [size_t n] [REC_0] ... [REC_n-1] [optional extra trailing storage]
79//
80// Long strings use extra_alloc_size == 1 to store the \0 terminator.
81//
82template <typename T, size_t extra_alloc_size = 0>
83static void* MakeVector(const void* src, size_t size, SkArenaAlloc& alloc) {
84 // The Ts are already in memory, so their size should be safe.
85 const auto total_size = sizeof(size_t) + size * sizeof(T) + extra_alloc_size;
86 auto* size_ptr = reinterpret_cast<size_t*>(alloc.makeBytesAlignedTo(total_size, kRecAlign));
Florin Malita28f5dd82018-06-14 13:56:53 -040087
Florin Malitad7bfcaf2018-06-14 18:03:26 -040088 *size_ptr = size;
89 sk_careful_memcpy(size_ptr + 1, src, size * sizeof(T));
Florin Malitaae252792018-06-14 11:24:50 -040090
91 return size_ptr;
92}
93
94ArrayValue::ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc) {
95 this->init_tagged_pointer(Tag::kArray, MakeVector<Value>(src, size, alloc));
96 SkASSERT(this->getTag() == Tag::kArray);
97}
98
99// Strings have two flavors:
100//
101// -- short strings (len <= 7) -> these are stored inline, in the record
102// (one byte reserved for null terminator/type):
103//
104// [str] [\0]|[max_len - actual_len]
105//
106// Storing [max_len - actual_len] allows the 'len' field to double-up as a
107// null terminator when size == max_len (this works 'cause kShortString == 0).
108//
109// -- long strings (len > 7) -> these are externally allocated vectors (VectorRec<char>).
110//
111// The string data plus a null-char terminator are copied over.
112//
Florin Malitafb3beb02018-06-18 22:25:31 -0400113namespace {
114
115// An internal string builder with a fast 8 byte short string load path
116// (for the common case where the string is not at the end of the stream).
117class FastString final : public Value {
118public:
119 FastString(const char* src, size_t size, const char* eos, SkArenaAlloc& alloc) {
120 SkASSERT(src <= eos);
121
122 if (size > kMaxInlineStringSize) {
123 this->initLongString(src, size, alloc);
124 SkASSERT(this->getTag() == Tag::kString);
125 return;
126 }
127
128 static_assert(static_cast<uint8_t>(Tag::kShortString) == 0, "please don't break this");
129 static_assert(sizeof(Value) == 8, "");
130
131 // TODO: LIKELY
132 if (src + 7 <= eos) {
133 this->initFastShortString(src, size);
134 } else {
135 this->initShortString(src, size);
136 }
137
138 SkASSERT(this->getTag() == Tag::kShortString);
139 }
140
141private:
Florin Malitad7bfcaf2018-06-14 18:03:26 -0400142 static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 1;
Florin Malitafb3beb02018-06-18 22:25:31 -0400143
144 void initLongString(const char* src, size_t size, SkArenaAlloc& alloc) {
145 SkASSERT(size > kMaxInlineStringSize);
146
Florin Malitaae252792018-06-14 11:24:50 -0400147 this->init_tagged_pointer(Tag::kString, MakeVector<char, 1>(src, size, alloc));
148
149 auto* data = this->cast<VectorValue<char, Value::Type::kString>>()->begin();
150 const_cast<char*>(data)[size] = '\0';
Florin Malita7796f002018-06-08 12:25:38 -0400151 }
152
Florin Malitafb3beb02018-06-18 22:25:31 -0400153 void initShortString(const char* src, size_t size) {
154 SkASSERT(size <= kMaxInlineStringSize);
Florin Malita7796f002018-06-08 12:25:38 -0400155
Florin Malitafb3beb02018-06-18 22:25:31 -0400156 this->init_tagged(Tag::kShortString);
157 sk_careful_memcpy(this->cast<char>(), src, size);
158 // Null terminator provided by init_tagged() above (fData8 is zero-initialized).
159 }
Florin Malita7796f002018-06-08 12:25:38 -0400160
Florin Malitafb3beb02018-06-18 22:25:31 -0400161 void initFastShortString(const char* src, size_t size) {
162 SkASSERT(size <= kMaxInlineStringSize);
163
164 // Load 8 chars and mask out the tag and \0 terminator.
165 uint64_t* s64 = this->cast<uint64_t>();
166 memcpy(s64, src, 8);
167
168#if defined(SK_CPU_LENDIAN)
169 *s64 &= 0x00ffffffffffffffULL >> ((kMaxInlineStringSize - size) * 8);
170#else
171 static_assert(false, "Big-endian builds are not supported at this time.");
172#endif
173 }
174};
175
176} // namespace
177
178StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) {
179 new (this) FastString(src, size, src, alloc);
Florin Malitaae252792018-06-14 11:24:50 -0400180}
Florin Malita7796f002018-06-08 12:25:38 -0400181
Florin Malitaae252792018-06-14 11:24:50 -0400182ObjectValue::ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc) {
183 this->init_tagged_pointer(Tag::kObject, MakeVector<Member>(src, size, alloc));
184 SkASSERT(this->getTag() == Tag::kObject);
185}
Florin Malita7796f002018-06-08 12:25:38 -0400186
187
188// Boring public Value glue.
189
Mike Reed0edad3e2018-08-13 17:18:47 -0400190static int inline_strcmp(const char a[], const char b[]) {
191 for (;;) {
192 char c = *a++;
193 if (c == 0) {
194 break;
195 }
196 if (c != *b++) {
197 return 1;
198 }
199 }
200 return *b != 0;
201}
202
Florin Malita7796f002018-06-08 12:25:38 -0400203const Value& ObjectValue::operator[](const char* key) const {
204 // Reverse search for duplicates resolution (policy: return last).
205 const auto* begin = this->begin();
206 const auto* member = this->end();
207
208 while (member > begin) {
209 --member;
Mike Reed0edad3e2018-08-13 17:18:47 -0400210 if (0 == inline_strcmp(key, member->fKey.as<StringValue>().begin())) {
Florin Malita7796f002018-06-08 12:25:38 -0400211 return member->fValue;
212 }
213 }
214
Florin Malitaae252792018-06-14 11:24:50 -0400215 static const Value g_null = NullValue();
216 return g_null;
Florin Malita7796f002018-06-08 12:25:38 -0400217}
218
219namespace {
220
221// Lexer/parser inspired by rapidjson [1], sajson [2] and pjson [3].
222//
223// [1] https://github.com/Tencent/rapidjson/
224// [2] https://github.com/chadaustin/sajson
225// [3] https://pastebin.com/hnhSTL3h
226
227
228// bit 0 (0x01) - plain ASCII string character
229// bit 1 (0x02) - whitespace
Florin Malita0052a312018-06-15 16:42:09 -0400230// bit 2 (0x04) - string terminator (" \0 [control chars] **AND } ]** <- see matchString notes)
Florin Malita7796f002018-06-08 12:25:38 -0400231// bit 3 (0x08) - 0-9
232// bit 4 (0x10) - 0-9 e E .
Florin Malita0052a312018-06-15 16:42:09 -0400233// bit 5 (0x20) - scope terminator (} ])
Florin Malita7796f002018-06-08 12:25:38 -0400234static constexpr uint8_t g_token_flags[256] = {
235 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
236 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 6, 4, 4, // 0
237 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1
238 3, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x11,1, // 2
239 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19, 1, 1, 1, 1, 1, 1, // 3
240 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
Florin Malita0052a312018-06-15 16:42:09 -0400241 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,0x25, 1, 1, // 5
Florin Malita7796f002018-06-08 12:25:38 -0400242 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
Florin Malita0052a312018-06-15 16:42:09 -0400243 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,0x25, 1, 1, // 7
Florin Malita7796f002018-06-08 12:25:38 -0400244
245 // 128-255
246 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
247 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
248 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
249 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
250};
251
Florin Malita0052a312018-06-15 16:42:09 -0400252static inline bool is_ws(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x02; }
253static inline bool is_eostring(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x04; }
254static inline bool is_digit(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x08; }
255static inline bool is_numeric(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x10; }
256static inline bool is_eoscope(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x20; }
Florin Malita7796f002018-06-08 12:25:38 -0400257
258static inline const char* skip_ws(const char* p) {
259 while (is_ws(*p)) ++p;
260 return p;
261}
262
263static inline float pow10(int32_t exp) {
264 static constexpr float g_pow10_table[63] =
265 {
266 1.e-031f, 1.e-030f, 1.e-029f, 1.e-028f, 1.e-027f, 1.e-026f, 1.e-025f, 1.e-024f,
267 1.e-023f, 1.e-022f, 1.e-021f, 1.e-020f, 1.e-019f, 1.e-018f, 1.e-017f, 1.e-016f,
268 1.e-015f, 1.e-014f, 1.e-013f, 1.e-012f, 1.e-011f, 1.e-010f, 1.e-009f, 1.e-008f,
269 1.e-007f, 1.e-006f, 1.e-005f, 1.e-004f, 1.e-003f, 1.e-002f, 1.e-001f, 1.e+000f,
270 1.e+001f, 1.e+002f, 1.e+003f, 1.e+004f, 1.e+005f, 1.e+006f, 1.e+007f, 1.e+008f,
271 1.e+009f, 1.e+010f, 1.e+011f, 1.e+012f, 1.e+013f, 1.e+014f, 1.e+015f, 1.e+016f,
272 1.e+017f, 1.e+018f, 1.e+019f, 1.e+020f, 1.e+021f, 1.e+022f, 1.e+023f, 1.e+024f,
273 1.e+025f, 1.e+026f, 1.e+027f, 1.e+028f, 1.e+029f, 1.e+030f, 1.e+031f
274 };
275
276 static constexpr int32_t k_exp_offset = SK_ARRAY_COUNT(g_pow10_table) / 2;
277
278 // We only support negative exponents for now.
279 SkASSERT(exp <= 0);
280
281 return (exp >= -k_exp_offset) ? g_pow10_table[exp + k_exp_offset]
282 : std::pow(10.0f, static_cast<float>(exp));
283}
284
285class DOMParser {
286public:
287 explicit DOMParser(SkArenaAlloc& alloc)
288 : fAlloc(alloc) {
Florin Malita7796f002018-06-08 12:25:38 -0400289 fValueStack.reserve(kValueStackReserve);
Florin Malita7796f002018-06-08 12:25:38 -0400290 }
291
Florin Malitafedfd542018-06-14 15:03:21 -0400292 const Value parse(const char* p, size_t size) {
293 if (!size) {
294 return this->error(NullValue(), p, "invalid empty input");
295 }
296
297 const char* p_stop = p + size - 1;
298
299 // We're only checking for end-of-stream on object/array close('}',']'),
300 // so we must trim any whitespace from the buffer tail.
301 while (p_stop > p && is_ws(*p_stop)) --p_stop;
302
303 SkASSERT(p_stop >= p && p_stop < p + size);
Florin Malita0052a312018-06-15 16:42:09 -0400304 if (!is_eoscope(*p_stop)) {
Florin Malitafedfd542018-06-14 15:03:21 -0400305 return this->error(NullValue(), p_stop, "invalid top-level value");
306 }
307
Florin Malita7796f002018-06-08 12:25:38 -0400308 p = skip_ws(p);
309
310 switch (*p) {
311 case '{':
312 goto match_object;
313 case '[':
314 goto match_array;
315 default:
Florin Malitaae252792018-06-14 11:24:50 -0400316 return this->error(NullValue(), p, "invalid top-level value");
Florin Malita7796f002018-06-08 12:25:38 -0400317 }
318
319 match_object:
320 SkASSERT(*p == '{');
321 p = skip_ws(p + 1);
322
323 this->pushObjectScope();
324
325 if (*p == '}') goto pop_object;
326
327 // goto match_object_key;
328 match_object_key:
329 p = skip_ws(p);
Florin Malitaae252792018-06-14 11:24:50 -0400330 if (*p != '"') return this->error(NullValue(), p, "expected object key");
Florin Malita7796f002018-06-08 12:25:38 -0400331
Florin Malitafb3beb02018-06-18 22:25:31 -0400332 p = this->matchString(p, p_stop, [this](const char* key, size_t size, const char* eos) {
333 this->pushObjectKey(key, size, eos);
Florin Malita7796f002018-06-08 12:25:38 -0400334 });
Florin Malitaae252792018-06-14 11:24:50 -0400335 if (!p) return NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400336
337 p = skip_ws(p);
Florin Malitaae252792018-06-14 11:24:50 -0400338 if (*p != ':') return this->error(NullValue(), p, "expected ':' separator");
Florin Malita7796f002018-06-08 12:25:38 -0400339
340 ++p;
341
342 // goto match_value;
343 match_value:
344 p = skip_ws(p);
345
346 switch (*p) {
347 case '\0':
Florin Malitaae252792018-06-14 11:24:50 -0400348 return this->error(NullValue(), p, "unexpected input end");
Florin Malita7796f002018-06-08 12:25:38 -0400349 case '"':
Florin Malitafb3beb02018-06-18 22:25:31 -0400350 p = this->matchString(p, p_stop, [this](const char* str, size_t size, const char* eos) {
351 this->pushString(str, size, eos);
Florin Malita7796f002018-06-08 12:25:38 -0400352 });
353 break;
354 case '[':
355 goto match_array;
356 case 'f':
357 p = this->matchFalse(p);
358 break;
359 case 'n':
360 p = this->matchNull(p);
361 break;
362 case 't':
363 p = this->matchTrue(p);
364 break;
365 case '{':
366 goto match_object;
367 default:
368 p = this->matchNumber(p);
369 break;
370 }
371
Florin Malitaae252792018-06-14 11:24:50 -0400372 if (!p) return NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400373
374 // goto match_post_value;
375 match_post_value:
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400376 SkASSERT(!this->inTopLevelScope());
Florin Malita7796f002018-06-08 12:25:38 -0400377
378 p = skip_ws(p);
379 switch (*p) {
380 case ',':
381 ++p;
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400382 if (this->inObjectScope()) {
Florin Malita7796f002018-06-08 12:25:38 -0400383 goto match_object_key;
384 } else {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400385 SkASSERT(this->inArrayScope());
Florin Malita7796f002018-06-08 12:25:38 -0400386 goto match_value;
387 }
388 case ']':
389 goto pop_array;
390 case '}':
391 goto pop_object;
392 default:
Florin Malitaae252792018-06-14 11:24:50 -0400393 return this->error(NullValue(), p - 1, "unexpected value-trailing token");
Florin Malita7796f002018-06-08 12:25:38 -0400394 }
395
396 // unreachable
397 SkASSERT(false);
398
399 pop_object:
400 SkASSERT(*p == '}');
401
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400402 if (this->inArrayScope()) {
Florin Malitaae252792018-06-14 11:24:50 -0400403 return this->error(NullValue(), p, "unexpected object terminator");
Florin Malita7796f002018-06-08 12:25:38 -0400404 }
405
406 this->popObjectScope();
407
408 // goto pop_common
409 pop_common:
Florin Malita0052a312018-06-15 16:42:09 -0400410 SkASSERT(is_eoscope(*p));
Florin Malita7796f002018-06-08 12:25:38 -0400411
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400412 if (this->inTopLevelScope()) {
Florin Malita7796f002018-06-08 12:25:38 -0400413 SkASSERT(fValueStack.size() == 1);
Florin Malita7796f002018-06-08 12:25:38 -0400414
Florin Malitafedfd542018-06-14 15:03:21 -0400415 // Success condition: parsed the top level element and reached the stop token.
416 return p == p_stop
Florin Malitaae252792018-06-14 11:24:50 -0400417 ? fValueStack.front()
Florin Malitafedfd542018-06-14 15:03:21 -0400418 : this->error(NullValue(), p + 1, "trailing root garbage");
Florin Malita7796f002018-06-08 12:25:38 -0400419 }
420
Florin Malita587f5a92018-06-15 09:21:36 -0400421 if (p == p_stop) {
422 return this->error(NullValue(), p, "unexpected end-of-input");
423 }
424
Florin Malitafedfd542018-06-14 15:03:21 -0400425 ++p;
426
Florin Malita7796f002018-06-08 12:25:38 -0400427 goto match_post_value;
428
429 match_array:
430 SkASSERT(*p == '[');
431 p = skip_ws(p + 1);
432
433 this->pushArrayScope();
434
435 if (*p != ']') goto match_value;
436
437 // goto pop_array;
438 pop_array:
439 SkASSERT(*p == ']');
440
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400441 if (this->inObjectScope()) {
Florin Malitaae252792018-06-14 11:24:50 -0400442 return this->error(NullValue(), p, "unexpected array terminator");
Florin Malita7796f002018-06-08 12:25:38 -0400443 }
444
445 this->popArrayScope();
446
447 goto pop_common;
448
449 SkASSERT(false);
Florin Malitaae252792018-06-14 11:24:50 -0400450 return NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400451 }
452
Florin Malitafedfd542018-06-14 15:03:21 -0400453 std::tuple<const char*, const SkString> getError() const {
454 return std::make_tuple(fErrorToken, fErrorMessage);
Florin Malita7796f002018-06-08 12:25:38 -0400455 }
456
457private:
Florin Malitafedfd542018-06-14 15:03:21 -0400458 SkArenaAlloc& fAlloc;
Florin Malita7796f002018-06-08 12:25:38 -0400459
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400460 // Pending values stack.
Florin Malita7796f002018-06-08 12:25:38 -0400461 static constexpr size_t kValueStackReserve = 256;
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400462 std::vector<Value> fValueStack;
Florin Malita7796f002018-06-08 12:25:38 -0400463
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400464 // Tracks the current object/array scope, as an index into fStack:
465 //
466 // - for objects: fScopeIndex = (index of first value in scope)
467 // - for arrays : fScopeIndex = -(index of first value in scope)
468 //
469 // fScopeIndex == 0 IFF we are at the top level (no current/active scope).
470 intptr_t fScopeIndex = 0;
471
472 // Error reporting.
Florin Malitafedfd542018-06-14 15:03:21 -0400473 const char* fErrorToken = nullptr;
474 SkString fErrorMessage;
Florin Malita7796f002018-06-08 12:25:38 -0400475
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400476 bool inTopLevelScope() const { return fScopeIndex == 0; }
477 bool inObjectScope() const { return fScopeIndex > 0; }
478 bool inArrayScope() const { return fScopeIndex < 0; }
479
480 // Helper for masquerading raw primitive types as Values (bypassing tagging, etc).
481 template <typename T>
482 class RawValue final : public Value {
483 public:
484 explicit RawValue(T v) {
485 static_assert(sizeof(T) <= sizeof(Value), "");
486 *this->cast<T>() = v;
487 }
488
489 T operator *() const { return *this->cast<T>(); }
490 };
491
Florin Malitaae252792018-06-14 11:24:50 -0400492 template <typename VectorT>
493 void popScopeAsVec(size_t scope_start) {
Florin Malita7796f002018-06-08 12:25:38 -0400494 SkASSERT(scope_start > 0);
495 SkASSERT(scope_start <= fValueStack.size());
496
Florin Malitaae252792018-06-14 11:24:50 -0400497 using T = typename VectorT::ValueT;
Florin Malita7796f002018-06-08 12:25:38 -0400498 static_assert( sizeof(T) >= sizeof(Value), "");
499 static_assert( sizeof(T) % sizeof(Value) == 0, "");
500 static_assert(alignof(T) == alignof(Value), "");
501
502 const auto scope_count = fValueStack.size() - scope_start,
503 count = scope_count / (sizeof(T) / sizeof(Value));
504 SkASSERT(scope_count % (sizeof(T) / sizeof(Value)) == 0);
505
506 const auto* begin = reinterpret_cast<const T*>(fValueStack.data() + scope_start);
507
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400508 // Restore the previous scope index from saved placeholder value,
509 // and instantiate as a vector of values in scope.
510 auto& placeholder = fValueStack[scope_start - 1];
511 fScopeIndex = *static_cast<RawValue<intptr_t>&>(placeholder);
512 placeholder = VectorT(begin, count, fAlloc);
Florin Malita7796f002018-06-08 12:25:38 -0400513
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400514 // Drop the (consumed) values in scope.
Florin Malita7796f002018-06-08 12:25:38 -0400515 fValueStack.resize(scope_start);
516 }
517
518 void pushObjectScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400519 // Save a scope index now, and then later we'll overwrite this value as the Object itself.
520 fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400521
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400522 // New object scope.
523 fScopeIndex = SkTo<intptr_t>(fValueStack.size());
Florin Malita7796f002018-06-08 12:25:38 -0400524 }
525
526 void popObjectScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400527 SkASSERT(this->inObjectScope());
528 this->popScopeAsVec<ObjectValue>(SkTo<size_t>(fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400529
530 SkDEBUGCODE(
531 const auto& obj = fValueStack.back().as<ObjectValue>();
532 SkASSERT(obj.is<ObjectValue>());
533 for (const auto& member : obj) {
534 SkASSERT(member.fKey.is<StringValue>());
535 }
536 )
537 }
538
539 void pushArrayScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400540 // Save a scope index now, and then later we'll overwrite this value as the Array itself.
541 fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400542
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400543 // New array scope.
544 fScopeIndex = -SkTo<intptr_t>(fValueStack.size());
Florin Malita7796f002018-06-08 12:25:38 -0400545 }
546
547 void popArrayScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400548 SkASSERT(this->inArrayScope());
549 this->popScopeAsVec<ArrayValue>(SkTo<size_t>(-fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400550
551 SkDEBUGCODE(
552 const auto& arr = fValueStack.back().as<ArrayValue>();
553 SkASSERT(arr.is<ArrayValue>());
554 )
555 }
556
Florin Malitafb3beb02018-06-18 22:25:31 -0400557 void pushObjectKey(const char* key, size_t size, const char* eos) {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400558 SkASSERT(this->inObjectScope());
559 SkASSERT(fValueStack.size() >= SkTo<size_t>(fScopeIndex));
560 SkASSERT(!((fValueStack.size() - SkTo<size_t>(fScopeIndex)) & 1));
Florin Malitafb3beb02018-06-18 22:25:31 -0400561 this->pushString(key, size, eos);
Florin Malita7796f002018-06-08 12:25:38 -0400562 }
563
564 void pushTrue() {
Florin Malitaae252792018-06-14 11:24:50 -0400565 fValueStack.push_back(BoolValue(true));
Florin Malita7796f002018-06-08 12:25:38 -0400566 }
567
568 void pushFalse() {
Florin Malitaae252792018-06-14 11:24:50 -0400569 fValueStack.push_back(BoolValue(false));
Florin Malita7796f002018-06-08 12:25:38 -0400570 }
571
572 void pushNull() {
Florin Malitaae252792018-06-14 11:24:50 -0400573 fValueStack.push_back(NullValue());
Florin Malita7796f002018-06-08 12:25:38 -0400574 }
575
Florin Malitafb3beb02018-06-18 22:25:31 -0400576 void pushString(const char* s, size_t size, const char* eos) {
577 fValueStack.push_back(FastString(s, size, eos, fAlloc));
Florin Malita7796f002018-06-08 12:25:38 -0400578 }
579
580 void pushInt32(int32_t i) {
Florin Malitaae252792018-06-14 11:24:50 -0400581 fValueStack.push_back(NumberValue(i));
Florin Malita7796f002018-06-08 12:25:38 -0400582 }
583
584 void pushFloat(float f) {
Florin Malitaae252792018-06-14 11:24:50 -0400585 fValueStack.push_back(NumberValue(f));
Florin Malita7796f002018-06-08 12:25:38 -0400586 }
587
588 template <typename T>
Florin Malitaae252792018-06-14 11:24:50 -0400589 T error(T&& ret_val, const char* p, const char* msg) {
Florin Malita7796f002018-06-08 12:25:38 -0400590#if defined(SK_JSON_REPORT_ERRORS)
Florin Malitafedfd542018-06-14 15:03:21 -0400591 fErrorToken = p;
592 fErrorMessage.set(msg);
Florin Malita7796f002018-06-08 12:25:38 -0400593#endif
594 return ret_val;
595 }
596
597 const char* matchTrue(const char* p) {
598 SkASSERT(p[0] == 't');
599
600 if (p[1] == 'r' && p[2] == 'u' && p[3] == 'e') {
601 this->pushTrue();
602 return p + 4;
603 }
604
605 return this->error(nullptr, p, "invalid token");
606 }
607
608 const char* matchFalse(const char* p) {
609 SkASSERT(p[0] == 'f');
610
611 if (p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[4] == 'e') {
612 this->pushFalse();
613 return p + 5;
614 }
615
616 return this->error(nullptr, p, "invalid token");
617 }
618
619 const char* matchNull(const char* p) {
620 SkASSERT(p[0] == 'n');
621
622 if (p[1] == 'u' && p[2] == 'l' && p[3] == 'l') {
623 this->pushNull();
624 return p + 4;
625 }
626
627 return this->error(nullptr, p, "invalid token");
628 }
629
630 template <typename MatchFunc>
Florin Malita0052a312018-06-15 16:42:09 -0400631 const char* matchString(const char* p, const char* p_stop, MatchFunc&& func) {
Florin Malita7796f002018-06-08 12:25:38 -0400632 SkASSERT(*p == '"');
633 const auto* s_begin = p + 1;
634
635 // TODO: unescape
Florin Malita7796f002018-06-08 12:25:38 -0400636
Florin Malita0052a312018-06-15 16:42:09 -0400637 do {
638 // Consume string chars.
639 for (p = p + 1; !is_eostring(*p); ++p);
Florin Malita7796f002018-06-08 12:25:38 -0400640
Florin Malita0052a312018-06-15 16:42:09 -0400641 if (*p == '"') {
642 // Valid string found.
Florin Malitafb3beb02018-06-18 22:25:31 -0400643 func(s_begin, p - s_begin, p_stop);
Florin Malita0052a312018-06-15 16:42:09 -0400644 return p + 1;
645 }
646
647 // End-of-scope chars are special: we use them to tag the end of the input.
648 // Thus they cannot be consumed indiscriminately -- we need to check if we hit the
649 // end of the input. To that effect, we treat them as string terminators above,
650 // then we catch them here.
651 } while (is_eoscope(*p) && (p != p_stop)); // Safe scope terminator char, keep going.
652
653 // Premature end-of-input, or illegal string char.
Florin Malita7796f002018-06-08 12:25:38 -0400654 return this->error(nullptr, s_begin - 1, "invalid string");
655 }
656
657 const char* matchFastFloatDecimalPart(const char* p, int sign, float f, int exp) {
658 SkASSERT(exp <= 0);
659
660 for (;;) {
661 if (!is_digit(*p)) break;
662 f = f * 10.f + (*p++ - '0'); --exp;
663 if (!is_digit(*p)) break;
664 f = f * 10.f + (*p++ - '0'); --exp;
665 }
666
667 if (is_numeric(*p)) {
668 SkASSERT(*p == '.' || *p == 'e' || *p == 'E');
669 // We either have malformed input, or an (unsupported) exponent.
670 return nullptr;
671 }
672
673 this->pushFloat(sign * f * pow10(exp));
674
675 return p;
676 }
677
678 const char* matchFastFloatPart(const char* p, int sign, float f) {
679 for (;;) {
680 if (!is_digit(*p)) break;
681 f = f * 10.f + (*p++ - '0');
682 if (!is_digit(*p)) break;
683 f = f * 10.f + (*p++ - '0');
684 }
685
686 if (!is_numeric(*p)) {
687 // Matched (integral) float.
688 this->pushFloat(sign * f);
689 return p;
690 }
691
692 return (*p == '.') ? this->matchFastFloatDecimalPart(p + 1, sign, f, 0)
693 : nullptr;
694 }
695
696 const char* matchFast32OrFloat(const char* p) {
697 int sign = 1;
698 if (*p == '-') {
699 sign = -1;
700 ++p;
701 }
702
703 const auto* digits_start = p;
704
705 int32_t n32 = 0;
706
707 // This is the largest absolute int32 value we can handle before
708 // risking overflow *on the next digit* (214748363).
709 static constexpr int32_t kMaxInt32 = (std::numeric_limits<int32_t>::max() - 9) / 10;
710
711 if (is_digit(*p)) {
712 n32 = (*p++ - '0');
713 for (;;) {
714 if (!is_digit(*p) || n32 > kMaxInt32) break;
715 n32 = n32 * 10 + (*p++ - '0');
716 }
717 }
718
719 if (!is_numeric(*p)) {
720 // Did we actually match any digits?
721 if (p > digits_start) {
722 this->pushInt32(sign * n32);
723 return p;
724 }
725 return nullptr;
726 }
727
728 if (*p == '.') {
729 const auto* decimals_start = ++p;
730
731 int exp = 0;
732
733 for (;;) {
734 if (!is_digit(*p) || n32 > kMaxInt32) break;
735 n32 = n32 * 10 + (*p++ - '0'); --exp;
736 if (!is_digit(*p) || n32 > kMaxInt32) break;
737 n32 = n32 * 10 + (*p++ - '0'); --exp;
738 }
739
740 if (!is_numeric(*p)) {
741 // Did we actually match any digits?
742 if (p > decimals_start) {
743 this->pushFloat(sign * n32 * pow10(exp));
744 return p;
745 }
746 return nullptr;
747 }
748
749 if (n32 > kMaxInt32) {
750 // we ran out on n32 bits
751 return this->matchFastFloatDecimalPart(p, sign, n32, exp);
752 }
753 }
754
755 return this->matchFastFloatPart(p, sign, n32);
756 }
757
758 const char* matchNumber(const char* p) {
759 if (const auto* fast = this->matchFast32OrFloat(p)) return fast;
760
761 // slow fallback
762 char* matched;
763 float f = strtof(p, &matched);
764 if (matched > p) {
765 this->pushFloat(f);
766 return matched;
767 }
768 return this->error(nullptr, p, "invalid numeric token");
769 }
770};
771
772void Write(const Value& v, SkWStream* stream) {
773 switch (v.getType()) {
774 case Value::Type::kNull:
775 stream->writeText("null");
776 break;
777 case Value::Type::kBool:
778 stream->writeText(*v.as<BoolValue>() ? "true" : "false");
779 break;
780 case Value::Type::kNumber:
Hal Canary8b681102018-09-05 22:32:41 -0400781 SkWStreamWriteScalarAsText(stream, *v.as<NumberValue>());
Florin Malita7796f002018-06-08 12:25:38 -0400782 break;
783 case Value::Type::kString:
784 stream->writeText("\"");
785 stream->writeText(v.as<StringValue>().begin());
786 stream->writeText("\"");
787 break;
788 case Value::Type::kArray: {
789 const auto& array = v.as<ArrayValue>();
790 stream->writeText("[");
791 bool first_value = true;
792 for (const auto& v : array) {
793 if (!first_value) stream->writeText(",");
794 Write(v, stream);
795 first_value = false;
796 }
797 stream->writeText("]");
798 break;
799 }
800 case Value::Type::kObject:
801 const auto& object = v.as<ObjectValue>();
802 stream->writeText("{");
803 bool first_member = true;
804 for (const auto& member : object) {
805 SkASSERT(member.fKey.getType() == Value::Type::kString);
806 if (!first_member) stream->writeText(",");
807 Write(member.fKey, stream);
808 stream->writeText(":");
809 Write(member.fValue, stream);
810 first_member = false;
811 }
812 stream->writeText("}");
813 break;
814 }
815}
816
817} // namespace
818
Florin Malitaae252792018-06-14 11:24:50 -0400819SkString Value::toString() const {
820 SkDynamicMemoryWStream wstream;
821 Write(*this, &wstream);
822 const auto data = wstream.detachAsData();
823 // TODO: is there a better way to pass data around without copying?
824 return SkString(static_cast<const char*>(data->data()), data->size());
825}
826
Florin Malita7796f002018-06-08 12:25:38 -0400827static constexpr size_t kMinChunkSize = 4096;
828
Florin Malitafedfd542018-06-14 15:03:21 -0400829DOM::DOM(const char* data, size_t size)
Florin Malita7796f002018-06-08 12:25:38 -0400830 : fAlloc(kMinChunkSize) {
831 DOMParser parser(fAlloc);
832
Florin Malitafedfd542018-06-14 15:03:21 -0400833 fRoot = parser.parse(data, size);
Florin Malita7796f002018-06-08 12:25:38 -0400834}
835
836void DOM::write(SkWStream* stream) const {
Florin Malitaae252792018-06-14 11:24:50 -0400837 Write(fRoot, stream);
Florin Malita7796f002018-06-08 12:25:38 -0400838}
839
840} // namespace skjson