blob: 8baf9fb8fc888e1c1fe309c24e5c4b0f051adb3c [file] [log] [blame]
Florin Malita7796f002018-06-08 12:25:38 -04001/*
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 "SkArenaAlloc.h"
Florin Malitaae252792018-06-14 11:24:50 -040012#include "SkTo.h"
Florin Malita7796f002018-06-08 12:25:38 -040013#include "SkTypes.h"
14
Florin Malitad7bfcaf2018-06-14 18:03:26 -040015#include <cstring>
16
Florin Malitaae252792018-06-14 11:24:50 -040017class SkString;
Florin Malita7796f002018-06-08 12:25:38 -040018class SkWStream;
19
20namespace skjson {
21
22/**
23 * A fast and likely non-conforming JSON parser.
24 *
25 * Some known limitations/compromises:
26 *
27 * -- single-precision FP numbers
28 *
29 * -- missing string unescaping (no current users, could be easily added)
30 *
31 *
32 * Values are opaque, fixed-size (64 bits), immutable records.
33 *
Florin Malitaae252792018-06-14 11:24:50 -040034 * They can be converted to facade types for type-specific functionality.
Florin Malita7796f002018-06-08 12:25:38 -040035 *
Florin Malitaae252792018-06-14 11:24:50 -040036 * E.g.:
Florin Malita7796f002018-06-08 12:25:38 -040037 *
Florin Malitaae252792018-06-14 11:24:50 -040038 * if (v.is<ArrayValue>()) {
39 * for (const auto& item : v.as<ArrayValue>()) {
40 * if (const NumberValue* n = item) {
41 * printf("Found number: %f", **n);
42 * }
43 * }
44 * }
45 *
46 * if (v.is<ObjectValue>()) {
47 * const StringValue* id = v.as<ObjectValue>()["id"];
48 * if (id) {
49 * printf("Found object ID: %s", id->begin());
50 * } else {
51 * printf("Missing object ID");
52 * }
53 * }
Florin Malita7796f002018-06-08 12:25:38 -040054 */
55class alignas(8) Value {
56public:
57 enum class Type {
58 kNull,
59 kBool,
60 kNumber,
61 kString,
62 kArray,
63 kObject,
64 };
65
66 /**
Florin Malitaae252792018-06-14 11:24:50 -040067 * @return The type of this value.
Florin Malita7796f002018-06-08 12:25:38 -040068 */
69 Type getType() const;
70
71 /**
72 * @return True if the record matches the facade type T.
73 */
74 template <typename T>
Florin Malitaae252792018-06-14 11:24:50 -040075 bool is() const { return this->getType() == T::kType; }
Florin Malita7796f002018-06-08 12:25:38 -040076
77 /**
Florin Malitaae252792018-06-14 11:24:50 -040078 * Unguarded conversion to facade types.
Florin Malita7796f002018-06-08 12:25:38 -040079 *
Florin Malitaae252792018-06-14 11:24:50 -040080 * @return The record cast as facade type T&.
Florin Malita7796f002018-06-08 12:25:38 -040081 */
82 template <typename T>
83 const T& as() const {
Florin Malitaae252792018-06-14 11:24:50 -040084 SkASSERT(this->is<T>());
85 return *reinterpret_cast<const T*>(this);
Florin Malita7796f002018-06-08 12:25:38 -040086 }
87
88 /**
Florin Malitaae252792018-06-14 11:24:50 -040089 * Guarded conversion to facade types.
90 *
91 * @return The record cast as facade type T*.
Florin Malita7796f002018-06-08 12:25:38 -040092 */
Florin Malitaae252792018-06-14 11:24:50 -040093 template <typename T>
94 operator const T*() const {
95 return this->is<T>() ? &this->as<T>() : nullptr;
96 }
97
98 /**
99 * @return The string representation of this value.
100 */
101 SkString toString() const;
Florin Malita7796f002018-06-08 12:25:38 -0400102
103protected:
Florin Malitaae252792018-06-14 11:24:50 -0400104 /*
105 Value implementation notes:
106
107 -- fixed 64-bit size
108
109 -- 8-byte aligned
110
111 -- union of:
112
113 bool
114 int32
115 float
116 char[8] (short string storage)
117 external payload (tagged) pointer
118
119 -- highest 3 bits reserved for type storage
120
121 */
122 enum class Tag : uint8_t {
123 // We picked kShortString == 0 so that tag 0x00 and stored max_size-size (7-7=0)
124 // conveniently overlap the '\0' terminator, allowing us to store a 7 character
125 // C string inline.
126 kShortString = 0b00000000, // inline payload
127 kNull = 0b00100000, // no payload
128 kBool = 0b01000000, // inline payload
129 kInt = 0b01100000, // inline payload
130 kFloat = 0b10000000, // inline payload
131 kString = 0b10100000, // ptr to external storage
132 kArray = 0b11000000, // ptr to external storage
133 kObject = 0b11100000, // ptr to external storage
134 };
135 static constexpr uint8_t kTagMask = 0b11100000;
136
137 void init_tagged(Tag);
138 void init_tagged_pointer(Tag, void*);
139
140 Tag getTag() const {
141 return static_cast<Tag>(fData8[kTagOffset] & kTagMask);
142 }
143
144 // Access the record data as T.
145 //
146 // This is also used to access the payload for inline records. Since the record type lives in
147 // the high bits, sizeof(T) must be less than sizeof(Value) when accessing inline payloads.
148 //
149 // E.g.
150 //
151 // uint8_t
152 // -----------------------------------------------------------------------
153 // | val8 | val8 | val8 | val8 | val8 | val8 | val8 | TYPE|
154 // -----------------------------------------------------------------------
155 //
156 // uint32_t
157 // -----------------------------------------------------------------------
158 // | val32 | unused | TYPE|
159 // -----------------------------------------------------------------------
160 //
161 // T* (64b)
162 // -----------------------------------------------------------------------
163 // | T* (kTypeShift bits) |TYPE|
164 // -----------------------------------------------------------------------
165 //
166 template <typename T>
167 const T* cast() const {
168 static_assert(sizeof (T) <= sizeof(Value), "");
169 static_assert(alignof(T) <= alignof(Value), "");
170 return reinterpret_cast<const T*>(this);
171 }
172
173 template <typename T>
174 T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
175
176 // Access the pointer payload.
177 template <typename T>
178 const T* ptr() const {
179 static_assert(sizeof(uintptr_t) == sizeof(Value) ||
180 sizeof(uintptr_t) * 2 == sizeof(Value), "");
181
182 return (sizeof(uintptr_t) < sizeof(Value))
183 // For 32-bit, pointers are stored unmodified.
184 ? *this->cast<const T*>()
185 // For 64-bit, we use the high bits of the pointer as tag storage.
186 : reinterpret_cast<T*>(*this->cast<uintptr_t>() & kTagPointerMask);
187 }
188
189private:
190 static constexpr size_t kValueSize = 8;
191
192 uint8_t fData8[kValueSize];
193
194#if defined(SK_CPU_LENDIAN)
195 static constexpr size_t kTagOffset = kValueSize - 1;
196
197 static constexpr uintptr_t kTagPointerMask =
198 ~(static_cast<uintptr_t>(kTagMask) << ((sizeof(uintptr_t) - 1) * 8));
199#else
200 // The current value layout assumes LE and will take some tweaking for BE.
201 static_assert(false, "Big-endian builds are not supported at this time.");
202#endif
Florin Malita7796f002018-06-08 12:25:38 -0400203};
204
205class NullValue final : public Value {
206public:
Florin Malitaae252792018-06-14 11:24:50 -0400207 static constexpr Type kType = Type::kNull;
208
209 NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400210};
211
Florin Malitaae252792018-06-14 11:24:50 -0400212class BoolValue final : public Value {
Florin Malita7796f002018-06-08 12:25:38 -0400213public:
Florin Malitaae252792018-06-14 11:24:50 -0400214 static constexpr Type kType = Type::kBool;
Florin Malita7796f002018-06-08 12:25:38 -0400215
Florin Malitaae252792018-06-14 11:24:50 -0400216 explicit BoolValue(bool);
217
218 bool operator *() const {
219 SkASSERT(this->getTag() == Tag::kBool);
220 return *this->cast<bool>();
221 }
222};
223
224class NumberValue final : public Value {
225public:
226 static constexpr Type kType = Type::kNumber;
227
228 explicit NumberValue(int32_t);
229 explicit NumberValue(float);
230
231 double operator *() const {
232 SkASSERT(this->getTag() == Tag::kInt ||
233 this->getTag() == Tag::kFloat);
234
235 return this->getTag() == Tag::kInt
236 ? static_cast<double>(*this->cast<int32_t>())
237 : static_cast<double>(*this->cast<float>());
238 }
Florin Malita7796f002018-06-08 12:25:38 -0400239};
240
241template <typename T, Value::Type vtype>
242class VectorValue : public Value {
243public:
Florin Malitaae252792018-06-14 11:24:50 -0400244 using ValueT = T;
245 static constexpr Type kType = vtype;
Florin Malita7796f002018-06-08 12:25:38 -0400246
Florin Malitaae252792018-06-14 11:24:50 -0400247 size_t size() const {
248 SkASSERT(this->getType() == kType);
249 return *this->ptr<size_t>();
250 }
Florin Malita7796f002018-06-08 12:25:38 -0400251
Florin Malitaae252792018-06-14 11:24:50 -0400252 const T* begin() const {
253 SkASSERT(this->getType() == kType);
254 const auto* size_ptr = this->ptr<size_t>();
255 return reinterpret_cast<const T*>(size_ptr + 1);
256 }
257
258 const T* end() const {
259 SkASSERT(this->getType() == kType);
260 const auto* size_ptr = this->ptr<size_t>();
261 return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
262 }
Florin Malita7796f002018-06-08 12:25:38 -0400263
264 const T& operator[](size_t i) const {
Florin Malitaae252792018-06-14 11:24:50 -0400265 SkASSERT(this->getType() == kType);
266 SkASSERT(i < this->size());
267
268 return *(this->begin() + i);
Florin Malita7796f002018-06-08 12:25:38 -0400269 }
270};
271
Florin Malitaae252792018-06-14 11:24:50 -0400272class ArrayValue final : public VectorValue<Value, Value::Type::kArray> {
273public:
274 ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc);
275};
276
277class StringValue final : public Value {
278public:
279 static constexpr Type kType = Type::kString;
280
281 StringValue();
282 StringValue(const char* src, size_t size, SkArenaAlloc& alloc);
283
284 size_t size() const {
285 switch (this->getTag()) {
286 case Tag::kShortString:
Florin Malitad7bfcaf2018-06-14 18:03:26 -0400287 // We don't bother storing a length for short strings on the assumption
288 // that strlen is fast in this case. If this becomes problematic, we
289 // can either go back to storing (7-len) in the tag byte or write a fast
290 // short_strlen.
291 return strlen(this->cast<char>());
Florin Malitaae252792018-06-14 11:24:50 -0400292 case Tag::kString:
293 return this->cast<VectorValue<char, Value::Type::kString>>()->size();
294 default:
295 return 0;
296 }
297 }
298
299 const char* begin() const {
300 return this->getTag() == Tag::kShortString
301 ? this->cast<char>()
302 : this->cast<VectorValue<char, Value::Type::kString>>()->begin();
303 }
304
305 const char* end() const {
Florin Malitad7bfcaf2018-06-14 18:03:26 -0400306 return this->getTag() == Tag::kShortString
307 ? strchr(this->cast<char>(), '\0')
308 : this->cast<VectorValue<char, Value::Type::kString>>()->end();
Florin Malitaae252792018-06-14 11:24:50 -0400309 }
Florin Malitaae252792018-06-14 11:24:50 -0400310};
Florin Malita7796f002018-06-08 12:25:38 -0400311
312struct Member {
313 StringValue fKey;
314 Value fValue;
Florin Malita7796f002018-06-08 12:25:38 -0400315};
316
317class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
318public:
Florin Malitaae252792018-06-14 11:24:50 -0400319 ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc);
320
Florin Malita7796f002018-06-08 12:25:38 -0400321 const Value& operator[](const char*) const;
Florin Malitaae252792018-06-14 11:24:50 -0400322
323private:
324 // Not particularly interesting - hiding for disambiguation.
325 const Member& operator[](size_t i) const = delete;
Florin Malita7796f002018-06-08 12:25:38 -0400326};
327
328class DOM final : public SkNoncopyable {
329public:
Florin Malitafedfd542018-06-14 15:03:21 -0400330 DOM(const char*, size_t);
Florin Malita7796f002018-06-08 12:25:38 -0400331
Florin Malitaae252792018-06-14 11:24:50 -0400332 const Value& root() const { return fRoot; }
Florin Malita7796f002018-06-08 12:25:38 -0400333
334 void write(SkWStream*) const;
335
336private:
337 SkArenaAlloc fAlloc;
Florin Malitaae252792018-06-14 11:24:50 -0400338 Value fRoot;
Florin Malita7796f002018-06-08 12:25:38 -0400339};
340
Florin Malitaae252792018-06-14 11:24:50 -0400341inline Value::Type Value::getType() const {
342 switch (this->getTag()) {
343 case Tag::kNull: return Type::kNull;
344 case Tag::kBool: return Type::kBool;
345 case Tag::kInt: return Type::kNumber;
346 case Tag::kFloat: return Type::kNumber;
347 case Tag::kShortString: return Type::kString;
348 case Tag::kString: return Type::kString;
349 case Tag::kArray: return Type::kArray;
350 case Tag::kObject: return Type::kObject;
351 }
352
353 SkASSERT(false); // unreachable
354 return Type::kNull;
355}
356
Florin Malita7796f002018-06-08 12:25:38 -0400357} // namespace skjson
358
359#endif // SkJSON_DEFINED
360