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