Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 596fa178 authored by Yurii Zubrytskyi's avatar Yurii Zubrytskyi
Browse files

[res] Optimize name lookups in idmap service

The most common operation when build idmaps is to find the
resource ID by name. This is the least optimized operation
in the current resources data structures, as they only
expect ID -> value lookups

This change adds an optional flag that creates a name->ID
hash map inside ResStringPool, and use it when loading
APKs in idmap2d

Bug: 282215580
Test: build + boot + perf record

Change-Id: I82f4d684cb48e2dcddcd677b882b11497c1c13b1
parent 4f48ffd4
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -268,10 +268,11 @@ struct ResState {
  std::unique_ptr<AssetManager2> am;
  ZipAssetsProvider* zip_assets;

  static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip) {
  static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip,
                                     package_property_t flags) {
    ResState state;
    state.zip_assets = zip.get();
    if ((state.apk_assets = ApkAssets::Load(std::move(zip))) == nullptr) {
    if ((state.apk_assets = ApkAssets::Load(std::move(zip), flags)) == nullptr) {
      return Error("failed to load apk asset");
    }

@@ -284,7 +285,7 @@ struct ResState {
    }

    state.am = std::make_unique<AssetManager2>();
    if (!state.am->SetApkAssets({state.apk_assets})) {
    if (!state.am->SetApkAssets({state.apk_assets}, false)) {
      return Error("failed to create asset manager");
    }

@@ -343,8 +344,8 @@ Result<const ResState*> ApkResourceContainer::GetState() const {
    return state;
  }

  auto state =
      ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)));
  auto state = ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)),
                                    PROPERTY_OPTIMIZE_NAME_LOOKUPS);
  if (!state) {
    return state.GetError();
  }
+2 −1
Original line number Diff line number Diff line
@@ -454,7 +454,8 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
                                                         package_property_t property_flags) {
  ATRACE_NAME("LoadedPackage::Load");
  std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage());
  const bool optimize_name_lookups = (property_flags & PROPERTY_OPTIMIZE_NAME_LOOKUPS) != 0;
  std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage(optimize_name_lookups));

  // typeIdOffset was added at some point, but we still must recognize apps built before this
  // was added.
+55 −15
Original line number Diff line number Diff line
@@ -447,14 +447,18 @@ Res_png_9patch* Res_png_9patch::deserialize(void* inData)
// --------------------------------------------------------------------
// --------------------------------------------------------------------

ResStringPool::ResStringPool()
    : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
{
ResStringPool::ResStringPool() : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) {
}

ResStringPool::ResStringPool(const void* data, size_t size, bool copyData)
    : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
{
ResStringPool::ResStringPool(bool optimize_name_lookups) : ResStringPool() {
  if (optimize_name_lookups) {
    mIndexLookupCache.emplace();
  }
}

ResStringPool::ResStringPool(const void* data, size_t size, bool copyData,
                             bool optimize_name_lookups)
    : ResStringPool(optimize_name_lookups) {
  setTo(data, size, copyData);
}

@@ -683,6 +687,14 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD
        mStylePoolSize = 0;
    }

    if (mIndexLookupCache) {
      if ((mHeader->flags & ResStringPool_header::UTF8_FLAG) != 0) {
        mIndexLookupCache->first.reserve(mHeader->stringCount);
      } else {
        mIndexLookupCache->second.reserve(mHeader->stringCount);
      }
    }

    return (mError=NO_ERROR);
}

@@ -708,6 +720,10 @@ void ResStringPool::uninit()
        free(mOwnedData);
        mOwnedData = NULL;
    }
    if (mIndexLookupCache) {
      mIndexLookupCache->first.clear();
      mIndexLookupCache->second.clear();
    }
}

