| // | |
| // Copyright 2012 The Android Open Source Project | |
| // | |
| // Manage a resource ID cache. | |
| #define LOG_TAG "ResourceIdCache" | |
| #include <utils/String16.h> | |
| #include <utils/Log.h> | |
| #include "ResourceIdCache.h" | |
| #include <map> | |
| static size_t mHits = 0; | |
| static size_t mMisses = 0; | |
| static size_t mCollisions = 0; | |
| static const size_t MAX_CACHE_ENTRIES = 2048; | |
| static const android::String16 TRUE16("1"); | |
| static const android::String16 FALSE16("0"); | |
| struct CacheEntry { | |
| // concatenation of the relevant strings into a single instance | |
| android::String16 hashedName; | |
| uint32_t id; | |
| CacheEntry() {} | |
| CacheEntry(const android::String16& name, uint32_t resId) : hashedName(name), id(resId) { } | |
| }; | |
| static std::map< uint32_t, CacheEntry > mIdMap; | |
| // djb2; reasonable choice for strings when collisions aren't particularly important | |
| static inline uint32_t hashround(uint32_t hash, int c) { | |
| return ((hash << 5) + hash) + c; /* hash * 33 + c */ | |
| } | |
| static uint32_t hash(const android::String16& hashableString) { | |
| uint32_t hash = 5381; | |
| const char16_t* str = hashableString.c_str(); | |
| while (int c = *str++) hash = hashround(hash, c); | |
| return hash; | |
| } | |
| namespace android { | |
| static inline String16 makeHashableName(const android::String16& package, | |
| const android::String16& type, | |
| const android::String16& name, | |
| bool onlyPublic) { | |
| String16 hashable = String16(name); | |
| hashable += type; | |
| hashable += package; | |
| hashable += (onlyPublic ? TRUE16 : FALSE16); | |
| return hashable; | |
| } | |
| uint32_t ResourceIdCache::lookup(const android::String16& package, | |
| const android::String16& type, | |
| const android::String16& name, | |
| bool onlyPublic) { | |
| const String16 hashedName = makeHashableName(package, type, name, onlyPublic); | |
| const uint32_t hashcode = hash(hashedName); | |
| std::map<uint32_t, CacheEntry>::iterator item = mIdMap.find(hashcode); | |
| if (item == mIdMap.end()) { | |
| // cache miss | |
| mMisses++; | |
| return 0; | |
| } | |
| // legit match? | |
| if (hashedName == (*item).second.hashedName) { | |
| mHits++; | |
| return (*item).second.id; | |
| } | |
| // collision | |
| mCollisions++; | |
| mIdMap.erase(hashcode); | |
| return 0; | |
| } | |
| // returns the resource ID being stored, for callsite convenience | |
| uint32_t ResourceIdCache::store(const android::String16& package, | |
| const android::String16& type, | |
| const android::String16& name, | |
| bool onlyPublic, | |
| uint32_t resId) { | |
| if (mIdMap.size() < MAX_CACHE_ENTRIES) { | |
| const String16 hashedName = makeHashableName(package, type, name, onlyPublic); | |
| const uint32_t hashcode = hash(hashedName); | |
| mIdMap[hashcode] = CacheEntry(hashedName, resId); | |
| } | |
| return resId; | |
| } | |
| void ResourceIdCache::dump() { | |
| printf("ResourceIdCache dump:\n"); | |
| printf("Size: %zd\n", mIdMap.size()); | |
| printf("Hits: %zd\n", mHits); | |
| printf("Misses: %zd\n", mMisses); | |
| printf("(Collisions: %zd)\n", mCollisions); | |
| } | |
| } |