/* * 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 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()) { * for (const auto& item : v.as()) { * if (const NumberValue* n = item) { * printf("Found number: %f", **n); * } * } * } * * if (v.is()) { * const StringValue* id = v.as()["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 bool is() const { return this->getType() == T::kType; } /** * Unguarded conversion to facade types. * * @return The record cast as facade type T&. */ template const T& as() const { SkASSERT(this->is()); return *reinterpret_cast(this); } /** * Guarded conversion to facade types. * * @return The record cast as facade type T*. */ template operator const T*() const { return this->is() ? &this->as() : 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 -- lowest 3 bits reserved for tag storage */ enum class Tag : uint8_t { // n.b.: we picked kShortString == 0 on purpose, // to enable certain short-string optimizations. kShortString = 0b00000000, // inline payload kNull = 0b00000001, // no payload kBool = 0b00000010, // inline payload kInt = 0b00000011, // inline payload kFloat = 0b00000100, // inline payload kString = 0b00000101, // ptr to external storage kArray = 0b00000110, // ptr to external storage kObject = 0b00000111, // ptr to external storage }; inline static constexpr uint8_t kTagMask = 0b00000111; void init_tagged(Tag); void init_tagged_pointer(Tag, void*); Tag getTag() const { return static_cast(fData8[0] & kTagMask); } // Access the record payload as T. // // Since the tag is stored in the lower bits, we skip the first word whenever feasible. // // E.g. (U == unused) // // uint8_t // ----------------------------------------------------------------------- // |TAG| U | val8 | U | U | U | U | U | U | // ----------------------------------------------------------------------- // // uint16_t // ----------------------------------------------------------------------- // |TAG| U | val16 | U | U | // ----------------------------------------------------------------------- // // uint32_t // ----------------------------------------------------------------------- // |TAG| U | val32 | // ----------------------------------------------------------------------- // // T* (32b) // ----------------------------------------------------------------------- // |TAG| U | T* (32bits) | // ----------------------------------------------------------------------- // // T* (64b) // ----------------------------------------------------------------------- // |TAG| T* (61bits) | // ----------------------------------------------------------------------- // template const T* cast() const { static_assert(sizeof (T) <= sizeof(Value), ""); static_assert(alignof(T) <= alignof(Value), ""); return (sizeof(T) > sizeof(*this) / 2) ? reinterpret_cast(this) + 0 // need all the bits : reinterpret_cast(this) + 1; // skip the first word (where the tag lives) } template T* cast() { return const_cast(const_cast(this)->cast()); } // Access the pointer payload. template 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() // For 64-bit, we use the lower bits of the pointer as tag storage. : reinterpret_cast(*this->cast() & ~static_cast(kTagMask)); } private: inline static constexpr size_t kValueSize = 8; uint8_t fData8[kValueSize]; #if !defined(SK_CPU_LENDIAN) // 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: inline static constexpr Type kType = Type::kNull; NullValue(); }; class BoolValue final : public Value { public: inline static constexpr Type kType = Type::kBool; explicit BoolValue(bool); bool operator *() const { SkASSERT(this->getTag() == Tag::kBool); return *this->cast(); } }; class NumberValue final : public Value { public: inline 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(*this->cast()) : static_cast(*this->cast()); } }; template class VectorValue : public Value { public: using ValueT = T; inline static constexpr Type kType = vtype; size_t size() const { SkASSERT(this->getType() == kType); return *this->ptr(); } const T* begin() const { SkASSERT(this->getType() == kType); const auto* size_ptr = this->ptr(); return reinterpret_cast(size_ptr + 1); } const T* end() const { SkASSERT(this->getType() == kType); const auto* size_ptr = this->ptr(); return reinterpret_cast(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 { public: ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc); }; class StringValue final : public Value { public: inline 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()); case Tag::kString: return this->cast>()->size(); default: return 0; } } const char* begin() const { return this->getTag() == Tag::kShortString ? this->cast() : this->cast>()->begin(); } const char* end() const { return this->getTag() == Tag::kShortString ? strchr(this->cast(), '\0') : this->cast>()->end(); } }; struct Member { StringValue fKey; Value fValue; }; class ObjectValue final : public VectorValue { public: ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc); const Value& operator[](const char*) const; const Member& operator[](size_t i) const { return this->VectorValue::operator[](i); } }; 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