blob: 1b46c4a1008cf8f75386bff10a48b758279d085b [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 Malitaae252792018-06-14 11:24:50 -040015class SkString;
Florin Malita7796f002018-06-08 12:25:38 -040016class SkWStream;
17
18namespace skjson {
19
20/**
21 * A fast and likely non-conforming JSON parser.
22 *
23 * Some known limitations/compromises:
24 *
25 * -- single-precision FP numbers
26 *
27 * -- missing string unescaping (no current users, could be easily added)
28 *
29 *
30 * Values are opaque, fixed-size (64 bits), immutable records.
31 *
Florin Malitaae252792018-06-14 11:24:50 -040032 * They can be converted to facade types for type-specific functionality.
Florin Malita7796f002018-06-08 12:25:38 -040033 *
Florin Malitaae252792018-06-14 11:24:50 -040034 * E.g.:
Florin Malita7796f002018-06-08 12:25:38 -040035 *
Florin Malitaae252792018-06-14 11:24:50 -040036 * if (v.is<ArrayValue>()) {
37 * for (const auto& item : v.as<ArrayValue>()) {
38 * if (const NumberValue* n = item) {
39 * printf("Found number: %f", **n);
40 * }
41 * }
42 * }
43 *
44 * if (v.is<ObjectValue>()) {
45 * const StringValue* id = v.as<ObjectValue>()["id"];
46 * if (id) {
47 * printf("Found object ID: %s", id->begin());
48 * } else {
49 * printf("Missing object ID");
50 * }
51 * }
Florin Malita7796f002018-06-08 12:25:38 -040052 */
53class alignas(8) Value {
54public:
55 enum class Type {
56 kNull,
57 kBool,
58 kNumber,
59 kString,
60 kArray,
61 kObject,
62 };
63
64 /**
Florin Malitaae252792018-06-14 11:24:50 -040065 * @return The type of this value.
Florin Malita7796f002018-06-08 12:25:38 -040066 */
67 Type getType() const;
68
69 /**
70 * @return True if the record matches the facade type T.
71 */
72 template <typename T>
Florin Malitaae252792018-06-14 11:24:50 -040073 bool is() const { return this->getType() == T::kType; }
Florin Malita7796f002018-06-08 12:25:38 -040074
75 /**
Florin Malitaae252792018-06-14 11:24:50 -040076 * Unguarded conversion to facade types.
Florin Malita7796f002018-06-08 12:25:38 -040077 *
Florin Malitaae252792018-06-14 11:24:50 -040078 * @return The record cast as facade type T&.
Florin Malita7796f002018-06-08 12:25:38 -040079 */
80 template <typename T>
81 const T& as() const {
Florin Malitaae252792018-06-14 11:24:50 -040082 SkASSERT(this->is<T>());
83 return *reinterpret_cast<const T*>(this);
Florin Malita7796f002018-06-08 12:25:38 -040084 }
85
86 /**
Florin Malitaae252792018-06-14 11:24:50 -040087 * Guarded conversion to facade types.
88 *
89 * @return The record cast as facade type T*.
Florin Malita7796f002018-06-08 12:25:38 -040090 */
Florin Malitaae252792018-06-14 11:24:50 -040091 template <typename T>
92 operator const T*() const {
93 return this->is<T>() ? &this->as<T>() : nullptr;
94 }
95
96 /**
97 * @return The string representation of this value.
98 */
99 SkString toString() const;
Florin Malita7796f002018-06-08 12:25:38 -0400100
101protected:
Florin Malitaae252792018-06-14 11:24:50 -0400102 /*
103 Value implementation notes:
104
105 -- fixed 64-bit size
106
107 -- 8-byte aligned
108
109 -- union of:
110
111 bool
112 int32
113 float
114 char[8] (short string storage)
115 external payload (tagged) pointer
116
117 -- highest 3 bits reserved for type storage
118
119 */
120 enum class Tag : uint8_t {
121 // We picked kShortString == 0 so that tag 0x00 and stored max_size-size (7-7=0)
122 // conveniently overlap the '\0' terminator, allowing us to store a 7 character
123 // C string inline.
124 kShortString = 0b00000000, // inline payload
125 kNull = 0b00100000, // no payload
126 kBool = 0b01000000, // inline payload
127 kInt = 0b01100000, // inline payload
128 kFloat = 0b10000000, // inline payload
129 kString = 0b10100000, // ptr to external storage
130 kArray = 0b11000000, // ptr to external storage
131 kObject = 0b11100000, // ptr to external storage
132 };
133 static constexpr uint8_t kTagMask = 0b11100000;
134
135 void init_tagged(Tag);
136 void init_tagged_pointer(Tag, void*);
137
138 Tag getTag() const {
139 return static_cast<Tag>(fData8[kTagOffset] & kTagMask);
140 }
141
142 // Access the record data as T.
143 //
144 // This is also used to access the payload for inline records. Since the record type lives in
145 // the high bits, sizeof(T) must be less than sizeof(Value) when accessing inline payloads.
146 //
147 // E.g.
148 //
149 // uint8_t
150 // -----------------------------------------------------------------------
151 // | val8 | val8 | val8 | val8 | val8 | val8 | val8 | TYPE|
152 // -----------------------------------------------------------------------
153 //
154 // uint32_t
155 // -----------------------------------------------------------------------
156 // | val32 | unused | TYPE|
157 // -----------------------------------------------------------------------
158 //
159 // T* (64b)
160 // -----------------------------------------------------------------------
161 // | T* (kTypeShift bits) |TYPE|
162 // -----------------------------------------------------------------------
163 //
164 template <typename T>
165 const T* cast() const {
166 static_assert(sizeof (T) <= sizeof(Value), "");
167 static_assert(alignof(T) <= alignof(Value), "");
168 return reinterpret_cast<const T*>(this);
169 }
170
171 template <typename T>
172 T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
173
174 // Access the pointer payload.
175 template <typename T>
176 const T* ptr() const {
177 static_assert(sizeof(uintptr_t) == sizeof(Value) ||
178 sizeof(uintptr_t) * 2 == sizeof(Value), "");
179
180 return (sizeof(uintptr_t) < sizeof(Value))
181 // For 32-bit, pointers are stored unmodified.
182 ? *this->cast<const T*>()
183 // For 64-bit, we use the high bits of the pointer as tag storage.
184 : reinterpret_cast<T*>(*this->cast<uintptr_t>() & kTagPointerMask);
185 }
186
187private:
188 static constexpr size_t kValueSize = 8;
189
190 uint8_t fData8[kValueSize];
191
192#if defined(SK_CPU_LENDIAN)
193 static constexpr size_t kTagOffset = kValueSize - 1;
194
195 static constexpr uintptr_t kTagPointerMask =
196 ~(static_cast<uintptr_t>(kTagMask) << ((sizeof(uintptr_t) - 1) * 8));
197#else
198 // The current value layout assumes LE and will take some tweaking for BE.
199 static_assert(false, "Big-endian builds are not supported at this time.");
200#endif
Florin Malita7796f002018-06-08 12:25:38 -0400201};
202
203class NullValue final : public Value {
204public:
Florin Malitaae252792018-06-14 11:24:50 -0400205 static constexpr Type kType = Type::kNull;
206
207 NullValue();
Florin Malita7796f002018-06-08 12:25:38 -0400208};
209
Florin Malitaae252792018-06-14 11:24:50 -0400210class BoolValue final : public Value {
Florin Malita7796f002018-06-08 12:25:38 -0400211public:
Florin Malitaae252792018-06-14 11:24:50 -0400212 static constexpr Type kType = Type::kBool;
Florin Malita7796f002018-06-08 12:25:38 -0400213
Florin Malitaae252792018-06-14 11:24:50 -0400214 explicit BoolValue(bool);
215
216 bool operator *() const {
217 SkASSERT(this->getTag() == Tag::kBool);
218 return *this->cast<bool>();
219 }
220};
221
222class NumberValue final : public Value {
223public:
224 static constexpr Type kType = Type::kNumber;
225
226 explicit NumberValue(int32_t);
227 explicit NumberValue(float);
228
229 double operator *() const {
230 SkASSERT(this->getTag() == Tag::kInt ||
231 this->getTag() == Tag::kFloat);
232
233 return this->getTag() == Tag::kInt
234 ? static_cast<double>(*this->cast<int32_t>())
235 : static_cast<double>(*this->cast<float>());
236 }
Florin Malita7796f002018-06-08 12:25:38 -0400237};
238
239template <typename T, Value::Type vtype>
240class VectorValue : public Value {
241public:
Florin Malitaae252792018-06-14 11:24:50 -0400242 using ValueT = T;
243 static constexpr Type kType = vtype;
Florin Malita7796f002018-06-08 12:25:38 -0400244
Florin Malitaae252792018-06-14 11:24:50 -0400245 size_t size() const {
246 SkASSERT(this->getType() == kType);
247 return *this->ptr<size_t>();
248 }
Florin Malita7796f002018-06-08 12:25:38 -0400249
Florin Malitaae252792018-06-14 11:24:50 -0400250 const T* begin() const {
251 SkASSERT(this->getType() == kType);
252 const auto* size_ptr = this->ptr<size_t>();
253 return reinterpret_cast<const T*>(size_ptr + 1);
254 }
255
256 const T* end() const {
257 SkASSERT(this->getType() == kType);
258 const auto* size_ptr = this->ptr<size_t>();
259 return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
260 }
Florin Malita7796f002018-06-08 12:25:38 -0400261
262 const T& operator[](size_t i) const {
Florin Malitaae252792018-06-14 11:24:50 -0400263 SkASSERT(this->getType() == kType);
264 SkASSERT(i < this->size());
265
266 return *(this->begin() + i);
Florin Malita7796f002018-06-08 12:25:38 -0400267 }
268};
269
Florin Malitaae252792018-06-14 11:24:50 -0400270class ArrayValue final : public VectorValue<Value, Value::Type::kArray> {
271public:
272 ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc);
273};
274
275class StringValue final : public Value {
276public:
277 static constexpr Type kType = Type::kString;
278
279 StringValue();
280 StringValue(const char* src, size_t size, SkArenaAlloc& alloc);
281
282 size_t size() const {
283 switch (this->getTag()) {
284 case Tag::kShortString:
285 return kMaxInlineStringSize - SkToSizeT(this->cast<char>()[kMaxInlineStringSize]);
286 case Tag::kString:
287 return this->cast<VectorValue<char, Value::Type::kString>>()->size();
288 default:
289 return 0;
290 }
291 }
292
293 const char* begin() const {
294 return this->getTag() == Tag::kShortString
295 ? this->cast<char>()
296 : this->cast<VectorValue<char, Value::Type::kString>>()->begin();
297 }
298
299 const char* end() const {
300 if (this->getTag() == Tag::kShortString) {
301 const auto* payload = this->cast<char>();
302 return payload + kMaxInlineStringSize - SkToSizeT(payload[kMaxInlineStringSize]);
303 }
304 return this->cast<VectorValue<char, Value::Type::kString>>()->end();
305 }
306
307private:
308 static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 1;
309};
Florin Malita7796f002018-06-08 12:25:38 -0400310
311struct Member {
312 StringValue fKey;
313 Value fValue;
Florin Malita7796f002018-06-08 12:25:38 -0400314};
315
316class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
317public:
Florin Malitaae252792018-06-14 11:24:50 -0400318 ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc);
319
Florin Malita7796f002018-06-08 12:25:38 -0400320 const Value& operator[](const char*) const;
Florin Malitaae252792018-06-14 11:24:50 -0400321
322private:
323 // Not particularly interesting - hiding for disambiguation.
324 const Member& operator[](size_t i) const = delete;
Florin Malita7796f002018-06-08 12:25:38 -0400325};
326
327class DOM final : public SkNoncopyable {
328public:
Florin Malitafedfd542018-06-14 15:03:21 -0400329 DOM(const char*, size_t);
Florin Malita7796f002018-06-08 12:25:38 -0400330
Florin Malitaae252792018-06-14 11:24:50 -0400331 const Value& root() const { return fRoot; }
Florin Malita7796f002018-06-08 12:25:38 -0400332
333 void write(SkWStream*) const;
334
335private:
336 SkArenaAlloc fAlloc;
Florin Malitaae252792018-06-14 11:24:50 -0400337 Value fRoot;
Florin Malita7796f002018-06-08 12:25:38 -0400338};
339
Florin Malitaae252792018-06-14 11:24:50 -0400340inline Value::Type Value::getType() const {
341 switch (this->getTag()) {
342 case Tag::kNull: return Type::kNull;
343 case Tag::kBool: return Type::kBool;
344 case Tag::kInt: return Type::kNumber;
345 case Tag::kFloat: return Type::kNumber;
346 case Tag::kShortString: return Type::kString;
347 case Tag::kString: return Type::kString;
348 case Tag::kArray: return Type::kArray;
349 case Tag::kObject: return Type::kObject;
350 }
351
352 SkASSERT(false); // unreachable
353 return Type::kNull;
354}
355
Florin Malita7796f002018-06-08 12:25:38 -0400356} // namespace skjson
357
358#endif // SkJSON_DEFINED
359