From e4058b402f195a38da2d9bc2d98a88a415fe4a19 Mon Sep 17 00:00:00 2001 From: "mike@reedtribe.org" Date: Wed, 4 Jan 2012 03:37:40 +0000 Subject: [PATCH] add Object::remove, some dox, leak debugging code git-svn-id: http://skia.googlecode.com/svn/trunk@2957 2bbb7eff-a529-9590-31e7-b0007b416f81 --- include/utils/SkJSON.h | 218 ++++++++++++++++++++-- src/utils/SkJSON.cpp | 400 +++++++++++++++++++++++++++++++++++------ 2 files changed, 544 insertions(+), 74 deletions(-) diff --git a/include/utils/SkJSON.h b/include/utils/SkJSON.h index 44826ace74..215d54a604 100644 --- a/include/utils/SkJSON.h +++ b/include/utils/SkJSON.h @@ -10,6 +10,7 @@ #include "SkTypes.h" +class SkStream; class SkString; class SkJSON { @@ -26,34 +27,151 @@ public: class Array; class Object { + private: + struct Slot; + public: - Object() : fHead(NULL), fTail(NULL) {} + Object(); Object(const Object&); ~Object(); - + + /** + * Create a new slot with the specified name and value. The name + * parameter is copied, but ownership of the Object parameter is + * transferred. The Object parameter may be null, but the name must + * not be null. + */ void addObject(const char name[], Object* value); - void addArray(const char name[], Array* value); - void addString(const char name[], const char value[]); - void addInt(const char name[], int32_t value); - void addFloat(const char name[], float value); - void addBool(const char name[], bool value); + /** + * Create a new slot with the specified name and value. The name + * parameter is copied, but ownership of the Array parameter is + * transferred. The Array parameter may be null, but the name must + * not be null. + */ + void addArray(const char name[], Array* value); + + /** + * Create a new slot with the specified name and value. Both parameters + * are copied. The value parameter may be null, but the name must + * not be null. + */ + void addString(const char name[], const char value[]); + + /** + * Create a new slot with the specified name and value. The name + * parameter is copied, and must not be null. + */ + void addInt(const char name[], int32_t value); + + /** + * Create a new slot with the specified name and value. The name + * parameter is copied, and must not be null. + */ + void addFloat(const char name[], float value); + + /** + * Create a new slot with the specified name and value. The name + * parameter is copied, and must not be null. + */ + void addBool(const char name[], bool value); + + /** + * Returns true if a slot matching the name and Type is found. + */ + bool find(const char name[], Type) const; bool findObject(const char name[], Object** = NULL) const; bool findArray(const char name[], Array** = NULL) const; bool findString(const char name[], SkString* = NULL) const; bool findInt(const char name[], int32_t* = NULL) const; bool findFloat(const char name[], float* = NULL) const; bool findBool(const char name[], bool* = NULL) const; - - void dump() const; - + + /** + * Finds the first slot matching the name and Type and removes it. + * Returns true if found, false if not. + */ + bool remove(const char name[], Type); + + void toDebugf() const; + + /** + * Iterator class which returns all of the fields/slots in an Object, + * in the order that they were added. + */ + class Iter { + public: + Iter(const Object&); + + /** + * Returns true when there are no more entries in the iterator. + * In this case, no other methods should be called. + */ + bool done() const; + + /** + * Moves the iterator to the next element. Should only be called + * if done() returns false. + */ + void next(); + + /** + * Returns the type of the current element. Should only be called + * if done() returns false. + */ + Type type() const; + + /** + * Returns the name of the current element. Should only be called + * if done() returns false. + */ + const char* name() const; + + /** + * Returns the type of the current element. Should only be called + * if done() returns false and type() returns kObject. + */ + Object* objectValue() const; + + /** + * Returns the type of the current element. Should only be called + * if done() returns false and type() returns kArray. + */ + Array* arrayValue() const; + + /** + * Returns the type of the current element. Should only be called + * if done() returns false and type() returns kString. + */ + const char* stringValue() const; + + /** + * Returns the type of the current element. Should only be called + * if done() returns false and type() returns kInt. + */ + int32_t intValue() const; + + /** + * Returns the type of the current element. Should only be called + * if done() returns false and type() returns kFloat. + */ + float floatValue() const; + + /** + * Returns the type of the current element. Should only be called + * if done() returns false and type() returns kBool. + */ + bool boolValue() const; + + private: + Slot* fSlot; + }; + private: - struct Slot; Slot* fHead; Slot* fTail; - const Slot* findSlot(const char name[]) const; - const Slot* findSlotAndType(const char name[], Type) const; + const Slot* findSlot(const char name[], Type) const; Slot* addSlot(Slot*); void dumpLevel(int level) const; @@ -62,26 +180,90 @@ public: class Array { public: - // do I support strings, objects, arrays? + /** + * Creates an array with the specified Type and element count. All + * entries are initialized to NULL/0/false. + */ Array(Type, int count); + + /** + * Creates an array of ints, initialized by copying the specified + * values. + */ Array(const int32_t values[], int count); + + /** + * Creates an array of floats, initialized by copying the specified + * values. + */ Array(const float values[], int count); + + /** + * Creates an array of bools, initialized by copying the specified + * values. + */ Array(const bool values[], int count); + Array(const Array&); ~Array(); int count() const { return fCount; } Type type() const { return fType; } + + /** + * Replace the element at the specified index with the specified + * Object (which may be null). Ownership of the Object is transferred. + * Should only be called if the Array's type is kObject. + */ + void setObject(int index, Object*); - int32_t* ints() const { return fArray.fInts; } - float* floats() const { return fArray.fFloats; } - bool* bools() const { return fArray.fBools; } - + /** + * Replace the element at the specified index with the specified + * Array (which may be null). Ownership of the Array is transferred. + * Should only be called if the Array's type is kArray. + */ + void setArray(int index, Array*); + + /** + * Replace the element at the specified index with a copy of the + * specified string (which may be null). Should only be called if the + * Array's type is kString. + */ + void setString(int index, const char str[]); + + Object* const* objects() const { + SkASSERT(kObject == fType); + return fArray.fObjects; + } + Array* const* arrays() const { + SkASSERT(kObject == fType); + return fArray.fArrays; + } + const char* const* strings() const { + SkASSERT(kString == fType); + return fArray.fStrings; + } + int32_t* ints() const { + SkASSERT(kInt == fType); + return fArray.fInts; + } + float* floats() const { + SkASSERT(kFloat == fType); + return fArray.fFloats; + } + bool* bools() const { + SkASSERT(kBool == fType); + return fArray.fBools; + } + private: int fCount; Type fType; union { void* fVoids; + Object** fObjects; + Array** fArrays; + char** fStrings; int32_t* fInts; float* fFloats; bool* fBools; diff --git a/src/utils/SkJSON.cpp b/src/utils/SkJSON.cpp index acdc9a71f0..6636c69d05 100644 --- a/src/utils/SkJSON.cpp +++ b/src/utils/SkJSON.cpp @@ -8,58 +8,194 @@ #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); - char* str = new char[len + 2]; + // extra 1 for str[0] which stores the type + char* str = alloc_string(1 + len); str[0] = (char)type; - memcpy(str + 1, name, len + 1); + // str[1] skips the type, len+1 includes the terminating 0 byte. + memcpy(&str[1], name, len + 1); fName = str; // fValue is uninitialized } ~Slot(); - const char* name() const { - return fName ? &fName[1] : ""; - } - - Type type() const { - return (Type)fName[0]; - } + Type type() const { return (Type)fName[0]; } + const char* name() const { return &fName[1]; } Slot* fNext; - char* fName; // fName[0] is the type + char* fName; // fName[0] is the type, &fName[1] is the "name" union { - SkJSON::Object* fObject; - SkJSON::Array* fArray; - char* fString; - int32_t fInt; - float fFloat; - bool fBool; - intptr_t fIntPtr; // for generic getter + 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: - delete[] fValue.fString; + free_string(fValue.fString); break; default: break; } - delete[] fName; + 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) { @@ -67,6 +203,7 @@ SkJSON::Object::~Object() { delete slot; slot = next; } + LEAK_CODE(SkASSERT(gObjectCount > 0); SkDebugf("~object[%d]\n", --gObjectCount);) } SkJSON::Object::Slot* SkJSON::Object::addSlot(Slot* slot) { @@ -80,63 +217,73 @@ SkJSON::Object::Slot* SkJSON::Object::addSlot(Slot* slot) { fTail->fNext = slot; fTail = slot; } + return slot; } void SkJSON::Object::addObject(const char name[], SkJSON::Object* value) { - Slot* slot = addSlot(new Slot(name, kObject)); - fTail->fValue.fObject = value; + this->addSlot(new Slot(name, kObject))->fValue.fObject = value; } void SkJSON::Object::addArray(const char name[], SkJSON::Array* value) { - Slot* slot = addSlot(new Slot(name, kArray)); - fTail->fValue.fArray = value; + this->addSlot(new Slot(name, kArray))->fValue.fArray = value; } void SkJSON::Object::addString(const char name[], const char value[]) { - Slot* slot = addSlot(new Slot(name, kString)); - size_t len = strlen(value); - char* str = new char[len + 1]; - memcpy(str, value, len + 1); - slot->fValue.fString = str; + this->addSlot(new Slot(name, kString))->fValue.fString = dup_string(value); } void SkJSON::Object::addInt(const char name[], int32_t value) { - Slot* slot = addSlot(new Slot(name, kInt)); - fTail->fValue.fInt = value; + this->addSlot(new Slot(name, kInt))->fValue.fInt = value; } void SkJSON::Object::addFloat(const char name[], float value) { - Slot* slot = addSlot(new Slot(name, kFloat)); - fTail->fValue.fFloat = value; + this->addSlot(new Slot(name, kFloat))->fValue.fFloat = value; } void SkJSON::Object::addBool(const char name[], bool value) { - Slot* slot = addSlot(new Slot(name, kBool)); - fTail->fValue.fBool = value; + this->addSlot(new Slot(name, kBool))->fValue.fBool = value; } /////////////////////////////////////////////////////////////////////////////// -const SkJSON::Object::Slot* SkJSON::Object::findSlot(const char name[]) const { +const SkJSON::Object::Slot* SkJSON::Object::findSlot(const char name[], + Type t) const { for (const Slot* slot = fHead; slot; slot = slot->fNext) { - if (!strcmp(slot->name(), name)) { + if (t == slot->type() && !strcmp(slot->name(), name)) { return slot; } } return NULL; } -const SkJSON::Object::Slot* SkJSON::Object::findSlotAndType(const char name[], - Type t) const { - const Slot* slot = this->findSlot(name); - if (slot && (slot->type() != t)) { - slot = 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 slot; + 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->findSlotAndType(name, kString); + const Slot* slot = this->findSlot(name, kString); if (slot) { if (value) { value->set(slot->fValue.fString); @@ -147,7 +294,7 @@ bool SkJSON::Object::findString(const char name[], SkString* value) const { } bool SkJSON::Object::findInt(const char name[], int32_t* value) const { - const Slot* slot = this->findSlotAndType(name, kInt); + const Slot* slot = this->findSlot(name, kInt); if (slot) { if (value) { *value = slot->fValue.fInt; @@ -158,7 +305,7 @@ bool SkJSON::Object::findInt(const char name[], int32_t* value) const { } bool SkJSON::Object::findFloat(const char name[], float* value) const { - const Slot* slot = this->findSlotAndType(name, kFloat); + const Slot* slot = this->findSlot(name, kFloat); if (slot) { if (value) { *value = slot->fValue.fFloat; @@ -169,7 +316,7 @@ bool SkJSON::Object::findFloat(const char name[], float* value) const { } bool SkJSON::Object::findBool(const char name[], bool* value) const { - const Slot* slot = this->findSlotAndType(name, kBool); + const Slot* slot = this->findSlot(name, kBool); if (slot) { if (value) { *value = slot->fValue.fBool; @@ -179,13 +326,23 @@ bool SkJSON::Object::findBool(const char name[], bool* value) const { return false; } -bool SkJSON::Object::findObject(const char name[], SkJSON::Object** value) const { - const Slot* slot = this->findSlotAndType(name, kObject); - if (slot) { - if (value) { - *value = slot->fValue.fObject; +bool SkJSON::Object::remove(const char name[], Type t) { + Slot* prev = NULL; + Slot* slot = fHead; + while (slot) { + Slot* next = slot->fNext; + if (t == slot->type() && !strcmp(slot->name(), name)) { + if (fHead == slot) { + fHead = next; + } + if (fTail == slot) { + fTail = next; + } + delete slot; + return true; } - return true; + prev = slot; + slot = next; } return false; } @@ -198,7 +355,7 @@ static void tabForLevel(int level) { } } -void SkJSON::Object::dump() const { +void SkJSON::Object::toDebugf() const { SkDebugf("{\n"); this->dumpLevel(0); SkDebugf("}\n"); @@ -259,6 +416,46 @@ void SkJSON::Array::dumpLevel(int level) const { 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]); @@ -294,7 +491,30 @@ static const uint8_t gBytesPerType[] = { 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; } @@ -304,7 +524,18 @@ void SkJSON::Array::init(Type type, int count, const void* src) { fType = type; fArray.fVoids = sk_malloc_throw(size); if (src) { - memcpy(fArray.fVoids, src, size); + 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); } } @@ -324,11 +555,68 @@ SkJSON::Array::Array(const bool values[], int count) { this->init(kBool, count, values); } -SkJSON::Array::Array(const Array& src) { - this->init(src.type(), src.count(), src.fArray.fVoids); +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); + } +} + + +