blob: cb7704c3c616bd5bd9fe112ea1cefcb45a40d81b [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"
12#include "SkString.h"
Florin Malita7796f002018-06-08 12:25:38 -040013
14#include <cmath>
Florin Malitafedfd542018-06-14 15:03:21 -040015#include <tuple>
Florin Malita7796f002018-06-08 12:25:38 -040016#include <vector>
17
18namespace skjson {
19
Florin Malitafedfd542018-06-14 15:03:21 -040020// #define SK_JSON_REPORT_ERRORS
Florin Malita7796f002018-06-08 12:25:38 -040021
Florin Malita7796f002018-06-08 12:25:38 -040022static_assert( sizeof(Value) == 8, "");
23static_assert(alignof(Value) == 8, "");
24
25static constexpr size_t kRecAlign = alignof(Value);
26
Florin Malitaae252792018-06-14 11:24:50 -040027void Value::init_tagged(Tag t) {
28 memset(fData8, 0, sizeof(fData8));
29 fData8[Value::kTagOffset] = SkTo<uint8_t>(t);
30 SkASSERT(this->getTag() == t);
31}
Florin Malita7796f002018-06-08 12:25:38 -040032
Florin Malitaae252792018-06-14 11:24:50 -040033// Pointer values store a type (in the upper kTagBits bits) and a pointer.
34void Value::init_tagged_pointer(Tag t, void* p) {
35 *this->cast<uintptr_t>() = reinterpret_cast<uintptr_t>(p);
Florin Malita7796f002018-06-08 12:25:38 -040036
Florin Malitaae252792018-06-14 11:24:50 -040037 if (sizeof(Value) == sizeof(uintptr_t)) {
38 // For 64-bit, we rely on the pointer upper bits being unused/zero.
39 SkASSERT(!(fData8[kTagOffset] & kTagMask));
40 fData8[kTagOffset] |= SkTo<uint8_t>(t);
41 } else {
42 // For 32-bit, we need to zero-initialize the upper 32 bits
43 SkASSERT(sizeof(Value) == sizeof(uintptr_t) * 2);
44 this->cast<uintptr_t>()[kTagOffset >> 2] = 0;
45 fData8[kTagOffset] = SkTo<uint8_t>(t);
Florin Malita7796f002018-06-08 12:25:38 -040046 }
47
Florin Malitaae252792018-06-14 11:24:50 -040048 SkASSERT(this->getTag() == t);
49 SkASSERT(this->ptr<void>() == p);
50}
51
52NullValue::NullValue() {
53 this->init_tagged(Tag::kNull);
54 SkASSERT(this->getTag() == Tag::kNull);
55}
56
57BoolValue::BoolValue(bool b) {
58 this->init_tagged(Tag::kBool);
59 *this->cast<bool>() = b;
60 SkASSERT(this->getTag() == Tag::kBool);
61}
62
63NumberValue::NumberValue(int32_t i) {
64 this->init_tagged(Tag::kInt);
65 *this->cast<int32_t>() = i;
66 SkASSERT(this->getTag() == Tag::kInt);
67}
68
69NumberValue::NumberValue(float f) {
70 this->init_tagged(Tag::kFloat);
71 *this->cast<float>() = f;
72 SkASSERT(this->getTag() == Tag::kFloat);
73}
74
75// Vector recs point to externally allocated slabs with the following layout:
76//
77// [size_t n] [REC_0] ... [REC_n-1] [optional extra trailing storage]
78//
79// Long strings use extra_alloc_size == 1 to store the \0 terminator.
80//
81template <typename T, size_t extra_alloc_size = 0>
82static void* MakeVector(const void* src, size_t size, SkArenaAlloc& alloc) {
83 // The Ts are already in memory, so their size should be safe.
84 const auto total_size = sizeof(size_t) + size * sizeof(T) + extra_alloc_size;
85 auto* size_ptr = reinterpret_cast<size_t*>(alloc.makeBytesAlignedTo(total_size, kRecAlign));
Florin Malita28f5dd82018-06-14 13:56:53 -040086
Florin Malitad7bfcaf2018-06-14 18:03:26 -040087 *size_ptr = size;
88 sk_careful_memcpy(size_ptr + 1, src, size * sizeof(T));
Florin Malitaae252792018-06-14 11:24:50 -040089
90 return size_ptr;
91}
92
93ArrayValue::ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc) {
94 this->init_tagged_pointer(Tag::kArray, MakeVector<Value>(src, size, alloc));
95 SkASSERT(this->getTag() == Tag::kArray);
96}
97
98// Strings have two flavors:
99//
100// -- short strings (len <= 7) -> these are stored inline, in the record
101// (one byte reserved for null terminator/type):
102//
103// [str] [\0]|[max_len - actual_len]
104//
105// Storing [max_len - actual_len] allows the 'len' field to double-up as a
106// null terminator when size == max_len (this works 'cause kShortString == 0).
107//
108// -- long strings (len > 7) -> these are externally allocated vectors (VectorRec<char>).
109//
110// The string data plus a null-char terminator are copied over.
111//
Florin Malitafb3beb02018-06-18 22:25:31 -0400112namespace {
113
114// An internal string builder with a fast 8 byte short string load path
115// (for the common case where the string is not at the end of the stream).
116class FastString final : public Value {
117public:
118 FastString(const char* src, size_t size, const char* eos, SkArenaAlloc& alloc) {
119 SkASSERT(src <= eos);
120
121 if (size > kMaxInlineStringSize) {
122 this->initLongString(src, size, alloc);
123 SkASSERT(this->getTag() == Tag::kString);
124 return;
125 }
126
127 static_assert(static_cast<uint8_t>(Tag::kShortString) == 0, "please don't break this");
128 static_assert(sizeof(Value) == 8, "");
129
130 // TODO: LIKELY
131 if (src + 7 <= eos) {
132 this->initFastShortString(src, size);
133 } else {
134 this->initShortString(src, size);
135 }
136
137 SkASSERT(this->getTag() == Tag::kShortString);
138 }
139
140private:
Florin Malitad7bfcaf2018-06-14 18:03:26 -0400141 static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 1;
Florin Malitafb3beb02018-06-18 22:25:31 -0400142
143 void initLongString(const char* src, size_t size, SkArenaAlloc& alloc) {
144 SkASSERT(size > kMaxInlineStringSize);
145
Florin Malitaae252792018-06-14 11:24:50 -0400146 this->init_tagged_pointer(Tag::kString, MakeVector<char, 1>(src, size, alloc));
147
148 auto* data = this->cast<VectorValue<char, Value::Type::kString>>()->begin();
149 const_cast<char*>(data)[size] = '\0';
Florin Malita7796f002018-06-08 12:25:38 -0400150 }
151
Florin Malitafb3beb02018-06-18 22:25:31 -0400152 void initShortString(const char* src, size_t size) {
153 SkASSERT(size <= kMaxInlineStringSize);
Florin Malita7796f002018-06-08 12:25:38 -0400154
Florin Malitafb3beb02018-06-18 22:25:31 -0400155 this->init_tagged(Tag::kShortString);
156 sk_careful_memcpy(this->cast<char>(), src, size);
157 // Null terminator provided by init_tagged() above (fData8 is zero-initialized).
158 }
Florin Malita7796f002018-06-08 12:25:38 -0400159
Florin Malitafb3beb02018-06-18 22:25:31 -0400160 void initFastShortString(const char* src, size_t size) {
161 SkASSERT(size <= kMaxInlineStringSize);
162
163 // Load 8 chars and mask out the tag and \0 terminator.
164 uint64_t* s64 = this->cast<uint64_t>();
165 memcpy(s64, src, 8);
166
167#if defined(SK_CPU_LENDIAN)
168 *s64 &= 0x00ffffffffffffffULL >> ((kMaxInlineStringSize - size) * 8);
169#else
170 static_assert(false, "Big-endian builds are not supported at this time.");
171#endif
172 }
173};
174
175} // namespace
176
177StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) {
178 new (this) FastString(src, size, src, alloc);
Florin Malitaae252792018-06-14 11:24:50 -0400179}
Florin Malita7796f002018-06-08 12:25:38 -0400180
Florin Malitaae252792018-06-14 11:24:50 -0400181ObjectValue::ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc) {
182 this->init_tagged_pointer(Tag::kObject, MakeVector<Member>(src, size, alloc));
183 SkASSERT(this->getTag() == Tag::kObject);
184}
Florin Malita7796f002018-06-08 12:25:38 -0400185
186
187// Boring public Value glue.
188
Mike Reed0edad3e2018-08-13 17:18:47 -0400189static int inline_strcmp(const char a[], const char b[]) {
190 for (;;) {
191 char c = *a++;
192 if (c == 0) {
193 break;
194 }
195 if (c != *b++) {
196 return 1;
197 }
198 }
199 return *b != 0;
200}
201
Florin Malita7796f002018-06-08 12:25:38 -0400202const Value& ObjectValue::operator[](const char* key) const {
203 // Reverse search for duplicates resolution (policy: return last).
204 const auto* begin = this->begin();
205 const auto* member = this->end();
206
207 while (member > begin) {
208 --member;
Mike Reed0edad3e2018-08-13 17:18:47 -0400209 if (0 == inline_strcmp(key, member->fKey.as<StringValue>().begin())) {
Florin Malita7796f002018-06-08 12:25:38 -0400210 return member->fValue;
211 }
212 }
213
Florin Malitaae252792018-06-14 11:24:50 -0400214 static const Value g_null = NullValue();
215 return g_null;
Florin Malita7796f002018-06-08 12:25:38 -0400216}
217
218namespace {
219
220// Lexer/parser inspired by rapidjson [1], sajson [2] and pjson [3].
221//
222// [1] https://github.com/Tencent/rapidjson/
223// [2] https://github.com/chadaustin/sajson
224// [3] https://pastebin.com/hnhSTL3h
225
226
227// bit 0 (0x01) - plain ASCII string character
228// bit 1 (0x02) - whitespace
Florin Malita0052a312018-06-15 16:42:09 -0400229// bit 2 (0x04) - string terminator (" \0 [control chars] **AND } ]** <- see matchString notes)
Florin Malita7796f002018-06-08 12:25:38 -0400230// bit 3 (0x08) - 0-9
231// bit 4 (0x10) - 0-9 e E .
Florin Malita0052a312018-06-15 16:42:09 -0400232// bit 5 (0x20) - scope terminator (} ])
Florin Malita7796f002018-06-08 12:25:38 -0400233static constexpr uint8_t g_token_flags[256] = {
234 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
235 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 6, 4, 4, // 0
236 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1
237 3, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x11,1, // 2
238 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19, 1, 1, 1, 1, 1, 1, // 3
239 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
Florin Malita0052a312018-06-15 16:42:09 -0400240 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,0x25, 1, 1, // 5
Florin Malita7796f002018-06-08 12:25:38 -0400241 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
Florin Malita0052a312018-06-15 16:42:09 -0400242 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,0x25, 1, 1, // 7
Florin Malita7796f002018-06-08 12:25:38 -0400243
244 // 128-255
245 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,
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};
250
Florin Malita0052a312018-06-15 16:42:09 -0400251static inline bool is_ws(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x02; }
252static inline bool is_eostring(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x04; }
253static inline bool is_digit(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x08; }
254static inline bool is_numeric(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x10; }
255static inline bool is_eoscope(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x20; }
Florin Malita7796f002018-06-08 12:25:38 -0400256
257static inline const char* skip_ws(const char* p) {
258 while (is_ws(*p)) ++p;
259 return p;
260}
261
262static inline float pow10(int32_t exp) {
263 static constexpr float g_pow10_table[63] =
264 {
265 1.e-031f, 1.e-030f, 1.e-029f, 1.e-028f, 1.e-027f, 1.e-026f, 1.e-025f, 1.e-024f,
266 1.e-023f, 1.e-022f, 1.e-021f, 1.e-020f, 1.e-019f, 1.e-018f, 1.e-017f, 1.e-016f,
267 1.e-015f, 1.e-014f, 1.e-013f, 1.e-012f, 1.e-011f, 1.e-010f, 1.e-009f, 1.e-008f,
268 1.e-007f, 1.e-006f, 1.e-005f, 1.e-004f, 1.e-003f, 1.e-002f, 1.e-001f, 1.e+000f,
269 1.e+001f, 1.e+002f, 1.e+003f, 1.e+004f, 1.e+005f, 1.e+006f, 1.e+007f, 1.e+008f,
270 1.e+009f, 1.e+010f, 1.e+011f, 1.e+012f, 1.e+013f, 1.e+014f, 1.e+015f, 1.e+016f,
271 1.e+017f, 1.e+018f, 1.e+019f, 1.e+020f, 1.e+021f, 1.e+022f, 1.e+023f, 1.e+024f,
272 1.e+025f, 1.e+026f, 1.e+027f, 1.e+028f, 1.e+029f, 1.e+030f, 1.e+031f
273 };
274
275 static constexpr int32_t k_exp_offset = SK_ARRAY_COUNT(g_pow10_table) / 2;
276
277 // We only support negative exponents for now.
278 SkASSERT(exp <= 0);
279
280 return (exp >= -k_exp_offset) ? g_pow10_table[exp + k_exp_offset]
281 : std::pow(10.0f, static_cast<float>(exp));
282}
283
284class DOMParser {
285public:
286 explicit DOMParser(SkArenaAlloc& alloc)
287 : fAlloc(alloc) {
Florin Malita7796f002018-06-08 12:25:38 -0400288 fValueStack.reserve(kValueStackReserve);
Florin Malita7796f002018-06-08 12:25:38 -0400289 }
290
Florin Malitafedfd542018-06-14 15:03:21 -0400291 const Value parse(const char* p, size_t size) {
292 if (!size) {
293 return this->error(NullValue(), p, "invalid empty input");
294 }
295
296 const char* p_stop = p + size - 1;
297
298 // We're only checking for end-of-stream on object/array close('}',']'),
299 // so we must trim any whitespace from the buffer tail.
300 while (p_stop > p && is_ws(*p_stop)) --p_stop;
301
302 SkASSERT(p_stop >= p && p_stop < p + size);
Florin Malita0052a312018-06-15 16:42:09 -0400303 if (!is_eoscope(*p_stop)) {
Florin Malitafedfd542018-06-14 15:03:21 -0400304 return this->error(NullValue(), p_stop, "invalid top-level value");
305 }
306
Florin Malita7796f002018-06-08 12:25:38 -0400307 p = skip_ws(p);
308
309 switch (*p) {
310 case '{':
311 goto match_object;
312 case '[':
313 goto match_array;
314 default:
Florin Malitaae252792018-06-14 11:24:50 -0400315 return this->error(NullValue(), p, "invalid top-level value");
Florin Malita7796f002018-06-08 12:25:38 -0400316 }
317
318 match_object:
319 SkASSERT(*p == '{');
320 p = skip_ws(p + 1);
321
322 this->pushObjectScope();
323
324 if (*p == '}') goto pop_object;
325
326 // goto match_object_key;
327 match_object_key:
328 p = skip_ws(p);
Florin Malitaae252792018-06-14 11:24:50 -0400329 if (*p != '"') return this->error(NullValue(), p, "expected object key");
Florin Malita7796f002018-06-08 12:25:38 -0400330
Florin Malitafb3beb02018-06-18 22:25:31 -0400331 p = this->matchString(p, p_stop, [this](const char* key, size_t size, const char* eos) {
332 this->pushObjectKey(key, size, eos);
Florin Malita7796f002018-06-08 12:25:38 -0400333 });
Florin Malitaae252792018-06-14 11:24:50 -0400334 if (!p) return NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400335
336 p = skip_ws(p);
Florin Malitaae252792018-06-14 11:24:50 -0400337 if (*p != ':') return this->error(NullValue(), p, "expected ':' separator");
Florin Malita7796f002018-06-08 12:25:38 -0400338
339 ++p;
340
341 // goto match_value;
342 match_value:
343 p = skip_ws(p);
344
345 switch (*p) {
346 case '\0':
Florin Malitaae252792018-06-14 11:24:50 -0400347 return this->error(NullValue(), p, "unexpected input end");
Florin Malita7796f002018-06-08 12:25:38 -0400348 case '"':
Florin Malitafb3beb02018-06-18 22:25:31 -0400349 p = this->matchString(p, p_stop, [this](const char* str, size_t size, const char* eos) {
350 this->pushString(str, size, eos);
Florin Malita7796f002018-06-08 12:25:38 -0400351 });
352 break;
353 case '[':
354 goto match_array;
355 case 'f':
356 p = this->matchFalse(p);
357 break;
358 case 'n':
359 p = this->matchNull(p);
360 break;
361 case 't':
362 p = this->matchTrue(p);
363 break;
364 case '{':
365 goto match_object;
366 default:
367 p = this->matchNumber(p);
368 break;
369 }
370
Florin Malitaae252792018-06-14 11:24:50 -0400371 if (!p) return NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400372
373 // goto match_post_value;
374 match_post_value:
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400375 SkASSERT(!this->inTopLevelScope());
Florin Malita7796f002018-06-08 12:25:38 -0400376
377 p = skip_ws(p);
378 switch (*p) {
379 case ',':
380 ++p;
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400381 if (this->inObjectScope()) {
Florin Malita7796f002018-06-08 12:25:38 -0400382 goto match_object_key;
383 } else {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400384 SkASSERT(this->inArrayScope());
Florin Malita7796f002018-06-08 12:25:38 -0400385 goto match_value;
386 }
387 case ']':
388 goto pop_array;
389 case '}':
390 goto pop_object;
391 default:
Florin Malitaae252792018-06-14 11:24:50 -0400392 return this->error(NullValue(), p - 1, "unexpected value-trailing token");
Florin Malita7796f002018-06-08 12:25:38 -0400393 }
394
395 // unreachable
396 SkASSERT(false);
397
398 pop_object:
399 SkASSERT(*p == '}');
400
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400401 if (this->inArrayScope()) {
Florin Malitaae252792018-06-14 11:24:50 -0400402 return this->error(NullValue(), p, "unexpected object terminator");
Florin Malita7796f002018-06-08 12:25:38 -0400403 }
404
405 this->popObjectScope();
406
407 // goto pop_common
408 pop_common:
Florin Malita0052a312018-06-15 16:42:09 -0400409 SkASSERT(is_eoscope(*p));
Florin Malita7796f002018-06-08 12:25:38 -0400410
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400411 if (this->inTopLevelScope()) {
Florin Malita7796f002018-06-08 12:25:38 -0400412 SkASSERT(fValueStack.size() == 1);
Florin Malita7796f002018-06-08 12:25:38 -0400413
Florin Malitafedfd542018-06-14 15:03:21 -0400414 // Success condition: parsed the top level element and reached the stop token.
415 return p == p_stop
Florin Malitaae252792018-06-14 11:24:50 -0400416 ? fValueStack.front()
Florin Malitafedfd542018-06-14 15:03:21 -0400417 : this->error(NullValue(), p + 1, "trailing root garbage");
Florin Malita7796f002018-06-08 12:25:38 -0400418 }
419
Florin Malita587f5a92018-06-15 09:21:36 -0400420 if (p == p_stop) {
421 return this->error(NullValue(), p, "unexpected end-of-input");
422 }
423
Florin Malitafedfd542018-06-14 15:03:21 -0400424 ++p;
425
Florin Malita7796f002018-06-08 12:25:38 -0400426 goto match_post_value;
427
428 match_array:
429 SkASSERT(*p == '[');
430 p = skip_ws(p + 1);
431
432 this->pushArrayScope();
433
434 if (*p != ']') goto match_value;
435
436 // goto pop_array;
437 pop_array:
438 SkASSERT(*p == ']');
439
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400440 if (this->inObjectScope()) {
Florin Malitaae252792018-06-14 11:24:50 -0400441 return this->error(NullValue(), p, "unexpected array terminator");
Florin Malita7796f002018-06-08 12:25:38 -0400442 }
443
444 this->popArrayScope();
445
446 goto pop_common;
447
448 SkASSERT(false);
Florin Malitaae252792018-06-14 11:24:50 -0400449 return NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400450 }
451
Florin Malitafedfd542018-06-14 15:03:21 -0400452 std::tuple<const char*, const SkString> getError() const {
453 return std::make_tuple(fErrorToken, fErrorMessage);
Florin Malita7796f002018-06-08 12:25:38 -0400454 }
455
456private:
Florin Malitafedfd542018-06-14 15:03:21 -0400457 SkArenaAlloc& fAlloc;
Florin Malita7796f002018-06-08 12:25:38 -0400458
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400459 // Pending values stack.
Florin Malita7796f002018-06-08 12:25:38 -0400460 static constexpr size_t kValueStackReserve = 256;
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400461 std::vector<Value> fValueStack;
Florin Malita7796f002018-06-08 12:25:38 -0400462
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400463 // Tracks the current object/array scope, as an index into fStack:
464 //
465 // - for objects: fScopeIndex = (index of first value in scope)
466 // - for arrays : fScopeIndex = -(index of first value in scope)
467 //
468 // fScopeIndex == 0 IFF we are at the top level (no current/active scope).
469 intptr_t fScopeIndex = 0;
470
471 // Error reporting.
Florin Malitafedfd542018-06-14 15:03:21 -0400472 const char* fErrorToken = nullptr;
473 SkString fErrorMessage;
Florin Malita7796f002018-06-08 12:25:38 -0400474
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400475 bool inTopLevelScope() const { return fScopeIndex == 0; }
476 bool inObjectScope() const { return fScopeIndex > 0; }
477 bool inArrayScope() const { return fScopeIndex < 0; }
478
479 // Helper for masquerading raw primitive types as Values (bypassing tagging, etc).
480 template <typename T>
481 class RawValue final : public Value {
482 public:
483 explicit RawValue(T v) {
484 static_assert(sizeof(T) <= sizeof(Value), "");
485 *this->cast<T>() = v;
486 }
487
488 T operator *() const { return *this->cast<T>(); }
489 };
490
Florin Malitaae252792018-06-14 11:24:50 -0400491 template <typename VectorT>
492 void popScopeAsVec(size_t scope_start) {
Florin Malita7796f002018-06-08 12:25:38 -0400493 SkASSERT(scope_start > 0);
494 SkASSERT(scope_start <= fValueStack.size());
495
Florin Malitaae252792018-06-14 11:24:50 -0400496 using T = typename VectorT::ValueT;
Florin Malita7796f002018-06-08 12:25:38 -0400497 static_assert( sizeof(T) >= sizeof(Value), "");
498 static_assert( sizeof(T) % sizeof(Value) == 0, "");
499 static_assert(alignof(T) == alignof(Value), "");
500
501 const auto scope_count = fValueStack.size() - scope_start,
502 count = scope_count / (sizeof(T) / sizeof(Value));
503 SkASSERT(scope_count % (sizeof(T) / sizeof(Value)) == 0);
504
505 const auto* begin = reinterpret_cast<const T*>(fValueStack.data() + scope_start);
506
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400507 // Restore the previous scope index from saved placeholder value,
508 // and instantiate as a vector of values in scope.
509 auto& placeholder = fValueStack[scope_start - 1];
510 fScopeIndex = *static_cast<RawValue<intptr_t>&>(placeholder);
511 placeholder = VectorT(begin, count, fAlloc);
Florin Malita7796f002018-06-08 12:25:38 -0400512
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400513 // Drop the (consumed) values in scope.
Florin Malita7796f002018-06-08 12:25:38 -0400514 fValueStack.resize(scope_start);
515 }
516
517 void pushObjectScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400518 // Save a scope index now, and then later we'll overwrite this value as the Object itself.
519 fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400520
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400521 // New object scope.
522 fScopeIndex = SkTo<intptr_t>(fValueStack.size());
Florin Malita7796f002018-06-08 12:25:38 -0400523 }
524
525 void popObjectScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400526 SkASSERT(this->inObjectScope());
527 this->popScopeAsVec<ObjectValue>(SkTo<size_t>(fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400528
529 SkDEBUGCODE(
530 const auto& obj = fValueStack.back().as<ObjectValue>();
531 SkASSERT(obj.is<ObjectValue>());
532 for (const auto& member : obj) {
533 SkASSERT(member.fKey.is<StringValue>());
534 }
535 )
536 }
537
538 void pushArrayScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400539 // Save a scope index now, and then later we'll overwrite this value as the Array itself.
540 fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400541
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400542 // New array scope.
543 fScopeIndex = -SkTo<intptr_t>(fValueStack.size());
Florin Malita7796f002018-06-08 12:25:38 -0400544 }
545
546 void popArrayScope() {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400547 SkASSERT(this->inArrayScope());
548 this->popScopeAsVec<ArrayValue>(SkTo<size_t>(-fScopeIndex));
Florin Malita7796f002018-06-08 12:25:38 -0400549
550 SkDEBUGCODE(
551 const auto& arr = fValueStack.back().as<ArrayValue>();
552 SkASSERT(arr.is<ArrayValue>());
553 )
554 }
555
Florin Malitafb3beb02018-06-18 22:25:31 -0400556 void pushObjectKey(const char* key, size_t size, const char* eos) {
Florin Malita2e5c1ae2018-06-20 14:23:23 -0400557 SkASSERT(this->inObjectScope());
558 SkASSERT(fValueStack.size() >= SkTo<size_t>(fScopeIndex));
559 SkASSERT(!((fValueStack.size() - SkTo<size_t>(fScopeIndex)) & 1));
Florin Malitafb3beb02018-06-18 22:25:31 -0400560 this->pushString(key, size, eos);
Florin Malita7796f002018-06-08 12:25:38 -0400561 }
562
563 void pushTrue() {
Florin Malitaae252792018-06-14 11:24:50 -0400564 fValueStack.push_back(BoolValue(true));
Florin Malita7796f002018-06-08 12:25:38 -0400565 }
566
567 void pushFalse() {
Florin Malitaae252792018-06-14 11:24:50 -0400568 fValueStack.push_back(BoolValue(false));
Florin Malita7796f002018-06-08 12:25:38 -0400569 }
570
571 void pushNull() {
Florin Malitaae252792018-06-14 11:24:50 -0400572 fValueStack.push_back(NullValue());
Florin Malita7796f002018-06-08 12:25:38 -0400573 }
574
Florin Malitafb3beb02018-06-18 22:25:31 -0400575 void pushString(const char* s, size_t size, const char* eos) {
576 fValueStack.push_back(FastString(s, size, eos, fAlloc));
Florin Malita7796f002018-06-08 12:25:38 -0400577 }
578
579 void pushInt32(int32_t i) {
Florin Malitaae252792018-06-14 11:24:50 -0400580 fValueStack.push_back(NumberValue(i));
Florin Malita7796f002018-06-08 12:25:38 -0400581 }
582
583 void pushFloat(float f) {
Florin Malitaae252792018-06-14 11:24:50 -0400584 fValueStack.push_back(NumberValue(f));
Florin Malita7796f002018-06-08 12:25:38 -0400585 }
586
587 template <typename T>
Florin Malitaae252792018-06-14 11:24:50 -0400588 T error(T&& ret_val, const char* p, const char* msg) {
Florin Malita7796f002018-06-08 12:25:38 -0400589#if defined(SK_JSON_REPORT_ERRORS)
Florin Malitafedfd542018-06-14 15:03:21 -0400590 fErrorToken = p;
591 fErrorMessage.set(msg);
Florin Malita7796f002018-06-08 12:25:38 -0400592#endif
593 return ret_val;
594 }
595
596 const char* matchTrue(const char* p) {
597 SkASSERT(p[0] == 't');
598
599 if (p[1] == 'r' && p[2] == 'u' && p[3] == 'e') {
600 this->pushTrue();
601 return p + 4;
602 }
603
604 return this->error(nullptr, p, "invalid token");
605 }
606
607 const char* matchFalse(const char* p) {
608 SkASSERT(p[0] == 'f');
609
610 if (p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[4] == 'e') {
611 this->pushFalse();
612 return p + 5;
613 }
614
615 return this->error(nullptr, p, "invalid token");
616 }
617
618 const char* matchNull(const char* p) {
619 SkASSERT(p[0] == 'n');
620
621 if (p[1] == 'u' && p[2] == 'l' && p[3] == 'l') {
622 this->pushNull();
623 return p + 4;
624 }
625
626 return this->error(nullptr, p, "invalid token");
627 }
628
629 template <typename MatchFunc>
Florin Malita0052a312018-06-15 16:42:09 -0400630 const char* matchString(const char* p, const char* p_stop, MatchFunc&& func) {
Florin Malita7796f002018-06-08 12:25:38 -0400631 SkASSERT(*p == '"');
632 const auto* s_begin = p + 1;
633
634 // TODO: unescape
Florin Malita7796f002018-06-08 12:25:38 -0400635
Florin Malita0052a312018-06-15 16:42:09 -0400636 do {
637 // Consume string chars.
638 for (p = p + 1; !is_eostring(*p); ++p);
Florin Malita7796f002018-06-08 12:25:38 -0400639
Florin Malita0052a312018-06-15 16:42:09 -0400640 if (*p == '"') {
641 // Valid string found.
Florin Malitafb3beb02018-06-18 22:25:31 -0400642 func(s_begin, p - s_begin, p_stop);
Florin Malita0052a312018-06-15 16:42:09 -0400643 return p + 1;
644 }
645
646 // End-of-scope chars are special: we use them to tag the end of the input.
647 // Thus they cannot be consumed indiscriminately -- we need to check if we hit the
648 // end of the input. To that effect, we treat them as string terminators above,
649 // then we catch them here.
650 } while (is_eoscope(*p) && (p != p_stop)); // Safe scope terminator char, keep going.
651
652 // Premature end-of-input, or illegal string char.
Florin Malita7796f002018-06-08 12:25:38 -0400653 return this->error(nullptr, s_begin - 1, "invalid string");
654 }
655
656 const char* matchFastFloatDecimalPart(const char* p, int sign, float f, int exp) {
657 SkASSERT(exp <= 0);
658
659 for (;;) {
660 if (!is_digit(*p)) break;
661 f = f * 10.f + (*p++ - '0'); --exp;
662 if (!is_digit(*p)) break;
663 f = f * 10.f + (*p++ - '0'); --exp;
664 }
665
666 if (is_numeric(*p)) {
667 SkASSERT(*p == '.' || *p == 'e' || *p == 'E');
668 // We either have malformed input, or an (unsupported) exponent.
669 return nullptr;
670 }
671
672 this->pushFloat(sign * f * pow10(exp));
673
674 return p;
675 }
676
677 const char* matchFastFloatPart(const char* p, int sign, float f) {
678 for (;;) {
679 if (!is_digit(*p)) break;
680 f = f * 10.f + (*p++ - '0');
681 if (!is_digit(*p)) break;
682 f = f * 10.f + (*p++ - '0');
683 }
684
685 if (!is_numeric(*p)) {
686 // Matched (integral) float.
687 this->pushFloat(sign * f);
688 return p;
689 }
690
691 return (*p == '.') ? this->matchFastFloatDecimalPart(p + 1, sign, f, 0)
692 : nullptr;
693 }
694
695 const char* matchFast32OrFloat(const char* p) {
696 int sign = 1;
697 if (*p == '-') {
698 sign = -1;
699 ++p;
700 }
701
702 const auto* digits_start = p;
703
704 int32_t n32 = 0;
705
706 // This is the largest absolute int32 value we can handle before
707 // risking overflow *on the next digit* (214748363).
708 static constexpr int32_t kMaxInt32 = (std::numeric_limits<int32_t>::max() - 9) / 10;
709
710 if (is_digit(*p)) {
711 n32 = (*p++ - '0');
712 for (;;) {
713 if (!is_digit(*p) || n32 > kMaxInt32) break;
714 n32 = n32 * 10 + (*p++ - '0');
715 }
716 }
717
718 if (!is_numeric(*p)) {
719 // Did we actually match any digits?
720 if (p > digits_start) {
721 this->pushInt32(sign * n32);
722 return p;
723 }
724 return nullptr;
725 }
726
727 if (*p == '.') {
728 const auto* decimals_start = ++p;
729
730 int exp = 0;
731
732 for (;;) {
733 if (!is_digit(*p) || n32 > kMaxInt32) break;
734 n32 = n32 * 10 + (*p++ - '0'); --exp;
735 if (!is_digit(*p) || n32 > kMaxInt32) break;
736 n32 = n32 * 10 + (*p++ - '0'); --exp;
737 }
738
739 if (!is_numeric(*p)) {
740 // Did we actually match any digits?
741 if (p > decimals_start) {
742 this->pushFloat(sign * n32 * pow10(exp));
743 return p;
744 }
745 return nullptr;
746 }
747
748 if (n32 > kMaxInt32) {
749 // we ran out on n32 bits
750 return this->matchFastFloatDecimalPart(p, sign, n32, exp);
751 }
752 }
753
754 return this->matchFastFloatPart(p, sign, n32);
755 }
756
757 const char* matchNumber(const char* p) {
758 if (const auto* fast = this->matchFast32OrFloat(p)) return fast;
759
760 // slow fallback
761 char* matched;
762 float f = strtof(p, &matched);
763 if (matched > p) {
764 this->pushFloat(f);
765 return matched;
766 }
767 return this->error(nullptr, p, "invalid numeric token");
768 }
769};
770
771void Write(const Value& v, SkWStream* stream) {
772 switch (v.getType()) {
773 case Value::Type::kNull:
774 stream->writeText("null");
775 break;
776 case Value::Type::kBool:
777 stream->writeText(*v.as<BoolValue>() ? "true" : "false");
778 break;
779 case Value::Type::kNumber:
780 stream->writeScalarAsText(*v.as<NumberValue>());
781 break;
782 case Value::Type::kString:
783 stream->writeText("\"");
784 stream->writeText(v.as<StringValue>().begin());
785 stream->writeText("\"");
786 break;
787 case Value::Type::kArray: {
788 const auto& array = v.as<ArrayValue>();
789 stream->writeText("[");
790 bool first_value = true;
791 for (const auto& v : array) {
792 if (!first_value) stream->writeText(",");
793 Write(v, stream);
794 first_value = false;
795 }
796 stream->writeText("]");
797 break;
798 }
799 case Value::Type::kObject:
800 const auto& object = v.as<ObjectValue>();
801 stream->writeText("{");
802 bool first_member = true;
803 for (const auto& member : object) {
804 SkASSERT(member.fKey.getType() == Value::Type::kString);
805 if (!first_member) stream->writeText(",");
806 Write(member.fKey, stream);
807 stream->writeText(":");
808 Write(member.fValue, stream);
809 first_member = false;
810 }
811 stream->writeText("}");
812 break;
813 }
814}
815
816} // namespace
817
Florin Malitaae252792018-06-14 11:24:50 -0400818SkString Value::toString() const {
819 SkDynamicMemoryWStream wstream;
820 Write(*this, &wstream);
821 const auto data = wstream.detachAsData();
822 // TODO: is there a better way to pass data around without copying?
823 return SkString(static_cast<const char*>(data->data()), data->size());
824}
825
Florin Malita7796f002018-06-08 12:25:38 -0400826static constexpr size_t kMinChunkSize = 4096;
827
Florin Malitafedfd542018-06-14 15:03:21 -0400828DOM::DOM(const char* data, size_t size)
Florin Malita7796f002018-06-08 12:25:38 -0400829 : fAlloc(kMinChunkSize) {
830 DOMParser parser(fAlloc);
831
Florin Malitafedfd542018-06-14 15:03:21 -0400832 fRoot = parser.parse(data, size);
Florin Malita7796f002018-06-08 12:25:38 -0400833}
834
835void DOM::write(SkWStream* stream) const {
Florin Malitaae252792018-06-14 11:24:50 -0400836 Write(fRoot, stream);
Florin Malita7796f002018-06-08 12:25:38 -0400837}
838
839} // namespace skjson