diff options
Diffstat (limited to 'utils/SkJSON.cpp')
-rw-r--r-- | utils/SkJSON.cpp | 634 |
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); + } +} |