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:
parent
0c888286f7
commit
8c5c7a905b
@ -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
156
include/core/SkDataTable.h
Normal 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
135
src/core/SkDataTable.cpp
Normal 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());
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user