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