Cache resource ID lookups in aapt

This speeds up certain workloads considerably, particularly
those involved in buildling apps via the SDK.  Windows-based
use should particularly benefit from the change.

(cherry picked from commit d8dde13a63565dcd72bcf03a5088407b737ba793)

Change-Id: I33835bc64ade77688d41e8bfcd371b0a5f59d8fd
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index d9b0681..5b88669 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -20,6 +20,7 @@
 	StringPool.cpp \
 	XMLNode.cpp \
 	ResourceFilter.cpp \
+	ResourceIdCache.cpp \
 	ResourceTable.cpp \
 	Images.cpp \
 	Resource.cpp \
diff --git a/tools/aapt/ResourceIdCache.cpp b/tools/aapt/ResourceIdCache.cpp
new file mode 100644
index 0000000..e03f4f6
--- /dev/null
+++ b/tools/aapt/ResourceIdCache.cpp
@@ -0,0 +1,107 @@
+//
+// 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>
+using namespace std;
+
+
+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 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.string();
+    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);
+    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: %ld\n", mIdMap.size());
+    printf("Hits:   %ld\n", mHits);
+    printf("Misses: %ld\n", mMisses);
+    printf("(Collisions: %ld)\n", mCollisions);
+}
+
+}
diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h
new file mode 100644
index 0000000..65f7781
--- /dev/null
+++ b/tools/aapt/ResourceIdCache.h
@@ -0,0 +1,30 @@
+//
+// Copyright 2012 The Android Open Source Project
+//
+// Manage a resource ID cache.
+
+#ifndef RESOURCE_ID_CACHE_H
+#define RESOURCE_ID_CACHE_H
+
+namespace android {
+class android::String16;
+
+class ResourceIdCache {
+public:
+    static uint32_t lookup(const android::String16& package,
+            const android::String16& type,
+            const android::String16& name,
+            bool onlyPublic);
+
+    static uint32_t store(const android::String16& package,
+            const android::String16& type,
+            const android::String16& name,
+            bool onlyPublic,
+            uint32_t resId);
+
+    static void dump(void);
+};
+
+}
+
+#endif
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 3d7b088..52ebaf0 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -8,6 +8,7 @@
 
 #include "XMLNode.h"
 #include "ResourceFilter.h"
+#include "ResourceIdCache.h"
 
 #include <androidfw/ResourceTypes.h>
 #include <utils/ByteOrder.h>
@@ -1998,6 +1999,9 @@
                                  const String16& name,
                                  bool onlyPublic) const
 {
+    uint32_t id = ResourceIdCache::lookup(package, type, name, onlyPublic);
+    if (id != 0) return id;     // cache hit
+
     sp<Package> p = mPackages.valueFor(package);
     if (p == NULL) return 0;
 
@@ -2016,11 +2020,10 @@
         }
         
         if (Res_INTERNALID(rid)) {
-            return rid;
+            return ResourceIdCache::store(package, type, name, onlyPublic, rid);
         }
-        return Res_MAKEID(p->getAssignedId()-1,
-                          Res_GETTYPE(rid),
-                          Res_GETENTRY(rid));
+        return ResourceIdCache::store(package, type, name, onlyPublic,
+                Res_MAKEID(p->getAssignedId()-1, Res_GETTYPE(rid), Res_GETENTRY(rid)));
     }
 
     sp<Type> t = p->getTypes().valueFor(type);
@@ -2029,7 +2032,9 @@
     if (c == NULL) return 0;
     int32_t ei = c->getEntryIndex();
     if (ei < 0) return 0;
-    return getResId(p, t, ei);
+
+    return ResourceIdCache::store(package, type, name, onlyPublic,
+            getResId(p, t, ei));
 }
 
 uint32_t ResourceTable::getResId(const String16& ref,