Convert bookmaker to SkJSON

Verified that running bookmaker produces an identical fiddle.json
before and after this change.

Bug: skia:
Change-Id: I1aa7477348a5f8c362201199b130508c2818116f
Reviewed-on: https://skia-review.googlesource.com/c/188303
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 7e98378..497587b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1698,7 +1698,6 @@
       ":flags",
       ":skia",
       ":tool_utils",
-      "//third_party/jsoncpp",
     ]
   }
 
diff --git a/tools/bookmaker/cataloger.cpp b/tools/bookmaker/cataloger.cpp
index cad7505..6156fcb 100644
--- a/tools/bookmaker/cataloger.cpp
+++ b/tools/bookmaker/cataloger.cpp
@@ -105,7 +105,7 @@
     fTextOut = false;
     fPngOut = true;
     JsonStatus* status = &fStack.back();
-    status->fIter = status->fObject.begin();
+    status->reset();
     fContinuation = false;
     return parseFiddles();
 }
diff --git a/tools/bookmaker/fiddleParser.cpp b/tools/bookmaker/fiddleParser.cpp
index 79c4a70..8e8eb0f5 100644
--- a/tools/bookmaker/fiddleParser.cpp
+++ b/tools/bookmaker/fiddleParser.cpp
@@ -24,52 +24,55 @@
         return false;
     }
     JsonStatus* status = &fStack.back();
