1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2018 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkJSON.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkMalloc.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/utils/SkParse.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkArenaAlloc.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUTF.h"
20*c8dee2aaSAndroid Build Coastguard Worker
21*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
22*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
23*c8dee2aaSAndroid Build Coastguard Worker #include <cstdlib>
24*c8dee2aaSAndroid Build Coastguard Worker #include <limits>
25*c8dee2aaSAndroid Build Coastguard Worker #include <new>
26*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
27*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker namespace skjson {
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker // #define SK_JSON_REPORT_ERRORS
32*c8dee2aaSAndroid Build Coastguard Worker
33*c8dee2aaSAndroid Build Coastguard Worker static_assert( sizeof(Value) == 8, "");
34*c8dee2aaSAndroid Build Coastguard Worker static_assert(alignof(Value) == 8, "");
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker static constexpr size_t kRecAlign = alignof(Value);
37*c8dee2aaSAndroid Build Coastguard Worker
init_tagged(Tag t)38*c8dee2aaSAndroid Build Coastguard Worker void Value::init_tagged(Tag t) {
39*c8dee2aaSAndroid Build Coastguard Worker memset(fData8, 0, sizeof(fData8));
40*c8dee2aaSAndroid Build Coastguard Worker fData8[0] = SkTo<uint8_t>(t);
41*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == t);
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker
44*c8dee2aaSAndroid Build Coastguard Worker // Pointer values store a type (in the lower kTagBits bits) and a pointer.
init_tagged_pointer(Tag t,void * p)45*c8dee2aaSAndroid Build Coastguard Worker void Value::init_tagged_pointer(Tag t, void* p) {
46*c8dee2aaSAndroid Build Coastguard Worker if (sizeof(Value) == sizeof(uintptr_t)) {
47*c8dee2aaSAndroid Build Coastguard Worker *this->cast<uintptr_t>() = reinterpret_cast<uintptr_t>(p);
48*c8dee2aaSAndroid Build Coastguard Worker // For 64-bit, we rely on the pointer lower bits being zero.
49*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!(fData8[0] & kTagMask));
50*c8dee2aaSAndroid Build Coastguard Worker fData8[0] |= SkTo<uint8_t>(t);
51*c8dee2aaSAndroid Build Coastguard Worker } else {
52*c8dee2aaSAndroid Build Coastguard Worker // For 32-bit, we store the pointer in the upper word
53*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(sizeof(Value) == sizeof(uintptr_t) * 2);
54*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged(t);
55*c8dee2aaSAndroid Build Coastguard Worker *this->cast<uintptr_t>() = reinterpret_cast<uintptr_t>(p);
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == t);
59*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->ptr<void>() == p);
60*c8dee2aaSAndroid Build Coastguard Worker }
61*c8dee2aaSAndroid Build Coastguard Worker
NullValue()62*c8dee2aaSAndroid Build Coastguard Worker NullValue::NullValue() {
63*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged(Tag::kNull);
64*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kNull);
65*c8dee2aaSAndroid Build Coastguard Worker }
66*c8dee2aaSAndroid Build Coastguard Worker
BoolValue(bool b)67*c8dee2aaSAndroid Build Coastguard Worker BoolValue::BoolValue(bool b) {
68*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged(Tag::kBool);
69*c8dee2aaSAndroid Build Coastguard Worker *this->cast<bool>() = b;
70*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kBool);
71*c8dee2aaSAndroid Build Coastguard Worker }
72*c8dee2aaSAndroid Build Coastguard Worker
NumberValue(int32_t i)73*c8dee2aaSAndroid Build Coastguard Worker NumberValue::NumberValue(int32_t i) {
74*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged(Tag::kInt);
75*c8dee2aaSAndroid Build Coastguard Worker *this->cast<int32_t>() = i;
76*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kInt);
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker
NumberValue(float f)79*c8dee2aaSAndroid Build Coastguard Worker NumberValue::NumberValue(float f) {
80*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged(Tag::kFloat);
81*c8dee2aaSAndroid Build Coastguard Worker *this->cast<float>() = f;
82*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kFloat);
83*c8dee2aaSAndroid Build Coastguard Worker }
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker // Vector recs point to externally allocated slabs with the following layout:
86*c8dee2aaSAndroid Build Coastguard Worker //
87*c8dee2aaSAndroid Build Coastguard Worker // [size_t n] [REC_0] ... [REC_n-1] [optional extra trailing storage]
88*c8dee2aaSAndroid Build Coastguard Worker //
89*c8dee2aaSAndroid Build Coastguard Worker // Long strings use extra_alloc_size == 1 to store the \0 terminator.
90*c8dee2aaSAndroid Build Coastguard Worker //
91*c8dee2aaSAndroid Build Coastguard Worker template <typename T, size_t extra_alloc_size = 0>
MakeVector(size_t vec_size,const void * src,size_t src_size,SkArenaAlloc & alloc)92*c8dee2aaSAndroid Build Coastguard Worker static void* MakeVector(size_t vec_size, const void* src, size_t src_size, SkArenaAlloc& alloc) {
93*c8dee2aaSAndroid Build Coastguard Worker // The Ts are already in memory, so their size should be safe.
94*c8dee2aaSAndroid Build Coastguard Worker const auto total_size = sizeof(size_t) + vec_size * sizeof(T) + extra_alloc_size;
95*c8dee2aaSAndroid Build Coastguard Worker auto* size_ptr = reinterpret_cast<size_t*>(alloc.makeBytesAlignedTo(total_size, kRecAlign));
96*c8dee2aaSAndroid Build Coastguard Worker
97*c8dee2aaSAndroid Build Coastguard Worker *size_ptr = vec_size;
98*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(size_ptr + 1, src, src_size * sizeof(T));
99*c8dee2aaSAndroid Build Coastguard Worker
100*c8dee2aaSAndroid Build Coastguard Worker return size_ptr;
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker template <typename T, size_t extra_alloc_size = 0>
MakeVector(size_t vec_size,const void * src,SkArenaAlloc & alloc)104*c8dee2aaSAndroid Build Coastguard Worker static void* MakeVector(size_t vec_size, const void* src, SkArenaAlloc& alloc) {
105*c8dee2aaSAndroid Build Coastguard Worker return MakeVector<T, extra_alloc_size>(vec_size, src, vec_size, alloc);
106*c8dee2aaSAndroid Build Coastguard Worker }
107*c8dee2aaSAndroid Build Coastguard Worker
ArrayValue(const Value * src,size_t size,SkArenaAlloc & alloc)108*c8dee2aaSAndroid Build Coastguard Worker ArrayValue::ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc) {
109*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged_pointer(Tag::kArray, MakeVector<Value>(size, src, alloc));
110*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kArray);
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker // Strings have two flavors:
114*c8dee2aaSAndroid Build Coastguard Worker //
115*c8dee2aaSAndroid Build Coastguard Worker // -- short strings (len <= 7) -> these are stored inline, in the record
116*c8dee2aaSAndroid Build Coastguard Worker // (one byte reserved for null terminator/type):
117*c8dee2aaSAndroid Build Coastguard Worker //
118*c8dee2aaSAndroid Build Coastguard Worker // [str] [\0]|[max_len - actual_len]
119*c8dee2aaSAndroid Build Coastguard Worker //
120*c8dee2aaSAndroid Build Coastguard Worker // Storing [max_len - actual_len] allows the 'len' field to double-up as a
121*c8dee2aaSAndroid Build Coastguard Worker // null terminator when size == max_len (this works 'cause kShortString == 0).
122*c8dee2aaSAndroid Build Coastguard Worker //
123*c8dee2aaSAndroid Build Coastguard Worker // -- long strings (len > 7) -> these are externally allocated vectors (VectorRec<char>).
124*c8dee2aaSAndroid Build Coastguard Worker //
125*c8dee2aaSAndroid Build Coastguard Worker // The string data plus a null-char terminator are copied over.
126*c8dee2aaSAndroid Build Coastguard Worker //
127*c8dee2aaSAndroid Build Coastguard Worker namespace {
128*c8dee2aaSAndroid Build Coastguard Worker
129*c8dee2aaSAndroid Build Coastguard Worker // An internal string builder with a fast 8 byte short string load path
130*c8dee2aaSAndroid Build Coastguard Worker // (for the common case where the string is not at the end of the stream).
131*c8dee2aaSAndroid Build Coastguard Worker class FastString final : public Value {
132*c8dee2aaSAndroid Build Coastguard Worker public:
FastString(const char * src,size_t size,const char * eos,SkArenaAlloc & alloc)133*c8dee2aaSAndroid Build Coastguard Worker FastString(const char* src, size_t size, const char* eos, SkArenaAlloc& alloc) {
134*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(src <= eos);
135*c8dee2aaSAndroid Build Coastguard Worker
136*c8dee2aaSAndroid Build Coastguard Worker if (size > kMaxInlineStringSize) {
137*c8dee2aaSAndroid Build Coastguard Worker this->initLongString(src, size, alloc);
138*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kString);
139*c8dee2aaSAndroid Build Coastguard Worker return;
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker
142*c8dee2aaSAndroid Build Coastguard Worker // initFastShortString is faster (doh), but requires access to 6 chars past src.
143*c8dee2aaSAndroid Build Coastguard Worker if (src && src + 6 <= eos) {
144*c8dee2aaSAndroid Build Coastguard Worker this->initFastShortString(src, size);
145*c8dee2aaSAndroid Build Coastguard Worker } else {
146*c8dee2aaSAndroid Build Coastguard Worker this->initShortString(src, size);
147*c8dee2aaSAndroid Build Coastguard Worker }
148*c8dee2aaSAndroid Build Coastguard Worker
149*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kShortString);
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker private:
153*c8dee2aaSAndroid Build Coastguard Worker // first byte reserved for tagging, \0 terminator => 6 usable chars
154*c8dee2aaSAndroid Build Coastguard Worker inline static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 2;
155*c8dee2aaSAndroid Build Coastguard Worker
initLongString(const char * src,size_t size,SkArenaAlloc & alloc)156*c8dee2aaSAndroid Build Coastguard Worker void initLongString(const char* src, size_t size, SkArenaAlloc& alloc) {
157*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(size > kMaxInlineStringSize);
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged_pointer(Tag::kString, MakeVector<char, 1>(size, src, alloc));
160*c8dee2aaSAndroid Build Coastguard Worker
161*c8dee2aaSAndroid Build Coastguard Worker auto* data = this->cast<VectorValue<char, Value::Type::kString>>()->begin();
162*c8dee2aaSAndroid Build Coastguard Worker const_cast<char*>(data)[size] = '\0';
163*c8dee2aaSAndroid Build Coastguard Worker }
164*c8dee2aaSAndroid Build Coastguard Worker
initShortString(const char * src,size_t size)165*c8dee2aaSAndroid Build Coastguard Worker void initShortString(const char* src, size_t size) {
166*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(size <= kMaxInlineStringSize);
167*c8dee2aaSAndroid Build Coastguard Worker
168*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged(Tag::kShortString);
169*c8dee2aaSAndroid Build Coastguard Worker sk_careful_memcpy(this->cast<char>(), src, size);
170*c8dee2aaSAndroid Build Coastguard Worker // Null terminator provided by init_tagged() above (fData8 is zero-initialized).
171*c8dee2aaSAndroid Build Coastguard Worker }
172*c8dee2aaSAndroid Build Coastguard Worker
initFastShortString(const char * src,size_t size)173*c8dee2aaSAndroid Build Coastguard Worker void initFastShortString(const char* src, size_t size) {
174*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(size <= kMaxInlineStringSize);
175*c8dee2aaSAndroid Build Coastguard Worker
176*c8dee2aaSAndroid Build Coastguard Worker uint64_t* s64 = this->cast<uint64_t>();
177*c8dee2aaSAndroid Build Coastguard Worker
178*c8dee2aaSAndroid Build Coastguard Worker // Load 8 chars and mask out the tag and \0 terminator.
179*c8dee2aaSAndroid Build Coastguard Worker // Note: we picked kShortString == 0 to avoid setting explicitly below.
180*c8dee2aaSAndroid Build Coastguard Worker static_assert(SkToU8(Tag::kShortString) == 0, "please don't break this");
181*c8dee2aaSAndroid Build Coastguard Worker
182*c8dee2aaSAndroid Build Coastguard Worker // Since the first byte is occupied by the tag, we want the string chars [0..5] to land
183*c8dee2aaSAndroid Build Coastguard Worker // on bytes [1..6] => the fastest way is to read8 @(src - 1) (always safe, because the
184*c8dee2aaSAndroid Build Coastguard Worker // string requires a " prefix at the very least).
185*c8dee2aaSAndroid Build Coastguard Worker memcpy(s64, src - 1, 8);
186*c8dee2aaSAndroid Build Coastguard Worker
187*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_CPU_LENDIAN)
188*c8dee2aaSAndroid Build Coastguard Worker // The mask for a max-length string (6), with a leading tag and trailing \0 is
189*c8dee2aaSAndroid Build Coastguard Worker // 0x00ffffffffffff00. Accounting for the final left-shift, this becomes
190*c8dee2aaSAndroid Build Coastguard Worker // 0x0000ffffffffffff.
191*c8dee2aaSAndroid Build Coastguard Worker *s64 &= (0x0000ffffffffffffULL >> ((kMaxInlineStringSize - size) * 8)) // trailing \0s
192*c8dee2aaSAndroid Build Coastguard Worker << 8; // tag byte
193*c8dee2aaSAndroid Build Coastguard Worker #else
194*c8dee2aaSAndroid Build Coastguard Worker static_assert(false, "Big-endian builds are not supported at this time.");
195*c8dee2aaSAndroid Build Coastguard Worker #endif
196*c8dee2aaSAndroid Build Coastguard Worker }
197*c8dee2aaSAndroid Build Coastguard Worker };
198*c8dee2aaSAndroid Build Coastguard Worker
199*c8dee2aaSAndroid Build Coastguard Worker } // namespace
200*c8dee2aaSAndroid Build Coastguard Worker
StringValue(const char * src,SkArenaAlloc & alloc)201*c8dee2aaSAndroid Build Coastguard Worker StringValue::StringValue(const char* src, SkArenaAlloc& alloc)
202*c8dee2aaSAndroid Build Coastguard Worker : StringValue(src, strlen(src), alloc) {}
203*c8dee2aaSAndroid Build Coastguard Worker
StringValue(const char * src,size_t size,SkArenaAlloc & alloc)204*c8dee2aaSAndroid Build Coastguard Worker StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) {
205*c8dee2aaSAndroid Build Coastguard Worker new (this) FastString(src, size, src, alloc);
206*c8dee2aaSAndroid Build Coastguard Worker }
207*c8dee2aaSAndroid Build Coastguard Worker
ObjectValue(const Member * src,size_t size,SkArenaAlloc & alloc)208*c8dee2aaSAndroid Build Coastguard Worker ObjectValue::ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc) {
209*c8dee2aaSAndroid Build Coastguard Worker this->init_tagged_pointer(Tag::kObject, MakeVector<Member>(size, src, alloc));
210*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->getTag() == Tag::kObject);
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker
213*c8dee2aaSAndroid Build Coastguard Worker
214*c8dee2aaSAndroid Build Coastguard Worker // Boring public Value glue.
215*c8dee2aaSAndroid Build Coastguard Worker
inline_strcmp(const char a[],const char b[])216*c8dee2aaSAndroid Build Coastguard Worker static int inline_strcmp(const char a[], const char b[]) {
217*c8dee2aaSAndroid Build Coastguard Worker for (;;) {
218*c8dee2aaSAndroid Build Coastguard Worker char c = *a++;
219*c8dee2aaSAndroid Build Coastguard Worker if (c == 0) {
220*c8dee2aaSAndroid Build Coastguard Worker break;
221*c8dee2aaSAndroid Build Coastguard Worker }
222*c8dee2aaSAndroid Build Coastguard Worker if (c != *b++) {
223*c8dee2aaSAndroid Build Coastguard Worker return 1;
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker }
226*c8dee2aaSAndroid Build Coastguard Worker return *b != 0;
227*c8dee2aaSAndroid Build Coastguard Worker }
228*c8dee2aaSAndroid Build Coastguard Worker
find(const char * key) const229*c8dee2aaSAndroid Build Coastguard Worker const Member* ObjectValue::find(const char* key) const {
230*c8dee2aaSAndroid Build Coastguard Worker // Reverse search for duplicates resolution (policy: return last).
231*c8dee2aaSAndroid Build Coastguard Worker const auto* begin = this->begin();
232*c8dee2aaSAndroid Build Coastguard Worker const auto* member = this->end();
233*c8dee2aaSAndroid Build Coastguard Worker
234*c8dee2aaSAndroid Build Coastguard Worker while (member > begin) {
235*c8dee2aaSAndroid Build Coastguard Worker --member;
236*c8dee2aaSAndroid Build Coastguard Worker if (0 == inline_strcmp(key, member->fKey.as<StringValue>().begin())) {
237*c8dee2aaSAndroid Build Coastguard Worker return member;
238*c8dee2aaSAndroid Build Coastguard Worker }
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker
241*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
242*c8dee2aaSAndroid Build Coastguard Worker }
243*c8dee2aaSAndroid Build Coastguard Worker
writable(const char * key,SkArenaAlloc & alloc) const244*c8dee2aaSAndroid Build Coastguard Worker Value& ObjectValue::writable(const char* key, SkArenaAlloc& alloc) const {
245*c8dee2aaSAndroid Build Coastguard Worker Member* writable_member = const_cast<Member*>(this->find(key));
246*c8dee2aaSAndroid Build Coastguard Worker
247*c8dee2aaSAndroid Build Coastguard Worker if (!writable_member) {
248*c8dee2aaSAndroid Build Coastguard Worker ObjectValue* writable_obj = const_cast<ObjectValue*>(this);
249*c8dee2aaSAndroid Build Coastguard Worker writable_obj->init_tagged_pointer(Tag::kObject, MakeVector<Member>(this->size() + 1,
250*c8dee2aaSAndroid Build Coastguard Worker this->begin(),
251*c8dee2aaSAndroid Build Coastguard Worker this->size(),
252*c8dee2aaSAndroid Build Coastguard Worker alloc));
253*c8dee2aaSAndroid Build Coastguard Worker writable_member = const_cast<Member*>(writable_obj->end() - 1);
254*c8dee2aaSAndroid Build Coastguard Worker writable_member->fKey = StringValue(key, strlen(key), alloc);
255*c8dee2aaSAndroid Build Coastguard Worker writable_member->fValue = NullValue();
256*c8dee2aaSAndroid Build Coastguard Worker }
257*c8dee2aaSAndroid Build Coastguard Worker
258*c8dee2aaSAndroid Build Coastguard Worker
259*c8dee2aaSAndroid Build Coastguard Worker return writable_member->fValue;
260*c8dee2aaSAndroid Build Coastguard Worker }
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Worker namespace {
263*c8dee2aaSAndroid Build Coastguard Worker
264*c8dee2aaSAndroid Build Coastguard Worker // Lexer/parser inspired by rapidjson [1], sajson [2] and pjson [3].
265*c8dee2aaSAndroid Build Coastguard Worker //
266*c8dee2aaSAndroid Build Coastguard Worker // [1] https://github.com/Tencent/rapidjson/
267*c8dee2aaSAndroid Build Coastguard Worker // [2] https://github.com/chadaustin/sajson
268*c8dee2aaSAndroid Build Coastguard Worker // [3] https://pastebin.com/hnhSTL3h
269*c8dee2aaSAndroid Build Coastguard Worker
270*c8dee2aaSAndroid Build Coastguard Worker
271*c8dee2aaSAndroid Build Coastguard Worker // bit 0 (0x01) - plain ASCII string character
272*c8dee2aaSAndroid Build Coastguard Worker // bit 1 (0x02) - whitespace
273*c8dee2aaSAndroid Build Coastguard Worker // bit 2 (0x04) - string terminator (" \\ \0 [control chars] **AND } ]** <- see matchString notes)
274*c8dee2aaSAndroid Build Coastguard Worker // bit 3 (0x08) - 0-9
275*c8dee2aaSAndroid Build Coastguard Worker // bit 4 (0x10) - 0-9 e E .
276*c8dee2aaSAndroid Build Coastguard Worker // bit 5 (0x20) - scope terminator (} ])
277*c8dee2aaSAndroid Build Coastguard Worker static constexpr uint8_t g_token_flags[256] = {
278*c8dee2aaSAndroid Build Coastguard Worker // 0 1 2 3 4 5 6 7 8 9 A B C D E F
279*c8dee2aaSAndroid Build Coastguard Worker 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 6, 4, 4, // 0
280*c8dee2aaSAndroid Build Coastguard Worker 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1
281*c8dee2aaSAndroid Build Coastguard Worker 3, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x11,1, // 2
282*c8dee2aaSAndroid Build Coastguard Worker 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19, 1, 1, 1, 1, 1, 1, // 3
283*c8dee2aaSAndroid Build Coastguard Worker 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4
284*c8dee2aaSAndroid Build Coastguard Worker 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,0x25, 1, 1, // 5
285*c8dee2aaSAndroid Build Coastguard Worker 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6
286*c8dee2aaSAndroid Build Coastguard Worker 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,0x25, 1, 1, // 7
287*c8dee2aaSAndroid Build Coastguard Worker
288*c8dee2aaSAndroid Build Coastguard Worker // 128-255
289*c8dee2aaSAndroid Build Coastguard Worker 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,
290*c8dee2aaSAndroid Build Coastguard Worker 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,
291*c8dee2aaSAndroid Build Coastguard Worker 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,
292*c8dee2aaSAndroid Build Coastguard Worker 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
293*c8dee2aaSAndroid Build Coastguard Worker };
294*c8dee2aaSAndroid Build Coastguard Worker
is_ws(char c)295*c8dee2aaSAndroid Build Coastguard Worker static inline bool is_ws(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x02; }
is_eostring(char c)296*c8dee2aaSAndroid Build Coastguard Worker static inline bool is_eostring(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x04; }
is_digit(char c)297*c8dee2aaSAndroid Build Coastguard Worker static inline bool is_digit(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x08; }
is_numeric(char c)298*c8dee2aaSAndroid Build Coastguard Worker static inline bool is_numeric(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x10; }
is_eoscope(char c)299*c8dee2aaSAndroid Build Coastguard Worker static inline bool is_eoscope(char c) { return g_token_flags[static_cast<uint8_t>(c)] & 0x20; }
300*c8dee2aaSAndroid Build Coastguard Worker
skip_ws(const char * p)301*c8dee2aaSAndroid Build Coastguard Worker static inline const char* skip_ws(const char* p) {
302*c8dee2aaSAndroid Build Coastguard Worker while (is_ws(*p)) ++p;
303*c8dee2aaSAndroid Build Coastguard Worker return p;
304*c8dee2aaSAndroid Build Coastguard Worker }
305*c8dee2aaSAndroid Build Coastguard Worker
pow10(int32_t exp)306*c8dee2aaSAndroid Build Coastguard Worker static inline float pow10(int32_t exp) {
307*c8dee2aaSAndroid Build Coastguard Worker static constexpr float g_pow10_table[63] =
308*c8dee2aaSAndroid Build Coastguard Worker {
309*c8dee2aaSAndroid Build Coastguard Worker 1.e-031f, 1.e-030f, 1.e-029f, 1.e-028f, 1.e-027f, 1.e-026f, 1.e-025f, 1.e-024f,
310*c8dee2aaSAndroid Build Coastguard Worker 1.e-023f, 1.e-022f, 1.e-021f, 1.e-020f, 1.e-019f, 1.e-018f, 1.e-017f, 1.e-016f,
311*c8dee2aaSAndroid Build Coastguard Worker 1.e-015f, 1.e-014f, 1.e-013f, 1.e-012f, 1.e-011f, 1.e-010f, 1.e-009f, 1.e-008f,
312*c8dee2aaSAndroid Build Coastguard Worker 1.e-007f, 1.e-006f, 1.e-005f, 1.e-004f, 1.e-003f, 1.e-002f, 1.e-001f, 1.e+000f,
313*c8dee2aaSAndroid Build Coastguard Worker 1.e+001f, 1.e+002f, 1.e+003f, 1.e+004f, 1.e+005f, 1.e+006f, 1.e+007f, 1.e+008f,
314*c8dee2aaSAndroid Build Coastguard Worker 1.e+009f, 1.e+010f, 1.e+011f, 1.e+012f, 1.e+013f, 1.e+014f, 1.e+015f, 1.e+016f,
315*c8dee2aaSAndroid Build Coastguard Worker 1.e+017f, 1.e+018f, 1.e+019f, 1.e+020f, 1.e+021f, 1.e+022f, 1.e+023f, 1.e+024f,
316*c8dee2aaSAndroid Build Coastguard Worker 1.e+025f, 1.e+026f, 1.e+027f, 1.e+028f, 1.e+029f, 1.e+030f, 1.e+031f
317*c8dee2aaSAndroid Build Coastguard Worker };
318*c8dee2aaSAndroid Build Coastguard Worker
319*c8dee2aaSAndroid Build Coastguard Worker static constexpr int32_t k_exp_offset = std::size(g_pow10_table) / 2;
320*c8dee2aaSAndroid Build Coastguard Worker
321*c8dee2aaSAndroid Build Coastguard Worker // We only support negative exponents for now.
322*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(exp <= 0);
323*c8dee2aaSAndroid Build Coastguard Worker
324*c8dee2aaSAndroid Build Coastguard Worker return (exp >= -k_exp_offset) ? g_pow10_table[exp + k_exp_offset]
325*c8dee2aaSAndroid Build Coastguard Worker : std::pow(10.0f, static_cast<float>(exp));
326*c8dee2aaSAndroid Build Coastguard Worker }
327*c8dee2aaSAndroid Build Coastguard Worker
328*c8dee2aaSAndroid Build Coastguard Worker class DOMParser {
329*c8dee2aaSAndroid Build Coastguard Worker public:
DOMParser(SkArenaAlloc & alloc)330*c8dee2aaSAndroid Build Coastguard Worker explicit DOMParser(SkArenaAlloc& alloc)
331*c8dee2aaSAndroid Build Coastguard Worker : fAlloc(alloc) {
332*c8dee2aaSAndroid Build Coastguard Worker fValueStack.reserve(kValueStackReserve);
333*c8dee2aaSAndroid Build Coastguard Worker fUnescapeBuffer.reserve(kUnescapeBufferReserve);
334*c8dee2aaSAndroid Build Coastguard Worker }
335*c8dee2aaSAndroid Build Coastguard Worker
parse(const char * p,size_t size)336*c8dee2aaSAndroid Build Coastguard Worker Value parse(const char* p, size_t size) {
337*c8dee2aaSAndroid Build Coastguard Worker if (!size) {
338*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p, "invalid empty input");
339*c8dee2aaSAndroid Build Coastguard Worker }
340*c8dee2aaSAndroid Build Coastguard Worker
341*c8dee2aaSAndroid Build Coastguard Worker const char* p_stop = p + size - 1;
342*c8dee2aaSAndroid Build Coastguard Worker
343*c8dee2aaSAndroid Build Coastguard Worker // We're only checking for end-of-stream on object/array close('}',']'),
344*c8dee2aaSAndroid Build Coastguard Worker // so we must trim any whitespace from the buffer tail.
345*c8dee2aaSAndroid Build Coastguard Worker while (p_stop > p && is_ws(*p_stop)) --p_stop;
346*c8dee2aaSAndroid Build Coastguard Worker
347*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(p_stop >= p && p_stop < p + size);
348*c8dee2aaSAndroid Build Coastguard Worker if (!is_eoscope(*p_stop)) {
349*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p_stop, "invalid top-level value");
350*c8dee2aaSAndroid Build Coastguard Worker }
351*c8dee2aaSAndroid Build Coastguard Worker
352*c8dee2aaSAndroid Build Coastguard Worker p = skip_ws(p);
353*c8dee2aaSAndroid Build Coastguard Worker
354*c8dee2aaSAndroid Build Coastguard Worker switch (*p) {
355*c8dee2aaSAndroid Build Coastguard Worker case '{':
356*c8dee2aaSAndroid Build Coastguard Worker goto match_object;
357*c8dee2aaSAndroid Build Coastguard Worker case '[':
358*c8dee2aaSAndroid Build Coastguard Worker goto match_array;
359*c8dee2aaSAndroid Build Coastguard Worker default:
360*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p, "invalid top-level value");
361*c8dee2aaSAndroid Build Coastguard Worker }
362*c8dee2aaSAndroid Build Coastguard Worker
363*c8dee2aaSAndroid Build Coastguard Worker match_object:
364*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*p == '{');
365*c8dee2aaSAndroid Build Coastguard Worker p = skip_ws(p + 1);
366*c8dee2aaSAndroid Build Coastguard Worker
367*c8dee2aaSAndroid Build Coastguard Worker this->pushObjectScope();
368*c8dee2aaSAndroid Build Coastguard Worker
369*c8dee2aaSAndroid Build Coastguard Worker if (*p == '}') goto pop_object;
370*c8dee2aaSAndroid Build Coastguard Worker
371*c8dee2aaSAndroid Build Coastguard Worker // goto match_object_key;
372*c8dee2aaSAndroid Build Coastguard Worker match_object_key:
373*c8dee2aaSAndroid Build Coastguard Worker p = skip_ws(p);
374*c8dee2aaSAndroid Build Coastguard Worker if (*p != '"') return this->error(NullValue(), p, "expected object key");
375*c8dee2aaSAndroid Build Coastguard Worker
376*c8dee2aaSAndroid Build Coastguard Worker p = this->matchString(p, p_stop, [this](const char* key, size_t size, const char* eos) {
377*c8dee2aaSAndroid Build Coastguard Worker this->pushObjectKey(key, size, eos);
378*c8dee2aaSAndroid Build Coastguard Worker });
379*c8dee2aaSAndroid Build Coastguard Worker if (!p) return NullValue();
380*c8dee2aaSAndroid Build Coastguard Worker
381*c8dee2aaSAndroid Build Coastguard Worker p = skip_ws(p);
382*c8dee2aaSAndroid Build Coastguard Worker if (*p != ':') return this->error(NullValue(), p, "expected ':' separator");
383*c8dee2aaSAndroid Build Coastguard Worker
384*c8dee2aaSAndroid Build Coastguard Worker ++p;
385*c8dee2aaSAndroid Build Coastguard Worker
386*c8dee2aaSAndroid Build Coastguard Worker // goto match_value;
387*c8dee2aaSAndroid Build Coastguard Worker match_value:
388*c8dee2aaSAndroid Build Coastguard Worker p = skip_ws(p);
389*c8dee2aaSAndroid Build Coastguard Worker
390*c8dee2aaSAndroid Build Coastguard Worker switch (*p) {
391*c8dee2aaSAndroid Build Coastguard Worker case '\0':
392*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p, "unexpected input end");
393*c8dee2aaSAndroid Build Coastguard Worker case '"':
394*c8dee2aaSAndroid Build Coastguard Worker p = this->matchString(p, p_stop, [this](const char* str, size_t size, const char* eos) {
395*c8dee2aaSAndroid Build Coastguard Worker this->pushString(str, size, eos);
396*c8dee2aaSAndroid Build Coastguard Worker });
397*c8dee2aaSAndroid Build Coastguard Worker break;
398*c8dee2aaSAndroid Build Coastguard Worker case '[':
399*c8dee2aaSAndroid Build Coastguard Worker goto match_array;
400*c8dee2aaSAndroid Build Coastguard Worker case 'f':
401*c8dee2aaSAndroid Build Coastguard Worker p = this->matchFalse(p);
402*c8dee2aaSAndroid Build Coastguard Worker break;
403*c8dee2aaSAndroid Build Coastguard Worker case 'n':
404*c8dee2aaSAndroid Build Coastguard Worker p = this->matchNull(p);
405*c8dee2aaSAndroid Build Coastguard Worker break;
406*c8dee2aaSAndroid Build Coastguard Worker case 't':
407*c8dee2aaSAndroid Build Coastguard Worker p = this->matchTrue(p);
408*c8dee2aaSAndroid Build Coastguard Worker break;
409*c8dee2aaSAndroid Build Coastguard Worker case '{':
410*c8dee2aaSAndroid Build Coastguard Worker goto match_object;
411*c8dee2aaSAndroid Build Coastguard Worker default:
412*c8dee2aaSAndroid Build Coastguard Worker p = this->matchNumber(p);
413*c8dee2aaSAndroid Build Coastguard Worker break;
414*c8dee2aaSAndroid Build Coastguard Worker }
415*c8dee2aaSAndroid Build Coastguard Worker
416*c8dee2aaSAndroid Build Coastguard Worker if (!p) return NullValue();
417*c8dee2aaSAndroid Build Coastguard Worker
418*c8dee2aaSAndroid Build Coastguard Worker // goto match_post_value;
419*c8dee2aaSAndroid Build Coastguard Worker match_post_value:
420*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->inTopLevelScope());
421*c8dee2aaSAndroid Build Coastguard Worker
422*c8dee2aaSAndroid Build Coastguard Worker p = skip_ws(p);
423*c8dee2aaSAndroid Build Coastguard Worker switch (*p) {
424*c8dee2aaSAndroid Build Coastguard Worker case ',':
425*c8dee2aaSAndroid Build Coastguard Worker ++p;
426*c8dee2aaSAndroid Build Coastguard Worker if (this->inObjectScope()) {
427*c8dee2aaSAndroid Build Coastguard Worker goto match_object_key;
428*c8dee2aaSAndroid Build Coastguard Worker } else {
429*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->inArrayScope());
430*c8dee2aaSAndroid Build Coastguard Worker goto match_value;
431*c8dee2aaSAndroid Build Coastguard Worker }
432*c8dee2aaSAndroid Build Coastguard Worker case ']':
433*c8dee2aaSAndroid Build Coastguard Worker goto pop_array;
434*c8dee2aaSAndroid Build Coastguard Worker case '}':
435*c8dee2aaSAndroid Build Coastguard Worker goto pop_object;
436*c8dee2aaSAndroid Build Coastguard Worker default:
437*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p - 1, "unexpected value-trailing token");
438*c8dee2aaSAndroid Build Coastguard Worker }
439*c8dee2aaSAndroid Build Coastguard Worker
440*c8dee2aaSAndroid Build Coastguard Worker // unreachable
441*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
442*c8dee2aaSAndroid Build Coastguard Worker
443*c8dee2aaSAndroid Build Coastguard Worker pop_object:
444*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*p == '}');
445*c8dee2aaSAndroid Build Coastguard Worker
446*c8dee2aaSAndroid Build Coastguard Worker if (this->inArrayScope()) {
447*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p, "unexpected object terminator");
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker
450*c8dee2aaSAndroid Build Coastguard Worker this->popObjectScope();
451*c8dee2aaSAndroid Build Coastguard Worker
452*c8dee2aaSAndroid Build Coastguard Worker // goto pop_common
453*c8dee2aaSAndroid Build Coastguard Worker pop_common:
454*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(is_eoscope(*p));
455*c8dee2aaSAndroid Build Coastguard Worker
456*c8dee2aaSAndroid Build Coastguard Worker if (this->inTopLevelScope()) {
457*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fValueStack.size() == 1);
458*c8dee2aaSAndroid Build Coastguard Worker
459*c8dee2aaSAndroid Build Coastguard Worker // Success condition: parsed the top level element and reached the stop token.
460*c8dee2aaSAndroid Build Coastguard Worker return p == p_stop
461*c8dee2aaSAndroid Build Coastguard Worker ? fValueStack.front()
462*c8dee2aaSAndroid Build Coastguard Worker : this->error(NullValue(), p + 1, "trailing root garbage");
463*c8dee2aaSAndroid Build Coastguard Worker }
464*c8dee2aaSAndroid Build Coastguard Worker
465*c8dee2aaSAndroid Build Coastguard Worker if (p == p_stop) {
466*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p, "unexpected end-of-input");
467*c8dee2aaSAndroid Build Coastguard Worker }
468*c8dee2aaSAndroid Build Coastguard Worker
469*c8dee2aaSAndroid Build Coastguard Worker ++p;
470*c8dee2aaSAndroid Build Coastguard Worker
471*c8dee2aaSAndroid Build Coastguard Worker goto match_post_value;
472*c8dee2aaSAndroid Build Coastguard Worker
473*c8dee2aaSAndroid Build Coastguard Worker match_array:
474*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*p == '[');
475*c8dee2aaSAndroid Build Coastguard Worker p = skip_ws(p + 1);
476*c8dee2aaSAndroid Build Coastguard Worker
477*c8dee2aaSAndroid Build Coastguard Worker this->pushArrayScope();
478*c8dee2aaSAndroid Build Coastguard Worker
479*c8dee2aaSAndroid Build Coastguard Worker if (*p != ']') goto match_value;
480*c8dee2aaSAndroid Build Coastguard Worker
481*c8dee2aaSAndroid Build Coastguard Worker // goto pop_array;
482*c8dee2aaSAndroid Build Coastguard Worker pop_array:
483*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*p == ']');
484*c8dee2aaSAndroid Build Coastguard Worker
485*c8dee2aaSAndroid Build Coastguard Worker if (this->inObjectScope()) {
486*c8dee2aaSAndroid Build Coastguard Worker return this->error(NullValue(), p, "unexpected array terminator");
487*c8dee2aaSAndroid Build Coastguard Worker }
488*c8dee2aaSAndroid Build Coastguard Worker
489*c8dee2aaSAndroid Build Coastguard Worker this->popArrayScope();
490*c8dee2aaSAndroid Build Coastguard Worker
491*c8dee2aaSAndroid Build Coastguard Worker goto pop_common;
492*c8dee2aaSAndroid Build Coastguard Worker
493*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
494*c8dee2aaSAndroid Build Coastguard Worker return NullValue();
495*c8dee2aaSAndroid Build Coastguard Worker }
496*c8dee2aaSAndroid Build Coastguard Worker
getError() const497*c8dee2aaSAndroid Build Coastguard Worker std::tuple<const char*, const SkString> getError() const {
498*c8dee2aaSAndroid Build Coastguard Worker return std::make_tuple(fErrorToken, fErrorMessage);
499*c8dee2aaSAndroid Build Coastguard Worker }
500*c8dee2aaSAndroid Build Coastguard Worker
501*c8dee2aaSAndroid Build Coastguard Worker private:
502*c8dee2aaSAndroid Build Coastguard Worker SkArenaAlloc& fAlloc;
503*c8dee2aaSAndroid Build Coastguard Worker
504*c8dee2aaSAndroid Build Coastguard Worker // Pending values stack.
505*c8dee2aaSAndroid Build Coastguard Worker inline static constexpr size_t kValueStackReserve = 256;
506*c8dee2aaSAndroid Build Coastguard Worker std::vector<Value> fValueStack;
507*c8dee2aaSAndroid Build Coastguard Worker
508*c8dee2aaSAndroid Build Coastguard Worker // String unescape buffer.
509*c8dee2aaSAndroid Build Coastguard Worker inline static constexpr size_t kUnescapeBufferReserve = 512;
510*c8dee2aaSAndroid Build Coastguard Worker std::vector<char> fUnescapeBuffer;
511*c8dee2aaSAndroid Build Coastguard Worker
512*c8dee2aaSAndroid Build Coastguard Worker // Tracks the current object/array scope, as an index into fStack:
513*c8dee2aaSAndroid Build Coastguard Worker //
514*c8dee2aaSAndroid Build Coastguard Worker // - for objects: fScopeIndex = (index of first value in scope)
515*c8dee2aaSAndroid Build Coastguard Worker // - for arrays : fScopeIndex = -(index of first value in scope)
516*c8dee2aaSAndroid Build Coastguard Worker //
517*c8dee2aaSAndroid Build Coastguard Worker // fScopeIndex == 0 IFF we are at the top level (no current/active scope).
518*c8dee2aaSAndroid Build Coastguard Worker intptr_t fScopeIndex = 0;
519*c8dee2aaSAndroid Build Coastguard Worker
520*c8dee2aaSAndroid Build Coastguard Worker // Error reporting.
521*c8dee2aaSAndroid Build Coastguard Worker const char* fErrorToken = nullptr;
522*c8dee2aaSAndroid Build Coastguard Worker SkString fErrorMessage;
523*c8dee2aaSAndroid Build Coastguard Worker
inTopLevelScope() const524*c8dee2aaSAndroid Build Coastguard Worker bool inTopLevelScope() const { return fScopeIndex == 0; }
inObjectScope() const525*c8dee2aaSAndroid Build Coastguard Worker bool inObjectScope() const { return fScopeIndex > 0; }
inArrayScope() const526*c8dee2aaSAndroid Build Coastguard Worker bool inArrayScope() const { return fScopeIndex < 0; }
527*c8dee2aaSAndroid Build Coastguard Worker
528*c8dee2aaSAndroid Build Coastguard Worker // Helper for masquerading raw primitive types as Values (bypassing tagging, etc).
529*c8dee2aaSAndroid Build Coastguard Worker template <typename T>
530*c8dee2aaSAndroid Build Coastguard Worker class RawValue final : public Value {
531*c8dee2aaSAndroid Build Coastguard Worker public:
RawValue(T v)532*c8dee2aaSAndroid Build Coastguard Worker explicit RawValue(T v) {
533*c8dee2aaSAndroid Build Coastguard Worker static_assert(sizeof(T) <= sizeof(Value), "");
534*c8dee2aaSAndroid Build Coastguard Worker *this->cast<T>() = v;
535*c8dee2aaSAndroid Build Coastguard Worker }
536*c8dee2aaSAndroid Build Coastguard Worker
operator *() const537*c8dee2aaSAndroid Build Coastguard Worker T operator *() const { return *this->cast<T>(); }
538*c8dee2aaSAndroid Build Coastguard Worker };
539*c8dee2aaSAndroid Build Coastguard Worker
540*c8dee2aaSAndroid Build Coastguard Worker template <typename VectorT>
popScopeAsVec(size_t scope_start)541*c8dee2aaSAndroid Build Coastguard Worker void popScopeAsVec(size_t scope_start) {
542*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(scope_start > 0);
543*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(scope_start <= fValueStack.size());
544*c8dee2aaSAndroid Build Coastguard Worker
545*c8dee2aaSAndroid Build Coastguard Worker using T = typename VectorT::ValueT;
546*c8dee2aaSAndroid Build Coastguard Worker static_assert( sizeof(T) >= sizeof(Value), "");
547*c8dee2aaSAndroid Build Coastguard Worker static_assert( sizeof(T) % sizeof(Value) == 0, "");
548*c8dee2aaSAndroid Build Coastguard Worker static_assert(alignof(T) == alignof(Value), "");
549*c8dee2aaSAndroid Build Coastguard Worker
550*c8dee2aaSAndroid Build Coastguard Worker const auto scope_count = fValueStack.size() - scope_start,
551*c8dee2aaSAndroid Build Coastguard Worker count = scope_count / (sizeof(T) / sizeof(Value));
552*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(scope_count % (sizeof(T) / sizeof(Value)) == 0);
553*c8dee2aaSAndroid Build Coastguard Worker
554*c8dee2aaSAndroid Build Coastguard Worker const auto* begin = reinterpret_cast<const T*>(fValueStack.data() + scope_start);
555*c8dee2aaSAndroid Build Coastguard Worker
556*c8dee2aaSAndroid Build Coastguard Worker // Restore the previous scope index from saved placeholder value,
557*c8dee2aaSAndroid Build Coastguard Worker // and instantiate as a vector of values in scope.
558*c8dee2aaSAndroid Build Coastguard Worker auto& placeholder = fValueStack[scope_start - 1];
559*c8dee2aaSAndroid Build Coastguard Worker fScopeIndex = *static_cast<RawValue<intptr_t>&>(placeholder);
560*c8dee2aaSAndroid Build Coastguard Worker placeholder = VectorT(begin, count, fAlloc);
561*c8dee2aaSAndroid Build Coastguard Worker
562*c8dee2aaSAndroid Build Coastguard Worker // Drop the (consumed) values in scope.
563*c8dee2aaSAndroid Build Coastguard Worker fValueStack.resize(scope_start);
564*c8dee2aaSAndroid Build Coastguard Worker }
565*c8dee2aaSAndroid Build Coastguard Worker
pushObjectScope()566*c8dee2aaSAndroid Build Coastguard Worker void pushObjectScope() {
567*c8dee2aaSAndroid Build Coastguard Worker // Save a scope index now, and then later we'll overwrite this value as the Object itself.
568*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
569*c8dee2aaSAndroid Build Coastguard Worker
570*c8dee2aaSAndroid Build Coastguard Worker // New object scope.
571*c8dee2aaSAndroid Build Coastguard Worker fScopeIndex = SkTo<intptr_t>(fValueStack.size());
572*c8dee2aaSAndroid Build Coastguard Worker }
573*c8dee2aaSAndroid Build Coastguard Worker
popObjectScope()574*c8dee2aaSAndroid Build Coastguard Worker void popObjectScope() {
575*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->inObjectScope());
576*c8dee2aaSAndroid Build Coastguard Worker this->popScopeAsVec<ObjectValue>(SkTo<size_t>(fScopeIndex));
577*c8dee2aaSAndroid Build Coastguard Worker
578*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(
579*c8dee2aaSAndroid Build Coastguard Worker const auto& obj = fValueStack.back().as<ObjectValue>();
580*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(obj.is<ObjectValue>());
581*c8dee2aaSAndroid Build Coastguard Worker for (const auto& member : obj) {
582*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(member.fKey.is<StringValue>());
583*c8dee2aaSAndroid Build Coastguard Worker }
584*c8dee2aaSAndroid Build Coastguard Worker )
585*c8dee2aaSAndroid Build Coastguard Worker }
586*c8dee2aaSAndroid Build Coastguard Worker
pushArrayScope()587*c8dee2aaSAndroid Build Coastguard Worker void pushArrayScope() {
588*c8dee2aaSAndroid Build Coastguard Worker // Save a scope index now, and then later we'll overwrite this value as the Array itself.
589*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(RawValue<intptr_t>(fScopeIndex));
590*c8dee2aaSAndroid Build Coastguard Worker
591*c8dee2aaSAndroid Build Coastguard Worker // New array scope.
592*c8dee2aaSAndroid Build Coastguard Worker fScopeIndex = -SkTo<intptr_t>(fValueStack.size());
593*c8dee2aaSAndroid Build Coastguard Worker }
594*c8dee2aaSAndroid Build Coastguard Worker
popArrayScope()595*c8dee2aaSAndroid Build Coastguard Worker void popArrayScope() {
596*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->inArrayScope());
597*c8dee2aaSAndroid Build Coastguard Worker this->popScopeAsVec<ArrayValue>(SkTo<size_t>(-fScopeIndex));
598*c8dee2aaSAndroid Build Coastguard Worker
599*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(
600*c8dee2aaSAndroid Build Coastguard Worker const auto& arr = fValueStack.back().as<ArrayValue>();
601*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(arr.is<ArrayValue>());
602*c8dee2aaSAndroid Build Coastguard Worker )
603*c8dee2aaSAndroid Build Coastguard Worker }
604*c8dee2aaSAndroid Build Coastguard Worker
pushObjectKey(const char * key,size_t size,const char * eos)605*c8dee2aaSAndroid Build Coastguard Worker void pushObjectKey(const char* key, size_t size, const char* eos) {
606*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->inObjectScope());
607*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fValueStack.size() >= SkTo<size_t>(fScopeIndex));
608*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!((fValueStack.size() - SkTo<size_t>(fScopeIndex)) & 1));
609*c8dee2aaSAndroid Build Coastguard Worker this->pushString(key, size, eos);
610*c8dee2aaSAndroid Build Coastguard Worker }
611*c8dee2aaSAndroid Build Coastguard Worker
pushTrue()612*c8dee2aaSAndroid Build Coastguard Worker void pushTrue() {
613*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(BoolValue(true));
614*c8dee2aaSAndroid Build Coastguard Worker }
615*c8dee2aaSAndroid Build Coastguard Worker
pushFalse()616*c8dee2aaSAndroid Build Coastguard Worker void pushFalse() {
617*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(BoolValue(false));
618*c8dee2aaSAndroid Build Coastguard Worker }
619*c8dee2aaSAndroid Build Coastguard Worker
pushNull()620*c8dee2aaSAndroid Build Coastguard Worker void pushNull() {
621*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(NullValue());
622*c8dee2aaSAndroid Build Coastguard Worker }
623*c8dee2aaSAndroid Build Coastguard Worker
pushString(const char * s,size_t size,const char * eos)624*c8dee2aaSAndroid Build Coastguard Worker void pushString(const char* s, size_t size, const char* eos) {
625*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(FastString(s, size, eos, fAlloc));
626*c8dee2aaSAndroid Build Coastguard Worker }
627*c8dee2aaSAndroid Build Coastguard Worker
pushInt32(int32_t i)628*c8dee2aaSAndroid Build Coastguard Worker void pushInt32(int32_t i) {
629*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(NumberValue(i));
630*c8dee2aaSAndroid Build Coastguard Worker }
631*c8dee2aaSAndroid Build Coastguard Worker
pushFloat(float f)632*c8dee2aaSAndroid Build Coastguard Worker void pushFloat(float f) {
633*c8dee2aaSAndroid Build Coastguard Worker fValueStack.push_back(NumberValue(f));
634*c8dee2aaSAndroid Build Coastguard Worker }
635*c8dee2aaSAndroid Build Coastguard Worker
636*c8dee2aaSAndroid Build Coastguard Worker template <typename T>
error(T && ret_val,const char * p,const char * msg)637*c8dee2aaSAndroid Build Coastguard Worker T error(T&& ret_val, const char* p, const char* msg) {
638*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_JSON_REPORT_ERRORS)
639*c8dee2aaSAndroid Build Coastguard Worker fErrorToken = p;
640*c8dee2aaSAndroid Build Coastguard Worker fErrorMessage.set(msg);
641*c8dee2aaSAndroid Build Coastguard Worker #endif
642*c8dee2aaSAndroid Build Coastguard Worker return ret_val;
643*c8dee2aaSAndroid Build Coastguard Worker }
644*c8dee2aaSAndroid Build Coastguard Worker
matchTrue(const char * p)645*c8dee2aaSAndroid Build Coastguard Worker const char* matchTrue(const char* p) {
646*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(p[0] == 't');
647*c8dee2aaSAndroid Build Coastguard Worker
648*c8dee2aaSAndroid Build Coastguard Worker if (p[1] == 'r' && p[2] == 'u' && p[3] == 'e') {
649*c8dee2aaSAndroid Build Coastguard Worker this->pushTrue();
650*c8dee2aaSAndroid Build Coastguard Worker return p + 4;
651*c8dee2aaSAndroid Build Coastguard Worker }
652*c8dee2aaSAndroid Build Coastguard Worker
653*c8dee2aaSAndroid Build Coastguard Worker return this->error(nullptr, p, "invalid token");
654*c8dee2aaSAndroid Build Coastguard Worker }
655*c8dee2aaSAndroid Build Coastguard Worker
matchFalse(const char * p)656*c8dee2aaSAndroid Build Coastguard Worker const char* matchFalse(const char* p) {
657*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(p[0] == 'f');
658*c8dee2aaSAndroid Build Coastguard Worker
659*c8dee2aaSAndroid Build Coastguard Worker if (p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[4] == 'e') {
660*c8dee2aaSAndroid Build Coastguard Worker this->pushFalse();
661*c8dee2aaSAndroid Build Coastguard Worker return p + 5;
662*c8dee2aaSAndroid Build Coastguard Worker }
663*c8dee2aaSAndroid Build Coastguard Worker
664*c8dee2aaSAndroid Build Coastguard Worker return this->error(nullptr, p, "invalid token");
665*c8dee2aaSAndroid Build Coastguard Worker }
666*c8dee2aaSAndroid Build Coastguard Worker
matchNull(const char * p)667*c8dee2aaSAndroid Build Coastguard Worker const char* matchNull(const char* p) {
668*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(p[0] == 'n');
669*c8dee2aaSAndroid Build Coastguard Worker
670*c8dee2aaSAndroid Build Coastguard Worker if (p[1] == 'u' && p[2] == 'l' && p[3] == 'l') {
671*c8dee2aaSAndroid Build Coastguard Worker this->pushNull();
672*c8dee2aaSAndroid Build Coastguard Worker return p + 4;
673*c8dee2aaSAndroid Build Coastguard Worker }
674*c8dee2aaSAndroid Build Coastguard Worker
675*c8dee2aaSAndroid Build Coastguard Worker return this->error(nullptr, p, "invalid token");
676*c8dee2aaSAndroid Build Coastguard Worker }
677*c8dee2aaSAndroid Build Coastguard Worker
unescapeString(const char * begin,const char * end)678*c8dee2aaSAndroid Build Coastguard Worker const std::vector<char>* unescapeString(const char* begin, const char* end) {
679*c8dee2aaSAndroid Build Coastguard Worker fUnescapeBuffer.clear();
680*c8dee2aaSAndroid Build Coastguard Worker
681*c8dee2aaSAndroid Build Coastguard Worker for (const auto* p = begin; p != end; ++p) {
682*c8dee2aaSAndroid Build Coastguard Worker if (*p != '\\') {
683*c8dee2aaSAndroid Build Coastguard Worker fUnescapeBuffer.push_back(*p);
684*c8dee2aaSAndroid Build Coastguard Worker continue;
685*c8dee2aaSAndroid Build Coastguard Worker }
686*c8dee2aaSAndroid Build Coastguard Worker
687*c8dee2aaSAndroid Build Coastguard Worker if (++p == end) {
688*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
689*c8dee2aaSAndroid Build Coastguard Worker }
690*c8dee2aaSAndroid Build Coastguard Worker
691*c8dee2aaSAndroid Build Coastguard Worker switch (*p) {
692*c8dee2aaSAndroid Build Coastguard Worker case '"': fUnescapeBuffer.push_back( '"'); break;
693*c8dee2aaSAndroid Build Coastguard Worker case '\\': fUnescapeBuffer.push_back('\\'); break;
694*c8dee2aaSAndroid Build Coastguard Worker case '/': fUnescapeBuffer.push_back( '/'); break;
695*c8dee2aaSAndroid Build Coastguard Worker case 'b': fUnescapeBuffer.push_back('\b'); break;
696*c8dee2aaSAndroid Build Coastguard Worker case 'f': fUnescapeBuffer.push_back('\f'); break;
697*c8dee2aaSAndroid Build Coastguard Worker case 'n': fUnescapeBuffer.push_back('\n'); break;
698*c8dee2aaSAndroid Build Coastguard Worker case 'r': fUnescapeBuffer.push_back('\r'); break;
699*c8dee2aaSAndroid Build Coastguard Worker case 't': fUnescapeBuffer.push_back('\t'); break;
700*c8dee2aaSAndroid Build Coastguard Worker case 'u': {
701*c8dee2aaSAndroid Build Coastguard Worker if (p + 4 >= end) {
702*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
703*c8dee2aaSAndroid Build Coastguard Worker }
704*c8dee2aaSAndroid Build Coastguard Worker
705*c8dee2aaSAndroid Build Coastguard Worker uint32_t hexed;
706*c8dee2aaSAndroid Build Coastguard Worker const char hex_str[] = {p[1], p[2], p[3], p[4], '\0'};
707*c8dee2aaSAndroid Build Coastguard Worker const auto* eos = SkParse::FindHex(hex_str, &hexed);
708*c8dee2aaSAndroid Build Coastguard Worker if (!eos || *eos) {
709*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
710*c8dee2aaSAndroid Build Coastguard Worker }
711*c8dee2aaSAndroid Build Coastguard Worker
712*c8dee2aaSAndroid Build Coastguard Worker char utf8[SkUTF::kMaxBytesInUTF8Sequence];
713*c8dee2aaSAndroid Build Coastguard Worker const auto utf8_len = SkUTF::ToUTF8(SkTo<SkUnichar>(hexed), utf8);
714*c8dee2aaSAndroid Build Coastguard Worker fUnescapeBuffer.insert(fUnescapeBuffer.end(), utf8, utf8 + utf8_len);
715*c8dee2aaSAndroid Build Coastguard Worker p += 4;
716*c8dee2aaSAndroid Build Coastguard Worker } break;
717*c8dee2aaSAndroid Build Coastguard Worker default: return nullptr;
718*c8dee2aaSAndroid Build Coastguard Worker }
719*c8dee2aaSAndroid Build Coastguard Worker }
720*c8dee2aaSAndroid Build Coastguard Worker
721*c8dee2aaSAndroid Build Coastguard Worker return &fUnescapeBuffer;
722*c8dee2aaSAndroid Build Coastguard Worker }
723*c8dee2aaSAndroid Build Coastguard Worker
724*c8dee2aaSAndroid Build Coastguard Worker template <typename MatchFunc>
matchString(const char * p,const char * p_stop,MatchFunc && func)725*c8dee2aaSAndroid Build Coastguard Worker const char* matchString(const char* p, const char* p_stop, MatchFunc&& func) {
726*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*p == '"');
727*c8dee2aaSAndroid Build Coastguard Worker const auto* s_begin = p + 1;
728*c8dee2aaSAndroid Build Coastguard Worker bool requires_unescape = false;
729*c8dee2aaSAndroid Build Coastguard Worker
730*c8dee2aaSAndroid Build Coastguard Worker do {
731*c8dee2aaSAndroid Build Coastguard Worker // Consume string chars.
732*c8dee2aaSAndroid Build Coastguard Worker // This is the fast path, and hopefully we only hit it once then quick-exit below.
733*c8dee2aaSAndroid Build Coastguard Worker for (p = p + 1; !is_eostring(*p); ++p);
734*c8dee2aaSAndroid Build Coastguard Worker
735*c8dee2aaSAndroid Build Coastguard Worker if (*p == '"') {
736*c8dee2aaSAndroid Build Coastguard Worker // Valid string found.
737*c8dee2aaSAndroid Build Coastguard Worker if (!requires_unescape) {
738*c8dee2aaSAndroid Build Coastguard Worker func(s_begin, p - s_begin, p_stop);
739*c8dee2aaSAndroid Build Coastguard Worker } else {
740*c8dee2aaSAndroid Build Coastguard Worker // Slow unescape. We could avoid this extra copy with some effort,
741*c8dee2aaSAndroid Build Coastguard Worker // but in practice escaped strings should be rare.
742*c8dee2aaSAndroid Build Coastguard Worker const auto* buf = this->unescapeString(s_begin, p);
743*c8dee2aaSAndroid Build Coastguard Worker if (!buf) {
744*c8dee2aaSAndroid Build Coastguard Worker break;
745*c8dee2aaSAndroid Build Coastguard Worker }
746*c8dee2aaSAndroid Build Coastguard Worker
747*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!buf->empty());
748*c8dee2aaSAndroid Build Coastguard Worker func(buf->data(), buf->size(), buf->data() + buf->size() - 1);
749*c8dee2aaSAndroid Build Coastguard Worker }
750*c8dee2aaSAndroid Build Coastguard Worker return p + 1;
751*c8dee2aaSAndroid Build Coastguard Worker }
752*c8dee2aaSAndroid Build Coastguard Worker
753*c8dee2aaSAndroid Build Coastguard Worker if (*p == '\\') {
754*c8dee2aaSAndroid Build Coastguard Worker requires_unescape = true;
755*c8dee2aaSAndroid Build Coastguard Worker ++p;
756*c8dee2aaSAndroid Build Coastguard Worker continue;
757*c8dee2aaSAndroid Build Coastguard Worker }
758*c8dee2aaSAndroid Build Coastguard Worker
759*c8dee2aaSAndroid Build Coastguard Worker // End-of-scope chars are special: we use them to tag the end of the input.
760*c8dee2aaSAndroid Build Coastguard Worker // Thus they cannot be consumed indiscriminately -- we need to check if we hit the
761*c8dee2aaSAndroid Build Coastguard Worker // end of the input. To that effect, we treat them as string terminators above,
762*c8dee2aaSAndroid Build Coastguard Worker // then we catch them here.
763*c8dee2aaSAndroid Build Coastguard Worker if (is_eoscope(*p)) {
764*c8dee2aaSAndroid Build Coastguard Worker continue;
765*c8dee2aaSAndroid Build Coastguard Worker }
766*c8dee2aaSAndroid Build Coastguard Worker
767*c8dee2aaSAndroid Build Coastguard Worker // Invalid/unexpected char.
768*c8dee2aaSAndroid Build Coastguard Worker break;
769*c8dee2aaSAndroid Build Coastguard Worker } while (p != p_stop);
770*c8dee2aaSAndroid Build Coastguard Worker
771*c8dee2aaSAndroid Build Coastguard Worker // Premature end-of-input, or illegal string char.
772*c8dee2aaSAndroid Build Coastguard Worker return this->error(nullptr, s_begin - 1, "invalid string");
773*c8dee2aaSAndroid Build Coastguard Worker }
774*c8dee2aaSAndroid Build Coastguard Worker
matchFastFloatDecimalPart(const char * p,int sign,float f,int exp)775*c8dee2aaSAndroid Build Coastguard Worker const char* matchFastFloatDecimalPart(const char* p, int sign, float f, int exp) {
776*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(exp <= 0);
777*c8dee2aaSAndroid Build Coastguard Worker
778*c8dee2aaSAndroid Build Coastguard Worker for (;;) {
779*c8dee2aaSAndroid Build Coastguard Worker if (!is_digit(*p)) break;
780*c8dee2aaSAndroid Build Coastguard Worker f = f * 10.f + (*p++ - '0'); --exp;
781*c8dee2aaSAndroid Build Coastguard Worker if (!is_digit(*p)) break;
782*c8dee2aaSAndroid Build Coastguard Worker f = f * 10.f + (*p++ - '0'); --exp;
783*c8dee2aaSAndroid Build Coastguard Worker }
784*c8dee2aaSAndroid Build Coastguard Worker
785*c8dee2aaSAndroid Build Coastguard Worker const auto decimal_scale = pow10(exp);
786*c8dee2aaSAndroid Build Coastguard Worker if (is_numeric(*p) || !decimal_scale) {
787*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((*p == '.' || *p == 'e' || *p == 'E') || !decimal_scale);
788*c8dee2aaSAndroid Build Coastguard Worker // Malformed input, or an (unsupported) exponent, or a collapsed decimal factor.
789*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
790*c8dee2aaSAndroid Build Coastguard Worker }
791*c8dee2aaSAndroid Build Coastguard Worker
792*c8dee2aaSAndroid Build Coastguard Worker this->pushFloat(sign * f * decimal_scale);
793*c8dee2aaSAndroid Build Coastguard Worker
794*c8dee2aaSAndroid Build Coastguard Worker return p;
795*c8dee2aaSAndroid Build Coastguard Worker }
796*c8dee2aaSAndroid Build Coastguard Worker
matchFastFloatPart(const char * p,int sign,float f)797*c8dee2aaSAndroid Build Coastguard Worker const char* matchFastFloatPart(const char* p, int sign, float f) {
798*c8dee2aaSAndroid Build Coastguard Worker for (;;) {
799*c8dee2aaSAndroid Build Coastguard Worker if (!is_digit(*p)) break;
800*c8dee2aaSAndroid Build Coastguard Worker f = f * 10.f + (*p++ - '0');
801*c8dee2aaSAndroid Build Coastguard Worker if (!is_digit(*p)) break;
802*c8dee2aaSAndroid Build Coastguard Worker f = f * 10.f + (*p++ - '0');
803*c8dee2aaSAndroid Build Coastguard Worker }
804*c8dee2aaSAndroid Build Coastguard Worker
805*c8dee2aaSAndroid Build Coastguard Worker if (!is_numeric(*p)) {
806*c8dee2aaSAndroid Build Coastguard Worker // Matched (integral) float.
807*c8dee2aaSAndroid Build Coastguard Worker this->pushFloat(sign * f);
808*c8dee2aaSAndroid Build Coastguard Worker return p;
809*c8dee2aaSAndroid Build Coastguard Worker }
810*c8dee2aaSAndroid Build Coastguard Worker
811*c8dee2aaSAndroid Build Coastguard Worker return (*p == '.') ? this->matchFastFloatDecimalPart(p + 1, sign, f, 0)
812*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
813*c8dee2aaSAndroid Build Coastguard Worker }
814*c8dee2aaSAndroid Build Coastguard Worker
matchFast32OrFloat(const char * p)815*c8dee2aaSAndroid Build Coastguard Worker const char* matchFast32OrFloat(const char* p) {
816*c8dee2aaSAndroid Build Coastguard Worker int sign = 1;
817*c8dee2aaSAndroid Build Coastguard Worker if (*p == '-') {
818*c8dee2aaSAndroid Build Coastguard Worker sign = -1;
819*c8dee2aaSAndroid Build Coastguard Worker ++p;
820*c8dee2aaSAndroid Build Coastguard Worker }
821*c8dee2aaSAndroid Build Coastguard Worker
822*c8dee2aaSAndroid Build Coastguard Worker const auto* digits_start = p;
823*c8dee2aaSAndroid Build Coastguard Worker
824*c8dee2aaSAndroid Build Coastguard Worker int32_t n32 = 0;
825*c8dee2aaSAndroid Build Coastguard Worker
826*c8dee2aaSAndroid Build Coastguard Worker // This is the largest absolute int32 value we can handle before
827*c8dee2aaSAndroid Build Coastguard Worker // risking overflow *on the next digit* (214748363).
828*c8dee2aaSAndroid Build Coastguard Worker static constexpr int32_t kMaxInt32 = (std::numeric_limits<int32_t>::max() - 9) / 10;
829*c8dee2aaSAndroid Build Coastguard Worker
830*c8dee2aaSAndroid Build Coastguard Worker if (is_digit(*p)) {
831*c8dee2aaSAndroid Build Coastguard Worker n32 = (*p++ - '0');
832*c8dee2aaSAndroid Build Coastguard Worker for (;;) {
833*c8dee2aaSAndroid Build Coastguard Worker if (!is_digit(*p) || n32 > kMaxInt32) break;
834*c8dee2aaSAndroid Build Coastguard Worker n32 = n32 * 10 + (*p++ - '0');
835*c8dee2aaSAndroid Build Coastguard Worker }
836*c8dee2aaSAndroid Build Coastguard Worker }
837*c8dee2aaSAndroid Build Coastguard Worker
838*c8dee2aaSAndroid Build Coastguard Worker if (!is_numeric(*p)) {
839*c8dee2aaSAndroid Build Coastguard Worker // Did we actually match any digits?
840*c8dee2aaSAndroid Build Coastguard Worker if (p > digits_start) {
841*c8dee2aaSAndroid Build Coastguard Worker this->pushInt32(sign * n32);
842*c8dee2aaSAndroid Build Coastguard Worker return p;
843*c8dee2aaSAndroid Build Coastguard Worker }
844*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
845*c8dee2aaSAndroid Build Coastguard Worker }
846*c8dee2aaSAndroid Build Coastguard Worker
847*c8dee2aaSAndroid Build Coastguard Worker if (*p == '.') {
848*c8dee2aaSAndroid Build Coastguard Worker const auto* decimals_start = ++p;
849*c8dee2aaSAndroid Build Coastguard Worker
850*c8dee2aaSAndroid Build Coastguard Worker int exp = 0;
851*c8dee2aaSAndroid Build Coastguard Worker
852*c8dee2aaSAndroid Build Coastguard Worker for (;;) {
853*c8dee2aaSAndroid Build Coastguard Worker if (!is_digit(*p) || n32 > kMaxInt32) break;
854*c8dee2aaSAndroid Build Coastguard Worker n32 = n32 * 10 + (*p++ - '0'); --exp;
855*c8dee2aaSAndroid Build Coastguard Worker if (!is_digit(*p) || n32 > kMaxInt32) break;
856*c8dee2aaSAndroid Build Coastguard Worker n32 = n32 * 10 + (*p++ - '0'); --exp;
857*c8dee2aaSAndroid Build Coastguard Worker }
858*c8dee2aaSAndroid Build Coastguard Worker
859*c8dee2aaSAndroid Build Coastguard Worker if (!is_numeric(*p)) {
860*c8dee2aaSAndroid Build Coastguard Worker // Did we actually match any digits?
861*c8dee2aaSAndroid Build Coastguard Worker if (p > decimals_start) {
862*c8dee2aaSAndroid Build Coastguard Worker this->pushFloat(sign * n32 * pow10(exp));
863*c8dee2aaSAndroid Build Coastguard Worker return p;
864*c8dee2aaSAndroid Build Coastguard Worker }
865*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
866*c8dee2aaSAndroid Build Coastguard Worker }
867*c8dee2aaSAndroid Build Coastguard Worker
868*c8dee2aaSAndroid Build Coastguard Worker if (n32 > kMaxInt32) {
869*c8dee2aaSAndroid Build Coastguard Worker // we ran out on n32 bits
870*c8dee2aaSAndroid Build Coastguard Worker return this->matchFastFloatDecimalPart(p, sign, n32, exp);
871*c8dee2aaSAndroid Build Coastguard Worker }
872*c8dee2aaSAndroid Build Coastguard Worker }
873*c8dee2aaSAndroid Build Coastguard Worker
874*c8dee2aaSAndroid Build Coastguard Worker return this->matchFastFloatPart(p, sign, n32);
875*c8dee2aaSAndroid Build Coastguard Worker }
876*c8dee2aaSAndroid Build Coastguard Worker
matchNumber(const char * p)877*c8dee2aaSAndroid Build Coastguard Worker const char* matchNumber(const char* p) {
878*c8dee2aaSAndroid Build Coastguard Worker if (const auto* fast = this->matchFast32OrFloat(p)) return fast;
879*c8dee2aaSAndroid Build Coastguard Worker
880*c8dee2aaSAndroid Build Coastguard Worker // slow fallback
881*c8dee2aaSAndroid Build Coastguard Worker char* matched;
882*c8dee2aaSAndroid Build Coastguard Worker float f = strtof(p, &matched);
883*c8dee2aaSAndroid Build Coastguard Worker if (matched > p) {
884*c8dee2aaSAndroid Build Coastguard Worker this->pushFloat(f);
885*c8dee2aaSAndroid Build Coastguard Worker return matched;
886*c8dee2aaSAndroid Build Coastguard Worker }
887*c8dee2aaSAndroid Build Coastguard Worker return this->error(nullptr, p, "invalid numeric token");
888*c8dee2aaSAndroid Build Coastguard Worker }
889*c8dee2aaSAndroid Build Coastguard Worker };
890*c8dee2aaSAndroid Build Coastguard Worker
Write(const Value & v,SkWStream * stream)891*c8dee2aaSAndroid Build Coastguard Worker void Write(const Value& v, SkWStream* stream) {
892*c8dee2aaSAndroid Build Coastguard Worker // We use the address of these as special tags in the pending list.
893*c8dee2aaSAndroid Build Coastguard Worker static const NullValue kArrayCloseTag, // ]
894*c8dee2aaSAndroid Build Coastguard Worker kObjectCloseTag, // }
895*c8dee2aaSAndroid Build Coastguard Worker kListSeparatorTag, // ,
896*c8dee2aaSAndroid Build Coastguard Worker kKeySeparatorTag; // :
897*c8dee2aaSAndroid Build Coastguard Worker
898*c8dee2aaSAndroid Build Coastguard Worker std::vector<const Value*> pending{&v};
899*c8dee2aaSAndroid Build Coastguard Worker
900*c8dee2aaSAndroid Build Coastguard Worker do {
901*c8dee2aaSAndroid Build Coastguard Worker const Value* val = pending.back();
902*c8dee2aaSAndroid Build Coastguard Worker pending.pop_back();
903*c8dee2aaSAndroid Build Coastguard Worker
904*c8dee2aaSAndroid Build Coastguard Worker if (val == &kArrayCloseTag) {
905*c8dee2aaSAndroid Build Coastguard Worker stream->writeText("]");
906*c8dee2aaSAndroid Build Coastguard Worker continue;
907*c8dee2aaSAndroid Build Coastguard Worker }
908*c8dee2aaSAndroid Build Coastguard Worker
909*c8dee2aaSAndroid Build Coastguard Worker if (val == &kObjectCloseTag) {
910*c8dee2aaSAndroid Build Coastguard Worker stream->writeText("}");
911*c8dee2aaSAndroid Build Coastguard Worker continue;
912*c8dee2aaSAndroid Build Coastguard Worker }
913*c8dee2aaSAndroid Build Coastguard Worker
914*c8dee2aaSAndroid Build Coastguard Worker if (val == &kListSeparatorTag) {
915*c8dee2aaSAndroid Build Coastguard Worker stream->writeText(",");
916*c8dee2aaSAndroid Build Coastguard Worker continue;
917*c8dee2aaSAndroid Build Coastguard Worker }
918*c8dee2aaSAndroid Build Coastguard Worker
919*c8dee2aaSAndroid Build Coastguard Worker if (val == &kKeySeparatorTag) {
920*c8dee2aaSAndroid Build Coastguard Worker stream->writeText(":");
921*c8dee2aaSAndroid Build Coastguard Worker continue;
922*c8dee2aaSAndroid Build Coastguard Worker }
923*c8dee2aaSAndroid Build Coastguard Worker
924*c8dee2aaSAndroid Build Coastguard Worker switch (val->getType()) {
925*c8dee2aaSAndroid Build Coastguard Worker case Value::Type::kNull:
926*c8dee2aaSAndroid Build Coastguard Worker stream->writeText("null");
927*c8dee2aaSAndroid Build Coastguard Worker break;
928*c8dee2aaSAndroid Build Coastguard Worker case Value::Type::kBool:
929*c8dee2aaSAndroid Build Coastguard Worker stream->writeText(*val->as<BoolValue>() ? "true" : "false");
930*c8dee2aaSAndroid Build Coastguard Worker break;
931*c8dee2aaSAndroid Build Coastguard Worker case Value::Type::kNumber:
932*c8dee2aaSAndroid Build Coastguard Worker stream->writeScalarAsText(*val->as<NumberValue>());
933*c8dee2aaSAndroid Build Coastguard Worker break;
934*c8dee2aaSAndroid Build Coastguard Worker case Value::Type::kString:
935*c8dee2aaSAndroid Build Coastguard Worker stream->writeText("\"");
936*c8dee2aaSAndroid Build Coastguard Worker stream->writeText(val->as<StringValue>().begin());
937*c8dee2aaSAndroid Build Coastguard Worker stream->writeText("\"");
938*c8dee2aaSAndroid Build Coastguard Worker break;
939*c8dee2aaSAndroid Build Coastguard Worker case Value::Type::kArray: {
940*c8dee2aaSAndroid Build Coastguard Worker const auto& array = val->as<ArrayValue>();
941*c8dee2aaSAndroid Build Coastguard Worker stream->writeText("[");
942*c8dee2aaSAndroid Build Coastguard Worker // "val, val, .. ]" in reverse order
943*c8dee2aaSAndroid Build Coastguard Worker pending.push_back(&kArrayCloseTag);
944*c8dee2aaSAndroid Build Coastguard Worker if (array.size() > 0) {
945*c8dee2aaSAndroid Build Coastguard Worker bool last_value = true;
946*c8dee2aaSAndroid Build Coastguard Worker for (const Value* it = array.end() - 1; it >= array.begin(); --it) {
947*c8dee2aaSAndroid Build Coastguard Worker if (!last_value) pending.push_back(&kListSeparatorTag);
948*c8dee2aaSAndroid Build Coastguard Worker pending.push_back(it);
949*c8dee2aaSAndroid Build Coastguard Worker last_value = false;
950*c8dee2aaSAndroid Build Coastguard Worker }
951*c8dee2aaSAndroid Build Coastguard Worker }
952*c8dee2aaSAndroid Build Coastguard Worker } break;
953*c8dee2aaSAndroid Build Coastguard Worker case Value::Type::kObject: {
954*c8dee2aaSAndroid Build Coastguard Worker const auto& object = val->as<ObjectValue>();
955*c8dee2aaSAndroid Build Coastguard Worker stream->writeText("{");
956*c8dee2aaSAndroid Build Coastguard Worker // "key: val, key: val, .. }" in reverse order
957*c8dee2aaSAndroid Build Coastguard Worker pending.push_back(&kObjectCloseTag);
958*c8dee2aaSAndroid Build Coastguard Worker if (object.size() > 0) {
959*c8dee2aaSAndroid Build Coastguard Worker bool last_member = true;
960*c8dee2aaSAndroid Build Coastguard Worker for (const Member* it = object.end() - 1; it >= object.begin(); --it) {
961*c8dee2aaSAndroid Build Coastguard Worker if (!last_member) pending.push_back(&kListSeparatorTag);
962*c8dee2aaSAndroid Build Coastguard Worker pending.push_back(&it->fValue);
963*c8dee2aaSAndroid Build Coastguard Worker pending.push_back(&kKeySeparatorTag);
964*c8dee2aaSAndroid Build Coastguard Worker pending.push_back(&it->fKey);
965*c8dee2aaSAndroid Build Coastguard Worker last_member = false;
966*c8dee2aaSAndroid Build Coastguard Worker }
967*c8dee2aaSAndroid Build Coastguard Worker }
968*c8dee2aaSAndroid Build Coastguard Worker } break;
969*c8dee2aaSAndroid Build Coastguard Worker }
970*c8dee2aaSAndroid Build Coastguard Worker } while (!pending.empty());
971*c8dee2aaSAndroid Build Coastguard Worker }
972*c8dee2aaSAndroid Build Coastguard Worker
973*c8dee2aaSAndroid Build Coastguard Worker } // namespace
974*c8dee2aaSAndroid Build Coastguard Worker
toString() const975*c8dee2aaSAndroid Build Coastguard Worker SkString Value::toString() const {
976*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream wstream;
977*c8dee2aaSAndroid Build Coastguard Worker Write(*this, &wstream);
978*c8dee2aaSAndroid Build Coastguard Worker const auto data = wstream.detachAsData();
979*c8dee2aaSAndroid Build Coastguard Worker // TODO: is there a better way to pass data around without copying?
980*c8dee2aaSAndroid Build Coastguard Worker return SkString(static_cast<const char*>(data->data()), data->size());
981*c8dee2aaSAndroid Build Coastguard Worker }
982*c8dee2aaSAndroid Build Coastguard Worker
983*c8dee2aaSAndroid Build Coastguard Worker static constexpr size_t kMinChunkSize = 4096;
984*c8dee2aaSAndroid Build Coastguard Worker
DOM(const char * data,size_t size)985*c8dee2aaSAndroid Build Coastguard Worker DOM::DOM(const char* data, size_t size)
986*c8dee2aaSAndroid Build Coastguard Worker : fAlloc(kMinChunkSize) {
987*c8dee2aaSAndroid Build Coastguard Worker DOMParser parser(fAlloc);
988*c8dee2aaSAndroid Build Coastguard Worker
989*c8dee2aaSAndroid Build Coastguard Worker fRoot = parser.parse(data, size);
990*c8dee2aaSAndroid Build Coastguard Worker }
991*c8dee2aaSAndroid Build Coastguard Worker
write(SkWStream * stream) const992*c8dee2aaSAndroid Build Coastguard Worker void DOM::write(SkWStream* stream) const {
993*c8dee2aaSAndroid Build Coastguard Worker Write(fRoot, stream);
994*c8dee2aaSAndroid Build Coastguard Worker }
995*c8dee2aaSAndroid Build Coastguard Worker
996*c8dee2aaSAndroid Build Coastguard Worker } // namespace skjson
997