123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- * Copyright 2018 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #ifndef SkJSON_DEFINED
- #define SkJSON_DEFINED
- #include "include/core/SkTypes.h"
- #include "include/private/SkNoncopyable.h"
- #include "include/private/SkTo.h"
- #include "src/core/SkArenaAlloc.h"
- #include <cstring>
- class SkString;
- class SkWStream;
- namespace skjson {
- /**
- * A fast and likely non-conforming JSON parser.
- *
- * Some known limitations/compromises:
- *
- * -- single-precision FP numbers
- *
- * -- missing string unescaping (no current users, could be easily added)
- *
- *
- * Values are opaque, fixed-size (64 bits), immutable records.
- *
- * They can be converted to facade types for type-specific functionality.
- *
- * E.g.:
- *
- * if (v.is<ArrayValue>()) {
- * for (const auto& item : v.as<ArrayValue>()) {
- * if (const NumberValue* n = item) {
- * printf("Found number: %f", **n);
- * }
- * }
- * }
- *
- * if (v.is<ObjectValue>()) {
- * const StringValue* id = v.as<ObjectValue>()["id"];
- * if (id) {
- * printf("Found object ID: %s", id->begin());
- * } else {
- * printf("Missing object ID");
- * }
- * }
- */
- class alignas(8) Value {
- public:
- enum class Type {
- kNull,
- kBool,
- kNumber,
- kString,
- kArray,
- kObject,
- };
- /**
- * @return The type of this value.
- */
- Type getType() const;
- /**
- * @return True if the record matches the facade type T.
- */
- template <typename T>
- bool is() const { return this->getType() == T::kType; }
- /**
- * Unguarded conversion to facade types.
- *
- * @return The record cast as facade type T&.
- */
- template <typename T>
- const T& as() const {
- SkASSERT(this->is<T>());
- return *reinterpret_cast<const T*>(this);
- }
- /**
- * Guarded conversion to facade types.
- *
- * @return The record cast as facade type T*.
- */
- template <typename T>
- operator const T*() const {
- return this->is<T>() ? &this->as<T>() : nullptr;
- }
- /**
- * @return The string representation of this value.
- */
- SkString toString() const;
- protected:
- /*
- Value implementation notes:
- -- fixed 64-bit size
- -- 8-byte aligned
- -- union of:
- bool
- int32
- float
- char[8] (short string storage)
- external payload (tagged) pointer
- -- highest 3 bits reserved for type storage
- */
- enum class Tag : uint8_t {
- // We picked kShortString == 0 so that tag 0x00 and stored max_size-size (7-7=0)
- // conveniently overlap the '\0' terminator, allowing us to store a 7 character
- // C string inline.
- kShortString = 0b00000000, // inline payload
- kNull = 0b00100000, // no payload
- kBool = 0b01000000, // inline payload
- kInt = 0b01100000, // inline payload
- kFloat = 0b10000000, // inline payload
- kString = 0b10100000, // ptr to external storage
- kArray = 0b11000000, // ptr to external storage
- kObject = 0b11100000, // ptr to external storage
- };
- static constexpr uint8_t kTagMask = 0b11100000;
- void init_tagged(Tag);
- void init_tagged_pointer(Tag, void*);
- Tag getTag() const {
- return static_cast<Tag>(fData8[kTagOffset] & kTagMask);
- }
- // Access the record data as T.
- //
- // This is also used to access the payload for inline records. Since the record type lives in
- // the high bits, sizeof(T) must be less than sizeof(Value) when accessing inline payloads.
- //
- // E.g.
- //
- // uint8_t
- // -----------------------------------------------------------------------
- // | val8 | val8 | val8 | val8 | val8 | val8 | val8 | TYPE|
- // -----------------------------------------------------------------------
- //
- // uint32_t
- // -----------------------------------------------------------------------
- // | val32 | unused | TYPE|
- // -----------------------------------------------------------------------
- //
- // T* (64b)
- // -----------------------------------------------------------------------
- // | T* (kTypeShift bits) |TYPE|
- // -----------------------------------------------------------------------
- //
- template <typename T>
- const T* cast() const {
- static_assert(sizeof (T) <= sizeof(Value), "");
- static_assert(alignof(T) <= alignof(Value), "");
- return reinterpret_cast<const T*>(this);
- }
- template <typename T>
- T* cast() { return const_cast<T*>(const_cast<const Value*>(this)->cast<T>()); }
- // Access the pointer payload.
- template <typename T>
- const T* ptr() const {
- static_assert(sizeof(uintptr_t) == sizeof(Value) ||
- sizeof(uintptr_t) * 2 == sizeof(Value), "");
- return (sizeof(uintptr_t) < sizeof(Value))
- // For 32-bit, pointers are stored unmodified.
- ? *this->cast<const T*>()
- // For 64-bit, we use the high bits of the pointer as tag storage.
- : reinterpret_cast<T*>(*this->cast<uintptr_t>() & kTagPointerMask);
- }
- private:
- static constexpr size_t kValueSize = 8;
- uint8_t fData8[kValueSize];
- #if defined(SK_CPU_LENDIAN)
- static constexpr size_t kTagOffset = kValueSize - 1;
- static constexpr uintptr_t kTagPointerMask =
- ~(static_cast<uintptr_t>(kTagMask) << ((sizeof(uintptr_t) - 1) * 8));
- #else
- // The current value layout assumes LE and will take some tweaking for BE.
- static_assert(false, "Big-endian builds are not supported at this time.");
- #endif
- };
- class NullValue final : public Value {
- public:
- static constexpr Type kType = Type::kNull;
- NullValue();
- };
- class BoolValue final : public Value {
- public:
- static constexpr Type kType = Type::kBool;
- explicit BoolValue(bool);
- bool operator *() const {
- SkASSERT(this->getTag() == Tag::kBool);
- return *this->cast<bool>();
- }
- };
- class NumberValue final : public Value {
- public:
- static constexpr Type kType = Type::kNumber;
- explicit NumberValue(int32_t);
- explicit NumberValue(float);
- double operator *() const {
- SkASSERT(this->getTag() == Tag::kInt ||
- this->getTag() == Tag::kFloat);
- return this->getTag() == Tag::kInt
- ? static_cast<double>(*this->cast<int32_t>())
- : static_cast<double>(*this->cast<float>());
- }
- };
- template <typename T, Value::Type vtype>
- class VectorValue : public Value {
- public:
- using ValueT = T;
- static constexpr Type kType = vtype;
- size_t size() const {
- SkASSERT(this->getType() == kType);
- return *this->ptr<size_t>();
- }
- const T* begin() const {
- SkASSERT(this->getType() == kType);
- const auto* size_ptr = this->ptr<size_t>();
- return reinterpret_cast<const T*>(size_ptr + 1);
- }
- const T* end() const {
- SkASSERT(this->getType() == kType);
- const auto* size_ptr = this->ptr<size_t>();
- return reinterpret_cast<const T*>(size_ptr + 1) + *size_ptr;
- }
- const T& operator[](size_t i) const {
- SkASSERT(this->getType() == kType);
- SkASSERT(i < this->size());
- return *(this->begin() + i);
- }
- };
- class ArrayValue final : public VectorValue<Value, Value::Type::kArray> {
- public:
- ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc);
- };
- class StringValue final : public Value {
- public:
- static constexpr Type kType = Type::kString;
- StringValue();
- StringValue(const char* src, size_t size, SkArenaAlloc& alloc);
- size_t size() const {
- switch (this->getTag()) {
- case Tag::kShortString:
- // We don't bother storing a length for short strings on the assumption
- // that strlen is fast in this case. If this becomes problematic, we
- // can either go back to storing (7-len) in the tag byte or write a fast
- // short_strlen.
- return strlen(this->cast<char>());
- case Tag::kString:
- return this->cast<VectorValue<char, Value::Type::kString>>()->size();
- default:
- return 0;
- }
- }
- const char* begin() const {
- return this->getTag() == Tag::kShortString
- ? this->cast<char>()
- : this->cast<VectorValue<char, Value::Type::kString>>()->begin();
- }
- const char* end() const {
- return this->getTag() == Tag::kShortString
- ? strchr(this->cast<char>(), '\0')
- : this->cast<VectorValue<char, Value::Type::kString>>()->end();
- }
- };
- struct Member {
- StringValue fKey;
- Value fValue;
- };
- class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
- public:
- ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc);
- const Value& operator[](const char*) const;
- private:
- // Not particularly interesting - hiding for disambiguation.
- const Member& operator[](size_t i) const = delete;
- };
- class DOM final : public SkNoncopyable {
- public:
- DOM(const char*, size_t);
- const Value& root() const { return fRoot; }
- void write(SkWStream*) const;
- private:
- SkArenaAlloc fAlloc;
- Value fRoot;
- };
- inline Value::Type Value::getType() const {
- switch (this->getTag()) {
- case Tag::kNull: return Type::kNull;
- case Tag::kBool: return Type::kBool;
- case Tag::kInt: return Type::kNumber;
- case Tag::kFloat: return Type::kNumber;
- case Tag::kShortString: return Type::kString;
- case Tag::kString: return Type::kString;
- case Tag::kArray: return Type::kArray;
- case Tag::kObject: return Type::kObject;
- }
- SkASSERT(false); // unreachable
- return Type::kNull;
- }
- } // namespace skjson
- #endif // SkJSON_DEFINED
|