From 80452bee11ebe6708ea459ea34e526a44c04bdb0 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Tue, 19 Jun 2018 11:27:20 -0400 Subject: Fold SkJSON into Skia/utils It's a tiny, core-ish component -- might as well treat as such to simplify dependencies. Change-Id: I6f31ce2d151f9a629d88bfc7f15d64891d5150c0 Reviewed-on: https://skia-review.googlesource.com/135780 Reviewed-by: Mike Klein Reviewed-by: Kevin Lubick Commit-Queue: Florin Malita --- modules/skjson/BUILD.gn | 86 ---- modules/skjson/include/SkJSON.h | 360 ----------------- modules/skjson/src/FuzzSkJSON.cpp | 24 -- modules/skjson/src/SkJSON.cpp | 803 ------------------------------------- modules/skjson/src/SkJSONBench.cpp | 161 -------- modules/skjson/src/SkJSONTest.cpp | 388 ------------------ modules/skottie/BUILD.gn | 1 - 7 files changed, 1823 deletions(-) delete mode 100644 modules/skjson/BUILD.gn delete mode 100644 modules/skjson/include/SkJSON.h delete mode 100644 modules/skjson/src/FuzzSkJSON.cpp delete mode 100644 modules/skjson/src/SkJSON.cpp delete mode 100644 modules/skjson/src/SkJSONBench.cpp delete mode 100644 modules/skjson/src/SkJSONTest.cpp (limited to 'modules') diff --git a/modules/skjson/BUILD.gn b/modules/skjson/BUILD.gn deleted file mode 100644 index dd8ca65973..0000000000 --- a/modules/skjson/BUILD.gn +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2018 Google Inc. -# -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -declare_args() { - skia_enable_skjson = true -} - -config("public_config") { - if (skia_enable_skjson) { - defines = [ "SK_ENABLE_SKJSON" ] - include_dirs = [ "include" ] - } -} - -source_set("skjson") { - if (skia_enable_skjson) { - public_configs = [ ":public_config" ] - public = [ - "include/SkJSON.h", - ] - sources = [ - "src/SkJSON.cpp", - ] - configs += [ "../../:skia_private" ] - deps = [ - "../..:skia", - ] - } -} - -source_set("tests") { - if (skia_enable_skjson) { - testonly = true - - configs += [ - "../..:skia_private", - "../..:tests_config", - ] - sources = [ - "src/SkJSONTest.cpp", - ] - - deps = [ - ":skjson", - "../..:gpu_tool_utils", - "../..:skia", - ] - } -} - -source_set("bench") { - if (skia_enable_skjson) { - testonly = true - - configs += [ - "../..:bench_config", - "../..:skia_private", - ] - sources = [ - "src/SkJSONBench.cpp", - ] - - deps = [ - ":skjson", - "../..:skia", - ] - } -} - -source_set("fuzz") { - if (skia_enable_skjson) { - testonly = true - - configs += [ "../..:skia_private" ] - sources = [ - "src/FuzzSkJSON.cpp", - ] - - deps = [ - ":skjson", - "../..:skia", - ] - } -} diff --git a/modules/skjson/include/SkJSON.h b/modules/skjson/include/SkJSON.h deleted file mode 100644 index 8baf9fb8fc..0000000000 --- a/modules/skjson/include/SkJSON.h +++ /dev/null @@ -1,360 +0,0 @@ -/* - * 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 "SkArenaAlloc.h" -#include "SkTo.h" -#include "SkTypes.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 - - -- 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(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 - const T* cast() const { - static_assert(sizeof (T) <= sizeof(Value), ""); - static_assert(alignof(T) <= alignof(Value), ""); - return reinterpret_cast(this); - } - - 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 high bits of the pointer as tag storage. - : reinterpret_cast(*this->cast() & 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(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(); - } -}; - -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(*this->cast()) - : static_cast(*this->cast()); - } -}; - -template -class VectorValue : public Value { -public: - using ValueT = T; - 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: - 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; - -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 - diff --git a/modules/skjson/src/FuzzSkJSON.cpp b/modules/skjson/src/FuzzSkJSON.cpp deleted file mode 100644 index 2e971ce249..0000000000 --- a/modules/skjson/src/FuzzSkJSON.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2018 Google, LLC - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkData.h" -#include "SkJSON.h" -#include "SkStream.h" - -void FuzzSkJSON(sk_sp bytes) { - skjson::DOM dom(static_cast(bytes->data()), bytes->size()); - SkDynamicMemoryWStream wstream; - dom.write(&wstream); -} - -#if defined(IS_FUZZING_WITH_LIBFUZZER) -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - auto bytes = SkData::MakeWithoutCopy(data, size); - FuzzSkJSON(bytes); - return 0; -} -#endif diff --git a/modules/skjson/src/SkJSON.cpp b/modules/skjson/src/SkJSON.cpp deleted file mode 100644 index 2548f4ef41..0000000000 --- a/modules/skjson/src/SkJSON.cpp +++ /dev/null @@ -1,803 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkJSON.h" - -#include "SkMalloc.h" -#include "SkStream.h" -#include "SkString.h" - -#include -#include -#include - -namespace skjson { - -// #define SK_JSON_REPORT_ERRORS - -static_assert( sizeof(Value) == 8, ""); -static_assert(alignof(Value) == 8, ""); - -static constexpr size_t kRecAlign = alignof(Value); - -void Value::init_tagged(Tag t) { - memset(fData8, 0, sizeof(fData8)); - fData8[Value::kTagOffset] = SkTo(t); - SkASSERT(this->getTag() == t); -} - -// Pointer values store a type (in the upper kTagBits bits) and a pointer. -void Value::init_tagged_pointer(Tag t, void* p) { - *this->cast() = reinterpret_cast(p); - - if (sizeof(Value) == sizeof(uintptr_t)) { - // For 64-bit, we rely on the pointer upper bits being unused/zero. - SkASSERT(!(fData8[kTagOffset] & kTagMask)); - fData8[kTagOffset] |= SkTo(t); - } else { - // For 32-bit, we need to zero-initialize the upper 32 bits - SkASSERT(sizeof(Value) == sizeof(uintptr_t) * 2); - this->cast()[kTagOffset >> 2] = 0; - fData8[kTagOffset] = SkTo(t); - } - - SkASSERT(this->getTag() == t); - SkASSERT(this->ptr() == p); -} - -NullValue::NullValue() { - this->init_tagged(Tag::kNull); - SkASSERT(this->getTag() == Tag::kNull); -} - -BoolValue::BoolValue(bool b) { - this->init_tagged(Tag::kBool); - *this->cast() = b; - SkASSERT(this->getTag() == Tag::kBool); -} - -NumberValue::NumberValue(int32_t i) { - this->init_tagged(Tag::kInt); - *this->cast() = i; - SkASSERT(this->getTag() == Tag::kInt); -} - -NumberValue::NumberValue(float f) { - this->init_tagged(Tag::kFloat); - *this->cast() = f; - SkASSERT(this->getTag() == Tag::kFloat); -} - -// Vector recs point to externally allocated slabs with the following layout: -// -// [size_t n] [REC_0] ... [REC_n-1] [optional extra trailing storage] -// -// Long strings use extra_alloc_size == 1 to store the \0 terminator. -// -template -static void* MakeVector(const void* src, size_t size, SkArenaAlloc& alloc) { - // The Ts are already in memory, so their size should be safe. - const auto total_size = sizeof(size_t) + size * sizeof(T) + extra_alloc_size; - auto* size_ptr = reinterpret_cast(alloc.makeBytesAlignedTo(total_size, kRecAlign)); - - *size_ptr = size; - sk_careful_memcpy(size_ptr + 1, src, size * sizeof(T)); - - return size_ptr; -} - -ArrayValue::ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc) { - this->init_tagged_pointer(Tag::kArray, MakeVector(src, size, alloc)); - SkASSERT(this->getTag() == Tag::kArray); -} - -// Strings have two flavors: -// -// -- short strings (len <= 7) -> these are stored inline, in the record -// (one byte reserved for null terminator/type): -// -// [str] [\0]|[max_len - actual_len] -// -// Storing [max_len - actual_len] allows the 'len' field to double-up as a -// null terminator when size == max_len (this works 'cause kShortString == 0). -// -// -- long strings (len > 7) -> these are externally allocated vectors (VectorRec). -// -// The string data plus a null-char terminator are copied over. -// -namespace { - -// An internal string builder with a fast 8 byte short string load path -// (for the common case where the string is not at the end of the stream). -class FastString final : public Value { -public: - FastString(const char* src, size_t size, const char* eos, SkArenaAlloc& alloc) { - SkASSERT(src <= eos); - - if (size > kMaxInlineStringSize) { - this->initLongString(src, size, alloc); - SkASSERT(this->getTag() == Tag::kString); - return; - } - - static_assert(static_cast(Tag::kShortString) == 0, "please don't break this"); - static_assert(sizeof(Value) == 8, ""); - - // TODO: LIKELY - if (src + 7 <= eos) { - this->initFastShortString(src, size); - } else { - this->initShortString(src, size); - } - - SkASSERT(this->getTag() == Tag::kShortString); - } - -private: - static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 1; - - void initLongString(const char* src, size_t size, SkArenaAlloc& alloc) { - SkASSERT(size > kMaxInlineStringSize); - - this->init_tagged_pointer(Tag::kString, MakeVector(src, size, alloc)); - - auto* data = this->cast>()->begin(); - const_cast(data)[size] = '\0'; - } - - void initShortString(const char* src, size_t size) { - SkASSERT(size <= kMaxInlineStringSize); - - this->init_tagged(Tag::kShortString); - sk_careful_memcpy(this->cast(), src, size); - // Null terminator provided by init_tagged() above (fData8 is zero-initialized). - } - - void initFastShortString(const char* src, size_t size) { - SkASSERT(size <= kMaxInlineStringSize); - - // Load 8 chars and mask out the tag and \0 terminator. - uint64_t* s64 = this->cast(); - memcpy(s64, src, 8); - -#if defined(SK_CPU_LENDIAN) - *s64 &= 0x00ffffffffffffffULL >> ((kMaxInlineStringSize - size) * 8); -#else - static_assert(false, "Big-endian builds are not supported at this time."); -#endif - } -}; - -} // namespace - -StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) { - new (this) FastString(src, size, src, alloc); -} - -ObjectValue::ObjectValue(const Member* src, size_t size, SkArenaAlloc& alloc) { - this->init_tagged_pointer(Tag::kObject, MakeVector(src, size, alloc)); - SkASSERT(this->getTag() == Tag::kObject); -} - - -// Boring public Value glue. - -const Value& ObjectValue::operator[](const char* key) const { - // Reverse search for duplicates resolution (policy: return last). - const auto* begin = this->begin(); - const auto* member = this->end(); - - while (member > begin) { - --member; - if (0 == strcmp(key, member->fKey.as().begin())) { - return member->fValue; - } - } - - static const Value g_null = NullValue(); - return g_null; -} - -namespace { - -// Lexer/parser inspired by rapidjson [1], sajson [2] and pjson [3]. -// -// [1] https://github.com/Tencent/rapidjson/ -// [2] https://github.com/chadaustin/sajson -// [3] https://pastebin.com/hnhSTL3h - - -// bit 0 (0x01) - plain ASCII string character -// bit 1 (0x02) - whitespace -// bit 2 (0x04) - string terminator (" \0 [control chars] **AND } ]** <- see matchString notes) -// bit 3 (0x08) - 0-9 -// bit 4 (0x10) - 0-9 e E . -// bit 5 (0x20) - scope terminator (} ]) -static constexpr uint8_t g_token_flags[256] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 6, 4, 4, // 0 - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 1 - 3, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x11,1, // 2 - 0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19, 0x19,0x19, 1, 1, 1, 1, 1, 1, // 3 - 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,0x25, 1, 1, // 5 - 1, 1, 1, 1, 1, 0x11,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,0x25, 1, 1, // 7 - - // 128-255 - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 -}; - -static inline bool is_ws(char c) { return g_token_flags[static_cast(c)] & 0x02; } -static inline bool is_eostring(char c) { return g_token_flags[static_cast(c)] & 0x04; } -static inline bool is_digit(char c) { return g_token_flags[static_cast(c)] & 0x08; } -static inline bool is_numeric(char c) { return g_token_flags[static_cast(c)] & 0x10; } -static inline bool is_eoscope(char c) { return g_token_flags[static_cast(c)] & 0x20; } - -static inline const char* skip_ws(const char* p) { - while (is_ws(*p)) ++p; - return p; -} - -static inline float pow10(int32_t exp) { - static constexpr float g_pow10_table[63] = - { - 1.e-031f, 1.e-030f, 1.e-029f, 1.e-028f, 1.e-027f, 1.e-026f, 1.e-025f, 1.e-024f, - 1.e-023f, 1.e-022f, 1.e-021f, 1.e-020f, 1.e-019f, 1.e-018f, 1.e-017f, 1.e-016f, - 1.e-015f, 1.e-014f, 1.e-013f, 1.e-012f, 1.e-011f, 1.e-010f, 1.e-009f, 1.e-008f, - 1.e-007f, 1.e-006f, 1.e-005f, 1.e-004f, 1.e-003f, 1.e-002f, 1.e-001f, 1.e+000f, - 1.e+001f, 1.e+002f, 1.e+003f, 1.e+004f, 1.e+005f, 1.e+006f, 1.e+007f, 1.e+008f, - 1.e+009f, 1.e+010f, 1.e+011f, 1.e+012f, 1.e+013f, 1.e+014f, 1.e+015f, 1.e+016f, - 1.e+017f, 1.e+018f, 1.e+019f, 1.e+020f, 1.e+021f, 1.e+022f, 1.e+023f, 1.e+024f, - 1.e+025f, 1.e+026f, 1.e+027f, 1.e+028f, 1.e+029f, 1.e+030f, 1.e+031f - }; - - static constexpr int32_t k_exp_offset = SK_ARRAY_COUNT(g_pow10_table) / 2; - - // We only support negative exponents for now. - SkASSERT(exp <= 0); - - return (exp >= -k_exp_offset) ? g_pow10_table[exp + k_exp_offset] - : std::pow(10.0f, static_cast(exp)); -} - -class DOMParser { -public: - explicit DOMParser(SkArenaAlloc& alloc) - : fAlloc(alloc) { - - fValueStack.reserve(kValueStackReserve); - fScopeStack.reserve(kScopeStackReserve); - } - - const Value parse(const char* p, size_t size) { - if (!size) { - return this->error(NullValue(), p, "invalid empty input"); - } - - const char* p_stop = p + size - 1; - - // We're only checking for end-of-stream on object/array close('}',']'), - // so we must trim any whitespace from the buffer tail. - while (p_stop > p && is_ws(*p_stop)) --p_stop; - - SkASSERT(p_stop >= p && p_stop < p + size); - if (!is_eoscope(*p_stop)) { - return this->error(NullValue(), p_stop, "invalid top-level value"); - } - - p = skip_ws(p); - - switch (*p) { - case '{': - goto match_object; - case '[': - goto match_array; - default: - return this->error(NullValue(), p, "invalid top-level value"); - } - - match_object: - SkASSERT(*p == '{'); - p = skip_ws(p + 1); - - this->pushObjectScope(); - - if (*p == '}') goto pop_object; - - // goto match_object_key; - match_object_key: - p = skip_ws(p); - if (*p != '"') return this->error(NullValue(), p, "expected object key"); - - p = this->matchString(p, p_stop, [this](const char* key, size_t size, const char* eos) { - this->pushObjectKey(key, size, eos); - }); - if (!p) return NullValue(); - - p = skip_ws(p); - if (*p != ':') return this->error(NullValue(), p, "expected ':' separator"); - - ++p; - - // goto match_value; - match_value: - p = skip_ws(p); - - switch (*p) { - case '\0': - return this->error(NullValue(), p, "unexpected input end"); - case '"': - p = this->matchString(p, p_stop, [this](const char* str, size_t size, const char* eos) { - this->pushString(str, size, eos); - }); - break; - case '[': - goto match_array; - case 'f': - p = this->matchFalse(p); - break; - case 'n': - p = this->matchNull(p); - break; - case 't': - p = this->matchTrue(p); - break; - case '{': - goto match_object; - default: - p = this->matchNumber(p); - break; - } - - if (!p) return NullValue(); - - // goto match_post_value; - match_post_value: - SkASSERT(!fScopeStack.empty()); - - p = skip_ws(p); - switch (*p) { - case ',': - ++p; - if (fScopeStack.back() >= 0) { - goto match_object_key; - } else { - goto match_value; - } - case ']': - goto pop_array; - case '}': - goto pop_object; - default: - return this->error(NullValue(), p - 1, "unexpected value-trailing token"); - } - - // unreachable - SkASSERT(false); - - pop_object: - SkASSERT(*p == '}'); - - if (fScopeStack.back() < 0) { - return this->error(NullValue(), p, "unexpected object terminator"); - } - - this->popObjectScope(); - - // goto pop_common - pop_common: - SkASSERT(is_eoscope(*p)); - - if (fScopeStack.empty()) { - SkASSERT(fValueStack.size() == 1); - - // Success condition: parsed the top level element and reached the stop token. - return p == p_stop - ? fValueStack.front() - : this->error(NullValue(), p + 1, "trailing root garbage"); - } - - if (p == p_stop) { - return this->error(NullValue(), p, "unexpected end-of-input"); - } - - ++p; - - goto match_post_value; - - match_array: - SkASSERT(*p == '['); - p = skip_ws(p + 1); - - this->pushArrayScope(); - - if (*p != ']') goto match_value; - - // goto pop_array; - pop_array: - SkASSERT(*p == ']'); - - if (fScopeStack.back() >= 0) { - return this->error(NullValue(), p, "unexpected array terminator"); - } - - this->popArrayScope(); - - goto pop_common; - - SkASSERT(false); - return NullValue(); - } - - std::tuple getError() const { - return std::make_tuple(fErrorToken, fErrorMessage); - } - -private: - SkArenaAlloc& fAlloc; - - static constexpr size_t kValueStackReserve = 256; - static constexpr size_t kScopeStackReserve = 128; - std::vector fValueStack; - std::vector fScopeStack; - - const char* fErrorToken = nullptr; - SkString fErrorMessage; - - template - void popScopeAsVec(size_t scope_start) { - SkASSERT(scope_start > 0); - SkASSERT(scope_start <= fValueStack.size()); - - using T = typename VectorT::ValueT; - static_assert( sizeof(T) >= sizeof(Value), ""); - static_assert( sizeof(T) % sizeof(Value) == 0, ""); - static_assert(alignof(T) == alignof(Value), ""); - - const auto scope_count = fValueStack.size() - scope_start, - count = scope_count / (sizeof(T) / sizeof(Value)); - SkASSERT(scope_count % (sizeof(T) / sizeof(Value)) == 0); - - const auto* begin = reinterpret_cast(fValueStack.data() + scope_start); - - // Instantiate the placeholder value added in onPush{Object/Array}. - fValueStack[scope_start - 1] = VectorT(begin, count, fAlloc); - - // Drop the current scope. - fScopeStack.pop_back(); - fValueStack.resize(scope_start); - } - - void pushObjectScope() { - // Object placeholder. - fValueStack.emplace_back(); - - // Object scope marker (size). - fScopeStack.push_back(SkTo(fValueStack.size())); - } - - void popObjectScope() { - const auto scope_start = fScopeStack.back(); - SkASSERT(scope_start > 0); - this->popScopeAsVec(SkTo(scope_start)); - - SkDEBUGCODE( - const auto& obj = fValueStack.back().as(); - SkASSERT(obj.is()); - for (const auto& member : obj) { - SkASSERT(member.fKey.is()); - } - ) - } - - void pushArrayScope() { - // Array placeholder. - fValueStack.emplace_back(); - - // Array scope marker (-size). - fScopeStack.push_back(-SkTo(fValueStack.size())); - } - - void popArrayScope() { - const auto scope_start = -fScopeStack.back(); - SkASSERT(scope_start > 0); - this->popScopeAsVec(SkTo(scope_start)); - - SkDEBUGCODE( - const auto& arr = fValueStack.back().as(); - SkASSERT(arr.is()); - ) - } - - void pushObjectKey(const char* key, size_t size, const char* eos) { - SkASSERT(fScopeStack.back() >= 0); - SkASSERT(fValueStack.size() >= SkTo(fScopeStack.back())); - SkASSERT(!((fValueStack.size() - SkTo(fScopeStack.back())) & 1)); - this->pushString(key, size, eos); - } - - void pushTrue() { - fValueStack.push_back(BoolValue(true)); - } - - void pushFalse() { - fValueStack.push_back(BoolValue(false)); - } - - void pushNull() { - fValueStack.push_back(NullValue()); - } - - void pushString(const char* s, size_t size, const char* eos) { - fValueStack.push_back(FastString(s, size, eos, fAlloc)); - } - - void pushInt32(int32_t i) { - fValueStack.push_back(NumberValue(i)); - } - - void pushFloat(float f) { - fValueStack.push_back(NumberValue(f)); - } - - template - T error(T&& ret_val, const char* p, const char* msg) { -#if defined(SK_JSON_REPORT_ERRORS) - fErrorToken = p; - fErrorMessage.set(msg); -#endif - return ret_val; - } - - const char* matchTrue(const char* p) { - SkASSERT(p[0] == 't'); - - if (p[1] == 'r' && p[2] == 'u' && p[3] == 'e') { - this->pushTrue(); - return p + 4; - } - - return this->error(nullptr, p, "invalid token"); - } - - const char* matchFalse(const char* p) { - SkASSERT(p[0] == 'f'); - - if (p[1] == 'a' && p[2] == 'l' && p[3] == 's' && p[4] == 'e') { - this->pushFalse(); - return p + 5; - } - - return this->error(nullptr, p, "invalid token"); - } - - const char* matchNull(const char* p) { - SkASSERT(p[0] == 'n'); - - if (p[1] == 'u' && p[2] == 'l' && p[3] == 'l') { - this->pushNull(); - return p + 4; - } - - return this->error(nullptr, p, "invalid token"); - } - - template - const char* matchString(const char* p, const char* p_stop, MatchFunc&& func) { - SkASSERT(*p == '"'); - const auto* s_begin = p + 1; - - // TODO: unescape - - do { - // Consume string chars. - for (p = p + 1; !is_eostring(*p); ++p); - - if (*p == '"') { - // Valid string found. - func(s_begin, p - s_begin, p_stop); - return p + 1; - } - - // End-of-scope chars are special: we use them to tag the end of the input. - // Thus they cannot be consumed indiscriminately -- we need to check if we hit the - // end of the input. To that effect, we treat them as string terminators above, - // then we catch them here. - } while (is_eoscope(*p) && (p != p_stop)); // Safe scope terminator char, keep going. - - // Premature end-of-input, or illegal string char. - return this->error(nullptr, s_begin - 1, "invalid string"); - } - - const char* matchFastFloatDecimalPart(const char* p, int sign, float f, int exp) { - SkASSERT(exp <= 0); - - for (;;) { - if (!is_digit(*p)) break; - f = f * 10.f + (*p++ - '0'); --exp; - if (!is_digit(*p)) break; - f = f * 10.f + (*p++ - '0'); --exp; - } - - if (is_numeric(*p)) { - SkASSERT(*p == '.' || *p == 'e' || *p == 'E'); - // We either have malformed input, or an (unsupported) exponent. - return nullptr; - } - - this->pushFloat(sign * f * pow10(exp)); - - return p; - } - - const char* matchFastFloatPart(const char* p, int sign, float f) { - for (;;) { - if (!is_digit(*p)) break; - f = f * 10.f + (*p++ - '0'); - if (!is_digit(*p)) break; - f = f * 10.f + (*p++ - '0'); - } - - if (!is_numeric(*p)) { - // Matched (integral) float. - this->pushFloat(sign * f); - return p; - } - - return (*p == '.') ? this->matchFastFloatDecimalPart(p + 1, sign, f, 0) - : nullptr; - } - - const char* matchFast32OrFloat(const char* p) { - int sign = 1; - if (*p == '-') { - sign = -1; - ++p; - } - - const auto* digits_start = p; - - int32_t n32 = 0; - - // This is the largest absolute int32 value we can handle before - // risking overflow *on the next digit* (214748363). - static constexpr int32_t kMaxInt32 = (std::numeric_limits::max() - 9) / 10; - - if (is_digit(*p)) { - n32 = (*p++ - '0'); - for (;;) { - if (!is_digit(*p) || n32 > kMaxInt32) break; - n32 = n32 * 10 + (*p++ - '0'); - } - } - - if (!is_numeric(*p)) { - // Did we actually match any digits? - if (p > digits_start) { - this->pushInt32(sign * n32); - return p; - } - return nullptr; - } - - if (*p == '.') { - const auto* decimals_start = ++p; - - int exp = 0; - - for (;;) { - if (!is_digit(*p) || n32 > kMaxInt32) break; - n32 = n32 * 10 + (*p++ - '0'); --exp; - if (!is_digit(*p) || n32 > kMaxInt32) break; - n32 = n32 * 10 + (*p++ - '0'); --exp; - } - - if (!is_numeric(*p)) { - // Did we actually match any digits? - if (p > decimals_start) { - this->pushFloat(sign * n32 * pow10(exp)); - return p; - } - return nullptr; - } - - if (n32 > kMaxInt32) { - // we ran out on n32 bits - return this->matchFastFloatDecimalPart(p, sign, n32, exp); - } - } - - return this->matchFastFloatPart(p, sign, n32); - } - - const char* matchNumber(const char* p) { - if (const auto* fast = this->matchFast32OrFloat(p)) return fast; - - // slow fallback - char* matched; - float f = strtof(p, &matched); - if (matched > p) { - this->pushFloat(f); - return matched; - } - return this->error(nullptr, p, "invalid numeric token"); - } -}; - -void Write(const Value& v, SkWStream* stream) { - switch (v.getType()) { - case Value::Type::kNull: - stream->writeText("null"); - break; - case Value::Type::kBool: - stream->writeText(*v.as() ? "true" : "false"); - break; - case Value::Type::kNumber: - stream->writeScalarAsText(*v.as()); - break; - case Value::Type::kString: - stream->writeText("\""); - stream->writeText(v.as().begin()); - stream->writeText("\""); - break; - case Value::Type::kArray: { - const auto& array = v.as(); - stream->writeText("["); - bool first_value = true; - for (const auto& v : array) { - if (!first_value) stream->writeText(","); - Write(v, stream); - first_value = false; - } - stream->writeText("]"); - break; - } - case Value::Type::kObject: - const auto& object = v.as(); - stream->writeText("{"); - bool first_member = true; - for (const auto& member : object) { - SkASSERT(member.fKey.getType() == Value::Type::kString); - if (!first_member) stream->writeText(","); - Write(member.fKey, stream); - stream->writeText(":"); - Write(member.fValue, stream); - first_member = false; - } - stream->writeText("}"); - break; - } -} - -} // namespace - -SkString Value::toString() const { - SkDynamicMemoryWStream wstream; - Write(*this, &wstream); - const auto data = wstream.detachAsData(); - // TODO: is there a better way to pass data around without copying? - return SkString(static_cast(data->data()), data->size()); -} - -static constexpr size_t kMinChunkSize = 4096; - -DOM::DOM(const char* data, size_t size) - : fAlloc(kMinChunkSize) { - DOMParser parser(fAlloc); - - fRoot = parser.parse(data, size); -} - -void DOM::write(SkWStream* stream) const { - Write(fRoot, stream); -} - -} // namespace skjson diff --git a/modules/skjson/src/SkJSONBench.cpp b/modules/skjson/src/SkJSONBench.cpp deleted file mode 100644 index 4ef1d6c01b..0000000000 --- a/modules/skjson/src/SkJSONBench.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Benchmark.h" -#include "SkData.h" -#include "SkJSON.h" -#include "SkStream.h" - -#if defined(SK_BUILD_FOR_ANDROID) -static constexpr const char* kBenchFile = "/data/local/tmp/bench.json"; -#else -static constexpr const char* kBenchFile = "/tmp/bench.json"; -#endif - -class JsonBench : public Benchmark { -public: - -protected: - const char* onGetName() override { return "json_skjson"; } - - bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; } - - void onPerCanvasPreDraw(SkCanvas*) override { - fData = SkData::MakeFromFileName(kBenchFile); - if (!fData) { - SkDebugf("!! Could not open bench file: %s\n", kBenchFile); - } - } - - void onPerCanvasPostDraw(SkCanvas*) override { - fData = nullptr; - } - - void onDraw(int loops, SkCanvas*) override { - if (!fData) return; - - for (int i = 0; i < loops; i++) { - skjson::DOM dom(static_cast(fData->data()), fData->size()); - if (dom.root().is()) { - SkDebugf("!! Parsing failed.\n"); - return; - } - } - } - -private: - sk_sp fData; - - using INHERITED = Benchmark; -}; - -DEF_BENCH( return new JsonBench; ) - -#if (0) - -#include "rapidjson/document.h" - -class RapidJsonBench : public Benchmark { -public: - -protected: - const char* onGetName() override { return "json_rapidjson"; } - - bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; } - - void onPerCanvasPreDraw(SkCanvas*) override { - if (auto stream = SkStream::MakeFromFile(kBenchFile)) { - SkASSERT(stream->hasLength()); - fCStringData = SkData::MakeUninitialized(stream->getLength() + 1); - auto* data8 = reinterpret_cast(fCStringData->writable_data()); - SkAssertResult(stream->read(data8, stream->getLength()) == stream->getLength()); - data8[stream->getLength()] = '\0'; - - } else { - SkDebugf("!! Could not open bench file: %s\n", kBenchFile); - } - } - - void onPerCanvasPostDraw(SkCanvas*) override { - fCStringData = nullptr; - } - - void onDraw(int loops, SkCanvas*) override { - if (!fCStringData) return; - - for (int i = 0; i < loops; i++) { - rapidjson::Document doc; - doc.Parse(static_cast(fCStringData->data())); - if (doc.HasParseError()) { - SkDebugf("!! Parsing failed.\n"); - return; - } - } - } - -private: - sk_sp fCStringData; - - using INHERITED = Benchmark; -}; - -DEF_BENCH( return new RapidJsonBench; ) - -#endif - -#if (0) - -#include "pjson.h" - -class PJsonBench : public Benchmark { -public: - -protected: - const char* onGetName() override { return "json_pjson"; } - - bool isSuitableFor(Backend backend) override { return backend == kNonRendering_Backend; } - - void onPerCanvasPreDraw(SkCanvas*) override { - if (auto stream = SkStream::MakeFromFile(kBenchFile)) { - SkASSERT(stream->hasLength()); - fCStringData = SkData::MakeUninitialized(stream->getLength() + 1); - auto* data8 = reinterpret_cast(fCStringData->writable_data()); - SkAssertResult(stream->read(data8, stream->getLength()) == stream->getLength()); - data8[stream->getLength()] = '\0'; - - } else { - SkDebugf("!! Could not open bench file: %s\n", kBenchFile); - } - } - - void onPerCanvasPostDraw(SkCanvas*) override { - fCStringData = nullptr; - } - - void onDraw(int loops, SkCanvas*) override { - if (!fCStringData) return; - - for (int i = 0; i < loops; i++) { - // Copy needed for in-place operation. - auto data = SkData::MakeWithCopy(fCStringData->data(), fCStringData->size()); - pjson::document doc; - if (!doc.deserialize_in_place(static_cast(data->writable_data()))) { - SkDebugf("!! Parsing failed.\n"); - return; - } - } - } - -private: - sk_sp fCStringData; - - using INHERITED = Benchmark; -}; - -DEF_BENCH( return new PJsonBench; ) - -#endif diff --git a/modules/skjson/src/SkJSONTest.cpp b/modules/skjson/src/SkJSONTest.cpp deleted file mode 100644 index df04ac3529..0000000000 --- a/modules/skjson/src/SkJSONTest.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "Test.h" - -#include "SkArenaAlloc.h" -#include "SkJSON.h" -#include "SkString.h" -#include "SkStream.h" - -using namespace skjson; - -DEF_TEST(SkJSON_Parse, reporter) { - static constexpr struct { - const char* in; - const char* out; - } g_tests[] = { - { "" , nullptr }, - { "[" , nullptr }, - { "]" , nullptr }, - { "[[]" , nullptr }, - { "[]]" , nullptr }, - { "[]f" , nullptr }, - { "{" , nullptr }, - { "}" , nullptr }, - { "{{}" , nullptr }, - { "{}}" , nullptr }, - { "{}f" , nullptr }, - { "{]" , nullptr }, - { "[}" , nullptr }, - { "{\"}" , nullptr }, - { "[\"]" , nullptr }, - { "1" , nullptr }, - { "true" , nullptr }, - { "false", nullptr }, - { "null" , nullptr }, - - { "[nulll]" , nullptr }, - { "[false2]", nullptr }, - { "[true:]" , nullptr }, - - { "[1 2]" , nullptr }, - { "[1,,2]" , nullptr }, - { "[1,2,]" , nullptr }, - { "[,1,2]" , nullptr }, - - { "[ \"foo" , nullptr }, - { "[ \"fo\0o\" ]" , nullptr }, - - { "{\"\":{}" , nullptr }, - { "{ null }" , nullptr }, - { "{ \"k\" : }" , nullptr }, - { "{ : null }" , nullptr }, - { "{ \"k\" : : null }" , nullptr }, - { "{ \"k\" : null , }" , nullptr }, - { "{ \"k\" : null \"k\" : 1 }", nullptr }, - - - { "[]" , "[]" }, - { " \n\r\t [ \n\r\t ] \n\r\t " , "[]" }, - { "[[]]" , "[[]]" }, - { "[ null ]" , "[null]" }, - { "[ true ]" , "[true]" }, - { "[ false ]" , "[false]" }, - { "[ 0 ]" , "[0]" }, - { "[ 1 ]" , "[1]" }, - { "[ 1.248 ]" , "[1.248]" }, - { "[ \"\" ]" , "[\"\"]" }, - { "[ \"foo{bar}baz\" ]" , "[\"foo{bar}baz\"]" }, - { "[ \" f o o \" ]" , "[\" f o o \"]" }, - { "[ \"123456\" ]" , "[\"123456\"]" }, - { "[ \"1234567\" ]" , "[\"1234567\"]" }, - { "[ \"12345678\" ]" , "[\"12345678\"]" }, - { "[ \"123456789\" ]" , "[\"123456789\"]" }, - { "[ null , true, false,0,12.8 ]", "[null,true,false,0,12.8]" }, - - { "{}" , "{}" }, - { " \n\r\t { \n\r\t } \n\r\t " , "{}" }, - { "{ \"k\" : null }" , "{\"k\":null}" }, - { "{ \"foo{\" : \"bar}baz\" }" , "{\"foo{\":\"bar}baz\"}" }, - { "{ \"k1\" : null, \"k2 \":0 }", "{\"k1\":null,\"k2 \":0}" }, - { "{ \"k1\" : null, \"k1\":0 }" , "{\"k1\":null,\"k1\":0}" }, - - { "{ \"k1\" : null, \n\ - \"k2\" : 0, \n\ - \"k3\" : [ \n\ - true, \r\n\ - { \"kk1\" : \"foo\" , \n\ - \"kk2\" : \"bar\" , \n\ - \"kk3\" : 1.28 , \n\ - \"kk4\" : [ 42 ] \n\ - } , \n\ - \"boo\" , \n\ - null \n\ - ] \n\ - }", - "{\"k1\":null,\"k2\":0,\"k3\":[true," - "{\"kk1\":\"foo\",\"kk2\":\"bar\",\"kk3\":1.28,\"kk4\":[42]},\"boo\",null]}" }, - }; - - for (const auto& tst : g_tests) { - DOM dom(tst.in, strlen(tst.in)); - const auto success = !dom.root().is(); - REPORTER_ASSERT(reporter, success == (tst.out != nullptr)); - if (!success) continue; - - SkDynamicMemoryWStream str; - dom.write(&str); - str.write8('\0'); - - auto data = str.detachAsData(); - REPORTER_ASSERT(reporter, !strcmp(tst.out, static_cast(data->data()))); - } - -} - -template -static void check_primitive(skiatest::Reporter* reporter, const Value& v, T pv, - bool is_type) { - - REPORTER_ASSERT(reporter, v.is() == is_type); - const VT* cast_t = v; - REPORTER_ASSERT(reporter, (cast_t != nullptr) == is_type); - - if (is_type) { - REPORTER_ASSERT(reporter, &v.as() == cast_t); - REPORTER_ASSERT(reporter, *v.as() == pv); - } -} - -template -static void check_vector(skiatest::Reporter* reporter, const Value& v, size_t expected_size, - bool is_vector) { - REPORTER_ASSERT(reporter, v.is() == is_vector); - const T* cast_t = v; - REPORTER_ASSERT(reporter, (cast_t != nullptr) == is_vector); - - if (is_vector) { - const auto& vec = v.as(); - REPORTER_ASSERT(reporter, &vec == cast_t); - REPORTER_ASSERT(reporter, vec.size() == expected_size); - REPORTER_ASSERT(reporter, vec.begin() != nullptr); - REPORTER_ASSERT(reporter, vec.end() == vec.begin() + expected_size); - } -} - -static void check_string(skiatest::Reporter* reporter, const Value& v, const char* s) { - check_vector(reporter, v, s ? strlen(s) : 0, !!s); - if (s) { - REPORTER_ASSERT(reporter, !strcmp(v.as().begin(), s)); - } -} - -DEF_TEST(SkJSON_DOM_visit, reporter) { - static constexpr char json[] = "{ \n\ - \"k1\": null, \n\ - \"k2\": false, \n\ - \"k3\": true, \n\ - \"k4\": 42, \n\ - \"k5\": .75, \n\ - \"k6\": \"foo\", \n\ - \"k7\": [ 1, true, \"bar\" ], \n\ - \"k8\": { \"kk1\": 2, \"kk2\": false, \"kk1\": \"baz\" } \n\ - }"; - - DOM dom(json, strlen(json)); - - const auto& jroot = dom.root().as(); - REPORTER_ASSERT(reporter, jroot.is()); - - { - const auto& v = jroot["k1"]; - REPORTER_ASSERT(reporter, v.is()); - - check_primitive(reporter, v, false, false); - check_primitive(reporter, v, 0, false); - - check_string(reporter, v, nullptr); - check_vector(reporter, v, 0, false); - check_vector(reporter, v, 0, false); - } - - { - const auto& v = jroot["k2"]; - REPORTER_ASSERT(reporter, !v.is()); - - check_primitive(reporter, v, false, true); - check_primitive(reporter, v, 0, false); - - check_string(reporter, v, nullptr); - check_vector(reporter, v, 0, false); - check_vector(reporter, v, 0, false); - } - - { - const auto& v = jroot["k3"]; - REPORTER_ASSERT(reporter, !v.is()); - - check_primitive(reporter, v, true, true); - check_primitive(reporter, v, 0, false); - - check_string(reporter, v, nullptr); - check_vector(reporter, v, 0, false); - check_vector(reporter, v, 0, false); - } - - { - const auto& v = jroot["k4"]; - REPORTER_ASSERT(reporter, !v.is()); - - check_primitive(reporter, v, false, false); - check_primitive(reporter, v, 42, true); - - check_string(reporter, v, nullptr); - check_vector(reporter, v, 0, false); - check_vector(reporter, v, 0, false); - } - - { - const auto& v = jroot["k5"]; - REPORTER_ASSERT(reporter, !v.is()); - - check_primitive(reporter, v, false, false); - check_primitive(reporter, v, .75f, true); - - check_string(reporter, v, nullptr); - check_vector(reporter, v, 0, false); - check_vector(reporter, v, 0, false); - } - - { - const auto& v = jroot["k6"]; - REPORTER_ASSERT(reporter, !v.is()); - - check_primitive(reporter, v, false, false); - check_primitive(reporter, v, 0, false); - - check_string(reporter, v, "foo"); - check_vector(reporter, v, 0, false); - check_vector(reporter, v, 0, false); - } - - { - const auto& v = jroot["k7"]; - REPORTER_ASSERT(reporter, !v.is()); - - check_primitive(reporter, v, false, false); - check_primitive(reporter, v, 0, false); - - check_string(reporter, v, nullptr); - check_vector(reporter, v, 0, false); - - check_vector(reporter, v, 3, true); - check_primitive(reporter, v.as()[0], 1, true); - check_primitive(reporter, v.as()[1], true, true); - check_vector(reporter, v.as()[2], 3, true); - } - - { - const auto& v = jroot["k8"]; - REPORTER_ASSERT(reporter, !v.is()); - - check_primitive(reporter, v, false, false); - check_primitive(reporter, v, 0, false); - - check_string(reporter, v, nullptr); - check_vector(reporter, v, 0, false); - - check_vector(reporter, v, 3, true); - - const auto& m0 = v.as().begin()[0]; - check_string(reporter, m0.fKey, "kk1"); - check_primitive(reporter, m0.fValue, 2, true); - - const auto& m1 = v.as().begin()[1]; - check_string(reporter, m1.fKey, "kk2"); - check_primitive(reporter, m1.fValue, false, true); - - const auto& m2 = v.as().begin()[2]; - check_string(reporter, m2.fKey, "kk1"); - check_string(reporter, m2.fValue, "baz"); - - REPORTER_ASSERT(reporter, v.as()[""].is()); - REPORTER_ASSERT(reporter, v.as()["nosuchkey"].is()); - check_string(reporter, v.as()["kk1"], "baz"); - check_primitive(reporter, v.as()["kk2"], false, true); - } -} - -template -void check_value(skiatest::Reporter* reporter, const Value& v, const char* expected_string) { - REPORTER_ASSERT(reporter, v.is()); - - const T* cast_t = v; - REPORTER_ASSERT(reporter, cast_t == &v.as()); - - const auto vstr = v.toString(); - REPORTER_ASSERT(reporter, 0 == strcmp(expected_string, vstr.c_str())); -} - -DEF_TEST(SkJSON_DOM_build, reporter) { - SkArenaAlloc alloc(4096); - - const auto v0 = NullValue(); - check_value(reporter, v0, "null"); - - const auto v1 = BoolValue(true); - check_value(reporter, v1, "true"); - - const auto v2 = BoolValue(false); - check_value(reporter, v2, "false"); - - const auto v3 = NumberValue(0); - check_value(reporter, v3, "0"); - - const auto v4 = NumberValue(42); - check_value(reporter, v4, "42"); - - const auto v5 = NumberValue(42.75f); - check_value(reporter, v5, "42.75"); - - const auto v6 = StringValue(nullptr, 0, alloc); - check_value(reporter, v6, "\"\""); - - const auto v7 = StringValue(" foo ", 5, alloc); - check_value(reporter, v7, "\" foo \""); - - const auto v8 = StringValue(" foo bar baz ", 13, alloc); - check_value(reporter, v8, "\" foo bar baz \""); - - const auto v9 = ArrayValue(nullptr, 0, alloc); - check_value(reporter, v9, "[]"); - - const Value values0[] = { v0, v3, v9 }; - const auto v10 = ArrayValue(values0, SK_ARRAY_COUNT(values0), alloc); - check_value(reporter, v10, "[null,0,[]]"); - - const auto v11 = ObjectValue(nullptr, 0, alloc); - check_value(reporter, v11, "{}"); - - const Member members0[] = { - { StringValue("key_0", 5, alloc), v1 }, - { StringValue("key_1", 5, alloc), v4 }, - { StringValue("key_2", 5, alloc), v11 }, - }; - const auto v12 = ObjectValue(members0, SK_ARRAY_COUNT(members0), alloc); - check_value(reporter, v12, "{" - "\"key_0\":true," - "\"key_1\":42," - "\"key_2\":{}" - "}"); - - const Value values1[] = { v2, v6, v12 }; - const auto v13 = ArrayValue(values1, SK_ARRAY_COUNT(values1), alloc); - check_value(reporter, v13, "[" - "false," - "\"\"," - "{" - "\"key_0\":true," - "\"key_1\":42," - "\"key_2\":{}" - "}" - "]"); - - const Member members1[] = { - { StringValue("key_00", 6, alloc), v5 }, - { StringValue("key_01", 6, alloc), v7 }, - { StringValue("key_02", 6, alloc), v13 }, - }; - const auto v14 = ObjectValue(members1, SK_ARRAY_COUNT(members1), alloc); - check_value(reporter, v14, "{" - "\"key_00\":42.75," - "\"key_01\":\" foo \"," - "\"key_02\":[" - "false," - "\"\"," - "{" - "\"key_0\":true," - "\"key_1\":42," - "\"key_2\":{}" - "}" - "]" - "}"); -} diff --git a/modules/skottie/BUILD.gn b/modules/skottie/BUILD.gn index 9442592968..afa2769add 100644 --- a/modules/skottie/BUILD.gn +++ b/modules/skottie/BUILD.gn @@ -34,7 +34,6 @@ source_set("skottie") { configs += [ "../../:skia_private" ] deps = [ "../..:skia", - "../skjson", "../sksg", ] } -- cgit v1.2.3