Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

Commit c8f71aa

Browse files
author
Adam Lesinski
committed
Add ResTable_sparseTypeEntry support
Benchmarks on bullhead-userdebug show that there is a negligent performance impact when using sparse entries on a 30% loaded sparse type of 1000 resources. Benchmark Time CPU Iterations ----------------------------------------------------------------------------------- BM_SparseEntryGetResourceSparseLarge 255 ns 254 ns 2751408 BM_SparseEntryGetResourceNotSparseLarge 254 ns 254 ns 2756534 Bug: 27381711 Test: make libandroidfw_tests aapt2_tests Change-Id: I051ea22f2f6b2bc3696e446adc9e2a34be18009f
1 parent c535d12 commit c8f71aa

30 files changed

+2125
-182
lines changed

libs/androidfw/ResourceTypes.cpp

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6074,6 +6074,10 @@ bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const {
60746074
return true;
60756075
}
60766076

6077+
static bool keyCompare(const ResTable_sparseTypeEntry& entry , uint16_t entryIdx) {
6078+
return dtohs(entry.idx) < entryIdx;
6079+
}
6080+
60776081
status_t ResTable::getEntry(
60786082
const PackageGroup* packageGroup, int typeIndex, int entryIndex,
60796083
const ResTable_config* config,
@@ -6115,6 +6119,9 @@ status_t ResTable::getEntry(
61156119
currentTypeIsOverlay = true;
61166120
}
61176121

6122+
// Check that the entry idx is within range of the declared entry count (ResTable_typeSpec).
6123+
// Particular types (ResTable_type) may be encoded with sparse entries, and so their
6124+
// entryCount do not need to match.
61186125
if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) {
61196126
ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
61206127
Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex),
@@ -6169,11 +6176,37 @@ status_t ResTable::getEntry(
61696176
continue;
61706177
}
61716178

6172-
// Check if there is the desired entry in this type.
61736179
const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
61746180
reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
61756181

6176-
uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
6182+
uint32_t thisOffset;
6183+
6184+
// Check if there is the desired entry in this type.
6185+
if (thisType->flags & ResTable_type::FLAG_SPARSE) {
6186+
// This is encoded as a sparse map, so perform a binary search.
6187+
const ResTable_sparseTypeEntry* sparseIndices =
6188+
reinterpret_cast<const ResTable_sparseTypeEntry*>(eindex);
6189+
const ResTable_sparseTypeEntry* result = std::lower_bound(
6190+
sparseIndices, sparseIndices + dtohl(thisType->entryCount), realEntryIndex,
6191+
keyCompare);
6192+
if (result == sparseIndices + dtohl(thisType->entryCount)
6193+
|| dtohs(result->idx) != realEntryIndex) {
6194+
// No entry found.
6195+
continue;
6196+
}
6197+
6198+
// Extract the offset from the entry. Each offset must be a multiple of 4
6199+
// so we store it as the real offset divided by 4.
6200+
thisOffset = dtohs(result->offset) * 4u;
6201+
} else {
6202+
if (static_cast<uint32_t>(realEntryIndex) >= dtohl(thisType->entryCount)) {
6203+
// Entry does not exist.
6204+
continue;
6205+
}
6206+
6207+
thisOffset = dtohl(eindex[realEntryIndex]);
6208+
}
6209+
61776210
if (thisOffset == ResTable_type::NO_ENTRY) {
61786211
// There is no entry for this index and configuration.
61796212
continue;
@@ -6480,12 +6513,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
64806513
}
64816514

