Flutter Engine
The Flutter Engine
SkJSON.h
Go to the documentation of this file.
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#ifndef SkJSON_DEFINED
9#define SkJSON_DEFINED
10
14
15#include <cstdint>
16#include <cstring>
17#include <string_view>
18
19class SkString;
20class SkWStream;
21
22namespace 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 */
57class alignas(8) Value {
58public:
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 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 const T& as() const {
86 SkASSERT(this->is<T>());
87 return *reinterpret_cast<const T*>(this);
88 }
89
90 /**
91 * Guarded conversion to facade types.
92 *
93 * @return The record cast as facade type T*.
94 */
95 template <typename T>
96 operator const T*() const {
97 return this->is<T>() ? &this->as<T>() : nullptr;
98 }
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
112protected:
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 Tag getTag() const {
149 return static_cast<Tag>(fData8[0] & kTagMask);
150 }
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 const T* cast() const {
185 static_assert(sizeof (T) <= sizeof(Value), "");
186 static_assert(alignof(T) <= alignof(Value), "");
187
188 return (sizeof(T) > sizeof(*this) / 2)
189 ? reinterpret_cast<const T*>(this) + 0 // need all the bits
190 : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191 }
192
193 template <typename T>
194 T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
195
196 // Access the pointer payload.
197 template <typename T>
198 const T* ptr() const {
199 static_assert(sizeof(uintptr_t) == sizeof(Value) ||
200 sizeof(uintptr_t) * 2 == sizeof(Value), "");
201
202 return (sizeof(uintptr_t) < sizeof(Value))
203 // For 32-bit, pointers are stored unmodified.
204 ? *this->cast<const T*>()
205 // For 64-bit, we use the lower bits of the pointer as tag storage.
206 : reinterpret_cast<T*>(*this->cast<uintptr_t>() & ~static_cast<uintptr_t>(kTagMask));
207 }
208
209private:
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
220class NullValue final : public Value {
221public:
222 inline static constexpr Type kType = Type::kNull;
223
224 NullValue();
225};
226
227class BoolValue final : public Value {
228public:
229 inline static constexpr Type kType = Type::kBool;
230
231 explicit BoolValue(bool);
232
233 bool operator *() const {
234 SkASSERT(this->getTag() == Tag::kBool);
235 return *this->cast<bool>();
236 }
237};
238
239class NumberValue final : public Value {
240public:
241 inline static constexpr Type kType = Type::kNumber;
242
243 explicit NumberValue(int32_t);
244 explicit NumberValue(float);
245
246 double operator *() const {
247 SkASSERT(this->getTag() == Tag::kInt ||
248 this->getTag() == Tag::kFloat);
249
250 return this->getTag() == Tag::kInt
251 ? static_cast<double>(*this->cast<int32_t>())
252 : static_cast<double>(*this->cast<float>());
253 }
254};
255
256template <typename T, Value::Type vtype>
257class VectorValue : public Value {
258public:
259 using ValueT = T;
260 inline static constexpr Type kType = vtype;
261
262 size_t size() const {
263 SkASSERT(this->getType() == kType);
264 return *this->ptr<size_t>();
265 }
266
267 const T* begin() const {
268 SkASSERT(this->getType() == kType);
269 const auto* size_ptr = this->ptr<size_t>();
270 return reinterpret_cast<const T*>(size_ptr + 1);
271 }
272
273 const T* end() const {
274 SkASSERT(this->getType() == kType);
275 const auto* size_ptr = this->ptr<size_t>();
276 return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
277 }
278
279 const T& operator[](size_t i) const {
280 SkASSERT(this->getType() == kType);
281 SkASSERT(i < this->size());
282
283 return *(this->begin() + i);
284 }
285};
286
287class ArrayValue final : public VectorValue<Value, Value::Type::kArray> {
288public:
289 ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc);
290};
291
292class StringValue final : public Value {
293public:
294 inline static constexpr Type kType = Type::kString;
295
297 StringValue(const char* src, SkArenaAlloc& alloc);
298 StringValue(const char* src, size_t size, SkArenaAlloc& alloc);
299
300 size_t size() const {
301 switch (this->getTag()) {
303 // We don't bother storing a length for short strings on the assumption
304 // that strlen is fast in this case. If this becomes problematic, we
305 // can either go back to storing (7-len) in the tag byte or write a fast
306 // short_strlen.
307 return strlen(this->cast<char>());
308 case Tag::kString:
309 return this->cast<VectorValue<char, Value::Type::kString>>()->size();
310 default:
311 return 0;
312 }
313 }
314
315 const char* begin() const {
316 return this->getTag() == Tag::kShortString
317 ? this->cast<char>()
318 : this->cast<VectorValue<char, Value::Type::kString>>()->begin();
319 }
320
321 const char* end() const {
322 return this->getTag() == Tag::kShortString
323 ? strchr(this->cast<char>(), '\0')
324 : this->cast<VectorValue<char, Value::Type::kString>>()->end();
325 }
326
327 std::string_view str() const {
328 return std::string_view(this->begin(), this->size());
329 }
330};
331
332struct Member {
335};
336
337class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
338public:
339 ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc);
340
341 const Value& operator[](const char* key) const {
342 static const Value gNullValue = NullValue();
343
344 const auto* member = this->find(key);
345 return member
346 ? member->fValue
347 : gNullValue;
348 }
349
350 // Writable access to the value associated with the given key.
351 // If the key is not present, it is added with a default NullValue.
352 Value& writable(const char* key, SkArenaAlloc&) const;
353
354private:
355 const Member* find(const char*) const;
356};
357
358class DOM final : public SkNoncopyable {
359public:
360 DOM(const char*, size_t);
361
362 const Value& root() const { return fRoot; }
363
364 void write(SkWStream*) const;
365
366private:
367 SkArenaAlloc fAlloc;
368 Value fRoot;
369};
370
372 switch (this->getTag()) {
373 case Tag::kNull: return Type::kNull;
374 case Tag::kBool: return Type::kBool;
375 case Tag::kInt: return Type::kNumber;
376 case Tag::kFloat: return Type::kNumber;
377 case Tag::kShortString: return Type::kString;
378 case Tag::kString: return Type::kString;
379 case Tag::kArray: return Type::kArray;
380 case Tag::kObject: return Type::kObject;
381 }
382
383 SkASSERT(false); // unreachable
384 return Type::kNull;
385}
386
387inline const Value& Value::operator[](const char* key) const {
388 static const Value gNullValue = NullValue();
389
390 return this->is<ObjectValue>()
391 ? this->as<ObjectValue>()[key]
392 : gNullValue;
393}
394
395} // namespace skjson
396
397#endif // SkJSON_DEFINED
#define SkASSERT(cond)
Definition: SkAssert.h:116
ArrayValue(const Value *src, size_t size, SkArenaAlloc &alloc)
Definition: SkJSON.cpp:108
bool operator*() const
Definition: SkJSON.h:233
static constexpr Type kType
Definition: SkJSON.h:229
DOM(const char *, size_t)
Definition: SkJSON.cpp:948
void write(SkWStream *) const
Definition: SkJSON.cpp:955
const Value & root() const
Definition: SkJSON.h:362
static constexpr Type kType
Definition: SkJSON.h:222
NumberValue(int32_t)
Definition: SkJSON.cpp:73
double operator*() const
Definition: SkJSON.h:246
static constexpr Type kType
Definition: SkJSON.h:241
const Value & operator[](const char *key) const
Definition: SkJSON.h:341
Value & writable(const char *key, SkArenaAlloc &) const
Definition: SkJSON.cpp:244
ObjectValue(const Member *src, size_t size, SkArenaAlloc &alloc)
Definition: SkJSON.cpp:208
const char * end() const
Definition: SkJSON.h:321
const char * begin() const
Definition: SkJSON.h:315
std::string_view str() const
Definition: SkJSON.h:327
size_t size() const
Definition: SkJSON.h:300
static constexpr Type kType
Definition: SkJSON.h:294
static constexpr uint8_t kTagMask
Definition: SkJSON.h:143
const Value & operator[](const char *key) const
Definition: SkJSON.h:387
const T * cast() const
Definition: SkJSON.h:184
Tag getTag() const
Definition: SkJSON.h:148
T * cast()
Definition: SkJSON.h:194
SkString toString() const
Definition: SkJSON.cpp:938
void init_tagged(Tag)
Definition: SkJSON.cpp:38
void init_tagged_pointer(Tag, void *)
Definition: SkJSON.cpp:45
const T & as() const
Definition: SkJSON.h:85
const T * ptr() const
Definition: SkJSON.h:198
bool is() const
Definition: SkJSON.h:77
Type getType() const
Definition: SkJSON.h:371
const T * end() const
Definition: SkJSON.h:273
const T * begin() const
Definition: SkJSON.h:267
size_t size() const
Definition: SkJSON.h:262
const T & operator[](size_t i) const
Definition: SkJSON.h:279
static constexpr Type kType
Definition: SkJSON.h:260
#define T
Definition: precompiler.cc:65
Value fValue
Definition: SkJSON.h:334
StringValue fKey
Definition: SkJSON.h:333