diff options
author | 2017-03-08 22:29:31 -0800 | |
---|---|---|
committer | 2017-03-10 15:48:10 -0800 | |
commit | b7f9a2400aaa2e0d29ffefd91576e90036d4cf83 (patch) | |
tree | 94d03ea18bed9bf45866b6081f6103e7955cbc05 | |
parent | 5f549b2089442cb80e8d7f4bd00ac69560375b2c (diff) |
convert BlobCache to STL
This finally gets rid of the utils/BlobCache.h
dependency
Test: compiled & run
Bug: vndk-stable
Change-Id: Idfc6ace472479fae99694f56edb973800b2bef20
-rw-r--r-- | opengl/libs/Android.bp | 11 | ||||
-rw-r--r-- | opengl/libs/EGL/BlobCache.cpp | 89 | ||||
-rw-r--r-- | opengl/libs/EGL/BlobCache.h | 28 | ||||
-rw-r--r-- | opengl/libs/EGL/BlobCache_test.cpp | 434 | ||||
-rw-r--r-- | opengl/libs/EGL/egl_cache.cpp | 23 | ||||
-rw-r--r-- | opengl/libs/EGL/egl_cache.h | 8 | ||||
-rw-r--r-- | opengl/libs/EGL/egl_display.cpp | 7 | ||||
-rw-r--r-- | opengl/libs/EGL/include/private/EGL/cache.h | 25 | ||||
-rw-r--r-- | opengl/libs/EGL/include/private/EGL/display.h | 27 |
9 files changed, 579 insertions, 73 deletions
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 6141e995a5..aa67d41bba 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -105,9 +105,20 @@ cc_library_shared { "EGL/egl.cpp", "EGL/eglApi.cpp", "EGL/Loader.cpp", + "EGL/BlobCache.cpp", ], static_libs: ["libEGL_getProcAddress"], ldflags: ["-Wl,--exclude-libs=ALL"], + export_include_dirs: ["EGL/include"], +} + +cc_test { + name: "libEGL_test", + defaults: ["egl_libs_defaults"], + srcs: [ + "EGL/BlobCache.cpp", + "EGL/BlobCache_test.cpp", + ], } cc_defaults { diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index b0fdd44b51..f1b30c7957 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -14,15 +14,15 @@ ** limitations under the License. */ -#define LOG_TAG "BlobCache" //#define LOG_NDEBUG 0 -#include <utils/BlobCache.h> -#include <utils/Timers.h> +#include "BlobCache.h" #include <inttypes.h> #include <cutils/properties.h> +#include <log/log.h> +#include <chrono> namespace android { @@ -40,7 +40,7 @@ BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize mMaxValueSize(maxValueSize), mMaxTotalSize(maxTotalSize), mTotalSize(0) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + int64_t now = std::chrono::steady_clock::now().time_since_epoch().count(); #ifdef _WIN32 srand(now); #else @@ -77,15 +77,15 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, return; } - sp<Blob> dummyKey(new Blob(key, keySize, false)); + std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); CacheEntry dummyEntry(dummyKey, NULL); while (true) { - ssize_t index = mCacheEntries.indexOf(dummyEntry); - if (index < 0) { + auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); + if (index == mCacheEntries.end() || dummyEntry < *index) { // Create a new cache entry. - sp<Blob> keyBlob(new Blob(key, keySize, true)); - sp<Blob> valueBlob(new Blob(value, valueSize, true)); + std::shared_ptr<Blob> keyBlob(new Blob(key, keySize, true)); + std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true)); size_t newTotalSize = mTotalSize + keySize + valueSize; if (mMaxTotalSize < newTotalSize) { if (isCleanable()) { @@ -100,14 +100,14 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, break; } } - mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); + mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob)); mTotalSize = newTotalSize; ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize, valueSize); } else { // Update the existing cache entry. - sp<Blob> valueBlob(new Blob(value, valueSize, true)); - sp<Blob> oldValueBlob(mCacheEntries[index].getValue()); + std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true)); + std::shared_ptr<Blob> oldValueBlob(index->getValue()); size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize(); if (mMaxTotalSize < newTotalSize) { if (isCleanable()) { @@ -121,7 +121,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, break; } } - mCacheEntries.editItemAt(index).setValue(valueBlob); + index->setValue(valueBlob); mTotalSize = newTotalSize; ALOGV("set: updated existing cache entry with %zu byte key and %zu byte " "value", keySize, valueSize); @@ -137,17 +137,17 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, keySize, mMaxKeySize); return 0; } - sp<Blob> dummyKey(new Blob(key, keySize, false)); + std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); CacheEntry dummyEntry(dummyKey, NULL); - ssize_t index = mCacheEntries.indexOf(dummyEntry); - if (index < 0) { + auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); + if (index == mCacheEntries.end() || dummyEntry < *index) { ALOGV("get: no cache entry found for key of size %zu", keySize); return 0; } // The key was found. Return the value if the caller's buffer is large // enough. - sp<Blob> valueBlob(mCacheEntries[index].getValue()); + std::shared_ptr<Blob> valueBlob(index->getValue()); size_t valueBlobSize = valueBlob->getSize(); if (valueBlobSize <= valueSize) { ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize); @@ -165,21 +165,19 @@ static inline size_t align4(size_t size) { size_t BlobCache::getFlattenedSize() const { size_t size = align4(sizeof(Header) + PROPERTY_VALUE_MAX); - for (size_t i = 0; i < mCacheEntries.size(); i++) { - const CacheEntry& e(mCacheEntries[i]); - sp<Blob> keyBlob = e.getKey(); - sp<Blob> valueBlob = e.getValue(); - size += align4(sizeof(EntryHeader) + keyBlob->getSize() + - valueBlob->getSize()); + for (const CacheEntry& e : mCacheEntries) { + std::shared_ptr<Blob> const& keyBlob = e.getKey(); + std::shared_ptr<Blob> const& valueBlob = e.getValue(); + size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize()); } return size; } -status_t BlobCache::flatten(void* buffer, size_t size) const { +int BlobCache::flatten(void* buffer, size_t size) const { // Write the cache header if (size < sizeof(Header)) { ALOGE("flatten: not enough room for cache header"); - return BAD_VALUE; + return 0; } Header* header = reinterpret_cast<Header*>(buffer); header->mMagicNumber = blobCacheMagic; @@ -193,10 +191,9 @@ status_t BlobCache::flatten(void* buffer, size_t size) const { // Write cache entries uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer); off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength); - for (size_t i = 0; i < mCacheEntries.size(); i++) { - const CacheEntry& e(mCacheEntries[i]); - sp<Blob> keyBlob = e.getKey(); - sp<Blob> valueBlob = e.getValue(); + for (const CacheEntry& e : mCacheEntries) { + std::shared_ptr<Blob> const& keyBlob = e.getKey(); + std::shared_ptr<Blob> const& valueBlob = e.getValue(); size_t keySize = keyBlob->getSize(); size_t valueSize = valueBlob->getSize(); @@ -204,11 +201,10 @@ status_t BlobCache::flatten(void* buffer, size_t size) const { size_t totalSize = align4(entrySize); if (byteOffset + totalSize > size) { ALOGE("flatten: not enough room for cache entries"); - return BAD_VALUE; + return -EINVAL; } - EntryHeader* eheader = reinterpret_cast<EntryHeader*>( - &byteBuffer[byteOffset]); + EntryHeader* eheader = reinterpret_cast<EntryHeader*>(&byteBuffer[byteOffset]); eheader->mKeySize = keySize; eheader->mValueSize = valueSize; @@ -224,22 +220,22 @@ status_t BlobCache::flatten(void* buffer, size_t size) const { byteOffset += totalSize; } - return OK; + return 0; } -status_t BlobCache::unflatten(void const* buffer, size_t size) { +int BlobCache::unflatten(void const* buffer, size_t size) { // All errors should result in the BlobCache being in an empty state. mCacheEntries.clear(); // Read the cache header if (size < sizeof(Header)) { ALOGE("unflatten: not enough room for cache header"); - return BAD_VALUE; + return -EINVAL; } const Header* header = reinterpret_cast<const Header*>(buffer); if (header->mMagicNumber != blobCacheMagic) { ALOGE("unflatten: bad magic number: %" PRIu32, header->mMagicNumber); - return BAD_VALUE; + return -EINVAL; } char buildId[PROPERTY_VALUE_MAX]; int len = property_get("ro.build.id", buildId, ""); @@ -248,7 +244,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) { len != header->mBuildIdLength || strncmp(buildId, header->mBuildId, len)) { // We treat version mismatches as an empty cache. - return OK; + return 0; } // Read cache entries @@ -259,7 +255,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) { if (byteOffset + sizeof(EntryHeader) > size) { mCacheEntries.clear(); ALOGE("unflatten: not enough room for cache entry headers"); - return BAD_VALUE; + return -EINVAL; } const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>( @@ -272,7 +268,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) { if (byteOffset + totalSize > size) { mCacheEntries.clear(); ALOGE("unflatten: not enough room for cache entry headers"); - return BAD_VALUE; + return -EINVAL; } const uint8_t* data = eheader->mData; @@ -281,7 +277,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size) { byteOffset += totalSize; } - return OK; + return 0; } long int BlobCache::blob_random() { @@ -299,7 +295,7 @@ void BlobCache::clean() { size_t i = size_t(blob_random() % (mCacheEntries.size())); const CacheEntry& entry(mCacheEntries[i]); mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize(); - mCacheEntries.removeAt(i); + mCacheEntries.erase(mCacheEntries.begin() + i); } } @@ -307,7 +303,7 @@ bool BlobCache::isCleanable() const { return mTotalSize > mMaxTotalSize / 2; } -BlobCache::Blob::Blob(const void* data, size_t size, bool copyData): +BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) { @@ -341,7 +337,8 @@ size_t BlobCache::Blob::getSize() const { BlobCache::CacheEntry::CacheEntry() { } -BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value): +BlobCache::CacheEntry::CacheEntry( + const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value): mKey(key), mValue(value) { } @@ -361,15 +358,15 @@ const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& return *this; } -sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const { +std::shared_ptr<BlobCache::Blob> BlobCache::CacheEntry::getKey() const { return mKey; } -sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const { +std::shared_ptr<BlobCache::Blob> BlobCache::CacheEntry::getValue() const { return mValue; } -void BlobCache::CacheEntry::setValue(const sp<Blob>& value) { +void BlobCache::CacheEntry::setValue(const std::shared_ptr<Blob>& value) { mValue = value; } diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h index 27a81e3c03..a0a270a5fe 100644 --- a/opengl/libs/EGL/BlobCache.h +++ b/opengl/libs/EGL/BlobCache.h @@ -19,8 +19,8 @@ #include <stddef.h> -#include <utils/RefBase.h> -#include <utils/SortedVector.h> +#include <memory> +#include <vector> namespace android { @@ -31,10 +31,8 @@ namespace android { // and then reloaded in a subsequent execution of the program. This // serialization is non-portable and the data should only be used by the device // that generated it. -class BlobCache : public RefBase { - +class BlobCache { public: - // Create an empty blob cache. The blob cache will cache key/value pairs // with key and value sizes less than or equal to maxKeySize and // maxValueSize, respectively. The total combined size of ALL cache entries @@ -89,7 +87,7 @@ public: // // Preconditions: // size >= this.getFlattenedSize() - status_t flatten(void* buffer, size_t size) const; + int flatten(void* buffer, size_t size) const; // unflatten replaces the contents of the cache with the serialized cache // contents in the memory pointed to by 'buffer'. The previous contents of @@ -97,7 +95,7 @@ public: // unflattening the serialized cache contents then the BlobCache will be // left in an empty state. // - status_t unflatten(void const* buffer, size_t size); + int unflatten(void const* buffer, size_t size); private: // Copying is disallowed. @@ -116,7 +114,7 @@ private: bool isCleanable() const; // A Blob is an immutable sized unstructured data blob. - class Blob : public RefBase { + class Blob { public: Blob(const void* data, size_t size, bool copyData); ~Blob(); @@ -146,24 +144,24 @@ private: class CacheEntry { public: CacheEntry(); - CacheEntry(const sp<Blob>& key, const sp<Blob>& value); + CacheEntry(const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value); CacheEntry(const CacheEntry& ce); bool operator<(const CacheEntry& rhs) const; const CacheEntry& operator=(const CacheEntry&); - sp<Blob> getKey() const; - sp<Blob> getValue() const; + std::shared_ptr<Blob> getKey() const; + std::shared_ptr<Blob> getValue() const; - void setValue(const sp<Blob>& value); + void setValue(const std::shared_ptr<Blob>& value); private: // mKey is the key that identifies the cache entry. - sp<Blob> mKey; + std::shared_ptr<Blob> mKey; // mValue is the cached data associated with the key. - sp<Blob> mValue; + std::shared_ptr<Blob> mValue; }; // A Header is the header for the entire BlobCache serialization format. No @@ -239,7 +237,7 @@ private: // mCacheEntries stores all the cache entries that are resident in memory. // Cache entries are added to it by the 'set' method. - SortedVector<CacheEntry> mCacheEntries; + std::vector<CacheEntry> mCacheEntries; }; } diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp new file mode 100644 index 0000000000..edbaaf090d --- /dev/null +++ b/opengl/libs/EGL/BlobCache_test.cpp @@ -0,0 +1,434 @@ +/* + ** Copyright 2011, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include <fcntl.h> +#include <stdio.h> + +#include <memory> + +#include <gtest/gtest.h> + +#include "BlobCache.h" + +namespace android { + +template<typename T> using sp = std::shared_ptr<T>; + +class BlobCacheTest : public ::testing::Test { +protected: + + enum { + OK = 0, + BAD_VALUE = -EINVAL + }; + + enum { + MAX_KEY_SIZE = 6, + MAX_VALUE_SIZE = 8, + MAX_TOTAL_SIZE = 13, + }; + + virtual void SetUp() { + mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); + } + + virtual void TearDown() { + mBC.reset(); + } + + std::unique_ptr<BlobCache> mBC; +}; + +TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { + unsigned char buf[2] = { 0xee, 0xee }; + mBC->set("ab", 2, "cd", 2); + mBC->set("ef", 2, "gh", 2); + ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); + ASSERT_EQ('c', buf[0]); + ASSERT_EQ('d', buf[1]); + ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2)); + ASSERT_EQ('g', buf[0]); + ASSERT_EQ('h', buf[1]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { + unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ('e', buf[1]); + ASSERT_EQ('f', buf[2]); + ASSERT_EQ('g', buf[3]); + ASSERT_EQ('h', buf[4]); + ASSERT_EQ(0xee, buf[5]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { + unsigned char buf[3] = { 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); +} + +TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); +} + +TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, "ijkl", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('i', buf[0]); + ASSERT_EQ('j', buf[1]); + ASSERT_EQ('k', buf[2]); + ASSERT_EQ('l', buf[3]); +} + +TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { + unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { + char key[MAX_KEY_SIZE+1]; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE+1; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); + ASSERT_EQ(0xee, buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) { + char buf[MAX_VALUE_SIZE+1]; + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1)); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + SCOPED_TRACE(i); + ASSERT_EQ(0xee, buf[i]); + } +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { + // Check a testing assumptions + ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE); + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { + char key[MAX_KEY_SIZE]; + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE, "wxyz", 4); + ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4)); + ASSERT_EQ('w', buf[0]); + ASSERT_EQ('x', buf[1]); + ASSERT_EQ('y', buf[2]); + ASSERT_EQ('z', buf[3]); +} + +TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) { + char buf[MAX_VALUE_SIZE]; + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, + MAX_VALUE_SIZE)); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + SCOPED_TRACE(i); + ASSERT_EQ('b', buf[i]); + } +} + +TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { + // Check a testing assumption + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, bufSize); + ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { + unsigned char buf[1] = { 0xee }; + mBC->set("x", 1, "y", 1); + ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); + ASSERT_EQ('y', buf[0]); +} + +TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { + for (int i = 0; i < 256; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + int numCached = 0; + for (int i = 0; i < 256; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached); +} + +TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + // Insert one more entry, causing a cache overflow. + { + uint8_t k = maxEntries; + mBC->set(&k, 1, "x", 1); + } + // Count the number of entries in the cache. + int numCached = 0; + for (int i = 0; i < maxEntries+1; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_EQ(maxEntries/2 + 1, numCached); +} + +class BlobCacheFlattenTest : public BlobCacheTest { +protected: + virtual void SetUp() { + BlobCacheTest::SetUp(); + mBC2.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); + } + + virtual void TearDown() { + mBC2.reset(); + BlobCacheTest::TearDown(); + } + + void roundTrip() { + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + ASSERT_EQ(OK, mBC2->unflatten(flat, size)); + delete[] flat; + } + + sp<BlobCache> mBC2; +}; + +TEST_F(BlobCacheFlattenTest, FlattenOneValue) { + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + roundTrip(); + ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheFlattenTest, FlattenFullCache) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + roundTrip(); + + // Verify the deserialized cache + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + uint8_t v = 0xee; + ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1)); + ASSERT_EQ(k, v); + } +} + +TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + delete[] flat; + + // Verify the cache that we just serialized + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + uint8_t v = 0xee; + ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1)); + ASSERT_EQ(k, v); + } +} + +TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + size_t size = mBC->getFlattenedSize() - 1; + uint8_t* flat = new uint8_t[size]; + // ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size)); + // TODO: The above fails. I expect this is so because getFlattenedSize() + // overstimates the size by using PROPERTY_VALUE_MAX. + delete[] flat; +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + flat[1] = ~flat[1]; + + // Bad magic should cause an error. + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size)); + delete[] flat; + + // The error should cause the unflatten to result in an empty cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + flat[5] = ~flat[5]; + + // Version mismatches shouldn't cause errors, but should not use the + // serialized entries + ASSERT_EQ(OK, mBC2->unflatten(flat, size)); + delete[] flat; + + // The version mismatch should cause the unflatten to result in an empty + // cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + flat[10] = ~flat[10]; + + // Version mismatches shouldn't cause errors, but should not use the + // serialized entries + ASSERT_EQ(OK, mBC2->unflatten(flat, size)); + delete[] flat; + + // The version mismatch should cause the unflatten to result in an empty + // cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { + unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + + // A buffer truncation shouldt cause an error + // ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1)); + // TODO: The above appears to fail because getFlattenedSize() is + // conservative. + delete[] flat; + + // The error should cause the unflatten to result in an empty cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +} // namespace android diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 0e7cee8e52..dc1a4af46e 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -20,6 +20,9 @@ #include "egl_display.h" + +#include <private/EGL/cache.h> + #include <inttypes.h> #include <sys/mman.h> #include <sys/stat.h> @@ -46,6 +49,11 @@ namespace android { #define BC_EXT_STR "EGL_ANDROID_blob_cache" +// called from android_view_ThreadedRenderer.cpp +void egl_set_cache_filename(const char* filename) { + egl_cache_t::get()->setCacheFilename(filename); +} + // // Callback functions passed to EGL. // @@ -63,8 +71,7 @@ static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, // egl_cache_t definition // egl_cache_t::egl_cache_t() : - mInitialized(false), - mBlobCache(NULL) { + mInitialized(false) { } egl_cache_t::~egl_cache_t() { @@ -130,7 +137,7 @@ void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, } if (mInitialized) { - sp<BlobCache> bc = getBlobCacheLocked(); + BlobCache* bc = getBlobCacheLocked(); bc->set(key, keySize, value, valueSize); if (!mSavePending) { @@ -158,7 +165,7 @@ EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, } if (mInitialized) { - sp<BlobCache> bc = getBlobCacheLocked(); + BlobCache* bc = getBlobCacheLocked(); return bc->get(key, keySize, value, valueSize); } return 0; @@ -169,12 +176,12 @@ void egl_cache_t::setCacheFilename(const char* filename) { mFilename = filename; } -sp<BlobCache> egl_cache_t::getBlobCacheLocked() { - if (mBlobCache == NULL) { - mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize); +BlobCache* egl_cache_t::getBlobCacheLocked() { + if (mBlobCache == nullptr) { + mBlobCache.reset(new BlobCache(maxKeySize, maxValueSize, maxTotalSize)); loadBlobCacheLocked(); } - return mBlobCache; + return mBlobCache.get(); } static uint32_t crc32c(const uint8_t* buf, size_t len) { diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h index 8dea302f63..56360f0bc8 100644 --- a/opengl/libs/EGL/egl_cache.h +++ b/opengl/libs/EGL/egl_cache.h @@ -20,9 +20,9 @@ #include <EGL/egl.h> #include <EGL/eglext.h> -#include <utils/BlobCache.h> -#include <utils/StrongPointer.h> +#include "BlobCache.h" +#include <memory> #include <mutex> #include <string> @@ -80,7 +80,7 @@ private: // key/value blob pairs. If the BlobCache object has not yet been created, // this will do so, loading the serialized cache contents from disk if // possible. - sp<BlobCache> getBlobCacheLocked(); + BlobCache* getBlobCacheLocked(); // saveBlobCache attempts to save the current contents of mBlobCache to // disk. @@ -101,7 +101,7 @@ private: // mBlobCache is the cache in which the key/value blob pairs are stored. It // is initially NULL, and will be initialized by getBlobCacheLocked the // first time it's needed. - sp<BlobCache> mBlobCache; + std::unique_ptr<BlobCache> mBlobCache; // mFilename is the name of the file for storing cache contents in between // program invocations. It is initialized to an empty string at diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index a3502f2908..b696920023 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -21,6 +21,8 @@ #include "../egl_impl.h" +#include <private/EGL/display.h> + #include "egl_cache.h" #include "egl_object.h" #include "egl_tls.h" @@ -54,6 +56,11 @@ static bool findExtension(const char* exts, const char* name, size_t nameLen) { return false; } +int egl_get_init_count(EGLDisplay dpy) { + egl_display_t* eglDisplay = egl_display_t::get(dpy); + return eglDisplay ? eglDisplay->getRefsCount() : 0; +} + egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS]; egl_display_t::egl_display_t() : diff --git a/opengl/libs/EGL/include/private/EGL/cache.h b/opengl/libs/EGL/include/private/EGL/cache.h new file mode 100644 index 0000000000..0a176a8683 --- /dev/null +++ b/opengl/libs/EGL/include/private/EGL/cache.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cutils/compiler.h> + +namespace android { + +ANDROID_API void egl_set_cache_filename(const char* filename); + +} // namespace android diff --git a/opengl/libs/EGL/include/private/EGL/display.h b/opengl/libs/EGL/include/private/EGL/display.h new file mode 100644 index 0000000000..9560de266d --- /dev/null +++ b/opengl/libs/EGL/include/private/EGL/display.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <EGL/egl.h> + +#include <cutils/compiler.h> + +namespace android { + +ANDROID_API int egl_get_init_count(EGLDisplay dpy); + +} // namespace android |