summaryrefslogtreecommitdiff
path: root/utils/SkJSON.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/SkJSON.cpp')
-rw-r--r--utils/SkJSON.cpp634
1 files changed, 634 insertions, 0 deletions
diff --git a/utils/SkJSON.cpp b/utils/SkJSON.cpp
new file mode 100644
index 00000000..9b122082
--- /dev/null
+++ b/utils/SkJSON.cpp
@@ -0,0 +1,634 @@
+/*
+ * Copyright 2011 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 "SkString.h"
+
+#ifdef SK_DEBUG
+// #define TRACE_SKJSON_LEAKS
+#endif
+
+#ifdef TRACE_SKJSON_LEAKS
+ static int gStringCount;
+ static int gSlotCount;
+ static int gObjectCount;
+ static int gArrayCount;
+ #define LEAK_CODE(code) code
+#else
+ #define LEAK_CODE(code)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static char* alloc_string(size_t len) {
+ LEAK_CODE(SkDebugf(" string[%d]\n", gStringCount++);)
+ char* str = (char*)sk_malloc_throw(len + 1);
+ str[len] = 0;
+ return str;
+}
+
+static char* dup_string(const char src[]) {
+ if (NULL == src) {
+ return NULL;
+ }
+ size_t len = strlen(src);
+ char* dst = alloc_string(len);
+ memcpy(dst, src, len);
+ return dst;
+}
+
+static void free_string(char* str) {
+ if (str) {
+ sk_free(str);
+ LEAK_CODE(SkASSERT(gStringCount > 0); SkDebugf("~string[%d]\n", --gStringCount);)
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct SkJSON::Object::Slot {
+ Slot(const char name[], Type type) {
+ LEAK_CODE(SkDebugf(" slot[%d]\n", gSlotCount++);)
+ SkASSERT(name);
+
+ fNext = NULL;
+
+ size_t len = strlen(name);
+ // extra 1 for str[0] which stores the type
+ char* str = alloc_string(1 + len);
+ str[0] = (char)type;
+ // str[1] skips the type, len+1 includes the terminating 0 byte.
+ memcpy(&str[1], name, len + 1);
+ fName = str;
+
+ // fValue is uninitialized
+ }
+ ~Slot();
+
+ Type type() const { return (Type)fName[0]; }
+ const char* name() const { return &fName[1]; }
+
+ Slot* fNext;
+ char* fName; // fName[0] is the type, &fName[1] is the "name"
+ union {
+ Object* fObject;
+ Array* fArray;
+ char* fString;
+ int32_t fInt;
+ float fFloat;
+ bool fBool;
+ } fValue;
+};
+
+SkJSON::Object::Slot::~Slot() {
+ free_string(fName);
+ switch (this->type()) {
+ case kObject:
+ delete fValue.fObject;
+ break;
+ case kArray:
+ delete fValue.fArray;
+ break;
+ case kString:
+ free_string(fValue.fString);
+ break;
+ default:
+ break;
+ }
+ LEAK_CODE(SkASSERT(gSlotCount > 0); SkDebugf("~slot[%d]\n", --gSlotCount);)
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkJSON::Object::Iter::Iter(const Object& obj) : fSlot(obj.fHead) {}
+
+bool SkJSON::Object::Iter::done() const {
+ return NULL == fSlot;
+}
+
+void SkJSON::Object::Iter::next() {
+ SkASSERT(fSlot);
+ fSlot = fSlot->fNext;
+}
+
+SkJSON::Type SkJSON::Object::Iter::type() const {
+ SkASSERT(fSlot);
+ return fSlot->type();
+}
+
+const char* SkJSON::Object::Iter::name() const {
+ SkASSERT(fSlot);
+ return fSlot->name();
+}
+
+SkJSON::Object* SkJSON::Object::Iter::objectValue() const {
+ SkASSERT(fSlot);
+ SkASSERT(kObject == fSlot->type());
+ return fSlot->fValue.fObject;
+}
+
+SkJSON::Array* SkJSON::Object::Iter::arrayValue() const {
+ SkASSERT(fSlot);
+ SkASSERT(kArray == fSlot->type());
+ return fSlot->fValue.fArray;
+}
+
+const char* SkJSON::Object::Iter::stringValue() const {
+ SkASSERT(fSlot);
+ SkASSERT(kString == fSlot->type());
+ return fSlot->fValue.fString;
+}
+
+int32_t SkJSON::Object::Iter::intValue() const {
+ SkASSERT(fSlot);
+ SkASSERT(kInt == fSlot->type());
+ return fSlot->fValue.fInt;
+}
+
+float SkJSON::Object::Iter::floatValue() const {
+ SkASSERT(fSlot);
+ SkASSERT(kFloat == fSlot->type());
+ return fSlot->fValue.fFloat;
+}
+
+bool SkJSON::Object::Iter::boolValue() const {
+ SkASSERT(fSlot);
+ SkASSERT(kBool == fSlot->type());
+ return fSlot->fValue.fBool;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkJSON::Object::Object() : fHead(NULL), fTail(NULL) {
+ LEAK_CODE(SkDebugf(" object[%d]\n", gObjectCount++);)
+}
+
+SkJSON::Object::Object(const Object& other) : fHead(NULL), fTail(NULL) {
+ LEAK_CODE(SkDebugf(" object[%d]\n", gObjectCount++);)
+
+ Iter iter(other);
+ while (!iter.done()) {
+ switch (iter.type()) {
+ case kObject:
+ this->addObject(iter.name(), new Object(*iter.objectValue()));
+ break;
+ case kArray:
+ this->addArray(iter.name(), new Array(*iter.arrayValue()));
+ break;
+ case kString:
+ this->addString(iter.name(), dup_string(iter.stringValue()));
+ break;
+ case kInt:
+ this->addInt(iter.name(), iter.intValue());
+ break;
+ case kFloat:
+ this->addFloat(iter.name(), iter.floatValue());
+ break;
+ case kBool:
+ this->addBool(iter.name(), iter.boolValue());
+ break;
+ }
+ iter.next();
+ }
+}
+
+SkJSON::Object::~Object() {
+ Slot* slot = fHead;
+ while (slot) {
+ Slot* next = slot->fNext;
+ delete slot;
+ slot = next;
+ }
+ LEAK_CODE(SkASSERT(gObjectCount > 0); SkDebugf("~object[%d]\n", --gObjectCount);)
+}
+
+int SkJSON::Object::count() const {
+ int n = 0;
+ for (const Slot* slot = fHead; slot; slot = slot->fNext) {
+ n += 1;
+ }
+ return n;
+}
+
+SkJSON::Object::Slot* SkJSON::Object::addSlot(Slot* slot) {
+ SkASSERT(NULL == slot->fNext);
+ if (NULL == fHead) {
+ SkASSERT(NULL == fTail);
+ fHead = fTail = slot;
+ } else {
+ SkASSERT(fTail);
+ SkASSERT(NULL == fTail->fNext);
+ fTail->fNext = slot;
+ fTail = slot;
+ }
+ return slot;
+}
+
+void SkJSON::Object::addObject(const char name[], SkJSON::Object* value) {
+ this->addSlot(new Slot(name, kObject))->fValue.fObject = value;
+}
+
+void SkJSON::Object::addArray(const char name[], SkJSON::Array* value) {
+ this->addSlot(new Slot(name, kArray))->fValue.fArray = value;
+}
+
+void SkJSON::Object::addString(const char name[], const char value[]) {
+ this->addSlot(new Slot(name, kString))->fValue.fString = dup_string(value);
+}
+
+void SkJSON::Object::addInt(const char name[], int32_t value) {
+ this->addSlot(new Slot(name, kInt))->fValue.fInt = value;
+}
+
+void SkJSON::Object::addFloat(const char name[], float value) {
+ this->addSlot(new Slot(name, kFloat))->fValue.fFloat = value;
+}
+
+void SkJSON::Object::addBool(const char name[], bool value) {
+ this->addSlot(new Slot(name, kBool))->fValue.fBool = value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkJSON::Object::Slot* SkJSON::Object::findSlot(const char name[],
+ Type t) const {
+ for (const Slot* slot = fHead; slot; slot = slot->fNext) {
+ if (t == slot->type() && !strcmp(slot->name(), name)) {
+ return slot;
+ }
+ }
+ return NULL;
+}
+
+bool SkJSON::Object::find(const char name[], Type t) const {
+ return this->findSlot(name, t) != NULL;
+}
+
+bool SkJSON::Object::findObject(const char name[], SkJSON::Object** value) const {
+ const Slot* slot = this->findSlot(name, kObject);
+ if (slot) {
+ if (value) {
+ *value = slot->fValue.fObject;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SkJSON::Object::findArray(const char name[], SkJSON::Array** value) const {
+ const Slot* slot = this->findSlot(name, kArray);
+ if (slot) {
+ if (value) {
+ *value = slot->fValue.fArray;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SkJSON::Object::findString(const char name[], SkString* value) const {
+ const Slot* slot = this->findSlot(name, kString);
+ if (slot) {
+ if (value) {
+ value->set(slot->fValue.fString);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SkJSON::Object::findInt(const char name[], int32_t* value) const {
+ const Slot* slot = this->findSlot(name, kInt);
+ if (slot) {
+ if (value) {
+ *value = slot->fValue.fInt;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SkJSON::Object::findFloat(const char name[], float* value) const {
+ const Slot* slot = this->findSlot(name, kFloat);
+ if (slot) {
+ if (value) {
+ *value = slot->fValue.fFloat;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SkJSON::Object::findBool(const char name[], bool* value) const {
+ const Slot* slot = this->findSlot(name, kBool);
+ if (slot) {
+ if (value) {
+ *value = slot->fValue.fBool;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SkJSON::Object::remove(const char name[], Type t) {
+ SkDEBUGCODE(int count = this->count();)
+ Slot* prev = NULL;
+ Slot* slot = fHead;
+ while (slot) {
+ Slot* next = slot->fNext;
+ if (t == slot->type() && !strcmp(slot->name(), name)) {
+ if (prev) {
+ SkASSERT(fHead != slot);
+ prev->fNext = next;
+ } else {
+ SkASSERT(fHead == slot);
+ fHead = next;
+ }
+ if (fTail == slot) {
+ fTail = prev;
+ }
+ delete slot;
+ SkASSERT(count - 1 == this->count());
+ return true;
+ }
+ prev = slot;
+ slot = next;
+ }
+ SkASSERT(count == this->count());
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void tabForLevel(int level) {
+ for (int i = 0; i < level; ++i) {
+ SkDebugf(" ");
+ }
+}
+
+void SkJSON::Object::toDebugf() const {
+ SkDebugf("{\n");
+ this->dumpLevel(0);
+ SkDebugf("}\n");
+}
+
+void SkJSON::Object::dumpLevel(int level) const {
+ for (Slot* slot = fHead; slot; slot = slot->fNext) {
+ Type t = slot->type();
+ tabForLevel(level + 1);
+ SkDebugf("\"%s\" : ", slot->name());
+ switch (slot->type()) {
+ case kObject:
+ if (slot->fValue.fObject) {
+ SkDebugf("{\n");
+ slot->fValue.fObject->dumpLevel(level + 1);
+ tabForLevel(level + 1);
+ SkDebugf("}");
+ } else {
+ SkDebugf("null");
+ }
+ break;
+ case kArray:
+ if (slot->fValue.fArray) {
+ SkDebugf("[");
+ slot->fValue.fArray->dumpLevel(level + 1);
+ SkDebugf("]");
+ } else {
+ SkDebugf("null");
+ }
+ break;
+ case kString:
+ SkDebugf("\"%s\"", slot->fValue.fString);
+ break;
+ case kInt:
+ SkDebugf("%d", slot->fValue.fInt);
+ break;
+ case kFloat:
+ SkDebugf("%g", slot->fValue.fFloat);
+ break;
+ case kBool:
+ SkDebugf("%s", slot->fValue.fBool ? "true" : "false");
+ break;
+ default:
+ SkASSERT(!"how did I get here");
+ break;
+ }
+ if (slot->fNext) {
+ SkDebugf(",");
+ }
+ SkDebugf("\n");
+ }
+}
+
+void SkJSON::Array::dumpLevel(int level) const {
+ if (0 == fCount) {
+ return;
+ }
+ int last = fCount - 1;
+
+ switch (this->type()) {
+ case kObject: {
+ SkDebugf("\n");
+ for (int i = 0; i <= last; ++i) {
+ Object* obj = fArray.fObjects[i];
+ tabForLevel(level + 1);
+ if (obj) {
+ SkDebugf("{\n");
+ obj->dumpLevel(level + 1);
+ tabForLevel(level + 1);
+ SkDebugf(i < last ? "}," : "}");
+ } else {
+ SkDebugf(i < last ? "null," : "null");
+ }
+ SkDebugf("\n");
+ }
+ } break;
+ case kArray: {
+ SkDebugf("\n");
+ for (int i = 0; i <= last; ++i) {
+ Array* array = fArray.fArrays[i];
+ tabForLevel(level + 1);
+ if (array) {
+ SkDebugf("[");
+ array->dumpLevel(level + 1);
+ tabForLevel(level + 1);
+ SkDebugf(i < last ? "]," : "]");
+ } else {
+ SkDebugf(i < last ? "null," : "null");
+ }
+ SkDebugf("\n");
+ }
+ } break;
+ case kString: {
+ for (int i = 0; i < last; ++i) {
+ const char* str = fArray.fStrings[i];
+ SkDebugf(str ? " \"%s\"," : " null,", str);
+ }
+ const char* str = fArray.fStrings[last];
+ SkDebugf(str ? " \"%s\" " : " null ", str);
+ } break;
+ case kInt: {
+ for (int i = 0; i < last; ++i) {
+ SkDebugf(" %d,", fArray.fInts[i]);
+ }
+ SkDebugf(" %d ", fArray.fInts[last]);
+ } break;
+ case kFloat: {
+ for (int i = 0; i < last; ++i) {
+ SkDebugf(" %g,", fArray.fFloats[i]);
+ }
+ SkDebugf(" %g ", fArray.fFloats[last]);
+ } break;
+ case kBool: {
+ for (int i = 0; i < last; ++i) {
+ SkDebugf(" %s,", fArray.fBools[i] ? "true" : "false");
+ }
+ SkDebugf(" %s ", fArray.fInts[last] ? "true" : "false");
+ } break;
+ default:
+ SkASSERT(!"unsupported array type");
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t gBytesPerType[] = {
+ sizeof(SkJSON::Object*),
+ sizeof(SkJSON::Array*),
+ sizeof(char*),
+ sizeof(int32_t),
+ sizeof(float),
+ sizeof(bool)
+};
+
+typedef void* (*DupProc)(const void*);
+
+static void* dup_object(const void* src) {
+ return SkNEW_ARGS(SkJSON::Object, (*(SkJSON::Object*)src));
+}
+
+static void* dup_array(const void* src) {
+ return SkNEW_ARGS(SkJSON::Array, (*(SkJSON::Array*)src));
+}
+
+static const DupProc gDupProcs[] = {
+ dup_object, // Object
+ dup_array, // Array
+ (DupProc)dup_string, // String
+ NULL, // int
+ NULL, // float
+ NULL, // bool
+};
+
+void SkJSON::Array::init(Type type, int count, const void* src) {
+ LEAK_CODE(SkDebugf(" array[%d]\n", gArrayCount++);)
+
+ SkASSERT((unsigned)type < SK_ARRAY_COUNT(gBytesPerType));
+
+ if (count < 0) {
+ count = 0;
+ }
+ size_t size = count * gBytesPerType[type];
+
+ fCount = count;
+ fType = type;
+ fArray.fVoids = sk_malloc_throw(size);
+ if (src) {
+ DupProc proc = gDupProcs[fType];
+ if (!proc) {
+ memcpy(fArray.fVoids, src, size);
+ } else {
+ void** srcPtr = (void**)src;
+ void** dstPtr = (void**)fArray.fVoids;
+ for (int i = 0; i < fCount; ++i) {
+ dstPtr[i] = proc(srcPtr[i]);
+ }
+ }
+ } else {
+ sk_bzero(fArray.fVoids, size);
+ }
+}
+
+SkJSON::Array::Array(Type type, int count) {
+ this->init(type, count, NULL);
+}
+
+SkJSON::Array::Array(const int32_t values[], int count) {
+ this->init(kInt, count, values);
+}
+
+SkJSON::Array::Array(const float values[], int count) {
+ this->init(kFloat, count, values);
+}
+
+SkJSON::Array::Array(const bool values[], int count) {
+ this->init(kBool, count, values);
+}
+
+SkJSON::Array::Array(const Array& other) {
+ this->init(other.type(), other.count(), other.fArray.fVoids);
+}
+
+typedef void (*FreeProc)(void*);
+
+static void free_object(void* obj) {
+ delete (SkJSON::Object*)obj;
+}
+
+static void free_array(void* array) {
+ delete (SkJSON::Array*)array;
+}
+
+static const FreeProc gFreeProcs[] = {
+ free_object, // Object
+ free_array, // Array
+ (FreeProc)free_string, // String
+ NULL, // int
+ NULL, // float
+ NULL, // bool
+};
+
+SkJSON::Array::~Array() {
+ FreeProc proc = gFreeProcs[fType];
+ if (proc) {
+ void** ptr = (void**)fArray.fVoids;
+ for (int i = 0; i < fCount; ++i) {
+ proc(ptr[i]);
+ }
+ }
+ sk_free(fArray.fVoids);
+
+ LEAK_CODE(SkASSERT(gArrayCount > 0); SkDebugf("~array[%d]\n", --gArrayCount);)
+}
+
+void SkJSON::Array::setObject(int index, Object* object) {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+ Object*& prev = fArray.fObjects[index];
+ if (prev != object) {
+ delete prev;
+ prev = object;
+ }
+}
+
+void SkJSON::Array::setArray(int index, Array* array) {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+ Array*& prev = fArray.fArrays[index];
+ if (prev != array) {
+ delete prev;
+ prev = array;
+ }
+}
+
+void SkJSON::Array::setString(int index, const char str[]) {
+ SkASSERT((unsigned)index < (unsigned)fCount);
+ char*& prev = fArray.fStrings[index];
+ if (prev != str) {
+ free_string(prev);
+ prev = dup_string(str);
+ }
+}