From d45c68dd24fe3dd510af5a9591b5e2f509b56772 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 31 Jul 2013 12:14:24 -0700 Subject: [PATCH] Resource memory optimization. Don't ever need the cached UTF-16 string conversions on the device. Don't need to create those strings, and don't need to create the lookup array for them at all. This requires fixing all of the remaining places in the platform where we interacted with the resources with the blind assumption that the strings are UTF-16. Change-Id: Ia0e5a150499837471e494e85b23b05fa06e0cf1d --- core/jni/android_util_AssetManager.cpp | 34 ++- include/androidfw/ResourceTypes.h | 14 +- libs/androidfw/ResourceTypes.cpp | 336 +++++++++++++++++++------ 3 files changed, 290 insertions(+), 94 deletions(-) diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 32f6ecf7cda30..2c23f9db9468b 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -586,7 +586,7 @@ static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject } ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, &name)) { + if (!am->getResources().getResourceName(resid, true, &name)) { return NULL; } @@ -594,19 +594,27 @@ static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject if (name.package != NULL) { str.setTo(name.package, name.packageLen); } - if (name.type != NULL) { + if (name.type8 != NULL || name.type != NULL) { if (str.size() > 0) { char16_t div = ':'; str.append(&div, 1); } - str.append(name.type, name.typeLen); + if (name.type8 != NULL) { + str.append(String16(name.type8, name.typeLen)); + } else { + str.append(name.type, name.typeLen); + } } - if (name.name != NULL) { + if (name.name8 != NULL || name.name != NULL) { if (str.size() > 0) { char16_t div = '/'; str.append(&div, 1); } - str.append(name.name, name.nameLen); + if (name.name8 != NULL) { + str.append(String16(name.name8, name.nameLen)); + } else { + str.append(name.name, name.nameLen); + } } return env->NewString((const jchar*)str.string(), str.size()); @@ -621,7 +629,7 @@ static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, } ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, &name)) { + if (!am->getResources().getResourceName(resid, true, &name)) { return NULL; } @@ -641,10 +649,14 @@ static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, job } ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, &name)) { + if (!am->getResources().getResourceName(resid, true, &name)) { return NULL; } + if (name.type8 != NULL) { + return env->NewStringUTF(name.type8); + } + if (name.type != NULL) { return env->NewString((const jchar*)name.type, name.typeLen); } @@ -661,10 +673,14 @@ static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jo } ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, &name)) { + if (!am->getResources().getResourceName(resid, true, &name)) { return NULL; } + if (name.name8 != NULL) { + return env->NewStringUTF(name.name8); + } + if (name.name != NULL) { return env->NewString((const jchar*)name.name, name.nameLen); } @@ -680,7 +696,7 @@ static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject { if (outValue == NULL) { jniThrowNullPointerException(env, "outValue"); - return NULL; + return 0; } AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index a305fc3f0c91f..97afa59bd7cd8 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -479,7 +479,7 @@ private: const uint32_t* mEntries; const uint32_t* mEntryStyles; const void* mStrings; - char16_t** mCache; + char16_t mutable** mCache; uint32_t mStringPoolSize; // number of uint16_t const uint32_t* mStyles; uint32_t mStylePoolSize; // number of uint32_t @@ -678,11 +678,15 @@ public: // Returns -1 if no namespace, -2 if idx out of range. int32_t getAttributeNamespaceID(size_t idx) const; const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; - + int32_t getAttributeNameID(size_t idx) const; const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; uint32_t getAttributeNameResID(size_t idx) const; - + + // These will work only if the underlying string pool is UTF-8. + const char* getAttributeNamespace8(size_t idx, size_t* outLen) const; + const char* getAttributeName8(size_t idx, size_t* outLen) const; + int32_t getAttributeValueStringID(size_t idx) const; const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; @@ -1294,12 +1298,14 @@ public: const char16_t* package; size_t packageLen; const char16_t* type; + const char* type8; size_t typeLen; const char16_t* name; + const char* name8; size_t nameLen; }; - bool getResourceName(uint32_t resID, resource_name* outName) const; + bool getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const; /** * Retrieve the value of a resource. If the resource is found, returns a diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 1128e024aaf80..1cc3563e0f3d6 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -35,7 +35,7 @@ #define INT32_MAX ((int32_t)(2147483647)) #endif -#define POOL_NOISY(x) //x +#define STRING_POOL_NOISY(x) //x #define XML_NOISY(x) //x #define TABLE_NOISY(x) //x #define TABLE_GETENTRY(x) //x @@ -378,7 +378,6 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) size_t charSize; if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { charSize = sizeof(uint8_t); - mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); } else { charSize = sizeof(char16_t); } @@ -593,6 +592,23 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); + if (mCache == NULL) { +#ifndef HAVE_ANDROID_OS + STRING_POOL_NOISY(ALOGI("CREATING STRING CACHE OF %d bytes", + mHeader->stringCount*sizeof(char16_t**))); +#else + // We do not want to be in this case when actually running Android. + ALOGW("CREATING STRING CACHE OF %d bytes", + mHeader->stringCount*sizeof(char16_t**)); +#endif + mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); + if (mCache == NULL) { + ALOGW("No memory trying to allocate decode cache table of %d bytes\n", + (int)(mHeader->stringCount*sizeof(char16_t**))); + return NULL; + } + } + if (mCache[idx] != NULL) { return mCache[idx]; } @@ -612,6 +628,7 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const return NULL; } + STRING_POOL_NOISY(ALOGI("Caching UTF8 string: %s", u8str)); utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; @@ -633,20 +650,20 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const const char* ResStringPool::string8At(size_t idx, size_t* outLen) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { - const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); + if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) { + return NULL; + } + const uint32_t off = mEntries[idx]/sizeof(char); if (off < (mStringPoolSize-1)) { - if (isUTF8) { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - *outLen = decodeLength(&str); - size_t encLen = decodeLength(&str); - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return (const char*)str; - } else { - ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); - } + const uint8_t* strings = (uint8_t*)mStrings; + const uint8_t* str = strings+off; + *outLen = decodeLength(&str); + size_t encLen = decodeLength(&str); + if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + return (const char*)str; + } else { + ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); } } else { ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", @@ -695,45 +712,104 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const size_t len; - // TODO optimize searching for UTF-8 strings taking into account - // the cache fill to determine when to convert the searched-for - // string key to UTF-8. + if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) { + STRING_POOL_NOISY(ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string())); - if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { - // Do a binary search for the string... - ssize_t l = 0; - ssize_t h = mHeader->stringCount-1; + // The string pool contains UTF 8 strings; we don't want to cause + // temporary UTF-16 strings to be created as we search. + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... this is a little tricky, + // because the strings are sorted with strzcmp16(). So to match + // the ordering, we need to convert strings in the pool to UTF-16. + // But we don't want to hit the cache, so instead we will have a + // local temporary allocation for the conversions. + char16_t* convBuffer = (char16_t*)malloc(strLen+4); + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; - ssize_t mid; - while (l <= h) { - mid = l + (h - l)/2; - const char16_t* s = stringAt(mid, &len); - int c = s ? strzcmp16(s, len, str, strLen) : -1; - POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(str).string(), - String8(s).string(), - c, (int)l, (int)mid, (int)h)); - if (c == 0) { - return mid; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const uint8_t* s = (const uint8_t*)string8At(mid, &len); + int c; + if (s != NULL) { + char16_t* end = utf8_to_utf16_n(s, len, convBuffer, strLen+3); + *end = 0; + c = strzcmp16(convBuffer, end-convBuffer, str, strLen); + } else { + c = -1; + } + STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + (const char*)s, c, (int)l, (int)mid, (int)h)); + if (c == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + free(convBuffer); + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + free(convBuffer); + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + String8 str8(str, strLen); + const size_t str8Len = str8.size(); + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char* s = string8At(i, &len); + STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", + String8(s).string(), + i)); + if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + return i; + } } } + } else { - // It is unusual to get the ID from an unsorted string block... - // most often this happens because we want to get IDs for style - // span tags; since those always appear at the end of the string - // block, start searching at the back. - for (int i=mHeader->stringCount-1; i>=0; i--) { - const char16_t* s = stringAt(i, &len); - POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", - String8(str, strLen).string(), - String8(s).string(), - i)); - if (s && strzcmp16(s, len, str, strLen) == 0) { - return i; + STRING_POOL_NOISY(ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string())); + + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; + + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const char16_t* s = stringAt(mid, &len); + int c = s ? strzcmp16(s, len, str, strLen) : -1; + STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + String8(s).string(), + c, (int)l, (int)mid, (int)h)); + if (c == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char16_t* s = stringAt(i, &len); + STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", + String8(s).string(), + i)); + if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) { + STRING_POOL_NOISY(ALOGI("MATCH!")); + return i; + } } } } @@ -936,6 +1012,14 @@ const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } +const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNamespaceID(idx); + //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; +} + int32_t ResXMLParser::getAttributeNameID(size_t idx) const { if (mEventCode == START_TAG) { @@ -959,6 +1043,14 @@ const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } +const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNameID(idx); + //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; +} + uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const { int32_t id = getAttributeNameID(idx); @@ -1048,22 +1140,67 @@ ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, const char16_t* attr, size_t attrLen) const { if (mEventCode == START_TAG) { + if (attr == NULL) { + return NAME_NOT_FOUND; + } const size_t N = getAttributeCount(); - for (size_t i=0; i attr=%s, curAttr=%s\n", - // String8(attr).string(), String8(curAttr).string()); - if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { - if (ns == NULL) { - if (curNs == NULL) return i; - } else if (curNs != NULL) { - //printf(" --> ns=%s, curNs=%s\n", - // String8(ns).string(), String8(curNs).string()); - if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; + if (mTree.mStrings.isUTF8()) { + String8 ns8, attr8; + if (ns != NULL) { + ns8 = String8(ns, nsLen); + } + attr8 = String8(attr, attrLen); + STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF8 %s (%d) / %s (%d)", ns8.string(), nsLen, + attr8.string(), attrLen)); + for (size_t i=0; i ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (memcmp(ns8.string(), curNs, nsLen) == 0) { + STRING_POOL_NOISY(ALOGI(" FOUND!")); + return i; + } + } + } + } + } else { + STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF16 %s (%d) / %s (%d)", + String8(ns, nsLen).string(), nsLen, + String8(attr, attrLen).string(), attrLen)); + for (size_t i=0; i ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) { + STRING_POOL_NOISY(ALOGI(" FOUND!")); + return i; + } + } } } } @@ -2940,7 +3077,7 @@ void ResTable::uninit() mHeaders.clear(); } -bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const +bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const { if (mError != NO_ERROR) { return false; @@ -2980,13 +3117,28 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const outName->package = grp->name.string(); outName->packageLen = grp->name.size(); - outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); - outName->name = grp->basePackage->keyStrings.stringAt( - dtohl(entry->key.index), &outName->nameLen); - - // If we have a bad index for some reason, we should abort. - if (outName->type == NULL || outName->name == NULL) { - return false; + if (allowUtf8) { + outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen); + outName->name8 = grp->basePackage->keyStrings.string8At( + dtohl(entry->key.index), &outName->nameLen); + } else { + outName->type8 = NULL; + outName->name8 = NULL; + } + if (outName->type8 == NULL) { + outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL) { + return false; + } + } + if (outName->name8 == NULL) { + outName->name = grp->basePackage->keyStrings.stringAt( + dtohl(entry->key.index), &outName->nameLen); + // If we have a bad index for some reason, we should abort. + if (outName->name == NULL) { + return false; + } } return true; @@ -4485,7 +4637,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, while (cnt > 0) { if (!Res_INTERNALID(bag->map.name.ident)) { //printf("Trying attr #%08x\n", bag->map.name.ident); - if (getResourceName(bag->map.name.ident, &rname)) { + if (getResourceName(bag->map.name.ident, false, &rname)) { #if 0 printf("Matching %s against %s (0x%08x)\n", String8(s, len).string(), @@ -4538,7 +4690,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, for (i=0; imap.name.ident)) { //printf("Trying attr #%08x\n", bagi->map.name.ident); - if (getResourceName(bagi->map.name.ident, &rname)) { + if (getResourceName(bagi->map.name.ident, false, &rname)) { #if 0 printf("Matching %s against %s (0x%08x)\n", String8(start,pos-start).string(), @@ -5216,7 +5368,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - if (!this->getResourceName(resID, &resName)) { + if (!this->getResourceName(resID, true, &resName)) { ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); // add dummy value, or trimming leading/trailing zeroes later will fail vector.push(0); @@ -5483,12 +5635,23 @@ void ResTable::print(bool inclValues) const | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - if (this->getResourceName(resID, &resName)) { + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); + } else { + name8 = String8(resName.name, resName.nameLen); + } printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", resID, CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen), + type8.string(), name8.string(), dtohl(typeConfigs->typeSpecFlags[entryIndex])); } else { printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); @@ -5531,11 +5694,22 @@ void ResTable::print(bool inclValues) const | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - if (this->getResourceName(resID, &resName)) { + if (this->getResourceName(resID, true, &resName)) { + String8 type8; + String8 name8; + if (resName.type8 != NULL) { + type8 = String8(resName.type8, resName.typeLen); + } else { + type8 = String8(resName.type, resName.typeLen); + } + if (resName.name8 != NULL) { + name8 = String8(resName.name8, resName.nameLen); + } else { + name8 = String8(resName.name, resName.nameLen); + } printf(" resource 0x%08x %s:%s/%s: ", resID, CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen)); + type8.string(), name8.string()); } else { printf(" INVALID RESOURCE 0x%08x: ", resID); }