blob: 27e13d81ff495ea41ee898de2250360982312aae [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
Adam Lesinski355f2852016-02-13 20:26:45 -080017#include "split/TableSplitter.h"
18
Adam Lesinski803c7c82016-04-06 16:09:43 -070019#include <algorithm>
Adam Lesinski355f2852016-02-13 20:26:45 -080020#include <map>
21#include <set>
Pierre Lecesne672384b2017-02-06 10:29:02 +000022#include <unordered_set>
Adam Lesinski355f2852016-02-13 20:26:45 -080023#include <unordered_map>
24#include <vector>
Adam Lesinskid5083f62017-01-16 15:07:21 -080025
Adam Lesinskice5e56e2016-10-21 17:56:45 -070026#include "android-base/logging.h"
27
28#include "ConfigDescription.h"
29#include "ResourceTable.h"
30#include "util/Util.h"
Adam Lesinski355f2852016-02-13 20:26:45 -080031
32namespace aapt {
33
34using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070035using ConfigDensityGroups =
36 std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
Adam Lesinski355f2852016-02-13 20:26:45 -080037
Adam Lesinskice5e56e2016-10-21 17:56:45 -070038static ConfigDescription CopyWithoutDensity(const ConfigDescription& config) {
39 ConfigDescription without_density = config;
40 without_density.density = 0;
41 return without_density;
Adam Lesinski355f2852016-02-13 20:26:45 -080042}
43
44/**
45 * Selects values that match exactly the constraints given.
46 */
47class SplitValueSelector {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070048 public:
49 explicit SplitValueSelector(const SplitConstraints& constraints) {
50 for (const ConfigDescription& config : constraints.configs) {
51 if (config.density == 0) {
52 density_independent_configs_.insert(config);
53 } else {
54 density_dependent_config_to_density_map_[CopyWithoutDensity(config)] =
55 config.density;
56 }
57 }
58 }
59
60 std::vector<ResourceConfigValue*> SelectValues(
61 const ConfigDensityGroups& density_groups,
62 ConfigClaimedMap* claimed_values) {
63 std::vector<ResourceConfigValue*> selected;
64
65 // Select the regular values.
66 for (auto& entry : *claimed_values) {
67 // Check if the entry has a density.
68 ResourceConfigValue* config_value = entry.first;
69 if (config_value->config.density == 0 && !entry.second) {
70 // This is still available.
71 if (density_independent_configs_.find(config_value->config) !=
72 density_independent_configs_.end()) {
73 selected.push_back(config_value);
74
75 // Mark the entry as taken.
76 entry.second = true;
Adam Lesinski355f2852016-02-13 20:26:45 -080077 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070078 }
Adam Lesinski355f2852016-02-13 20:26:45 -080079 }
80
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081 // Now examine the densities
82 for (auto& entry : density_groups) {
83 // We do not care if the value is claimed, since density values can be
84 // in multiple splits.
85 const ConfigDescription& config = entry.first;
86 const std::vector<ResourceConfigValue*>& related_values = entry.second;
87 auto density_value_iter =
88 density_dependent_config_to_density_map_.find(config);
89 if (density_value_iter !=
90 density_dependent_config_to_density_map_.end()) {
91 // Select the best one!
92 ConfigDescription target_density = config;
93 target_density.density = density_value_iter->second;
Adam Lesinski355f2852016-02-13 20:26:45 -080094
Adam Lesinskice5e56e2016-10-21 17:56:45 -070095 ResourceConfigValue* best_value = nullptr;
96 for (ResourceConfigValue* this_value : related_values) {
97 if (!best_value ||
98 this_value->config.isBetterThan(best_value->config,
99 &target_density)) {
100 best_value = this_value;
101 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800102 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700103 CHECK(best_value != nullptr);
Adam Lesinski355f2852016-02-13 20:26:45 -0800104
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700105 // When we select one of these, they are all claimed such that the base
106 // doesn't include any anymore.
107 (*claimed_values)[best_value] = true;
108 selected.push_back(best_value);
109 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800110 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700111 return selected;
112 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800113
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700114 private:
115 DISALLOW_COPY_AND_ASSIGN(SplitValueSelector);
116
117 std::set<ConfigDescription> density_independent_configs_;
118 std::map<ConfigDescription, uint16_t>
119 density_dependent_config_to_density_map_;
Adam Lesinski355f2852016-02-13 20:26:45 -0800120};
121
122/**
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700123 * Marking non-preferred densities as claimed will make sure the base doesn't
124 * include them,
Adam Lesinski355f2852016-02-13 20:26:45 -0800125 * leaving only the preferred density behind.
126 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700127static void MarkNonPreferredDensitiesAsClaimed(
Pierre Lecesne672384b2017-02-06 10:29:02 +0000128 const std::vector<uint16_t>& preferred_densities, const ConfigDensityGroups& density_groups,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700129 ConfigClaimedMap* config_claimed_map) {
130 for (auto& entry : density_groups) {
131 const ConfigDescription& config = entry.first;
132 const std::vector<ResourceConfigValue*>& related_values = entry.second;
Adam Lesinski355f2852016-02-13 20:26:45 -0800133
Pierre Lecesne672384b2017-02-06 10:29:02 +0000134 // There can be multiple best values if there are multiple preferred densities.
135 std::unordered_set<ResourceConfigValue*> best_values;
136
137 // For each preferred density, find the value that is the best.
138 for (uint16_t preferred_density : preferred_densities) {
139 ConfigDescription target_density = config;
140 target_density.density = preferred_density;
141 ResourceConfigValue* best_value = nullptr;
142 for (ResourceConfigValue* this_value : related_values) {
143 if (!best_value || this_value->config.isBetterThan(best_value->config, &target_density)) {
144 best_value = this_value;
145 }
146 }
147 CHECK(best_value != nullptr);
148 best_values.insert(best_value);
149 }
150
151 // Claim all the values that aren't the best so that they will be removed from the base.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700152 for (ResourceConfigValue* this_value : related_values) {
Pierre Lecesne672384b2017-02-06 10:29:02 +0000153 if (best_values.find(this_value) == best_values.end()) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700154 (*config_claimed_map)[this_value] = true;
155 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800156 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700157 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800158}
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700159bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
160 bool error = false;
161 for (size_t i = 0; i < split_constraints_.size(); i++) {
162 for (size_t j = i + 1; j < split_constraints_.size(); j++) {
163 for (const ConfigDescription& config : split_constraints_[i].configs) {
164 if (split_constraints_[j].configs.find(config) !=
165 split_constraints_[j].configs.end()) {
166 context->GetDiagnostics()->Error(DiagMessage()
167 << "config '" << config
168 << "' appears in multiple splits, "
169 << "target split ambiguous");
170 error = true;
Adam Lesinski355f2852016-02-13 20:26:45 -0800171 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700172 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800173 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700174 }
175 return !error;
Adam Lesinski355f2852016-02-13 20:26:45 -0800176}
177
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700178void TableSplitter::SplitTable(ResourceTable* original_table) {
179 const size_t split_count = split_constraints_.size();
180 for (auto& pkg : original_table->packages) {
181 // Initialize all packages for splits.
182 for (size_t idx = 0; idx < split_count; idx++) {
183 ResourceTable* split_table = splits_[idx].get();
184 split_table->CreatePackage(pkg->name, pkg->id);
Adam Lesinski355f2852016-02-13 20:26:45 -0800185 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700186
187 for (auto& type : pkg->types) {
188 if (type->type == ResourceType::kMipmap) {
189 // Always keep mipmaps.
190 continue;
191 }
192
193 for (auto& entry : type->entries) {
194 if (options_.config_filter) {
195 // First eliminate any resource that we definitely don't want.
196 for (std::unique_ptr<ResourceConfigValue>& config_value :
197 entry->values) {
198 if (!options_.config_filter->Match(config_value->config)) {
199 // null out the entry. We will clean up and remove nulls at the
200 // end for performance reasons.
201 config_value.reset();
202 }
203 }
204 }
205
206 // Organize the values into two separate buckets. Those that are
207 // density-dependent
208 // and those that are density-independent.
209 // One density technically matches all density, it's just that some
210 // densities
211 // match better. So we need to be aware of the full set of densities to
212 // make this
213 // decision.
214 ConfigDensityGroups density_groups;
215 ConfigClaimedMap config_claimed_map;
216 for (const std::unique_ptr<ResourceConfigValue>& config_value :
217 entry->values) {
218 if (config_value) {
219 config_claimed_map[config_value.get()] = false;
220
221 if (config_value->config.density != 0) {
222 // Create a bucket for this density-dependent config.
223 density_groups[CopyWithoutDensity(config_value->config)]
224 .push_back(config_value.get());
225 }
226 }
227 }
228
229 // First we check all the splits. If it doesn't match one of the splits,
230 // we
231 // leave it in the base.
232 for (size_t idx = 0; idx < split_count; idx++) {
233 const SplitConstraints& split_constraint = split_constraints_[idx];
234 ResourceTable* split_table = splits_[idx].get();
235
236 // Select the values we want from this entry for this split.
237 SplitValueSelector selector(split_constraint);
238 std::vector<ResourceConfigValue*> selected_values =
239 selector.SelectValues(density_groups, &config_claimed_map);
240
241 // No need to do any work if we selected nothing.
242 if (!selected_values.empty()) {
243 // Create the same resource structure in the split. We do this
244 // lazily because we might not have actual values for each
245 // type/entry.
246 ResourceTablePackage* split_pkg =
247 split_table->FindPackage(pkg->name);
248 ResourceTableType* split_type =
249 split_pkg->FindOrCreateType(type->type);
250 if (!split_type->id) {
251 split_type->id = type->id;
252 split_type->symbol_status = type->symbol_status;
253 }
254
255 ResourceEntry* split_entry =
256 split_type->FindOrCreateEntry(entry->name);
257 if (!split_entry->id) {
258 split_entry->id = entry->id;
259 split_entry->symbol_status = entry->symbol_status;
260 }
261
262 // Copy the selected values into the new Split Entry.
263 for (ResourceConfigValue* config_value : selected_values) {
264 ResourceConfigValue* new_config_value =
265 split_entry->FindOrCreateValue(config_value->config,
266 config_value->product);
267 new_config_value->value = std::unique_ptr<Value>(
268 config_value->value->Clone(&split_table->string_pool));
269 }
270 }
271 }
272
Pierre Lecesne672384b2017-02-06 10:29:02 +0000273 if (!options_.preferred_densities.empty()) {
274 MarkNonPreferredDensitiesAsClaimed(options_.preferred_densities,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700275 density_groups,
276 &config_claimed_map);
277 }
278
279 // All splits are handled, now check to see what wasn't claimed and
280 // remove
281 // whatever exists in other splits.
282 for (std::unique_ptr<ResourceConfigValue>& config_value :
283 entry->values) {
284 if (config_value && config_claimed_map[config_value.get()]) {
285 // Claimed, remove from base.
286 config_value.reset();
287 }
288 }
289
290 // Now erase all nullptrs.
291 entry->values.erase(
292 std::remove(entry->values.begin(), entry->values.end(), nullptr),
293 entry->values.end());
294 }
295 }
296 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800297}
298
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700299} // namespace aapt