xref: /aosp_15_r20/external/skia/src/utils/SkJSON.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/base/SkNoncopyable.h"
13 #include "src/base/SkArenaAlloc.h"
14 
15 #include <cstdint>
16 #include <cstring>
17 #include <string_view>
18 
19 class SkString;
20 class SkWStream;
21 
22 namespace skjson {
23 
24 /**
25  *  A fast and likely non-conforming JSON parser.
26  *
27  *  Some known limitations/compromises:
28  *
29  *    -- single-precision FP numbers
30  *
31  *    -- missing string unescaping (no current users, could be easily added)
32  *
33  *
34  *  Values are opaque, fixed-size (64 bits), immutable records.
35  *
36  *  They can be converted to facade types for type-specific functionality.
37  *
38  *  E.g.:
39  *
40  *     if (v.is<ArrayValue>()) {
41  *         for (const auto& item : v.as<ArrayValue>()) {
42  *             if (const NumberValue* n = item) {
43  *                 printf("Found number: %f", **n);
44  *             }
45  *         }
46  *     }
47  *
48  *     if (v.is<ObjectValue>()) {
49  *         const StringValue* id = v.as<ObjectValue>()["id"];
50  *         if (id) {
51  *             printf("Found object ID: %s", id->begin());
52  *         } else {
53  *             printf("Missing object ID");
54  *         }
55  *     }
56  */
57 class alignas(8) Value {
58 public:
59     enum class Type {
60         kNull,
61         kBool,
62         kNumber,
63         kString,
64         kArray,
65         kObject,
66     };
67 
68     /**
69      * @return    The type of this value.
70      */
71     Type getType() const;
72 
73     /**
74      * @return    True if the record matches the facade type T.
75      */
76     template <typename T>
is()77     bool is() const { return this->getType() == T::kType; }
78 
79     /**
80      * Unguarded conversion to facade types.
81      *
82      * @return    The record cast as facade type T&.
83      */
84     template <typename T>
as()85     const T& as() const {
86         SkASSERT(this->is<T>());
87         return *reinterpret_cast<const T*>(this);
88     }
89 
90     /**
91      * Guarded conversion to facade types.
92      *
93      * @return    The record cast as facade type T*.
94      */
95     template <typename T>
96     operator const T*() const {
97         return this->is<T>() ? &this->as<T>() : nullptr;
98     }
99 
100     /**
101      * @return    The string representation of this value.
102      */
103     SkString toString() const;
104 
105     /**
106      * Helper for fluent key lookup: v["foo"]["bar"]["baz"]
107      *
108      * @return    The lookup result value on success, otherwise NullValue.
109      */
110     const Value& operator[](const char* key) const;
111 
112 protected:
113     /*
114       Value implementation notes:
115 
116         -- fixed 64-bit size
117 
118         -- 8-byte aligned
119 
120         -- union of:
121 
122              bool
123              int32
124              float
125              char[8] (short string storage)
126              external payload (tagged) pointer
127 
128          -- lowest 3 bits reserved for tag storage
129 
130      */
131     enum class Tag : uint8_t {
132         // n.b.: we picked kShortString == 0 on purpose,
133         // to enable certain short-string optimizations.
134         kShortString                  = 0b00000000,  // inline payload
135         kNull                         = 0b00000001,  // no payload
136         kBool                         = 0b00000010,  // inline payload
137         kInt                          = 0b00000011,  // inline payload
138         kFloat                        = 0b00000100,  // inline payload
139         kString                       = 0b00000101,  // ptr to external storage
140         kArray                        = 0b00000110,  // ptr to external storage
141         kObject                       = 0b00000111,  // ptr to external storage
142     };
143     inline static constexpr uint8_t kTagMask = 0b00000111;
144 
145     void init_tagged(Tag);
146     void init_tagged_pointer(Tag, void*);
147 
getTag()148     Tag getTag() const {
149         return static_cast<Tag>(fData8[0] & kTagMask);
150     }
151 
152     // Access the record payload as T.
153     //
154     // Since the tag is stored in the lower bits, we skip the first word whenever feasible.
155     //
156     // E.g. (U == unused)
157     //
158     //   uint8_t
159     //    -----------------------------------------------------------------------
160     //   |TAG| U  |  val8  |   U    |   U    |   U    |   U    |   U    |   U    |
161     //    -----------------------------------------------------------------------
162     //
163     //   uint16_t
164     //    -----------------------------------------------------------------------
165     //   |TAG|      U      |      val16      |        U        |        U        |
166     //    -----------------------------------------------------------------------
167     //
168     //   uint32_t
169     //    -----------------------------------------------------------------------
170     //   |TAG|             U                 |                val32              |
171     //    -----------------------------------------------------------------------
172     //
173     //   T* (32b)
174     //    -----------------------------------------------------------------------
175     //   |TAG|             U                 |             T* (32bits)           |
176     //    -----------------------------------------------------------------------
177     //
178     //   T* (64b)
179     //    -----------------------------------------------------------------------
180     //   |TAG|                        T* (61bits)                                |
181     //    -----------------------------------------------------------------------
182     //
183     template <typename T>
cast()184     const T* cast() const {
185         static_assert(sizeof (T) <=  sizeof(Value), "");
186         static_assert(alignof(T) <= alignof(Value), "");
187 
188         return (sizeof(T) > sizeof(*this) / 2)
189                 ? reinterpret_cast<const T*>(this) + 0  // need all the bits
190                 : reinterpret_cast<const T*>(this) + 1; // skip the first word (where the tag lives)
191     }
192 
193     template <typename T>
cast()194     T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
195 
196     // Access the pointer payload.
197     template <typename T>
ptr()198     const T* ptr() const {
199         static_assert(sizeof(uintptr_t)     == sizeof(Value) ||
200                       sizeof(uintptr_t) * 2 == sizeof(Value), "");
201 
202         return (sizeof(uintptr_t) < sizeof(Value))
203             // For 32-bit, pointers are stored unmodified.
204             ? *this->cast<const T*>()
205             // For 64-bit, we use the lower bits of the pointer as tag storage.
206             : reinterpret_cast<T*>(*this->cast<uintptr_t>() & ~static_cast<uintptr_t>(kTagMask));
207     }
208 
209 private:
210     inline static constexpr size_t kValueSize = 8;
211 
212     uint8_t fData8[kValueSize];
213 
214 #if !defined(SK_CPU_LENDIAN)
215     // The current value layout assumes LE and will take some tweaking for BE.
216     static_assert(false, "Big-endian builds are not supported at this time.");
217 #endif
218 };
219 
220 class NullValue final : public Value {
221 public:
222     inline static constexpr Type kType = Type::kNull;
223 
224     NullValue();
225 };
226 
227 class BoolValue final : public Value {
228 public:
229     inline static constexpr Type kType = Type::kBool;
230 
231     explicit BoolValue(bool);
232 
233     bool operator *() const {
234         SkASSERT(this->getTag() == Tag::kBool);
235         return *this->cast<bool>();
236     }
237 };
238 
239 class NumberValue final : public Value {
240 public:
241     inline static constexpr Type kType = Type::kNumber;
242 
243     explicit NumberValue(int32_t);
244     explicit NumberValue(float);
245 
246     double operator *() const {
247         SkASSERT(this->getTag() == Tag::kInt ||
248                  this->getTag() == Tag::kFloat);
249 
250         return this->getTag() == Tag::kInt
251             ? static_cast<double>(*this->cast<int32_t>())
252             : static_cast<double>(*this->cast<float>());
253     }
254 };
255 
256 template <typename T, Value::Type vtype>
257 class VectorValue : public Value {
258 public:
259     using ValueT = T;
260     inline static constexpr Type kType = vtype;
261 
size()262     size_t size() const {
263         SkASSERT(this->getType() == kType);
264         return *this->ptr<size_t>();
265     }
266 
begin()267     const T* begin() const {
268         SkASSERT(this->getType() == kType);
269         const auto* size_ptr = this->ptr<size_t>();
270         return reinterpret_cast<const T*>(size_ptr + 1);
271     }
272 
end()273     const T* end() const {
274         SkASSERT(this->getType() == kType);
275         const auto* size_ptr = this->ptr<size_t>();
276         return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
277     }
278 
279     const T& operator[](size_t i) const {
280         SkASSERT(this->getType() == kType);
281         SkASSERT(i < this->size());
282 
283         return *(this->begin() + i);
284     }
285 };
286 
287 class ArrayValue final : public VectorValue<Value, Value::Type::kArray> {
288 public:
289     ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc);
290 };
291 
292 class StringValue final : public Value {
293 public:
294     inline static constexpr Type kType = Type::kString;
295 
296     StringValue();
297     StringValue(const char* src, SkArenaAlloc& alloc);
298     StringValue(const char* src, size_t size, SkArenaAlloc& alloc);
299 
size()300     size_t size() const {
301         switch (this->getTag()) {
302         case Tag::kShortString:
303             // We don't bother storing a length for short strings on the assumption
304             // that strlen is fast in this case.  If this becomes problematic, we
305             // can either go back to storing (7-len) in the tag byte or write a fast
306             // short_strlen.
307             return strlen(this->cast<char>());
308         case Tag::kString:
309             return this->cast<VectorValue<char, Value::Type::kString>>()->size();
310         default:
311             return 0;
312         }
313     }
314 
begin()315     const char* begin() const {
316         return this->getTag() == Tag::kShortString
317             ? this->cast<char>()
318             : this->cast<VectorValue<char, Value::Type::kString>>()->begin();
319     }
320 
end()321     const char* end() const {
322         return this->getTag() == Tag::kShortString
323             ? strchr(this->cast<char>(), '\0')
324             : this->cast<VectorValue<char, Value::Type::kString>>()->end();
325     }
326 
str()327     std::string_view str() const {
328         return std::string_view(this->begin(), this->size());
329     }
330 };
331 
332 struct Member {
333     StringValue fKey;
334           Value fValue;
335 };
336 
337 class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
338 public:
339     ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc);
340 
341     const Value& operator[](const char* key) const {
342         static const Value gNullValue = NullValue();
343 
344         const auto* member = this->find(key);
345         return member
346             ? member->fValue
347             : gNullValue;
348     }
349 
350     // Writable access to the value associated with the given key.
351     // If the key is not present, it is added with a default NullValue.
352     Value& writable(const char* key, SkArenaAlloc&) const;
353 
354 private:
355     const Member* find(const char*) const;
356 };
357 
358 class DOM final : public SkNoncopyable {
359 public:
360     DOM(const char*, size_t);
361 
root()362     const Value& root() const { return fRoot; }
363 
364     void write(SkWStream*) const;
365 
366 private:
367     SkArenaAlloc fAlloc;
368     Value        fRoot;
369 };
370 
getType()371 inline Value::Type Value::getType() const {
372     switch (this->getTag()) {
373     case Tag::kNull:        return Type::kNull;
374     case Tag::kBool:        return Type::kBool;
375     case Tag::kInt:         return Type::kNumber;
376     case Tag::kFloat:       return Type::kNumber;
377     case Tag::kShortString: return Type::kString;
378     case Tag::kString:      return Type::kString;
379     case Tag::kArray:       return Type::kArray;
380     case Tag::kObject:      return Type::kObject;
381     }
382 
383     SkASSERT(false); // unreachable
384     return Type::kNull;
385 }
386 
387 inline const Value& Value::operator[](const char* key) const {
388     static const Value gNullValue = NullValue();
389 
390     return this->is<ObjectValue>()
391         ? this->as<ObjectValue>()[key]
392         : gNullValue;
393 }
394 
395 } // namespace skjson
396 
397 #endif // SkJSON_DEFINED
398