aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar mike@reedtribe.org <mike@reedtribe.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-04-21 01:37:46 +0000
committerGravatar mike@reedtribe.org <mike@reedtribe.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2013-04-21 01:37:46 +0000
commitcac3ae37522bf070244c723960d1689e53da4dcd (patch)
tree775464fdcac7cb93f493b2b6ba8b9b6e5cff612e
parent4309a02bd1aac3250c0a7c7f7378aac36341022d (diff)
specialize SkDataTable for arrays where all elements are the same size.
optimize impl to not require another level of indirection (SkData) for storage. add unittests for flattening. optimize builder to not make a deepcopy of its chunkalloc heap. git-svn-id: http://skia.googlecode.com/svn/trunk@8790 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/core/SkDataTable.h64
-rw-r--r--src/core/SkDataTable.cpp230
-rw-r--r--tests/DataRefTest.cpp90
3 files changed, 297 insertions, 87 deletions
diff --git a/include/core/SkDataTable.h b/include/core/SkDataTable.h
index 37bcee9242..cb74c3cb39 100644
--- a/include/core/SkDataTable.h
+++ b/include/core/SkDataTable.h
@@ -37,7 +37,7 @@ public:
* Return the size of the index'th entry in the table. The caller must
* ensure that index is valid for this table.
*/
- size_t atSize(int index) const;
+ size_t atSize(int index) const;
/**
* Return a pointer to the data of the index'th entry in the table.
@@ -46,11 +46,11 @@ public:
* @param size If non-null, this returns the byte size of this entry. This
* will be the same value that atSize(index) would return.
*/
- const void* atData(int index, size_t* size = NULL) const;
+ const void* at(int index, size_t* size = NULL) const;
template <typename T>
- const T* atDataT(int index, size_t* size = NULL) const {
- return reinterpret_cast<const T*>(this->atData(index, size));
+ const T* atT(int index, size_t* size = NULL) const {
+ return reinterpret_cast<const T*>(this->at(index, size));
}
/**
@@ -59,11 +59,13 @@ public:
*/
const char* atStr(int index) const {
size_t size;
- const char* str = this->atDataT<const char>(index, &size);
+ const char* str = this->atT<const char>(index, &size);
SkASSERT(strlen(str) + 1 == size);
return str;
}
+ typedef void (*FreeProc)(void* context);
+
static SkDataTable* NewEmpty();
/**
@@ -75,8 +77,8 @@ public:
* ptrs[] array.
* @param count the number of array elements in ptrs[] and sizes[] to copy.
*/
- static SkDataTable* NewCopyArrays(const void * const * ptrs, const size_t sizes[],
- int count);
+ static SkDataTable* NewCopyArrays(const void * const * ptrs,
+ const size_t sizes[], int count);
/**
* Return a new table that contains a copy of the data in array.
@@ -89,6 +91,9 @@ public:
static SkDataTable* NewCopyArray(const void* array, size_t elemSize,
int count);
+ static SkDataTable* NewArrayProc(const void* array, size_t elemSize,
+ int count, FreeProc proc, void* context);
+
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDataTable)
protected:
@@ -96,11 +101,28 @@ protected:
virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
private:
- SkDataTable(int count, SkData* dataWeTakeOverOwnership);
+ struct Dir {
+ const void* fPtr;
+ uintptr_t fSize;
+ };
+
+ int fCount;
+ size_t fElemSize;
+ union {
+ const Dir* fDir;
+ const char* fElems;
+ } fU;
+
+ FreeProc fFreeProc;
+ void* fFreeProcContext;
+
+ SkDataTable();
+ SkDataTable(const void* array, size_t elemSize, int count,
+ FreeProc, void* context);
+ SkDataTable(const Dir*, int count, FreeProc, void* context);
virtual ~SkDataTable();
- int fCount;
- SkData* fData;
+ friend class SkDataTableBuilder; // access to Dir
typedef SkFlattenable INHERITED;
};
@@ -109,17 +131,21 @@ private:
* Helper class that allows for incrementally building up the data needed to
* create a SkDataTable.
*/
-class SK_API SkDataTableBuilder {
+class SK_API SkDataTableBuilder : SkNoncopyable {
public:
SkDataTableBuilder(size_t minChunkSize);
~SkDataTableBuilder();
- int count() const { return fSizes.count(); }
+ int count() const { return fDir.count(); }
+ size_t minChunkSize() const { return fMinChunkSize; }
/**
* Forget any previously appended entries, setting count() back to 0.
*/
- void reset();
+ void reset(size_t minChunkSize);
+ void reset() {
+ this->reset(fMinChunkSize);
+ }
/**
* Copy size-bytes from data, and append it to the growing SkDataTable.
@@ -144,15 +170,15 @@ public:
/**
* Return an SkDataTable from the accumulated entries that were added by
- * calls to append(). This data is logically distinct from the builder, and
- * will not be affected by any subsequent calls to the builder.
+ * calls to append(). This call also clears any accumluated entries from
+ * this builder, so its count() will be 0 after this call.
*/
- SkDataTable* createDataTable();
+ SkDataTable* detachDataTable();
private:
- SkTDArray<size_t> fSizes;
- SkTDArray<void*> fPtrs;
- SkChunkAlloc fHeap;
+ SkTDArray<SkDataTable::Dir> fDir;
+ SkChunkAlloc* fHeap;
+ size_t fMinChunkSize;
};
#endif
diff --git a/src/core/SkDataTable.cpp b/src/core/SkDataTable.cpp
index 5c3bfefe2c..e5dbd8472b 100644
--- a/src/core/SkDataTable.cpp
+++ b/src/core/SkDataTable.cpp
@@ -11,46 +11,129 @@
SK_DEFINE_INST_COUNT(SkDataTable)
-SkDataTable::SkDataTable(int count, SkData* data)
- : fCount(count)
- , fData(data) {}
+static void malloc_freeproc(void* context) {
+ sk_free(context);
+}
-SkDataTable::~SkDataTable() {
- fData->unref();
+// Makes empty table
+SkDataTable::SkDataTable() {
+ fCount = 0;
+ fElemSize = 0; // 0 signals that we use fDir instead of fElems
+ fU.fDir = NULL;
+ fFreeProc = NULL;
+ fFreeProcContext = NULL;
}
-struct ElemHead {
- const void* fPtr;
- uintptr_t fSize;
+SkDataTable::SkDataTable(const void* array, size_t elemSize, int count,
+ FreeProc proc, void* context) {
+ SkASSERT(count > 0);
+
+ fCount = count;
+ fElemSize = elemSize; // non-zero signals we use fElems instead of fDir
+ fU.fElems = (const char*)array;
+ fFreeProc = proc;
+ fFreeProcContext = context;
+}
- static const ElemHead* Get(SkData* data) {
- return (const ElemHead*)(data->data());
+SkDataTable::SkDataTable(const Dir* dir, int count, FreeProc proc, void* ctx) {
+ SkASSERT(count > 0);
+
+ fCount = count;
+ fElemSize = 0; // 0 signals that we use fDir instead of fElems
+ fU.fDir = dir;
+ fFreeProc = proc;
+ fFreeProcContext = ctx;
+}
+
+SkDataTable::~SkDataTable() {
+ if (fFreeProc) {
+ fFreeProc(fFreeProcContext);
}
-};
+}
size_t SkDataTable::atSize(int index) const {
SkASSERT((unsigned)index < (unsigned)fCount);
- return ElemHead::Get(fData)[index].fSize;
+
+ if (fElemSize) {
+ return fElemSize;
+ } else {
+ return fU.fDir[index].fSize;
+ }
}
-const void* SkDataTable::atData(int index, size_t* size) const {
+const void* SkDataTable::at(int index, size_t* size) const {
SkASSERT((unsigned)index < (unsigned)fCount);
- const ElemHead& head = ElemHead::Get(fData)[index];
- if (size) {
- *size = head.fSize;
+
+ if (fElemSize) {
+ if (size) {
+ *size = fElemSize;
+ }
+ return fU.fElems + index * fElemSize;
+ } else {
+ if (size) {
+ *size = fU.fDir[index].fSize;
+ }
+ return fU.fDir[index].fPtr;
}
- return head.fPtr;
}
SkDataTable::SkDataTable(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+ fElemSize = 0;
+ fU.fElems = NULL;
+ fFreeProc = NULL;
+ fFreeProcContext = NULL;
+
fCount = buffer.read32();
- fData = buffer.readFlattenableT<SkData>();
+ if (fCount) {
+ fElemSize = buffer.read32();
+ if (fElemSize) {
+ size_t size = buffer.getArrayCount();
+ // size is the size of our elems data
+ SkASSERT(fCount * fElemSize == size);
+ void* addr = sk_malloc_throw(size);
+ if (buffer.readByteArray(addr) != size) {
+ sk_throw();
+ }
+ fU.fElems = (const char*)addr;
+ fFreeProcContext = addr;
+ } else {
+ size_t dataSize = buffer.read32();
+
+ size_t allocSize = fCount * sizeof(Dir) + dataSize;
+ void* addr = sk_malloc_throw(allocSize);
+ Dir* dir = (Dir*)addr;
+ char* elem = (char*)(dir + fCount);
+ for (int i = 0; i < fCount; ++i) {
+ dir[i].fPtr = elem;
+ dir[i].fSize = buffer.readByteArray(elem);
+ elem += dir[i].fSize;
+ }
+ fU.fDir = dir;
+ fFreeProcContext = addr;
+ }
+ fFreeProc = malloc_freeproc;
+ }
}
void SkDataTable::flatten(SkFlattenableWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
+
buffer.write32(fCount);
- buffer.writeFlattenable(fData);
+ if (fCount) {
+ buffer.write32(fElemSize);
+ if (fElemSize) {
+ buffer.writeByteArray(fU.fElems, fCount * fElemSize);
+ } else {
+ size_t dataSize = 0;
+ for (int i = 0; i < fCount; ++i) {
+ dataSize += fU.fDir[i].fSize;
+ }
+ buffer.write32(dataSize);
+ for (int i = 0; i < fCount; ++i) {
+ buffer.writeByteArray(fU.fDir[i].fPtr, fU.fDir[i].fSize);
+ }
+ }
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -58,7 +141,7 @@ void SkDataTable::flatten(SkFlattenableWriteBuffer& buffer) const {
SkDataTable* SkDataTable::NewEmpty() {
static SkDataTable* gEmpty;
if (NULL == gEmpty) {
- gEmpty = SkNEW_ARGS(SkDataTable, (0, SkData::NewEmpty()));
+ gEmpty = SkNEW(SkDataTable);
}
gEmpty->ref();
return gEmpty;
@@ -66,80 +149,103 @@ SkDataTable* SkDataTable::NewEmpty() {
SkDataTable* SkDataTable::NewCopyArrays(const void * const * ptrs,
const size_t sizes[], int count) {
- if (count < 0) {
- count = 0;
+ if (count <= 0) {
+ return SkDataTable::NewEmpty();
}
- size_t headerSize = count * sizeof(ElemHead);
size_t dataSize = 0;
for (int i = 0; i < count; ++i) {
dataSize += sizes[i];
}
- size_t bufferSize = headerSize + dataSize;
+ size_t bufferSize = count * sizeof(Dir) + dataSize;
void* buffer = sk_malloc_throw(bufferSize);
- ElemHead* headerCurr = (ElemHead*)buffer;
- char* dataCurr = (char*)buffer + headerSize;
+ Dir* dir = (Dir*)buffer;
+ char* elem = (char*)(dir + count);
for (int i = 0; i < count; ++i) {
- headerCurr[i].fPtr = dataCurr;
- headerCurr[i].fSize = sizes[i];
- memcpy(dataCurr, ptrs[i], sizes[i]);
- dataCurr += sizes[i];
+ dir[i].fPtr = elem;
+ dir[i].fSize = sizes[i];
+ memcpy(elem, ptrs[i], sizes[i]);
+ elem += sizes[i];
}
-
- return SkNEW_ARGS(SkDataTable, (count,
- SkData::NewFromMalloc(buffer, bufferSize)));
+
+ return SkNEW_ARGS(SkDataTable, (dir, count, malloc_freeproc, buffer));
}
SkDataTable* SkDataTable::NewCopyArray(const void* array, size_t elemSize,
int count) {
- if (count < 0) {
- count = 0;
+ if (count <= 0) {
+ return SkDataTable::NewEmpty();
}
- size_t headerSize = count * sizeof(ElemHead);
- size_t dataSize = count * elemSize;
-
- size_t bufferSize = headerSize + dataSize;
+ size_t bufferSize = elemSize * count;
void* buffer = sk_malloc_throw(bufferSize);
+ memcpy(buffer, array, bufferSize);
- ElemHead* headerCurr = (ElemHead*)buffer;
- char* dataCurr = (char*)buffer + headerSize;
- for (int i = 0; i < count; ++i) {
- headerCurr[i].fPtr = dataCurr;
- headerCurr[i].fSize = elemSize;
- dataCurr += elemSize;
- }
- memcpy((char*)buffer + headerSize, array, dataSize);
+ return SkNEW_ARGS(SkDataTable,
+ (buffer, elemSize, count, malloc_freeproc, buffer));
+}
- return SkNEW_ARGS(SkDataTable, (count,
- SkData::NewFromMalloc(buffer, bufferSize)));
+SkDataTable* SkDataTable::NewArrayProc(const void* array, size_t elemSize,
+ int count, FreeProc proc, void* ctx) {
+ if (count <= 0) {
+ return SkDataTable::NewEmpty();
+ }
+ return SkNEW_ARGS(SkDataTable, (array, elemSize, count, proc, ctx));
}
///////////////////////////////////////////////////////////////////////////////
+static void chunkalloc_freeproc(void* context) {
+ SkDELETE((SkChunkAlloc*)context);
+}
+
SkDataTableBuilder::SkDataTableBuilder(size_t minChunkSize)
- : fHeap(minChunkSize) {}
+ : fHeap(NULL)
+ , fMinChunkSize(minChunkSize) {}
-SkDataTableBuilder::~SkDataTableBuilder() {}
+SkDataTableBuilder::~SkDataTableBuilder() { this->reset(); }
-void SkDataTableBuilder::reset() {
- fSizes.reset();
- fPtrs.reset();
- fHeap.reset();
+void SkDataTableBuilder::reset(size_t minChunkSize) {
+ fMinChunkSize = minChunkSize;
+ fDir.reset();
+ if (fHeap) {
+ SkDELETE(fHeap);
+ fHeap = NULL;
+ }
}
void SkDataTableBuilder::append(const void* src, size_t size) {
- void* dst = fHeap.alloc(size, SkChunkAlloc::kThrow_AllocFailType);
+ if (NULL == fHeap) {
+ fHeap = SkNEW_ARGS(SkChunkAlloc, (fMinChunkSize));
+ }
+
+ void* dst = fHeap->alloc(size, SkChunkAlloc::kThrow_AllocFailType);
memcpy(dst, src, size);
- *fSizes.append() = size;
- *fPtrs.append() = dst;
+ SkDataTable::Dir* dir = fDir.append();
+ dir->fPtr = dst;
+ dir->fSize = size;
}
-SkDataTable* SkDataTableBuilder::createDataTable() {
- SkASSERT(fSizes.count() == fPtrs.count());
- return SkDataTable::NewCopyArrays(fPtrs.begin(), fSizes.begin(),
- fSizes.count());
+SkDataTable* SkDataTableBuilder::detachDataTable() {
+ const int count = fDir.count();
+ if (0 == count) {
+ return SkDataTable::NewEmpty();
+ }
+
+ // Copy the dir into the heap;
+ void* dir = fHeap->alloc(count * sizeof(SkDataTable::Dir),
+ SkChunkAlloc::kThrow_AllocFailType);
+ memcpy(dir, fDir.begin(), count * sizeof(SkDataTable::Dir));
+
+ SkDataTable* table = SkNEW_ARGS(SkDataTable,
+ ((SkDataTable::Dir*)dir, count,
+ chunkalloc_freeproc, fHeap));
+ // we have to detach our fHeap, since we are giving that to the table
+ fHeap = NULL;
+ fDir.reset();
+ return table;
}
+
diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp
index 26c263aa13..dbe02e88d4 100644
--- a/tests/DataRefTest.cpp
+++ b/tests/DataRefTest.cpp
@@ -10,6 +10,8 @@
#include "SkDataSet.h"
#include "SkDataTable.h"
#include "SkStream.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
template <typename T> class SkTUnref {
public:
@@ -23,6 +25,58 @@ private:
T* fRef;
};
+static void test_is_equal(skiatest::Reporter* reporter,
+ const SkDataTable* a, const SkDataTable* b) {
+ REPORTER_ASSERT(reporter, a->count() == b->count());
+ for (int i = 0; i < a->count(); ++i) {
+ size_t sizea, sizeb;
+ const void* mema = a->at(i, &sizea);
+ const void* memb = b->at(i, &sizeb);
+ REPORTER_ASSERT(reporter, sizea == sizeb);
+ REPORTER_ASSERT(reporter, !memcmp(mema, memb, sizea));
+ }
+}
+
+static void test_datatable_flatten(skiatest::Reporter* reporter,
+ SkDataTable* table) {
+ SkOrderedWriteBuffer wb(1024);
+ wb.writeFlattenable(table);
+
+ size_t wsize = wb.size();
+ SkAutoMalloc storage(wsize);
+ wb.writeToMemory(storage.get());
+
+ SkOrderedReadBuffer rb(storage.get(), wsize);
+ SkAutoTUnref<SkDataTable> newTable((SkDataTable*)rb.readFlattenable());
+
+ SkDebugf("%d entries, %d flatten-size\n", table->count(), wsize);
+ test_is_equal(reporter, table, newTable);
+}
+
+static void test_datatable_is_empty(skiatest::Reporter* reporter,
+ SkDataTable* table) {
+ REPORTER_ASSERT(reporter, table->isEmpty());
+ REPORTER_ASSERT(reporter, 0 == table->count());
+ test_datatable_flatten(reporter, table);
+}
+
+static void test_emptytable(skiatest::Reporter* reporter) {
+ SkAutoTUnref<SkDataTable> table0(SkDataTable::NewEmpty());
+ SkAutoTUnref<SkDataTable> table1(SkDataTable::NewCopyArrays(NULL, NULL, 0));
+ SkAutoTUnref<SkDataTable> table2(SkDataTable::NewCopyArray(NULL, NULL, 0));
+ SkAutoTUnref<SkDataTable> table3(SkDataTable::NewArrayProc(NULL, NULL, 0,
+ NULL, NULL));
+
+ test_datatable_is_empty(reporter, table0);
+ test_datatable_is_empty(reporter, table1);
+ test_datatable_is_empty(reporter, table2);
+ test_datatable_is_empty(reporter, table3);
+
+ test_is_equal(reporter, table0, table1);
+ test_is_equal(reporter, table0, table2);
+ test_is_equal(reporter, table0, table3);
+}
+
static void test_simpletable(skiatest::Reporter* reporter) {
const int idata[] = { 1, 4, 9, 16, 25, 63 };
int icount = SK_ARRAY_COUNT(idata);
@@ -33,9 +87,10 @@ static void test_simpletable(skiatest::Reporter* reporter) {
for (int i = 0; i < icount; ++i) {
size_t size;
REPORTER_ASSERT(reporter, sizeof(int) == itable->atSize(i));
- REPORTER_ASSERT(reporter, *itable->atDataT<int>(i, &size) == idata[i]);
+ REPORTER_ASSERT(reporter, *itable->atT<int>(i, &size) == idata[i]);
REPORTER_ASSERT(reporter, sizeof(int) == size);
}
+ test_datatable_flatten(reporter, itable);
}
static void test_vartable(skiatest::Reporter* reporter) {
@@ -55,13 +110,14 @@ static void test_vartable(skiatest::Reporter* reporter) {
for (int i = 0; i < count; ++i) {
size_t size;
REPORTER_ASSERT(reporter, table->atSize(i) == sizes[i]);
- REPORTER_ASSERT(reporter, !strcmp(table->atDataT<const char>(i, &size),
+ REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size),
str[i]));
REPORTER_ASSERT(reporter, size == sizes[i]);
const char* s = table->atStr(i);
REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
}
+ test_datatable_flatten(reporter, table);
}
static void test_tablebuilder(skiatest::Reporter* reporter) {
@@ -75,25 +131,47 @@ static void test_tablebuilder(skiatest::Reporter* reporter) {
for (int i = 0; i < count; ++i) {
builder.append(str[i], strlen(str[i]) + 1);
}
- SkAutoTUnref<SkDataTable> table(builder.createDataTable());
+ SkAutoTUnref<SkDataTable> table(builder.detachDataTable());
REPORTER_ASSERT(reporter, table->count() == count);
for (int i = 0; i < count; ++i) {
size_t size;
REPORTER_ASSERT(reporter, table->atSize(i) == strlen(str[i]) + 1);
- REPORTER_ASSERT(reporter, !strcmp(table->atDataT<const char>(i, &size),
+ REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size),
str[i]));
REPORTER_ASSERT(reporter, size == strlen(str[i]) + 1);
const char* s = table->atStr(i);
REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
}
+ test_datatable_flatten(reporter, table);
+}
+
+static void test_globaltable(skiatest::Reporter* reporter) {
+ static const int gData[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ };
+ int count = SK_ARRAY_COUNT(gData);
+
+ SkAutoTUnref<SkDataTable> table(SkDataTable::NewArrayProc(gData,
+ sizeof(gData[0]), count, NULL, NULL));
+
+ REPORTER_ASSERT(reporter, table->count() == count);
+ for (int i = 0; i < count; ++i) {
+ size_t size;
+ REPORTER_ASSERT(reporter, table->atSize(i) == sizeof(int));
+ REPORTER_ASSERT(reporter, *table->atT<const char>(i, &size) == i);
+ REPORTER_ASSERT(reporter, sizeof(int) == size);
+ }
+ test_datatable_flatten(reporter, table);
}
-static void test_datatable(skiatest::Reporter* reporter) {
+static void TestDataTable(skiatest::Reporter* reporter) {
+ test_emptytable(reporter);
test_simpletable(reporter);
test_vartable(reporter);
test_tablebuilder(reporter);
+ test_globaltable(reporter);
}
static void unrefAll(const SkDataSet::Pair pairs[], int count) {
@@ -220,8 +298,8 @@ static void TestData(skiatest::Reporter* reporter) {
test_cstring(reporter);
test_dataset(reporter);
- test_datatable(reporter);
}
#include "TestClassDef.h"
DEFINE_TESTCLASS("Data", DataTestClass, TestData)
+DEFINE_TESTCLASS("DataTable", DataTableTestClass, TestDataTable)