-    while (status->fIter != status->fObject.end()) {
-        const char* blockName = status->fIter.memberName();
+    while (!status->atEnd()) {
+        const char* blockName = status->fObjectIter->fKey.begin();
         Definition* example = nullptr;
         string textString;
-        if (!status->fObject.isObject()) {
+        if (!status->fObject) {
             return report_error(blockName, "expected object");
         }
-        for (auto iter = status->fIter->begin(); status->fIter->end() != iter; ++iter) {
-            const char* memberName = iter.memberName();
+        const skjson::ObjectValue* obj = status->fObjectIter->fValue;
+        for (auto iter = obj->begin(); obj->end() != iter; ++iter) {
+            const char* memberName = iter->fKey.begin();
             if (!strcmp("compile_errors", memberName)) {
-                if (!iter->isArray()) {
+                if (!iter->fValue.is<skjson::ArrayValue>()) {
                     return report_error(blockName, "expected array");
                 }
-                if (iter->size()) {
+                if (iter->fValue.as<skjson::ArrayValue>().size()) {
                     return report_error(blockName, "fiddle compiler error");
                 }
                 continue;
             }
             if (!strcmp("runtime_error", memberName)) {
-                if (!iter->isString()) {
+                if (!iter->fValue.is<skjson::StringValue>()) {
                     return report_error(blockName, "expected string 1");
                 }
-                if (iter->asString().length()) {
+                if (iter->fValue.as<skjson::StringValue>().size()) {
                     return report_error(blockName, "fiddle runtime error");
                 }
                 continue;
             }
             if (!strcmp("fiddleHash", memberName)) {
-                if (!iter->isString()) {
+                const skjson::StringValue* sv = iter->fValue;
+                if (!sv) {
                     return report_error(blockName, "expected string 2");
                 }
                 example = this->findExample(blockName);
                 if (!example) {
                     return report_error(blockName, "missing example");
                 }
-                if (example->fHash.length() && example->fHash != iter->asString()) {
+                if (example->fHash.length() && example->fHash != sv->begin()) {
                     return example->reportError<bool>("mismatched hash");
                 }
-                example->fHash = iter->asString();
+                example->fHash = sv->begin();
                 continue;
             }
             if (!strcmp("text", memberName)) {
-                if (!iter->isString()) {
+                const skjson::StringValue* sv = iter->fValue;
+                if (!sv) {
                     return report_error(blockName, "expected string 3");
                 }
-                textString = iter->asString();
+                textString = sv->begin();
                 continue;
             }
             return report_error(blockName, "unexpected key");
@@ -86,7 +89,7 @@
         } else if (fPngOut && !this->pngOut(example)) {
             return false;
         }
-        status->fIter++;
+        status->advance();
     }
     return true;
 }
diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp
index 73a8015..48e7c5f 100644
--- a/tools/bookmaker/includeParser.cpp
+++ b/tools/bookmaker/includeParser.cpp
@@ -10,6 +10,7 @@
 
 #include "bmhParser.h"
 #include "includeParser.h"
+#include <map>
 
 const IncludeKey kKeyWords[] = {
     { "",           KeyWord::kNone,         KeyProperty::kNone           },
diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp
index 3e16b06..366135f 100644
--- a/tools/bookmaker/mdOut.cpp
+++ b/tools/bookmaker/mdOut.cpp
@@ -11,6 +11,7 @@
 
 #include "SkOSFile.h"
 #include "SkOSPath.h"
+#include <map>
 
 class SubtopicKeys {
 public:
diff --git a/tools/bookmaker/parserCommon.cpp b/tools/bookmaker/parserCommon.cpp
index 271551b..5a8867a 100644
--- a/tools/bookmaker/parserCommon.cpp
+++ b/tools/bookmaker/parserCommon.cpp
@@ -414,7 +414,7 @@
 };
 
 string StatusIter::baseDir() {
-    SkASSERT(fStack.back().fObject.isArray());
+    SkASSERT(fStack.back().fArray);
     SkASSERT(fStack.size() > 2);
     string dir;
     for (unsigned index = 2; index < fStack.size(); ++index) {
@@ -438,7 +438,7 @@
                 return false;
             }
             status = &fStack.back();
-            if (status->fIter != status->fObject.end()) {
+            if (!status->atEnd()) {
                 break;
             }
             fStack.pop_back();
@@ -446,8 +446,9 @@
         if (1 == fStack.size()) {
             do {
                 blockType = StatusFilter::kUnknown;
+                SkASSERT(status->fObject);
                 for (unsigned index = 0; index < SK_ARRAY_COUNT(block_names); ++index) {
-                    if (status->fIter.key().asString() == block_names[index]) {
+                    if (!strcmp(status->fObjectIter->fKey.begin(), block_names[index])) {
                         blockType = (StatusFilter) index;
                         break;
                     }
@@ -455,34 +456,35 @@
                 if (blockType <= fFilter) {
                     break;
                 }
-                status->fIter++;
-            } while (status->fIter != status->fObject.end());
-            if (status->fIter == status->fObject.end()) {
+                status->advance();
+            } while (!status->atEnd());
+            if (status->atEnd()) {
                 continue;
             }
         }
-        if (!status->fObject.isArray()) {
-            SkASSERT(status->fIter != status->fObject.end());
-            JsonStatus block = {
-                *status->fIter,
-                status->fIter->begin(),
-                status->fIter.key().asString(),
-                blockType
-            };
+        if (!status->fArray) {
+            SkASSERT(status->fObjectIter != status->fObject->end());
+            JsonStatus block = JsonStatus::Make(status->current(),
+                                                status->fObjectIter->fKey.begin(),
+                                                blockType);
             fStack.emplace_back(block);
             status = &(&fStack.back())[-1];
-            status->fIter++;
+            status->advance();
             status = &fStack.back();
             continue;
         }
-        str = status->fIter->asString();
+        if (const skjson::StringValue* sv = status->current()) {
+            str = sv->begin();
+        } else {
+            str = status->current().toString().c_str();
+        }
         if (strPtr) {
             *strPtr = str;
         }
         if (filter) {
             *filter = status->fStatusFilter;
         }
-        status->fIter++;
+        status->advance();
         if (str.length() - strlen(fSuffix) == str.find(fSuffix)) {
             return true;
         }
@@ -496,14 +498,12 @@
         SkDebugf("file %s:\n", path);
         return this->reportError<bool>("file not readable");
     }
-    Json::Reader reader;
-    const char* data = (const char*)json->data();
-    if (!reader.parse(data, data + json->size(), fRoot)) {
+    fDom.reset(new skjson::DOM((const char*)json->data(), json->size()));
+    if (!fDom->root().is<skjson::ObjectValue>()) {
         SkDebugf("file %s:\n", path);
         return this->reportError<bool>("file not parsable");
     }
-    JsonStatus block = { fRoot, fRoot.begin(), "", StatusFilter::kUnknown };
+    JsonStatus block = JsonStatus::Make(fDom->root(), "", StatusFilter::kUnknown);
     fStack.emplace_back(block);
     return true;
 }
-
diff --git a/tools/bookmaker/parserCommon.h b/tools/bookmaker/parserCommon.h
index f31cbd6..ecf5c2f 100644
--- a/tools/bookmaker/parserCommon.h
+++ b/tools/bookmaker/parserCommon.h
@@ -9,11 +9,13 @@
 #define parserCommon_DEFINED
 
 #include "SkData.h"
-#include "SkJSONCPP.h"
+#include "SkJSON.h"
 
 #include "definition.h"
 #include "textParser.h"
 
+#include <memory>
+
 enum class StatusFilter {
     kCompleted,
     kInProgress,
@@ -255,10 +257,50 @@
 };
 
 struct JsonStatus {
-    const Json::Value& fObject;
-    Json::Value::iterator fIter;
+    const skjson::ArrayValue* fArray;
+    const skjson::Value* fArrayIter;
+    const skjson::ObjectValue* fObject;
+    const skjson::Member* fObjectIter;
     string fName;
     StatusFilter fStatusFilter;
+
+    static JsonStatus Make(const skjson::Value& value, string name, StatusFilter filter) {
+        JsonStatus status = { value, nullptr, value, nullptr, name, filter };
+        status.reset();
+        return status;
+    }
+
+    void reset() {
+        fArrayIter = fArray ? fArray->begin() : nullptr;
+        fObjectIter = fObject ? fObject->begin() : nullptr;
+    }
+
+    bool atEnd() const {
+        if (fArray) {
+            return fArrayIter == fArray->end();
+        } else if (fObject) {
+            return fObjectIter == fObject->end();
+        } else {
+            return true;
+        }
+    }
+
+    void advance() {
+        if (fArrayIter) {
+            fArrayIter++;
+        } else if (fObjectIter) {
+            fObjectIter++;
+        }
+    }
+
+    const skjson::Value& current() const {
+        if (fArrayIter) {
+            return *fArrayIter;
+        } else {
+            SkASSERT(fObjectIter);
+            return fObjectIter->fValue;
+        }
+    }
 };
 
 class JsonCommon : public ParserCommon {
@@ -272,7 +314,7 @@
     }
 
     vector<JsonStatus> fStack;
-    Json::Value fRoot;
+    std::unique_ptr<skjson::DOM> fDom;
 private:
     typedef ParserCommon INHERITED;
 };