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