Coverage Report

Created: 2024-09-14 07:19

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