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