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 -- highest 3 bits reserved for type storage
121
122 */
123 #if defined(SK_CPU_LENDIAN)
124 enum class Tag : uint8_t {
125 // We picked kShortString == 0 so that tag 0x00 and stored max_size-size (7-7=0)
126 // conveniently overlap the '\0' terminator, allowing us to store a 7 character
127 // C string inline.
128 kShortString = 0b00000000, // inline payload
129 kNull = 0b00100000, // no payload
130 kBool = 0b01000000, // inline payload
131 kInt = 0b01100000, // inline payload
132 kFloat = 0b10000000, // inline payload
133 kString = 0b10100000, // ptr to external storage
134 kArray = 0b11000000, // ptr to external storage
135 kObject = 0b11100000, // ptr to external storage
136 };
137 static constexpr uint8_t kTagMask = 0b11100000;
138 #else
139 enum class Tag : uint8_t {
140 // We picked kShortString == 0 so that tag 0x00 and stored max_size-size (7-7=0)
141 // conveniently overlap the '\0' terminator, allowing us to store a 7 character
142 // C string inline.
143 kShortString = 0b00000000, // inline payload
144 kNull = 0b00000001, // no payload
145 kBool = 0b00000010, // inline payload
146 kInt = 0b00000011, // inline payload
147 kFloat = 0b00000100, // inline payload
148 kString = 0b00000101, // ptr to external storage
149 kArray = 0b00000110, // ptr to external storage
150 kObject = 0b00000111, // ptr to external storage
151 };
152 static constexpr uint8_t kTagMask = 0b00000111;
153 #endif
154
155 void init_tagged(Tag);
156 void init_tagged_pointer(Tag, void*);
157
getTag()158 Tag getTag() const {
159 return static_cast<Tag>(fData8[kTagOffset] & kTagMask);
160 }
161
162 // Access the record data as T.
163 //
164 // This is also used to access the payload for inline records. Since the record type lives in
165 // the high bits, sizeof(T) must be less than sizeof(Value) when accessing inline payloads.
166 //
167 // E.g.
168 //
169 // uint8_t
170 // -----------------------------------------------------------------------
171 // | val8 | val8 | val8 | val8 | val8 | val8 | val8 | TYPE|
172 // -----------------------------------------------------------------------
173 //
174 // uint32_t
175 // -----------------------------------------------------------------------
176 // | val32 | unused | TYPE|
177 // -----------------------------------------------------------------------
178 //
179 // T* (64b)
180 // -----------------------------------------------------------------------
181 // | T* (kTypeShift bits) |TYPE|
182 // -----------------------------------------------------------------------
183 //
184 template <typename T>
cast()185 const T* cast() const {
186 static_assert(sizeof (T) <= sizeof(Value), "");
187 static_assert(alignof(T) <= alignof(Value), "");
188 return reinterpret_cast<const T*>(this);
189 }
190
191 template <typename T>
cast()192 T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
193
194 // Access the pointer payload.
195 template <typename T>
ptr()196 const T* ptr() const {
197 static_assert(sizeof(uintptr_t) == sizeof(Value) ||
198 sizeof(uintptr_t) * 2 == sizeof(Value), "");
199
200 return (sizeof(uintptr_t) < sizeof(Value))
201 // For 32-bit, pointers are stored unmodified.
202 ? *this->cast<const T*>()
203 // For 64-bit, we use the high bits of the pointer as tag storage.
204 : reinterpret_cast<T*>(*this->cast<uintptr_t>() & kTagPointerMask);
205 }
206
207 private:
208 static constexpr size_t kValueSize = 8;
209
210 uint8_t fData8[kValueSize];
211
212 static constexpr size_t kTagOffset = kValueSize - 1;
213
214 #if defined(SK_CPU_LENDIAN)
215 static constexpr uintptr_t kTagPointerMask =
216 ~(static_cast<uintptr_t>(kTagMask) << ((sizeof(uintptr_t) - 1) * 8));
217 #else
218 static constexpr uintptr_t kTagPointerMask =
219 ~static_cast<uintptr_t>(kTagMask);
220 #endif
221 };
222
223 class NullValue final : public Value {
224 public:
225 static constexpr Type kType = Type::kNull;
226
227 NullValue();
228 };
229
230 class BoolValue final : public Value {
231 public:
232 static constexpr Type kType = Type::kBool;
233
234 explicit BoolValue(bool);
235
236 bool operator *() const {
237 SkASSERT(this->getTag() == Tag::kBool);
238 return *this->cast<bool>();
239 }
240 };
241
242 class NumberValue final : public Value {
243 public:
244 static constexpr Type kType = Type::kNumber;
245
246 explicit NumberValue(int32_t);
247 explicit NumberValue(float);
248
249 double operator *() const {
250 SkASSERT(this->getTag() == Tag::kInt ||
251 this->getTag() == Tag::kFloat);
252
253 return this->getTag() == Tag::kInt
254 ? static_cast<double>(*this->cast<int32_t>())
255 : static_cast<double>(*this->cast<float>());
256 }
257 };
258
259 template <typename T, Value::Type vtype>
260 class VectorValue : public Value {
261 public:
262 using ValueT = T;
263 static constexpr Type kType = vtype;
264
size()265 size_t size() const {
266 SkASSERT(this->getType() == kType);
267 return *this->ptr<size_t>();
268 }
269
begin()270 const T* begin() const {
271 SkASSERT(this->getType() == kType);
272 const auto* size_ptr = this->ptr<size_t>();
273 return reinterpret_cast<const T*>(size_ptr + 1);
274 }
275
end()276 const T* end() const {
277 SkASSERT(this->getType() == kType);
278 const auto* size_ptr = this->ptr<size_t>();
279 return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
280 }
281
282 const T& operator[](size_t i) const {
283 SkASSERT(this->getType() == kType);
284 SkASSERT(i < this->size());
285
286 return *(this->begin() + i);
287 }
288 };
289
290 class ArrayValue final : public VectorValue<Value, Value::Type::kArray> {
291 public:
292 ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc);
293 };
294
295 class StringValue final : public Value {
296 public:
297 static constexpr Type kType = Type::kString;
298
299 StringValue();
300 StringValue(const char* src, size_t size, SkArenaAlloc& alloc);
301
size()302 size_t size() const {
303 switch (this->getTag()) {
304 case Tag::kShortString:
305 // We don't bother storing a length for short strings on the assumption
306 // that strlen is fast in this case. If this becomes problematic, we
307 // can either go back to storing (7-len) in the tag byte or write a fast
308 // short_strlen.
309 return strlen(this->cast<char>());
310 case Tag::kString:
311 return this->cast<VectorValue<char, Value::Type::kString>>()->size();
312 default:
313 return 0;
314 }
315 }
316
begin()317 const char* begin() const {
318 return this->getTag() == Tag::kShortString
319 ? this->cast<char>()
320 : this->cast<VectorValue<char, Value::Type::kString>>()->begin();
321 }
322
end()323 const char* end() const {
324 return this->getTag() == Tag::kShortString
325 ? strchr(this->cast<char>(), '\0')
326 : this->cast<VectorValue<char, Value::Type::kString>>()->end();
327 }
328 };
329
330 struct Member {
331 StringValue fKey;
332 Value fValue;
333 };
334
335 class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
336 public:
337 ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc);
338
339 const Value& operator[](const char*) const;
340
341 private:
342 // Not particularly interesting - hiding for disambiguation.
343 const Member& operator[](size_t i) const = delete;
344 };
345
346 class DOM final : public SkNoncopyable {
347 public:
348 DOM(const char*, size_t);
349
root()350 const Value& root() const { return fRoot; }
351
352 void write(SkWStream*) const;
353
354 private:
355 SkArenaAlloc fAlloc;
356 Value fRoot;
357 };
358
getType()359 inline Value::Type Value::getType() const {
360 switch (this->getTag()) {
361 case Tag::kNull: return Type::kNull;
362 case Tag::kBool: return Type::kBool;
363 case Tag::kInt: return Type::kNumber;
364 case Tag::kFloat: return Type::kNumber;
365 case Tag::kShortString: return Type::kString;
366 case Tag::kString: return Type::kString;
367 case Tag::kArray: return Type::kArray;
368 case Tag::kObject: return Type::kObject;
369 }
370
371 SkASSERT(false); // unreachable
372 return Type::kNull;
373 }
374
375 } // namespace skjson
376
377 #endif // SkJSON_DEFINED
378
379