add SkDataTable, to efficiently store an immutable array. Includes a builder

helper class.
Review URL: https://codereview.chromium.org/14188049

git-svn-id: http://skia.googlecode.com/svn/trunk@8779 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@google.com 2013-04-19 20:16:01 +00:00
parent 0c888286f7
commit 8c5c7a905b
4 changed files with 367 additions and 0 deletions

View File

@ -65,6 +65,7 @@
'<(skia_src_path)/core/SkCubicClipper.cpp',
'<(skia_src_path)/core/SkCubicClipper.h',
'<(skia_src_path)/core/SkData.cpp',
'<(skia_src_path)/core/SkDataTable.cpp',
'<(skia_src_path)/core/SkDebug.cpp',
'<(skia_src_path)/core/SkDeque.cpp',
'<(skia_src_path)/core/SkDevice.cpp',

156
include/core/SkDataTable.h Normal file
View File

@ -0,0 +1,156 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkDataTable_DEFINED
#define SkDataTable_DEFINED
#include "SkChunkAlloc.h"
#include "SkData.h"
#include "SkFlattenable.h"
#include "SkString.h"
#include "SkTDArray.h"
/**
* Like SkData, SkDataTable holds an immutable data buffer. The data buffer is
* organized into a table of entries, each with a length, so the entries are
* not required to all be the same size.
*/
class SK_API SkDataTable : public SkFlattenable {
public:
SK_DECLARE_INST_COUNT(SkDataTable)
/**
* Returns true if the table is empty (i.e. has no entries).
*/
bool isEmpty() const { return 0 == fCount; }
/**
* Return the number of entries in the table. 0 for an empty table
*/
int count() const { return fCount; }
/**
* 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;
/**
* Return a pointer to the data of the index'th entry in the table.
* The caller must ensure that index is valid for this table.
*
* @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;
template <typename T>
const T* atDataT(int index, size_t* size = NULL) const {
return reinterpret_cast<const T*>(this->atData(index, size));
}
/**
* Returns the index'th entry as a c-string, and assumes that the trailing
* null byte had been copied into the table as well.
*/
const char* atStr(int index) const {
size_t size;
const char* str = this->atDataT<const char>(index, &size);
SkASSERT(strlen(str) + 1 == size);
return str;
}
/**
* Return a new DataTable that contains a copy of the data stored in each
* "array".
*
* @param ptrs array of points to each element to be copied into the table.
* @param sizes array of byte-lengths for each entry in the corresponding
* 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);
/**
* Return a new table that contains a copy of the data in array.
*
* @param array contiguous array of data for all elements to be copied.
* @param elemSize byte-length for a given element.
* @param count the number of entries to be copied out of array. The number
* of bytes that will be copied is count * elemSize.
*/
static SkDataTable* NewCopyArray(const void* array, size_t elemSize,
int count);
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDataTable)
protected:
SkDataTable(SkFlattenableReadBuffer&);
virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
private:
SkDataTable(int count, SkData* dataWeTakeOverOwnership);
virtual ~SkDataTable();
int fCount;
SkData* fData;
typedef SkFlattenable INHERITED;
};
/**
* Helper class that allows for incrementally building up the data needed to
* create a SkDataTable.
*/
class SK_API SkDataTableBuilder {
public:
SkDataTableBuilder(size_t minChunkSize);
~SkDataTableBuilder();
int count() const { return fSizes.count(); }
/**
* Forget any previously appended entries, setting count() back to 0.
*/
void reset();
/**
* Copy size-bytes from data, and append it to the growing SkDataTable.
*/
void append(const void* data, size_t size);
/**
* Helper version of append() passes strlen() + 1 for the size,
* so the trailing-zero will be copied as well.
*/
void appendStr(const char str[]) {
this->append(str, strlen(str) + 1);
}
/**
* Helper version of append() passes string.size() + 1 for the size,
* so the trailing-zero will be copied as well.
*/
void appendString(const SkString& string) {
this->append(string.c_str(), string.size() + 1);
}
/**
* 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.
*/
SkDataTable* createDataTable();
private:
SkTDArray<size_t> fSizes;
SkTDArray<void*> fPtrs;
SkChunkAlloc fHeap;
};
#endif

135
src/core/SkDataTable.cpp Normal file
View File

