aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkDataSet.h69
-rw-r--r--src/core/SkData.cpp120
-rw-r--r--tests/DataRefTest.cpp84
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)