Coverage Report

Created: 2025-06-24 08:20

/src/skia/modules/jsonreader/SkJSONReader.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2024 Google LLC
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
#ifndef SkJSON_DEFINED
9
#define SkJSON_DEFINED
10
11
#include "include/core/SkTypes.h"
12
#include "include/private/base/SkNoncopyable.h"
13
#include "src/base/SkArenaAlloc.h"
14
15
#include <cstdint>
16
#include <cstring>
17
#include <string_view>
18
19
class SkString;
20
class SkWStream;
21
22
namespace skjson {
23
24
/**
25
 *  A fast and likely non-conforming JSON parser.
26
 *
27
 *  Some known limitations/compromises:
28
 *
29
 *    -- single-precision FP numbers
30
 *
31
 *    -- missing string unescaping (no current users, could be easily added)
32
 *
33
 *
34
 *  Values are opaque, fixed-size (64 bits), immutable records.
35
 *
36
 *  They can be converted to facade types for type-specific functionality.
37
 *
38
 *  E.g.:
39
 *
40
 *     if (v.is<ArrayValue>()) {
41
 *         for (const auto& item : v.as<ArrayValue>()) {
42
 *             if (const NumberValue* n = item) {
43
 *                 printf("Found number: %f", **n);
44
 *             }
45
 *         }
46
 *     }
47
 *
48
 *     if (v.is<ObjectValue>()) {
49
 *         const StringValue* id = v.as<ObjectValue>()["id"];
50
 *         if (id) {
51
 *             printf("Found object ID: %s", id->begin());
52
 *         } else {
53
 *             printf("Missing object ID");
54
 *         }
55
 *     }
56
 */
57
class alignas(8) Value {
58
public:
59
    enum class Type {
60
        kNull,
61
        kBool,
62
        kNumber,
63
        kString,
64
        kArray,
65
        kObject,
66
    };
67
68
    /**
69
     * @return    The type of this value.
70
     */
71
    Type getType() const;
72
73
    /**
74
     * @return    True if the record matches the facade type T.
75
     */
76
    template <typename T>
77
56.4M
    bool is() const { return this->getType() == T::kType; }
bool skjson::Value::is<skjson::ArrayValue>() const
Line
Count
Source
77
15.4M
    bool is() const { return this->getType() == T::kType; }
bool skjson::Value::is<skjson::ObjectValue>() const
Line
Count
Source
77
15.7M
    bool is() const { return this->getType() == T::kType; }
bool skjson::Value::is<skjson::StringValue>() const
Line
Count
Source
77
7.74M
    bool is() const { return this->getType() == T::kType; }
bool skjson::Value::is<skjson::NumberValue>() const
Line
Count
Source
77
16.3M
    bool is() const { return this->getType() == T::kType; }
bool skjson::Value::is<skjson::BoolValue>() const
Line
Count
Source
77
716k
    bool is() const { return this->getType() == T::kType; }
bool skjson::Value::is<skjson::NullValue>() const
Line
Count
Source
77
497k
    bool is() const { return this->getType() == T::kType; }
78
79
    /**
80
     * Unguarded conversion to facade types.
81
     *
82
     * @return    The record cast as facade type T&.
83
     */
84
    template <typename T>
85
229M
    const T& as() const {
86
229M
        SkASSERT(this->is<T>());
87
229M
        return *reinterpret_cast<const T*>(this);
88
229M
    }
skjson::ObjectValue const& skjson::Value::as<skjson::ObjectValue>() const
Line
Count
Source
85
6.22M
    const T& as() const {
86
6.22M
        SkASSERT(this->is<T>());
87
6.22M
        return *reinterpret_cast<const T*>(this);
88
6.22M
    }
skjson::StringValue const& skjson::Value::as<skjson::StringValue>() const
Line
Count
Source
85
201M
    const T& as() const {
86
201M
        SkASSERT(this->is<T>());
87
201M
        return *reinterpret_cast<const T*>(this);
88
201M
    }
skjson::ArrayValue const& skjson::Value::as<skjson::ArrayValue>() const
Line
Count
Source
85
3.94M
    const T& as() const {
86
3.94M
        SkASSERT(this->is<T>());
87
3.94M
        return *reinterpret_cast<const T*>(this);
88
3.94M
    }
skjson::NumberValue const& skjson::Value::as<skjson::NumberValue>() const
Line
Count
Source
85
17.7M
    const T& as() const {
86
17.7M
        SkASSERT(this->is<T>());
87
17.7M
        return *reinterpret_cast<const T*>(this);
88
17.7M
    }
skjson::BoolValue const& skjson::Value::as<skjson::BoolValue>() const
Line
Count
Source
85
284k
    const T& as() const {
86
284k
        SkASSERT(this->is<T>());
87
284k
        return *reinterpret_cast<const T*>(this);
88
284k
    }
89
90
    /**
91
     * Guarded conversion to facade types.
92
     *
93
     * @return    The record cast as facade type T*.
94
     */
95
    template <typename T>
96
55.1M
    operator const T*() const {
97
55.1M
        return this->is<T>() ? &this->as<T>() : nullptr;
98
55.1M
    }
skjson::Value::operator skjson::ArrayValue const*<skjson::ArrayValue>() const
Line
Count
Source
96
14.8M
    operator const T*() const {
97
14.8M
        return this->is<T>() ? &this->as<T>() : nullptr;
98
14.8M
    }
skjson::Value::operator skjson::ObjectValue const*<skjson::ObjectValue>() const
Line
Count
Source
96
15.4M
    operator const T*() const {
97
15.4M
        return this->is<T>() ? &this->as<T>() : nullptr;
98
15.4M
    }
skjson::Value::operator skjson::StringValue const*<skjson::StringValue>() const
Line
Count
Source
96
7.74M
    operator const T*() const {
97
7.74M
        return this->is<T>() ? &this->as<T>() : nullptr;
98
7.74M
    }
skjson::Value::operator skjson::NumberValue const*<skjson::NumberValue>() const
Line
Count
Source
96
16.3M
    operator const T*() const {
97
16.3M
        return this->is<T>() ? &this->as<T>() : nullptr;
98
16.3M
    }
skjson::Value::operator skjson::BoolValue const*<skjson::BoolValue>() const
Line
Count
Source
96
716k
    operator const T*() const {
97
716k
        return this->is<T>() ? &this->as<T>() : nullptr;
98
716k
    }
99
100
    /**
101
     * @return    The string representation of this value.
102
     */
103
    SkString toString() const;
104
105
    /**
106
     * Helper for fluent key lookup: v["foo"]["bar"]["baz"]
107
     *
108
     * @return    The lookup result value on success, otherwise NullValue.
109
     */
110
    const Value& operator[](const char* key) const;
111
112
protected:
113
    /*
114
      Value implementation notes:
115
116
        -- fixed 64-bit size
117
118
        -- 8-byte aligned
119
120
        -- union of:
121
122
             bool
123
             int32
124
             float
125
             char[8] (short string storage)
126
             external payload (tagged) pointer
127
128
         -- lowest 3 bits reserved for tag storage
129
130
     */
131
    enum class Tag : uint8_t {
132
        // n.b.: we picked kShortString == 0 on purpose,
133
        // to enable certain short-string optimizations.
134
        kShortString                  = 0b00000000,  // inline payload
135
        kNull                         = 0b00000001,  // no payload
136
        kBool                         = 0b00000010,  // inline payload
137
        kInt                          = 0b00000011,  // inline payload
138
        kFloat                        = 0b00000100,  // inline payload
139
        kString                       = 0b00000101,  // ptr to external storage
140
        kArray                        = 0b00000110,  // ptr to external storage
141
        kObject                       = 0b00000111,  // ptr to external storage
142
    };
143
    inline static constexpr uint8_t kTagMask = 0b00000111;
144
145
    void init_tagged(Tag);
146
    void init_tagged_pointer(Tag, void*);
147
148
290M
    Tag getTag() const {
149
290M
        return static_cast<Tag>(fData8[0] & kTagMask);
150
290M
    }
151
152
    // Access the record payload as T.
153
    //
154
    // Since the tag is stored in the lower bits, we skip the first word whenever feasible.
155
    //
156
    // E.g. (U == unused)
157
    //
158
    //   uint8_t
159
    //    -----------------------------------------------------------------------
160
    //   |TAG| U  |  val8  |   U    |   U    |   U    |   U    |   U    |   U    |
161
    //    -----------------------------------------------------------------------
162
    //
163
    //   uint16_t
164
    //    -----------------------------------------------------------------------
165
    //   |TAG|      U      |      val16      |        U        |        U        |
166
    //    -----------------------------------------------------------------------
167
    //
168
    //   uint32_t
169
    //    -----------------------------------------------------------------------
170
    //   |TAG|             U                 |                val32              |
171
    //    -----------------------------------------------------------------------
172
    //
173
    //   T* (32b)
174
    //    -----------------------------------------------------------------------
175
    //   |TAG|             U                 |             T* (32bits)           |
176
    //    -----------------------------------------------------------------------
177
    //
178
    //   T* (64b)
179
    //    -----------------------------------------------------------------------
180
    //   |TAG|                        T* (61bits)                                |
181
    //    -----------------------------------------------------------------------
182
    //
183
    template <typename T>
184
395M
    const T* cast() const {
185
395M
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
395M
        static_assert(alignof(T) <= alignof(Value), "");
187
188
395M
        return (sizeof(T) > sizeof(*this) / 2)
189
395M
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
395M
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
395M
    }
char const* skjson::Value::cast<char>() const
Line
Count
Source
184
185M
    const T* cast() const {
185
185M
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
185M
        static_assert(alignof(T) <= alignof(Value), "");
187
188
185M
        return (sizeof(T) > sizeof(*this) / 2)
189
185M
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
185M
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
185M
    }
skjson::VectorValue<char, (skjson::Value::Type)3> const* skjson::Value::cast<skjson::VectorValue<char, (skjson::Value::Type)3> >() const
Line
Count
Source
184
16.8M
    const T* cast() const {
185
16.8M
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
16.8M
        static_assert(alignof(T) <= alignof(Value), "");
187
188
16.8M
        return (sizeof(T) > sizeof(*this) / 2)
189
16.8M
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
16.8M
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
16.8M
    }
unsigned long const* skjson::Value::cast<unsigned long>() const
Line
Count
Source
184
139M
    const T* cast() const {
185
139M
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
139M
        static_assert(alignof(T) <= alignof(Value), "");
187
188
139M
        return (sizeof(T) > sizeof(*this) / 2)
189
139M
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
139M
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
139M
    }
bool const* skjson::Value::cast<bool>() const
Line
Count
Source
184
621k
    const T* cast() const {
185
621k
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
621k
        static_assert(alignof(T) <= alignof(Value), "");
187
188
621k
        return (sizeof(T) > sizeof(*this) / 2)
189
621k
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
621k
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
621k
    }
int const* skjson::Value::cast<int>() const
Line
Count
Source
184
28.4M
    const T* cast() const {
185
28.4M
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
28.4M
        static_assert(alignof(T) <= alignof(Value), "");
187
188
28.4M
        return (sizeof(T) > sizeof(*this) / 2)
189
28.4M
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
28.4M
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
28.4M
    }
float const* skjson::Value::cast<float>() const
Line
Count
Source
184
3.46M
    const T* cast() const {
185
3.46M
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
3.46M
        static_assert(alignof(T) <= alignof(Value), "");
187
188
3.46M
        return (sizeof(T) > sizeof(*this) / 2)
189
3.46M
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
3.46M
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
3.46M
    }
Unexecuted instantiation: unsigned long const* const* skjson::Value::cast<unsigned long const*>() const
long const* skjson::Value::cast<long>() const
Line
Count
Source
184
21.3M
    const T* cast() const {
185
21.3M
        static_assert(sizeof (T) <=  sizeof(Value), "");
186
21.3M
        static_assert(alignof(T) <= alignof(Value), "");
187
188
21.3M
        return (sizeof(T) > sizeof(*this) / 2)
189
21.3M
                ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190
21.3M
                : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191
21.3M
    }
Unexecuted instantiation: void const* const* skjson::Value::cast<void const*>() const
192
193
    template <typename T>
194
44.9M
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
skjson::VectorValue<char, (skjson::Value::Type)3>* skjson::Value::cast<skjson::VectorValue<char, (skjson::Value::Type)3> >()
Line
Count
Source
194
691k
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
char* skjson::Value::cast<char>()
Line
Count
Source
194
14.1k
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
long* skjson::Value::cast<long>()
Line
Count
Source
194
17.4M
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
unsigned long* skjson::Value::cast<unsigned long>()
Line
Count
Source
194
12.2M
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
bool* skjson::Value::cast<bool>()
Line
Count
Source
194
336k
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
int* skjson::Value::cast<int>()
Line
Count
Source
194
12.8M
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
float* skjson::Value::cast<float>()
Line
Count
Source
194
1.27M
    T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
195
196
    // Access the pointer payload.
197
    template <typename T>
198
127M
    const T* ptr() const {
199
127M
        static_assert(sizeof(uintptr_t)     == sizeof(Value) ||
200
127M
                      sizeof(uintptr_t) * 2 == sizeof(Value), "");
201
202
127M
        return (sizeof(uintptr_t) < sizeof(Value))
203
            // For 32-bit, pointers are stored unmodified.
204
127M
            ? *this->cast<const T*>()
205
            // For 64-bit, we use the lower bits of the pointer as tag storage.
206
127M
            : reinterpret_cast<T*>(*this->cast<uintptr_t>() & ~static_cast<uintptr_t>(kTagMask));
207
127M
    }
unsigned long const* skjson::Value::ptr<unsigned long>() const
Line
Count
Source
198
127M
    const T* ptr() const {
199
127M
        static_assert(sizeof(uintptr_t)     == sizeof(Value) ||
200
127M
                      sizeof(uintptr_t) * 2 == sizeof(Value), "");
201
202
127M
        return (sizeof(uintptr_t) < sizeof(Value))
203
            // For 32-bit, pointers are stored unmodified.
204
127M
            ? *this->cast<const T*>()
205
            // For 64-bit, we use the lower bits of the pointer as tag storage.
206
127M
            : reinterpret_cast<T*>(*this->cast<uintptr_t>() & ~static_cast<uintptr_t>(kTagMask));
207
127M
    }
Unexecuted instantiation: void const* skjson::Value::ptr<void>() const
208
209
private:
210
    inline static constexpr size_t kValueSize = 8;
211
212
    uint8_t fData8[kValueSize];
213
214
#if !defined(SK_CPU_LENDIAN)
215
    // The current value layout assumes LE and will take some tweaking for BE.
216
    static_assert(false, "Big-endian builds are not supported at this time.");
217
#endif
218
};
219
220
class NullValue final : public Value {
221
public:
222
    inline static constexpr Type kType = Type::kNull;
223
224
    NullValue();
225
};
226
227
class BoolValue final : public Value {
228
public:
229
    inline static constexpr Type kType = Type::kBool;
230
231
    explicit BoolValue(bool);
232
233
284k
    bool operator*() const {
234
284k
        SkASSERT(this->getTag() == Tag::kBool);
235
284k
        return *this->cast<bool>();
236
284k
    }
237
};
238
239
class NumberValue final : public Value {
240
public:
241
    inline static constexpr Type kType = Type::kNumber;
242
243
    explicit NumberValue(int32_t);
244
    explicit NumberValue(float);
245
246
17.7M
    double operator*() const {
247
17.7M
        SkASSERT(this->getTag() == Tag::kInt || this->getTag() == Tag::kFloat);
248
249
17.7M
        return this->getTag() == Tag::kInt ? static_cast<double>(*this->cast<int32_t>())
250
17.7M
                                           : static_cast<double>(*this->cast<float>());
251
17.7M
    }
252
};
253
254
template <typename T, Value::Type vtype>
255
class VectorValue : public Value {
256
public:
257
    using ValueT = T;
258
    inline static constexpr Type kType = vtype;
259
260
8.97M
    size_t size() const {
261
8.97M
        SkASSERT(this->getType() == kType);
262
8.97M
        return *this->ptr<size_t>();
263
8.97M
    }
skjson::VectorValue<char, (skjson::Value::Type)3>::size() const
Line
Count
Source
260
26.1k
    size_t size() const {
261
26.1k
        SkASSERT(this->getType() == kType);
262
26.1k
        return *this->ptr<size_t>();
263
26.1k
    }
skjson::VectorValue<skjson::Value, (skjson::Value::Type)4>::size() const
Line
Count
Source
260
8.94M
    size_t size() const {
261
8.94M
        SkASSERT(this->getType() == kType);
262
8.94M
        return *this->ptr<size_t>();
263
8.94M
    }
skjson::VectorValue<skjson::Member, (skjson::Value::Type)5>::size() const
Line
Count
Source
260
1.08k
    size_t size() const {
261
1.08k
        SkASSERT(this->getType() == kType);
262
1.08k
        return *this->ptr<size_t>();
263
1.08k
    }
264
265
75.3M
    const T* begin() const {
266
75.3M
        SkASSERT(this->getType() == kType);
267
75.3M
        const auto* size_ptr = this->ptr<size_t>();
268
75.3M
        return reinterpret_cast<const T*>(size_ptr + 1);
269
75.3M
    }
skjson::VectorValue<char, (skjson::Value::Type)3>::begin() const
Line
Count
Source
265
16.8M
    const T* begin() const {
266
16.8M
        SkASSERT(this->getType() == kType);
267
16.8M
        const auto* size_ptr = this->ptr<size_t>();
268
16.8M
        return reinterpret_cast<const T*>(size_ptr + 1);
269
16.8M
    }
skjson::VectorValue<skjson::Value, (skjson::Value::Type)4>::begin() const
Line
Count
Source
265
15.6M
    const T* begin() const {
266
15.6M
        SkASSERT(this->getType() == kType);
267
15.6M
        const auto* size_ptr = this->ptr<size_t>();
268
15.6M
        return reinterpret_cast<const T*>(size_ptr + 1);
269
15.6M
    }
skjson::VectorValue<skjson::Member, (skjson::Value::Type)5>::begin() const
Line
Count
Source
265
42.8M
    const T* begin() const {
266
42.8M
        SkASSERT(this->getType() == kType);
267
42.8M
        const auto* size_ptr = this->ptr<size_t>();
268
42.8M
        return reinterpret_cast<const T*>(size_ptr + 1);
269
42.8M
    }
270
271
42.6M
    const T* end() const {
272
42.6M
        SkASSERT(this->getType() == kType);
273
42.6M
        const auto* size_ptr = this->ptr<size_t>();
274
42.6M
        return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
275
42.6M
    }
Unexecuted instantiation: skjson::VectorValue<char, (skjson::Value::Type)3>::end() const
skjson::VectorValue<skjson::Value, (skjson::Value::Type)4>::end() const
Line
Count
Source
271
600k
    const T* end() const {
272
600k
        SkASSERT(this->getType() == kType);
273
600k
        const auto* size_ptr = this->ptr<size_t>();
274
600k
        return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
275
600k
    }
skjson::VectorValue<skjson::Member, (skjson::Value::Type)5>::end() const
Line
Count
Source
271
42.0M
    const T* end() const {
272
42.0M
        SkASSERT(this->getType() == kType);
273
42.0M
        const auto* size_ptr = this->ptr<size_t>();
274
42.0M
        return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
275
42.0M
    }
276
277
7.63M
    const T& operator[](size_t i) const {
278
7.63M
        SkASSERT(this->getType() == kType);
279
7.63M
        SkASSERT(i < this->size());
280
281
7.63M
        return *(this->begin() + i);
282
7.63M
    }
283
};
284
285
class ArrayValue final : public VectorValue<Value, Value::Type::kArray> {
286
public:
287
    ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc);
288
};
289
290
class StringValue final : public Value {
291
public:
292
    inline static constexpr Type kType = Type::kString;
293
294
    StringValue();
295
    StringValue(const char* src, SkArenaAlloc& alloc);
296
    StringValue(const char* src, size_t size, SkArenaAlloc& alloc);
297
298
296k
    size_t size() const {
299
296k
        switch (this->getTag()) {
300
270k
            case Tag::kShortString:
301
                // We don't bother storing a length for short strings on the assumption
302
                // that strlen is fast in this case.  If this becomes problematic, we
303
                // can either go back to storing (7-len) in the tag byte or write a fast
304
                // short_strlen.
305
270k
                return strlen(this->cast<char>());
306
26.1k
            case Tag::kString:
307
26.1k
                return this->cast<VectorValue<char, Value::Type::kString>>()->size();
308
0
            default:
309
0
                return 0;
310
296k
        }
311
296k
    }
312
313
201M
    const char* begin() const {
314
201M
        return this->getTag() == Tag::kShortString
315
201M
                       ? this->cast<char>()
316
201M
                       : this->cast<VectorValue<char, Value::Type::kString>>()->begin();
317
201M
    }
318
319
0
    const char* end() const {
320
0
        return this->getTag() == Tag::kShortString
321
0
                       ? strchr(this->cast<char>(), '\0')
322
0
                       : this->cast<VectorValue<char, Value::Type::kString>>()->end();
323
0
    }
324
325
0
    std::string_view str() const { return std::string_view(this->begin(), this->size()); }
326
};
327
328
struct Member {
329
    StringValue fKey;
330
    Value       fValue;
331
};
332
333
class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
334
public:
335
    ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc);
