ResourceIdCache.cpp revision 40e8eefbedcafc51948945647d746daaee092f16
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
13static size_t mHits = 0;
14static size_t mMisses = 0;
15static size_t mCollisions = 0;
16
17static const size_t MAX_CACHE_ENTRIES = 2048;
18static const android::String16 TRUE16("1");
19static const android::String16 FALSE16("0");
20
21struct CacheEntry {
22    // concatenation of the relevant strings into a single instance
23    android::String16 hashedName;
24    uint32_t id;
25
26    CacheEntry() {}
27    CacheEntry(const android::String16& name, uint32_t resId) : hashedName(name), id(resId) { }
28};
29
30static std::map< uint32_t, CacheEntry > mIdMap;
31
32
33// djb2; reasonable choice for strings when collisions aren't particularly important
34static inline uint32_t hashround(uint32_t hash, int c) {
35    return ((hash << 5) + hash) + c;    /* hash * 33 + c */
36}
37
38static 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
45namespace android {
46
47static 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
58uint32_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
84uint32_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
97void 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