1 //
2 // Copyright 2012 The Android Open Source Project
3 //
4 // Manage a resource ID cache.
5 
6 #define LOG_TAG "ResourceIdCache"
7 
8 #include <utils/String16.h>
9 #include <utils/Log.h>
10 #include "ResourceIdCache.h"
11 #include <map>
12 
13 static size_t mHits = 0;
14 static size_t mMisses = 0;
15 static size_t mCollisions = 0;
16 
17 static const size_t MAX_CACHE_ENTRIES = 2048;
18 static const android::String16 TRUE16("1");
19 static const android::String16 FALSE16("0");
20 
21 struct CacheEntry {
22     // concatenation of the relevant strings into a single instance
23     android::String16 hashedName;
24     uint32_t id;
25 
CacheEntryCacheEntry26     CacheEntry() {}
CacheEntryCacheEntry27     CacheEntry(const android::String16& name, uint32_t resId) : hashedName(name), id(resId) { }
28 };
29 
30 static std::map< uint32_t, CacheEntry > mIdMap;
31 
32 
33 // djb2; reasonable choice for strings when collisions aren't particularly important
hashround(uint32_t hash,int c)34 static inline uint32_t hashround(uint32_t hash, int c) {
35     return ((hash << 5) + hash) + c;    /* hash * 33 + c */
36 }
37 
hash(const android::String16 & hashableString)38 static uint32_t hash(const android::String16& hashableString) {
39     uint32_t hash = 5381;
40     const char16_t* str = hashableString.string();
41     while (int c = *str++) hash = hashround(hash, c);
42     return hash;
43 }
44 
45 namespace android {
46 
makeHashableName(const android::String16 & package,const android::String16 & type,const android::String16 & name,bool onlyPublic)47 static inline String16 makeHashableName(const android::String16& package,
48         const android::String16& type,
49         const android::String16& name,
50         bool onlyPublic) {
51     String16 hashable = String16(name);
52     hashable += type;
53     hashable += package;
54     hashable += (onlyPublic ? TRUE16 : FALSE16);
55     return hashable;
56 }
57 
lookup(const android::String16 & package,const android::String16 & type,const android::String16 & name,bool onlyPublic)58 uint32_t ResourceIdCache::lookup(const android::String16& package,
59         const android::String16& type,
60         const android::String16& name,
61         bool onlyPublic) {
62     const String16 hashedName = makeHashableName(package, type, name, onlyPublic);
63     const uint32_t hashcode = hash(hashedName);
64     std::map<uint32_t, CacheEntry>::iterator item = mIdMap.find(hashcode);
65     if (item == mIdMap.end()) {
66         // cache miss
67         mMisses++;
68         return 0;
69     }
70 
71     // legit match?
72     if (hashedName == (*item).second.hashedName) {
73         mHits++;
74         return (*item).second.id;
75     }
76 
77     // collision
78     mCollisions++;
79     mIdMap.erase(hashcode);
80     return 0;
81 }
82 
83 // returns the resource ID being stored, for callsite convenience
store(const android::String16 & package,const android::String16 & type,const android::String16 & name,bool onlyPublic,uint32_t resId)84 uint32_t ResourceIdCache::store(const android::String16& package,
85         const android::String16& type,
86         const android::String16& name,
87         bool onlyPublic,
88         uint32_t resId) {
89     if (mIdMap.size() < MAX_CACHE_ENTRIES) {
90         const String16 hashedName = makeHashableName(package, type, name, onlyPublic);
91         const uint32_t hashcode = hash(hashedName);
92         mIdMap[hashcode] = CacheEntry(hashedName, resId);
93     }
94     return resId;
95 }
96 
dump()97 void ResourceIdCache::dump() {
98     printf("ResourceIdCache dump:\n");
99     printf("Size: %zd\n", mIdMap.size());
100     printf("Hits:   %zd\n", mHits);
101     printf("Misses: %zd\n", mMisses);
102     printf("(Collisions: %zd)\n", mCollisions);
103 }
104 
105 }
106