blob: 4bfdb1205e199c04b53f11940a66e2da2f0ea4fe [file] [log] [blame]
Adam Lesinski355f2852016-02-13 20:26:45 -08001/*
2 * Copyright (C) 2016 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 "ConfigDescription.h"
18#include "ResourceTable.h"
19#include "split/TableSplitter.h"
20
Adam Lesinski803c7c82016-04-06 16:09:43 -070021#include <algorithm>
Adam Lesinski355f2852016-02-13 20:26:45 -080022#include <map>
23#include <set>
24#include <unordered_map>
25#include <vector>
26
27namespace aapt {
28
29using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>;
30using ConfigDensityGroups = std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
31
32static ConfigDescription copyWithoutDensity(const ConfigDescription& config) {
33 ConfigDescription withoutDensity = config;
34 withoutDensity.density = 0;
35 return withoutDensity;
36}
37
38/**
39 * Selects values that match exactly the constraints given.
40 */
41class SplitValueSelector {
42public:
43 SplitValueSelector(const SplitConstraints& constraints) {
44 for (const ConfigDescription& config : constraints.configs) {
45 if (config.density == 0) {
46 mDensityIndependentConfigs.insert(config);
47 } else {
48 mDensityDependentConfigToDensityMap[copyWithoutDensity(config)] = config.density;
49 }
50 }
51 }
52
53 std::vector<ResourceConfigValue*> selectValues(const ConfigDensityGroups& densityGroups,
54 ConfigClaimedMap* claimedValues) {
55 std::vector<ResourceConfigValue*> selected;
56
57 // Select the regular values.
58 for (auto& entry : *claimedValues) {
59 // Check if the entry has a density.
60 ResourceConfigValue* configValue = entry.first;
61 if (configValue->config.density == 0 && !entry.second) {
62 // This is still available.
63 if (mDensityIndependentConfigs.find(configValue->config) !=
64 mDensityIndependentConfigs.end()) {
65 selected.push_back(configValue);
66
67 // Mark the entry as taken.
68 entry.second = true;
69 }
70 }
71 }
72
73 // Now examine the densities
74 for (auto& entry : densityGroups) {
75 // We do not care if the value is claimed, since density values can be
76 // in multiple splits.
77 const ConfigDescription& config = entry.first;
78 const std::vector<ResourceConfigValue*>& relatedValues = entry.second;
79
80 auto densityValueIter = mDensityDependentConfigToDensityMap.find(config);
81 if (densityValueIter != mDensityDependentConfigToDensityMap.end()) {
82 // Select the best one!
83 ConfigDescription targetDensity = config;
84 targetDensity.density = densityValueIter->second;
85
86 ResourceConfigValue* bestValue = nullptr;
87 for (ResourceConfigValue* thisValue : relatedValues) {
88 if (!bestValue ||
89 thisValue->config.isBetterThan(bestValue->config, &targetDensity)) {
90 bestValue = thisValue;
91 }
92
93 // When we select one of these, they are all claimed such that the base
94 // doesn't include any anymore.
95 (*claimedValues)[thisValue] = true;
96 }
97 assert(bestValue);
98 selected.push_back(bestValue);
99 }
100 }
101 return selected;
102 }
103
104private:
105 std::set<ConfigDescription> mDensityIndependentConfigs;
106 std::map<ConfigDescription, uint16_t> mDensityDependentConfigToDensityMap;
107};
108
109/**
110 * Marking non-preferred densities as claimed will make sure the base doesn't include them,
111 * leaving only the preferred density behind.
112 */
113static void markNonPreferredDensitiesAsClaimed(uint16_t preferredDensity,
114 const ConfigDensityGroups& densityGroups,
115 ConfigClaimedMap* configClaimedMap) {
116 for (auto& entry : densityGroups) {
117 const ConfigDescription& config = entry.first;
118 const std::vector<ResourceConfigValue*>& relatedValues = entry.second;
119
120 ConfigDescription targetDensity = config;
121 targetDensity.density = preferredDensity;
122 ResourceConfigValue* bestValue = nullptr;
123 for (ResourceConfigValue* thisValue : relatedValues) {
124 if (!bestValue) {
125 bestValue = thisValue;
126 } else if (thisValue->config.isBetterThan(bestValue->config, &targetDensity)) {
127 // Claim the previous value so that it is not included in the base.
128 (*configClaimedMap)[bestValue] = true;
129 bestValue = thisValue;
130 } else {
131 // Claim this value so that it is not included in the base.
132 (*configClaimedMap)[thisValue] = true;
133 }
134 }
135 assert(bestValue);
136 }
137}
138
139bool TableSplitter::verifySplitConstraints(IAaptContext* context) {
140 bool error = false;
141 for (size_t i = 0; i < mSplitConstraints.size(); i++) {
142 for (size_t j = i + 1; j < mSplitConstraints.size(); j++) {
143 for (const ConfigDescription& config : mSplitConstraints[i].configs) {
144 if (mSplitConstraints[j].configs.find(config) !=
145 mSplitConstraints[j].configs.end()) {
146 context->getDiagnostics()->error(DiagMessage() << "config '" << config
147 << "' appears in multiple splits, "
148 << "target split ambiguous");
149 error = true;
150 }
151 }
152 }
153 }
154 return !error;
155}
156
157void TableSplitter::splitTable(ResourceTable* originalTable) {
158 const size_t splitCount = mSplitConstraints.size();
159 for (auto& pkg : originalTable->packages) {
160 // Initialize all packages for splits.
161 for (size_t idx = 0; idx < splitCount; idx++) {
162 ResourceTable* splitTable = mSplits[idx].get();
163 splitTable->createPackage(pkg->name, pkg->id);
164 }
165
166 for (auto& type : pkg->types) {
167 if (type->type == ResourceType::kMipmap) {
168 // Always keep mipmaps.
169 continue;
170 }
171
172 for (auto& entry : type->entries) {
173 if (mConfigFilter) {
174 // First eliminate any resource that we definitely don't want.
175 for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
176 if (!mConfigFilter->match(configValue->config)) {
177 // null out the entry. We will clean up and remove nulls at the end
178 // for performance reasons.
179 configValue.reset();
180 }
181 }
182 }
183
184 // Organize the values into two separate buckets. Those that are density-dependent
185 // and those that are density-independent.
186 // One density technically matches all density, it's just that some densities
187 // match better. So we need to be aware of the full set of densities to make this
188 // decision.
189 ConfigDensityGroups densityGroups;
190 ConfigClaimedMap configClaimedMap;
191 for (const std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
192 if (configValue) {
193 configClaimedMap[configValue.get()] = false;
194
195 if (configValue->config.density != 0) {
196 // Create a bucket for this density-dependent config.
197 densityGroups[copyWithoutDensity(configValue->config)]
198 .push_back(configValue.get());
199 }
200 }
201 }
202
203 // First we check all the splits. If it doesn't match one of the splits, we
204 // leave it in the base.
205 for (size_t idx = 0; idx < splitCount; idx++) {
206 const SplitConstraints& splitConstraint = mSplitConstraints[idx];
207 ResourceTable* splitTable = mSplits[idx].get();
208
209 // Select the values we want from this entry for this split.
210 SplitValueSelector selector(splitConstraint);
211 std::vector<ResourceConfigValue*> selectedValues =
212 selector.selectValues(densityGroups, &configClaimedMap);
213
214 // No need to do any work if we selected nothing.
215 if (!selectedValues.empty()) {
216 // Create the same resource structure in the split. We do this lazily
217 // because we might not have actual values for each type/entry.
218 ResourceTablePackage* splitPkg = splitTable->findPackage(pkg->name);
219 ResourceTableType* splitType = splitPkg->findOrCreateType(type->type);
220 if (!splitType->id) {
221 splitType->id = type->id;
222 splitType->symbolStatus = type->symbolStatus;
223 }
224
225 ResourceEntry* splitEntry = splitType->findOrCreateEntry(entry->name);
226 if (!splitEntry->id) {
227 splitEntry->id = entry->id;
228 splitEntry->symbolStatus = entry->symbolStatus;
229 }
230
231 // Copy the selected values into the new Split Entry.
232 for (ResourceConfigValue* configValue : selectedValues) {
233 ResourceConfigValue* newConfigValue = splitEntry->findOrCreateValue(
234 configValue->config, configValue->product);
235 newConfigValue->value = std::unique_ptr<Value>(
236 configValue->value->clone(&splitTable->stringPool));
237 }
238 }
239 }
240
241 if (mPreferredDensity) {
242 markNonPreferredDensitiesAsClaimed(mPreferredDensity.value(),
243 densityGroups,
244 &configClaimedMap);
245 }
246
247 // All splits are handled, now check to see what wasn't claimed and remove
248 // whatever exists in other splits.
249 for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
250 if (configValue && configClaimedMap[configValue.get()]) {
251 // Claimed, remove from base.
252 configValue.reset();
253 }
254 }
255
256 // Now erase all nullptrs.
257 entry->values.erase(
258 std::remove(entry->values.begin(), entry->values.end(), nullptr),
259 entry->values.end());
260 }
261 }
262 }
263}
264
265} // namespace aapt