From: mike@reedtribe.org Date: Sun, 21 Apr 2013 01:37:46 +0000 (+0000) Subject: specialize SkDataTable for arrays where all elements are the same size. X-Git-Tag: accepted/tizen/5.0/unified/20181102.025319~12663 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=cac3ae37522bf070244c723960d1689e53da4dcd;p=platform%2Fupstream%2FlibSkiaSharp.git 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 --- diff --git a/include/core/SkDataTable.h b/include/core/SkDataTable.h index 37bcee9..cb74c3c 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 - const T* atDataT(int index, size_t* size = NULL) const { - return reinterpret_cast(this->atData(index, size)); + const T* atT(int index, size_t* size = NULL) const { + return reinterpret_cast(this->at(index, size)); } /** @@ -59,11 +59,13 @@ public: */ const char* atStr(int index) const { size_t size; - const char* str = this->atDataT(index, &size); + const char* str = this->atT(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 fSizes; - SkTDArray fPtrs; - SkChunkAlloc fHeap; + SkTDArray fDir; + SkChunkAlloc* fHeap; + size_t fMinChunkSize; }; #endif diff --git a/src/core/SkDataTable.cpp b/src/core/SkDataTable.cpp index 5c3bfef..e5dbd84 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(); + 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 26c263a..dbe02e8 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 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 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 table0(SkDataTable::NewEmpty()); + SkAutoTUnref table1(SkDataTable::NewCopyArrays(NULL, NULL, 0)); + SkAutoTUnref table2(SkDataTable::NewCopyArray(NULL, NULL, 0)); + SkAutoTUnref 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(i, &size) == idata[i]); + REPORTER_ASSERT(reporter, *itable->atT(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(i, &size), + REPORTER_ASSERT(reporter, !strcmp(table->atT(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 table(builder.createDataTable()); + SkAutoTUnref 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(i, &size), + REPORTER_ASSERT(reporter, !strcmp(table->atT(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 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(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)