/**
@@ -824,7 +840,7 @@ base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx)

                // encLen must be less than 0x7FFF due to encoding.
                if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) {
                    AutoMutex lock(mDecodeLock);
                  AutoMutex lock(mCachesLock);

                  if (mCache != NULL && mCache[idx] != NULL) {
                    return StringPiece16(mCache[idx], *u16len);
@@ -1093,12 +1109,24 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_
            // block, start searching at the back.
            String8 str8(str, strLen);
            const size_t str8Len = str8.size();
            std::optional<AutoMutex> cacheLock;
            if (mIndexLookupCache) {
              cacheLock.emplace(mCachesLock);
              if (auto it = mIndexLookupCache->first.find(std::string_view(str8));
                  it != mIndexLookupCache->first.end()) {
                return it->second;
              }
            }

            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 (mIndexLookupCache) {
                    mIndexLookupCache->first.insert({*s, i});
                  }
                    if (kDebugStringPoolNoisy) {
                        ALOGI("Looking at %s, i=%d\n", s->data(), i);
                    }
@@ -1151,24 +1179,36 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_
            // 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.
            std::optional<AutoMutex> cacheLock;
            if (mIndexLookupCache) {
              cacheLock.emplace(mCachesLock);
              if (auto it = mIndexLookupCache->second.find({str, strLen});
                  it != mIndexLookupCache->second.end()) {
                return it->second;
              }
            }
            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()).c_str(), i);
                  ALOGI("Looking16 at %s, i=%d\n", String8(s->data(), s->size()).c_str(), i);
                }
                if (s.has_value()) {
                  if (mIndexLookupCache) {
                    mIndexLookupCache->second.insert({*s, i});
                  }
                if (s.has_value() && strLen == s->size() &&
                        strzcmp16(s->data(), s->size(), str, strLen) == 0) {
                  if (strLen == s->size() && strzcmp16(s->data(), s->size(), str, strLen) == 0) {
                    if (kDebugStringPoolNoisy) {
                        ALOGI("MATCH!");
                      ALOGI("MATCH16!");
                    }
                    return i;
                  }
                }
            }
        }
    }
    return base::unexpected(std::nullopt);
}

+6 −1
Original line number Diff line number Diff line
@@ -99,6 +99,9 @@ enum : package_property_t {

  // The apk assets only contain the overlayable declarations information.
  PROPERTY_ONLY_OVERLAYABLES = 1U << 5U,

  // Optimize the resource lookups by name via an in-memory lookup table.
  PROPERTY_OPTIMIZE_NAME_LOOKUPS = 1U << 6U,
};

struct OverlayableInfo {
@@ -285,7 +288,9 @@ class LoadedPackage {
 private:
  DISALLOW_COPY_AND_ASSIGN(LoadedPackage);

  LoadedPackage() = default;
  explicit LoadedPackage(bool optimize_name_lookups = false)
      : type_string_pool_(optimize_name_lookups), key_string_pool_(optimize_name_lookups) {
  }

  ResStringPool type_string_pool_;
  ResStringPool key_string_pool_;
+25 −19
Original line number Diff line number Diff line
@@ -22,27 +22,28 @@

#include <android-base/expected.h>
#include <android-base/unique_fd.h>

#include <android/configuration.h>
#include <androidfw/Asset.h>
#include <androidfw/Errors.h>
#include <androidfw/LocaleData.h>
#include <androidfw/StringPiece.h>
#include <utils/ByteOrder.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/String16.h>
#include <utils/Vector.h>
#include <utils/KeyedVector.h>

#include <utils/threads.h>

#include <stdint.h>
#include <sys/types.h>

#include <android/configuration.h>

#include <array>
#include <map>
#include <memory>
#include <optional>
#include <string_view>
#include <unordered_map>
#include <utility>

namespace android {

@@ -513,7 +514,9 @@ class ResStringPool
{
public:
 ResStringPool();
    ResStringPool(const void* data, size_t size, bool copyData=false);
 explicit ResStringPool(bool optimize_name_lookups);
 ResStringPool(const void* data, size_t size, bool copyData = false,
               bool optimize_name_lookups = false);
 virtual ~ResStringPool();

 void setToEmpty();
@@ -525,8 +528,7 @@ public:

 // Return string entry as UTF16; if the pool is UTF8, the string will
 // be converted before returning.
    inline base::expected<StringPiece16, NullOrIOError> stringAt(
            const ResStringPool_ref& ref) const {
 inline base::expected<StringPiece16, NullOrIOError> stringAt(const ResStringPool_ref& ref) const {
   return stringAt(ref.index);
 }
    virtual base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const;
@@ -557,7 +559,7 @@ private:
    void*                                         mOwnedData;
    incfs::verified_map_ptr<ResStringPool_header> mHeader;
    size_t                                        mSize;
    mutable Mutex                                 mDecodeLock;
    mutable Mutex                                 mCachesLock;
    incfs::map_ptr<uint32_t>                      mEntries;
    incfs::map_ptr<uint32_t>                      mEntryStyles;
    incfs::map_ptr<void>                          mStrings;
@@ -566,6 +568,10 @@ private:
    incfs::map_ptr<uint32_t>                      mStyles;
    uint32_t                                      mStylePoolSize;    // number of uint32_t

    mutable std::optional<std::pair<std::unordered_map<std::string_view, int>,
                                    std::unordered_map<std::u16string_view, int>>>
        mIndexLookupCache;

    base::expected<StringPiece, NullOrIOError> stringDecodeAt(
        size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
};