SkJson

For now this is only wired to a bench and a couple of tests.

Local numbers, for a ~500KB BM "compressed" json:

    micros   	bench
   2456.54  	json_rapidjson	nonrendering
   1192.38  	json_skjson	nonrendering


Change-Id: I7b3514f84c7c525d1787722c43ad6095e3692563
Reviewed-on: https://skia-review.googlesource.com/127622
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skjson/include/SkJSON.h b/modules/skjson/include/SkJSON.h
new file mode 100644
index 0000000..4cb312c
--- /dev/null
+++ b/modules/skjson/include/SkJSON.h
@@ -0,0 +1,139 @@
+/*
+ * 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 "SkTypes.h"
+
+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 freely converted to any of the facade types for type-specific functionality.
+ *
+ *  Note: type checking is lazy/deferred, to facilitate chained property access - e.g.
+ *
+ *      if (!v.as<ObjectValue>()["foo"].as<ObjectValue>()["bar"].is<NullValue>())
+ *          LOG("found v.foo.bar!");
+ */
+class alignas(8) Value {
+public:
+    enum class Type {
+        kNull,
+        kBool,
+        kNumber,
+        kString,
+        kArray,
+        kObject,
+    };
+
+    /**
+     * @return    The public type of this record.
+     */
+    Type getType() const;
+
+    /**
+     * @return    True if the record matches the facade type T.
+     */
+    template <typename T>
+    bool is() const { return T::IsType(this->getType()); }
+
+    /**
+     * @return    The record cast as facade type T.
+     *
+     * Note: this is always safe, as proper typing is enforced in the facade methods.
+     */
+    template <typename T>
+    const T& as() const {
+        return *reinterpret_cast<const T*>(this->is<T>() ? this : &Value::Null());
+    }
+
+    /**
+     * @return    Null value singleton.
+     */
+    static const Value& Null();
+
+protected:
+    uint8_t   fData8[8];
+};
+
+class NullValue final : public Value {
+public:
+    static bool IsType(Value::Type t) { return t == Type::kNull; }
+};
+
+template <typename T, Value::Type vtype>
+class PrimitiveValue final : public Value {
+public:
+    static bool IsType(Value::Type t) { return t == vtype; }
+
+    T operator *() const;
+};
+
+template <typename T, Value::Type vtype>
+class VectorValue : public Value {
+public:
+    static bool IsType(Value::Type t) { return t == vtype; }
+
+    size_t size() const;
+
+    const T* begin() const;
+    const T*   end() const;
+
+    const T& operator[](size_t i) const {
+        return (i < this->size()) ? *(this->begin() + i) : T::Null();
+    }
+};
+
+using   BoolValue = PrimitiveValue<bool  , Value::Type::kBool  >;
+using NumberValue = PrimitiveValue<double, Value::Type::kNumber>;
+using StringValue =    VectorValue<char  , Value::Type::kString>;
+using  ArrayValue =    VectorValue<Value , Value::Type::kArray >;
+
+struct Member {
+    StringValue fKey;
+          Value fValue;
+
+    static const Member& Null();
+};
+
+class ObjectValue final : public VectorValue<Member, Value::Type::kObject> {
+public:
+    const Value& operator[](const char*) const;
+};
+
+class DOM final : public SkNoncopyable {
+public:
+    explicit DOM(const char*);
+
+    const Value& root() const { return *fRoot; }
+
+    void write(SkWStream*) const;
+
+private:
+    SkArenaAlloc fAlloc;
+    const Value* fRoot;
+};
+
+} // namespace skjson
+
+#endif // SkJSON_DEFINED
+