Home | History | Annotate | Download | only in utils
      1 /*
      2  ** Copyright 2011, The Android Open Source Project
      3  **
      4  ** Licensed under the Apache License, Version 2.0 (the "License");
      5  ** you may not use this file except in compliance with the License.
      6  ** You may obtain a copy of the License at
      7  **
      8  **     http://www.apache.org/licenses/LICENSE-2.0
      9  **
     10  ** Unless required by applicable law or agreed to in writing, software
     11  ** distributed under the License is distributed on an "AS IS" BASIS,
     12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  ** See the License for the specific language governing permissions and
     14  ** limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "BlobCache"
     18 //#define LOG_NDEBUG 0
     19 
     20 #include <stdlib.h>
     21 #include <string.h>
     22 
     23 #include <utils/BlobCache.h>
     24 #include <utils/Log.h>
     25 
     26 namespace android {
     27 
     28 BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
     29         mMaxKeySize(maxKeySize),
     30         mMaxValueSize(maxValueSize),
     31         mMaxTotalSize(maxTotalSize),
     32         mTotalSize(0) {
     33     nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
     34 #ifdef _WIN32
     35     srand(now);
     36 #else
     37     mRandState[0] = (now >> 0) & 0xFFFF;
     38     mRandState[1] = (now >> 16) & 0xFFFF;
     39     mRandState[2] = (now >> 32) & 0xFFFF;
     40 #endif
     41     LOGV("initializing random seed using %lld", now);
     42 }
     43 
     44 void BlobCache::set(const void* key, size_t keySize, const void* value,
     45         size_t valueSize) {
     46     if (mMaxKeySize < keySize) {
     47         LOGV("set: not caching because the key is too large: %d (limit: %d)",
     48                 keySize, mMaxKeySize);
     49         return;
     50     }
     51     if (mMaxValueSize < valueSize) {
     52         LOGV("set: not caching because the value is too large: %d (limit: %d)",
     53                 valueSize, mMaxValueSize);
     54         return;
     55     }
     56     if (mMaxTotalSize < keySize + valueSize) {
     57         LOGV("set: not caching because the combined key/value size is too "
     58                 "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize);
     59         return;
     60     }
     61     if (keySize == 0) {
     62         LOGW("set: not caching because keySize is 0");
     63         return;
     64     }
     65     if (valueSize <= 0) {
     66         LOGW("set: not caching because valueSize is 0");
     67         return;
     68     }
     69 
     70     Mutex::Autolock lock(mMutex);
     71     sp<Blob> dummyKey(new Blob(key, keySize, false));
     72     CacheEntry dummyEntry(dummyKey, NULL);
     73 
     74     while (true) {
     75 
     76         ssize_t index = mCacheEntries.indexOf(dummyEntry);
     77         if (index < 0) {
     78             // Create a new cache entry.
     79             sp<Blob> keyBlob(new Blob(key, keySize, true));
     80             sp<Blob> valueBlob(new Blob(value, valueSize, true));
     81             size_t newTotalSize = mTotalSize + keySize + valueSize;
     82             if (mMaxTotalSize < newTotalSize) {
     83                 if (isCleanable()) {
     84                     // Clean the cache and try again.
     85                     clean();
     86                     continue;
     87                 } else {
     88                     LOGV("set: not caching new key/value pair because the "
     89                             "total cache size limit would be exceeded: %d "
     90                             "(limit: %d)",
     91                             keySize + valueSize, mMaxTotalSize);
     92                     break;
     93                 }
     94             }
     95             mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
     96             mTotalSize = newTotalSize;
     97             LOGV("set: created new cache entry with %d byte key and %d byte value",
     98                     keySize, valueSize);
     99         } else {
    100             // Update the existing cache entry.
    101             sp<Blob> valueBlob(new Blob(value, valueSize, true));
    102             sp<Blob> oldValueBlob(mCacheEntries[index].getValue());
    103             size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
    104             if (mMaxTotalSize < newTotalSize) {
    105                 if (isCleanable()) {
    106                     // Clean the cache and try again.
    107                     clean();
    108                     continue;
    109                 } else {
    110                     LOGV("set: not caching new value because the total cache "
    111                             "size limit would be exceeded: %d (limit: %d)",
    112                             keySize + valueSize, mMaxTotalSize);
    113                     break;
    114                 }
    115             }
    116             mCacheEntries.editItemAt(index).setValue(valueBlob);
    117             mTotalSize = newTotalSize;
    118             LOGV("set: updated existing cache entry with %d byte key and %d byte "
    119                     "value", keySize, valueSize);
    120         }
    121         break;
    122     }
    123 }
    124 
    125 size_t BlobCache::get(const void* key, size_t keySize, void* value,
    126         size_t valueSize) {
    127     if (mMaxKeySize < keySize) {
    128         LOGV("get: not searching because the key is too large: %d (limit %d)",
    129                 keySize, mMaxKeySize);
    130         return 0;
    131     }
    132     Mutex::Autolock lock(mMutex);
    133     sp<Blob> dummyKey(new Blob(key, keySize, false));
    134     CacheEntry dummyEntry(dummyKey, NULL);
    135     ssize_t index = mCacheEntries.indexOf(dummyEntry);
    136     if (index < 0) {
    137         LOGV("get: no cache entry found for key of size %d", keySize);
    138         return 0;
    139     }
    140 
    141     // The key was found. Return the value if the caller's buffer is large
    142     // enough.
    143     sp<Blob> valueBlob(mCacheEntries[index].getValue());
    144     size_t valueBlobSize = valueBlob->getSize();
    145     if (valueBlobSize <= valueSize) {
    146         LOGV("get: copying %d bytes to caller's buffer", valueBlobSize);
    147         memcpy(value, valueBlob->getData(), valueBlobSize);
    148     } else {
    149         LOGV("get: caller's buffer is too small for value: %d (needs %d)",
    150                 valueSize, valueBlobSize);
    151     }
    152     return valueBlobSize;
    153 }
    154 
    155 long int BlobCache::blob_random() {
    156 #ifdef _WIN32
    157     return rand();
    158 #else
    159     return nrand48(mRandState);
    160 #endif
    161 }
    162 
    163 void BlobCache::clean() {
    164     // Remove a random cache entry until the total cache size gets below half
    165     // the maximum total cache size.
    166     while (mTotalSize > mMaxTotalSize / 2) {
    167         size_t i = size_t(blob_random() % (mCacheEntries.size()));
    168         const CacheEntry& entry(mCacheEntries[i]);
    169         mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
    170         mCacheEntries.removeAt(i);
    171     }
    172 }
    173 
    174 bool BlobCache::isCleanable() const {
    175     return mTotalSize > mMaxTotalSize / 2;
    176 }
    177 
    178 BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
    179         mData(copyData ? malloc(size) : data),
    180         mSize(size),
    181         mOwnsData(copyData) {
    182     if (copyData) {
    183         memcpy(const_cast<void*>(mData), data, size);
    184     }
    185 }
    186 
    187 BlobCache::Blob::~Blob() {
    188     if (mOwnsData) {
    189         free(const_cast<void*>(mData));
    190     }
    191 }
    192 
    193 bool BlobCache::Blob::operator<(const Blob& rhs) const {
    194     if (mSize == rhs.mSize) {
    195         return memcmp(mData, rhs.mData, mSize) < 0;
    196     } else {
    197         return mSize < rhs.mSize;
    198     }
    199 }
    200 
    201 const void* BlobCache::Blob::getData() const {
    202     return mData;
    203 }
    204 
    205 size_t BlobCache::Blob::getSize() const {
    206     return mSize;
    207 }
    208 
    209 BlobCache::CacheEntry::CacheEntry() {
    210 }
    211 
    212 BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value):
    213         mKey(key),
    214         mValue(value) {
    215 }
    216 
    217 BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
    218         mKey(ce.mKey),
    219         mValue(ce.mValue) {
    220 }
    221 
    222 bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
    223     return *mKey < *rhs.mKey;
    224 }
    225 
    226 const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
    227     mKey = rhs.mKey;
    228     mValue = rhs.mValue;
    229     return *this;
    230 }
    231 
    232 sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
    233     return mKey;
    234 }
    235 
    236 sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
    237     return mValue;
    238 }
    239 
    240 void BlobCache::CacheEntry::setValue(const sp<Blob>& value) {
    241     mValue = value;
    242 }
    243 
    244 } // namespace android
    245