diff options
-rw-r--r-- | include/core/SkDataSet.h | 69 | ||||
-rw-r--r-- | src/core/SkData.cpp | 120 | ||||
-rw-r--r-- | tests/DataRefTest.cpp | 84 |
3 files changed, 271 insertions, 2 deletions
diff --git a/include/core/SkDataSet.h b/include/core/SkDataSet.h new file mode 100644 index 0000000000..eed8c20a24 --- /dev/null +++ b/include/core/SkDataSet.h @@ -0,0 +1,69 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkDataSet_DEFINED +#define SkDataSet_DEFINED + +#include "SkRefCnt.h" +#include "SkData.h" + +class SkStream; +class SkWStream; + +class SkDataSet : public SkRefCnt { +public: + struct Pair { + const char* fKey; + SkData* fValue; + }; + + SkDataSet(const char key[], SkData* value); + SkDataSet(const Pair[], int count); + virtual ~SkDataSet(); + + bool isEmpty() const { return 0 == fCount; } + int count() const { return fCount; } + SkData* find(const char name[]) const; + + class Iter { + public: + Iter(const SkDataSet& ds) { + fPair = ds.fPairs; + fStop = ds.fPairs + ds.fCount; + } + + const char* key() const { + SkASSERT(!this->done()); + return fPair->fKey; + } + + SkData* value() const { + SkASSERT(!this->done()); + return fPair->fValue; + } + + bool done() const { return fPair >= fStop; } + void next() { + SkASSERT(!this->done()); + fPair += 1; + } + + private: + const SkDataSet::Pair* fPair; + const SkDataSet::Pair* fStop; + }; + + explicit SkDataSet(SkStream*); + void writeToStream(SkWStream*) const; + +private: + int32_t fCount; + uint32_t fKeySize; + Pair* fPairs; +}; + +#endif diff --git a/src/core/SkData.cpp b/src/core/SkData.cpp index 496d599379..618ee35f07 100644 --- a/src/core/SkData.cpp +++ b/src/core/SkData.cpp @@ -122,3 +122,123 @@ SkData* SkData::NewWithCString(const char cstr[]) { return NewWithCopy(cstr, size); } +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#include "SkDataSet.h" +#include "SkStream.h" + +static SkData* dupdata(SkData* data) { + if (data) { + data->ref(); + } else { + data = SkData::NewEmpty(); + } + return data; +} + +static SkData* findValue(const char key[], const SkDataSet::Pair array[], int n) { + for (int i = 0; i < n; ++i) { + if (!strcmp(key, array[i].fKey)) { + return array[i].fValue; + } + } + return NULL; +} + +static SkDataSet::Pair* allocatePairStorage(int count, size_t storage) { + size_t size = count * sizeof(SkDataSet::Pair) + storage; + return (SkDataSet::Pair*)sk_malloc_throw(size); +} + +SkDataSet::SkDataSet(const char key[], SkData* value) { + size_t keyLen = strlen(key); + + fCount = 1; + fKeySize = keyLen + 1; + fPairs = allocatePairStorage(1, keyLen + 1); + + fPairs[0].fKey = (char*)(fPairs + 1); + memcpy(const_cast<char*>(fPairs[0].fKey), key, keyLen + 1); + + fPairs[0].fValue = dupdata(value); +} + +SkDataSet::SkDataSet(const Pair array[], int count) { + if (count < 1) { + fCount = 0; + fKeySize = 0; + fPairs = NULL; + return; + } + + int i; + size_t keySize = 0; + for (i = 0; i < count; ++i) { + keySize += strlen(array[i].fKey) + 1; + } + + Pair* pairs = fPairs = allocatePairStorage(count, keySize); + char* keyStorage = (char*)(pairs + count); + + keySize = 0; // reset this, so we can compute the size for unique keys + int uniqueCount = 0; + for (int i = 0; i < count; ++i) { + if (!findValue(array[i].fKey, pairs, uniqueCount)) { + size_t len = strlen(array[i].fKey); + memcpy(keyStorage, array[i].fKey, len + 1); + pairs[uniqueCount].fKey = keyStorage; + keyStorage += len + 1; + keySize += len + 1; + + pairs[uniqueCount].fValue = dupdata(array[i].fValue); + uniqueCount += 1; + } + } + fCount = uniqueCount; + fKeySize = keySize; +} + +SkDataSet::~SkDataSet() { + for (int i = 0; i < fCount; ++i) { + fPairs[i].fValue->unref(); + } + sk_free(fPairs); // this also frees the key storage +} + +SkData* SkDataSet::find(const char key[]) const { + return findValue(key, fPairs, fCount); +} + +void SkDataSet::writeToStream(SkWStream* stream) const { + stream->write32(fCount); + if (fCount > 0) { + stream->write32(fKeySize); + // our first key points to all the key storage + stream->write(fPairs[0].fKey, fKeySize); + for (int i = 0; i < fCount; ++i) { + stream->writeData(fPairs[i].fValue); + } + } +} + +SkDataSet::SkDataSet(SkStream* stream) { + fCount = stream->readU32(); + if (fCount > 0) { + fKeySize = stream->readU32(); + fPairs = allocatePairStorage(fCount, fKeySize); + char* keyStorage = (char*)(fPairs + fCount); + + stream->read(keyStorage, fKeySize); + + for (int i = 0; i < fCount; ++i) { + fPairs[i].fKey = keyStorage; + keyStorage += strlen(keyStorage) + 1; + fPairs[i].fValue = stream->readData(); + } + } else { + fKeySize = 0; + fPairs = NULL; + } +} + diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp index dc7f42407f..8c9d15154d 100644 --- a/tests/DataRefTest.cpp +++ b/tests/DataRefTest.cpp @@ -7,6 +7,85 @@ */ #include "Test.h" #include "SkData.h" +#include "SkDataSet.h" +#include "SkStream.h" + +template <typename T> class SkTUnref { +public: + SkTUnref(T* ref) : fRef(ref) {} + ~SkTUnref() { fRef->unref(); } + + operator T*() { return fRef; } + operator const T*() { return fRef; } + +private: + T* fRef; +}; + +static void unrefAll(const SkDataSet::Pair pairs[], int count) { + for (int i = 0; i < count; ++i) { + pairs[i].fValue->unref(); + } +} + +// asserts that inner is a subset of outer +static void test_dataset_subset(skiatest::Reporter* reporter, + const SkDataSet& outer, const SkDataSet& inner) { + SkDataSet::Iter iter(inner); + for (; !iter.done(); iter.next()) { + SkData* outerData = outer.find(iter.key()); + REPORTER_ASSERT(reporter, outerData); + REPORTER_ASSERT(reporter, outerData->equals(iter.value())); + } +} + +static void test_datasets_equal(skiatest::Reporter* reporter, + const SkDataSet& ds0, const SkDataSet& ds1) { + REPORTER_ASSERT(reporter, ds0.count() == ds1.count()); + + test_dataset_subset(reporter, ds0, ds1); + test_dataset_subset(reporter, ds1, ds0); +} + +static void test_dataset(skiatest::Reporter* reporter, const SkDataSet& ds, + int count) { + REPORTER_ASSERT(reporter, ds.count() == count); + + SkDataSet::Iter iter(ds); + int index = 0; + for (; !iter.done(); iter.next()) { + const char* name = iter.key(); + SkData* data = iter.value(); + SkDebugf("[%d] %s:%s\n", index, name, (const char*)data->bytes()); + index += 1; + } + REPORTER_ASSERT(reporter, index == count); + + SkDynamicMemoryWStream ostream; + ds.writeToStream(&ostream); + SkMemoryStream istream; + istream.setData(ostream.copyToData())->unref(); + SkDataSet copy(&istream); + + test_datasets_equal(reporter, ds, copy); +} + +static void test_dataset(skiatest::Reporter* reporter) { + SkDataSet set0(NULL, 0); + SkDataSet set1("hello", SkTUnref<SkData>(SkData::NewWithCString("world"))); + + const SkDataSet::Pair pairs[] = { + { "one", SkData::NewWithCString("1") }, + { "two", SkData::NewWithCString("2") }, + { "three", SkData::NewWithCString("3") }, + }; + SkDataSet set3(pairs, 3); + unrefAll(pairs, 3); + + test_dataset(reporter, set0, 0); + test_dataset(reporter, set1, 1); + test_dataset(reporter, set3, 3); +} static void* gGlobal; @@ -40,7 +119,7 @@ static void test_cstring(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, 0 == *r2->bytes()); } -static void TestDataRef(skiatest::Reporter* reporter) { +static void TestData(skiatest::Reporter* reporter) { const char* str = "We the people, in order to form a more perfect union."; const int N = 10; @@ -66,7 +145,8 @@ static void TestDataRef(skiatest::Reporter* reporter) { tmp->unref(); test_cstring(reporter); + test_dataset(reporter); } #include "TestClassDef.h" -DEFINE_TESTCLASS("DataRef", DataRefTestClass, TestDataRef) +DEFINE_TESTCLASS("Data", DataTestClass, TestData) |