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