64826515
Type* t = typeList.editItemAt(typeList.size() - 1);
6483-
if (newEntryCount != t->entryCount) {
6484-
ALOGE("ResTable_type entry count inconsistent: given %d, previously %d",
6485-
(int)newEntryCount, (int)t->entryCount);
6486-
return (mError=BAD_TYPE);
6487-
}
6488-
64896516
if (t->package != package) {
64906517
ALOGE("No TypeSpec for type %d", type->id);
64916518
return (mError=BAD_TYPE);
@@ -7096,8 +7123,17 @@ void ResTable::print(bool inclValues) const
70967123
thisConfig.copyFromDtoH(type->config);
70977124

70987125
String8 configStr = thisConfig.toString();
7099-
printf(" config %s:\n", configStr.size() > 0
7126+
printf(" config %s", configStr.size() > 0
71007127
? configStr.string() : "(default)");
7128+
if (type->flags != 0u) {
7129+
printf(" flags=0x%02x", type->flags);
7130+
if (type->flags & ResTable_type::FLAG_SPARSE) {
7131+
printf(" [sparse]");
7132+
}
7133+
}
7134+
7135+
printf(":\n");
7136+
71017137
size_t entryCount = dtohl(type->entryCount);
71027138
uint32_t entriesStart = dtohl(type->entriesStart);
71037139
if ((entriesStart&0x3) != 0) {
@@ -7109,18 +7145,30 @@ void ResTable::print(bool inclValues) const
71097145
printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
71107146
continue;
71117147
}
7112-
for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
7113-
const uint32_t* const eindex = (const uint32_t*)
7114-
(((const uint8_t*)type) + dtohs(type->header.headerSize));
71157148

7116-
uint32_t thisOffset = dtohl(eindex[entryIndex]);
7117-
if (thisOffset == ResTable_type::NO_ENTRY) {
7118-
continue;
7149+
const uint32_t* const eindex = (const uint32_t*)
7150+
(((const uint8_t*)type) + dtohs(type->header.headerSize));
7151+
for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
7152+
size_t entryId;
7153+
uint32_t thisOffset;
7154+
if (type->flags & ResTable_type::FLAG_SPARSE) {
7155+
const ResTable_sparseTypeEntry* entry =
7156+
reinterpret_cast<const ResTable_sparseTypeEntry*>(
7157+
eindex + entryIndex);
7158+
entryId = dtohs(entry->idx);
7159+
// Offsets are encoded as divided by 4.
7160+
thisOffset = static_cast<uint32_t>(dtohs(entry->offset)) * 4u;
7161+
} else {
7162+
entryId = entryIndex;
7163+
thisOffset = dtohl(eindex[entryIndex]);
7164+
if (thisOffset == ResTable_type::NO_ENTRY) {
7165+
continue;
7166+
}
71197167
}
71207168

71217169
uint32_t resID = (0xff000000 & ((packageId)<<24))
71227170
| (0x00ff0000 & ((typeIndex+1)<<16))
7123-
| (0x0000ffff & (entryIndex));
7171+
| (0x0000ffff & (entryId));
71247172
if (packageId == 0) {
71257173
pg->dynamicRefTable.lookupResourceId(&resID);
71267174
}

libs/androidfw/TypeWrappers.cpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,45 @@
1616

1717
#include <androidfw/TypeWrappers.h>
1818

19+
#include <algorithm>
20+
1921
namespace android {
2022

23+
TypeVariant::TypeVariant(const ResTable_type* data) : data(data), mLength(dtohl(data->entryCount)) {
24+
if (data->flags & ResTable_type::FLAG_SPARSE) {
25+
const uint32_t entryCount = dtohl(data->entryCount);
26+
const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(data) + dtohl(data->header.size);
27+
const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
28+
reinterpret_cast<uintptr_t>(data) + dtohs(data->header.headerSize));
29+
if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount)
30+
> containerEnd) {
31+
ALOGE("Type's entry indices extend beyond its boundaries");
32+
mLength = 0;
33+
} else {
34+
mLength = ResTable_sparseTypeEntry{entryIndices[entryCount - 1]}.idx + 1;
35+
}
36+
}
37+
}
38+
2139
TypeVariant::iterator& TypeVariant::iterator::operator++() {
2240
mIndex++;
23-
if (mIndex > dtohl(mTypeVariant->data->entryCount)) {
24-
mIndex = dtohl(mTypeVariant->data->entryCount);
41+
if (mIndex > mTypeVariant->mLength) {
42+
mIndex = mTypeVariant->mLength;
2543
}
2644
return *this;
2745
}
2846

47+
static bool keyCompare(uint32_t entry, uint16_t index) {
48+
return dtohs(ResTable_sparseTypeEntry{entry}.idx) < index;
49+
}
50+
2951
const ResTable_entry* TypeVariant::iterator::operator*() const {
3052
const ResTable_type* type = mTypeVariant->data;
31-
const uint32_t entryCount = dtohl(type->entryCount);
32-
if (mIndex >= entryCount) {
53+
if (mIndex >= mTypeVariant->mLength) {
3354
return NULL;
3455
}
3556

57+
const uint32_t entryCount = dtohl(mTypeVariant->data->entryCount);
3658
const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
3759
+ dtohl(type->header.size);
3860
const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
@@ -42,7 +64,19 @@ const ResTable_entry* TypeVariant::iterator::operator*() const {
4264
return NULL;
4365
}
4466

45-
const uint32_t entryOffset = dtohl(entryIndices[mIndex]);
67+
uint32_t entryOffset;
68+
if (type->flags & ResTable_type::FLAG_SPARSE) {
69+
auto iter = std::lower_bound(entryIndices, entryIndices + entryCount, mIndex, keyCompare);
70+
if (iter == entryIndices + entryCount
71+
|| dtohs(ResTable_sparseTypeEntry{*iter}.idx) != mIndex) {
72+
return NULL;
73+
}
74+
75+
entryOffset = static_cast<uint32_t>(dtohs(ResTable_sparseTypeEntry{*iter}.offset)) * 4u;
76+
} else {
77+
entryOffset = dtohl(entryIndices[mIndex]);
78+
}
79+
4680
if (entryOffset == ResTable_type::NO_ENTRY) {
4781
return NULL;
4882
}

libs/androidfw/include/androidfw/ResourceTypes.h

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,12 +1339,21 @@ struct ResTable_typeSpec
13391339

13401340
/**
13411341
* A collection of resource entries for a particular resource data
1342-
* type. Followed by an array of uint32_t defining the resource
1342+
* type.
1343+
*
1344+
* If the flag FLAG_SPARSE is not set in `flags`, then this struct is
1345+
* followed by an array of uint32_t defining the resource
13431346
* values, corresponding to the array of type strings in the
13441347
* ResTable_package::typeStrings string block. Each of these hold an
13451348
* index from entriesStart; a value of NO_ENTRY means that entry is
13461349
* not defined.
13471350
*
1351+
* If the flag FLAG_SPARSE is set in `flags`, then this struct is followed
1352+
* by an array of ResTable_sparseTypeEntry defining only the entries that
1353+
* have values for this type. Each entry is sorted by their entry ID such
1354+
* that a binary search can be performed over the entries. The ID and offset
1355+
* are encoded in a uint32_t. See ResTabe_sparseTypeEntry.
1356+
*
13481357
* There may be multiple of these chunks for a particular resource type,
13491358
* supply different configuration variations for the resource values of
13501359
* that type.
@@ -1365,10 +1374,17 @@ struct ResTable_type
13651374
// resource identifier). 0 is invalid.
13661375
uint8_t id;
13671376

1377+
enum {
1378+
// If set, the entry is sparse, and encodes both the entry ID and offset into each entry,
1379+
// and a binary search is used to find the key. Only available on platforms >= O.
1380+
// Mark any types that use this with a v26 qualifier to prevent runtime issues on older
1381+
// platforms.
1382+
FLAG_SPARSE = 0x01,
1383+
};
1384+
uint8_t flags;
1385+
13681386
// Must be 0.
1369-
uint8_t res0;
1370-
// Must be 0.
1371-
uint16_t res1;
1387+
uint16_t reserved;
13721388

13731389
// Number of uint32_t entry indices that follow.
13741390
uint32_t entryCount;
@@ -1380,6 +1396,24 @@ struct ResTable_type
13801396
ResTable_config config;
13811397
};
13821398

1399+
/**
1400+
* An entry in a ResTable_type with the flag `FLAG_SPARSE` set.
1401+
*/
1402+
union ResTable_sparseTypeEntry {
1403+
// Holds the raw uint32_t encoded value. Do not read this.
1404+
uint32_t entry;
1405+
struct {
1406+
// The index of the entry.
1407+
uint16_t idx;
1408+
1409+
// The offset from ResTable_type::entriesStart, divided by 4.
1410+
uint16_t offset;
1411+
};
1412+
};
1413+
1414+
static_assert(sizeof(ResTable_sparseTypeEntry) == sizeof(uint32_t),
1415+
"ResTable_sparseTypeEntry must be 4 bytes in size");
1416+
13831417
/**
13841418
* This is the beginning of information about an entry in the resource
13851419
* table. It holds the reference to the name of this entry, and is

libs/androidfw/include/androidfw/TypeWrappers.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
namespace android {
2424

2525
struct TypeVariant {
26-
TypeVariant(const ResTable_type* data)
27-
: data(data) {}
26+
TypeVariant(const ResTable_type* data);
2827

2928
class iterator {
3029
public:
@@ -72,10 +71,13 @@ struct TypeVariant {
7271
}
7372

7473
iterator endEntries() const {
75-
return iterator(this, dtohl(data->entryCount));
74+
return iterator(this, mLength);
7675
}
7776

7877
const ResTable_type* data;
78+
79+
private:
80+
size_t mLength;
7981
};
8082

8183
} // namespace android

libs/androidfw/tests/Android.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ testFiles := \
4545
benchmarkFiles := \
4646
AssetManager2_bench.cpp \
4747
BenchMain.cpp \
48+
BenchmarkHelpers.cpp \
49+
SparseEntry_bench.cpp \
4850
TestHelpers.cpp \
4951
Theme_bench.cpp
5052

libs/androidfw/tests/AssetManager2_bench.cpp

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "androidfw/AssetManager2.h"
2323
#include "androidfw/ResourceTypes.h"
2424

25+
#include "BenchmarkHelpers.h"
2526
#include "TestHelpers.h"
2627
#include "data/basic/R.h"
2728
#include "data/libclient/R.h"
@@ -112,34 +113,6 @@ static void GetResourceBenchmark(const std::vector<std::string>& paths,
112113
}
113114
}
114115

115-
static void GetResourceBenchmarkOld(const std::vector<std::string>& paths,
116-
const ResTable_config* config, uint32_t resid,
117-
benchmark::State& state) {
118-
AssetManager assetmanager;
119-
for (const std::string& path : paths) {
120-
if (!assetmanager.addAssetPath(String8(path.c_str()), nullptr /* cookie */,
121-
false /* appAsLib */, false /* isSystemAssets */)) {
122-
state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
123-
return;
124-
}
125-
}
126-
127-
if (config != nullptr) {
128-
assetmanager.setConfiguration(*config);
129-
}
130-
131-
const ResTable& table = assetmanager.getResources(true);
132-
133-
Res_value value;
134-
ResTable_config selected_config;
135-
uint32_t flags;
136-
137-
while (state.KeepRunning()) {
138-
table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
139-
&selected_config);
140-
}
141-
}
142-
143116
static void BM_AssetManagerGetResource(benchmark::State& state) {
144117
GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
145118
basic::R::integer::number1, state);
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (C) 2017 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "BenchmarkHelpers.h"
18+
19+
#include "android-base/stringprintf.h"
20+
#include "androidfw/AssetManager.h"
21+
22+
namespace android {
23+
24+
void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTable_config* config,
25+
uint32_t resid, benchmark::State& state) {
26+
AssetManager assetmanager;
27+
for (const std::string& path : paths) {
28+
if (!assetmanager.addAssetPath(String8(path.c_str()), nullptr /* cookie */,
29+
false /* appAsLib */, false /* isSystemAssets */)) {
30+
state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
31+
return;
32+
}
33+
}
34+
35+
if (config != nullptr) {
36+
assetmanager.setConfiguration(*config);
37+
}
38+
39+
const ResTable& table = assetmanager.getResources(true);
40+
41+
Res_value value;
42+
ResTable_config selected_config;
43+
uint32_t flags;
44+
45+
while (state.KeepRunning()) {
46+
table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
47+
&selected_config);
48+
}
49+
}
50+
51+
} // namespace android

0 commit comments

Comments
 (0)