336
337
42.0M
    const Value& operator[](const char* key) const {
338
42.0M
        static const Value gNullValue = NullValue();
339
340
42.0M
        const auto* member = this->find(key);
341
42.0M
        return member ? member->fValue : gNullValue;
342
42.0M
    }
343
344
    // Writable access to the value associated with the given key.
345
    // If the key is not present, it is added with a default NullValue.
346
    Value& writable(const char* key, SkArenaAlloc&) const;
347
348
private:
349
    const Member* find(const char*) const;
350
};
351
352
class DOM final : public SkNoncopyable {
353
public:
354
    DOM(const char*, size_t);
355
356
31.9k
    const Value& root() const { return fRoot; }
357
358
    void write(SkWStream*) const;
359
360
private:
361
    SkArenaAlloc fAlloc;
362
    Value        fRoot;
363
};
364
365
70.5M
inline Value::Type Value::getType() const {
366
70.5M
    switch (this->getTag()) {
367
32.3M
    case Tag::kNull:        return Type::kNull;
368
284k
    case Tag::kBool:        return Type::kBool;
369
20.6M
    case Tag::kInt:         return Type::kNumber;
370
3.84M
    case Tag::kFloat:       return Type::kNumber;
371
2.76M
    case Tag::kShortString: return Type::kString;
372
157k
    case Tag::kString:      return Type::kString;
373
3.94M
    case Tag::kArray:       return Type::kArray;
374
6.51M
    case Tag::kObject:      return Type::kObject;
375
70.5M
    }
376
377
0
    SkASSERT(false);  // unreachable
378
0
    return Type::kNull;
379
70.5M
}
380
381
0
inline const Value& Value::operator[](const char* key) const {
382
0
    static const Value gNullValue = NullValue();
383
0
384
0
    return this->is<ObjectValue>() ? this->as<ObjectValue>()[key] : gNullValue;
385
0
}
386
387
}  // namespace skjson
388
389
#endif  // SkJSON_DEFINED