@ -0,0 +1,135 @@
/*
* Copyright 2013 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkData.h"
#include "SkDataTable.h"
#include "SkFlattenableBuffers.h"
SK_DEFINE_INST_COUNT(SkDataTable)
SkDataTable::SkDataTable(int count, SkData* data)
: fCount(count)
, fData(data) {}
SkDataTable::~SkDataTable() {
fData->unref();
}
struct ElemHead {
const void* fPtr;
uintptr_t fSize;
static const ElemHead* Get(SkData* data) {
return (const ElemHead*)(data->data());
}
};
size_t SkDataTable::atSize(int index) const {
SkASSERT((unsigned)index < (unsigned)fCount);
return ElemHead::Get(fData)[index].fSize;
}
const void* SkDataTable::atData(int index, size_t* size) const {
SkASSERT((unsigned)index < (unsigned)fCount);
const ElemHead& head = ElemHead::Get(fData)[index];
if (size) {
*size = head.fSize;
}
return head.fPtr;
}
SkDataTable::SkDataTable(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
fCount = buffer.read32();
fData = buffer.readFlattenableT<SkData>();
}
void SkDataTable::flatten(SkFlattenableWriteBuffer& buffer) const {
this->INHERITED::flatten(buffer);
buffer.write32(fCount);
buffer.writeFlattenable(fData);
}
SkDataTable* SkDataTable::NewCopyArrays(const void * const * ptrs,
const size_t sizes[], int count) {
if (count < 0) {
count = 0;
}
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;
void* buffer = sk_malloc_throw(bufferSize);
ElemHead* headerCurr = (ElemHead*)buffer;
char* dataCurr = (char*)buffer + headerSize;
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];
}
return SkNEW_ARGS(SkDataTable, (count,
SkData::NewFromMalloc(buffer, bufferSize)));
}
SkDataTable* SkDataTable::NewCopyArray(const void* array, size_t elemSize,
int count) {
if (count < 0) {
count = 0;
}
size_t headerSize = count * sizeof(ElemHead);
size_t dataSize = count * elemSize;
size_t bufferSize = headerSize + dataSize;
void* buffer = sk_malloc_throw(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, (count,
SkData::NewFromMalloc(buffer, bufferSize)));
}
///////////////////////////////////////////////////////////////////////////////
SkDataTableBuilder::SkDataTableBuilder(size_t minChunkSize)
: fHeap(minChunkSize) {}
SkDataTableBuilder::~SkDataTableBuilder() {}
void SkDataTableBuilder::reset() {
fSizes.reset();
fPtrs.reset();
fHeap.reset();
}
void SkDataTableBuilder::append(const void* src, size_t size) {
void* dst = fHeap.alloc(size, SkChunkAlloc::kThrow_AllocFailType);
memcpy(dst, src, size);
*fSizes.append() = size;
*fPtrs.append() = dst;
}
SkDataTable* SkDataTableBuilder::createDataTable() {
SkASSERT(fSizes.count() == fPtrs.count());
return SkDataTable::NewCopyArrays(fPtrs.begin(), fSizes.begin(),
fSizes.count());
}

View File

@ -8,6 +8,7 @@
#include "Test.h"
#include "SkData.h"
#include "SkDataSet.h"
#include "SkDataTable.h"
#include "SkStream.h"
template <typename T> class SkTUnref {
@ -22,6 +23,79 @@ private:
T* fRef;
};
static void test_simpletable(skiatest::Reporter* reporter) {
const int idata[] = { 1, 4, 9, 16, 25, 63 };
int icount = SK_ARRAY_COUNT(idata);
SkAutoTUnref<SkDataTable> itable(SkDataTable::NewCopyArray(idata,
sizeof(idata[0]),
icount));
REPORTER_ASSERT(reporter, itable->count() == icount);
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, sizeof(int) == size);
}
}
static void test_vartable(skiatest::Reporter* reporter) {
const char* str[] = {
"", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg"
};
int count = SK_ARRAY_COUNT(str);
size_t sizes[SK_ARRAY_COUNT(str)];
for (int i = 0; i < count; ++i) {
sizes[i] = strlen(str[i]) + 1;
}
SkAutoTUnref<SkDataTable> table(SkDataTable::NewCopyArrays(
(const void*const*)str, sizes, count));
REPORTER_ASSERT(reporter, table->count() == count);
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),
str[i]));
REPORTER_ASSERT(reporter, size == sizes[i]);
const char* s = table->atStr(i);
REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
}
}
static void test_tablebuilder(skiatest::Reporter* reporter) {
const char* str[] = {
"", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg"
};
int count = SK_ARRAY_COUNT(str);
SkDataTableBuilder builder(16);
for (int i = 0; i < count; ++i) {
builder.append(str[i], strlen(str[i]) + 1);
}
SkAutoTUnref<SkDataTable> table(builder.createDataTable());
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),
str[i]));
REPORTER_ASSERT(reporter, size == strlen(str[i]) + 1);
const char* s = table->atStr(i);
REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
}
}
static void test_datatable(skiatest::Reporter* reporter) {
test_simpletable(reporter);
test_vartable(reporter);
test_tablebuilder(reporter);
}
static void unrefAll(const SkDataSet::Pair pairs[], int count) {
for (int i = 0; i < count; ++i) {
pairs[i].fValue->unref();
@ -146,6 +220,7 @@ static void TestData(skiatest::Reporter* reporter) {
test_cstring(reporter);
test_dataset(reporter);
test_datatable(reporter);
}
#include "TestClassDef.h"