Merge "libandroidfw: use map for more efficient indexOfString()"

This commit is contained in:
Eric Miao
2022-10-12 21:10:29 +00:00
committed by Android (Google) Code Review
2 changed files with 62 additions and 124 deletions

View File

@@ -1020,6 +1020,40 @@ base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool:
return base::unexpected(std::nullopt);
}
template <typename TChar, typename SP>
base::expected<size_t, NullOrIOError> ResStringPool::stringIndex(
SP sp, std::unordered_map<SP, size_t>& map) const
{
AutoMutex lock(mStringIndexLock);
if (map.empty()) {
// build string index on the first call
for (size_t i = 0; i < mHeader->stringCount; i++) {
base::expected<SP, NullOrIOError> s;
if constexpr(std::is_same_v<TChar, char16_t>) {
s = stringAt(i);
} else {
s = string8At(i);
}
if (s.has_value()) {
const auto r = map.insert({*s, i});
if (!r.second) {
ALOGE("failed to build string index, string id=%zu\n", i);
}
} else {
return base::unexpected(s.error());
}
}
}
if (!map.empty()) {
const auto result = map.find(sp);
if (result != map.end())
return result->second;
}
return base::unexpected(std::nullopt);
}
base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_t* str,
size_t strLen) const
{
@@ -1027,134 +1061,28 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_
return base::unexpected(std::nullopt);
}
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) {
if (kDebugStringPoolNoisy) {
ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string());
}
// 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.
size_t convBufferLen = strLen + 4;
std::vector<char16_t> convBuffer(convBufferLen);
ssize_t l = 0;
ssize_t h = mHeader->stringCount-1;
ssize_t mid;
while (l <= h) {
mid = l + (h - l)/2;
int c = -1;
const base::expected<StringPiece, NullOrIOError> s = string8At(mid);
if (UNLIKELY(IsIOError(s))) {
return base::unexpected(s.error());
}
if (s.has_value()) {
char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()),
s->size(), convBuffer.data(), convBufferLen);
c = strzcmp16(convBuffer.data(), end-convBuffer.data(), str, strLen);
}
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
s->data(), c, (int)l, (int)mid, (int)h);
}
if (c == 0) {
if (kDebugStringPoolNoisy) {
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.
String8 str8(str, strLen);
const size_t str8Len = str8.size();
for (int i=mHeader->stringCount-1; i>=0; i--) {
const base::expected<StringPiece, NullOrIOError> s = string8At(i);
if (UNLIKELY(IsIOError(s))) {
return base::unexpected(s.error());
}
if (s.has_value()) {
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, i=%d\n", s->data(), i);
}
if (str8Len == s->size()
&& memcmp(s->data(), str8.string(), str8Len) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI("MATCH!");
}
return i;
}
}
}
}
if (kDebugStringPoolNoisy) {
ALOGI("indexOfString (%s): %s", isUTF8() ? "UTF-8" : "UTF-16",
String8(str, strLen).string());
}
base::expected<size_t, NullOrIOError> idx;
if (isUTF8()) {
auto str8 = String8(str, strLen);
idx = stringIndex<char>(StringPiece(str8.c_str(), str8.size()), mStringIndex8);
} else {
idx = stringIndex<char16_t>(StringPiece16(str, strLen), mStringIndex16);
}
if (UNLIKELY(!idx.has_value())) {
return base::unexpected(idx.error());
}
if (*idx < mHeader->stringCount) {
if (kDebugStringPoolNoisy) {
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 base::expected<StringPiece16, NullOrIOError> s = stringAt(mid);
if (UNLIKELY(IsIOError(s))) {
return base::unexpected(s.error());
}
int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1;
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h);
}
if (c == 0) {
if (kDebugStringPoolNoisy) {
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 base::expected<StringPiece16, NullOrIOError> s = stringAt(i);
if (UNLIKELY(IsIOError(s))) {
return base::unexpected(s.error());
}
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i);
}
if (s.has_value() && strLen == s->size() &&
strzcmp16(s->data(), s->size(), str, strLen) == 0) {
if (kDebugStringPoolNoisy) {
ALOGI("MATCH!");
}
return i;
}
}
ALOGI("MATCH! (idx=%zu)", *idx);
}
return *idx;
}
return base::unexpected(std::nullopt);
}

View File

@@ -41,6 +41,7 @@
#include <array>
#include <map>
#include <memory>
#include <unordered_map>
namespace android {
@@ -562,8 +563,17 @@ private:
incfs::map_ptr<uint32_t> mStyles;
uint32_t mStylePoolSize; // number of uint32_t
// mStringIndex is used to quickly map a string to its ID
mutable Mutex mStringIndexLock;
mutable std::unordered_map<StringPiece, size_t> mStringIndex8;
mutable std::unordered_map<StringPiece16, size_t> mStringIndex16;
base::expected<StringPiece, NullOrIOError> stringDecodeAt(
size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
template <typename TChar, typename SP=BasicStringPiece<TChar>>
base::expected<size_t, NullOrIOError> stringIndex(
SP str, std::unordered_map<SP, size_t>& map) const;
};
/**