blob: 38cfd2e610545201dd9937ad0f09a02cf33df680 [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>
22#include <unordered_map>
23#include <vector>
Adam Lesinskid5083f62017-01-16 15:07:21 -080024
Adam Lesinskice5e56e2016-10-21 17:56:45 -070025#include "android-base/logging.h"
26
27#include "ConfigDescription.h"
28#include "ResourceTable.h"
29#include "util/Util.h"
Adam Lesinski355f2852016-02-13 20:26:45 -080030
31namespace aapt {
32
33using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070034using ConfigDensityGroups =
35 std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
Adam Lesinski355f2852016-02-13 20:26:45 -080036
Adam Lesinskice5e56e2016-10-21 17:56:45 -070037static ConfigDescription CopyWithoutDensity(const ConfigDescription& config) {
38 ConfigDescription without_density = config;
39 without_density.density = 0;
40 return without_density;
Adam Lesinski355f2852016-02-13 20:26:45 -080041}
42
43/**
44 * Selects values that match exactly the constraints given.
45 */
46class SplitValueSelector {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070047 public:
48 explicit SplitValueSelector(const SplitConstraints& constraints) {
49 for (const ConfigDescription& config : constraints.configs) {
50 if (config.density == 0) {
51 density_independent_configs_.insert(config);
52 } else {
53 density_dependent_config_to_density_map_[CopyWithoutDensity(config)] =
54 config.density;
55 }
56 }
57 }
58
59 std::vector<ResourceConfigValue*> SelectValues(
60 const ConfigDensityGroups& density_groups,
61 ConfigClaimedMap* claimed_values) {
62 std::vector<ResourceConfigValue*> selected;
63
64 // Select the regular values.
65 for (auto& entry : *claimed_values) {
66 // Check if the entry has a density.
67 ResourceConfigValue* config_value = entry.first;
68 if (config_value->config.density == 0 && !entry.second) {
69 // This is still available.
70 if (density_independent_configs_.find(config_value->config) !=
71 density_independent_configs_.end()) {
72 selected.push_back(config_value);
73
74 // Mark the entry as taken.
75 entry.second = true;
Adam Lesinski355f2852016-02-13 20:26:45 -080076 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070077 }
Adam Lesinski355f2852016-02-13 20:26:45 -080078 }
79
Adam Lesinskice5e56e2016-10-21 17:56:45 -070080 // Now examine the densities
81 for (auto& entry : density_groups) {
82 // We do not care if the value is claimed, since density values can be
83 // in multiple splits.
84 const ConfigDescription& config = entry.first;
85 const std::vector<ResourceConfigValue*>& related_values = entry.second;
86 auto density_value_iter =
87 density_dependent_config_to_density_map_.find(config);
88 if (density_value_iter !=
89 density_dependent_config_to_density_map_.end()) {
90 // Select the best one!
91 ConfigDescription target_density = config;
92 target_density.density = density_value_iter->second;
Adam Lesinski355f2852016-02-13 20:26:45 -080093
Adam Lesinskice5e56e2016-10-21 17:56:45 -070094 ResourceConfigValue* best_value = nullptr;
95 for (ResourceConfigValue* this_value : related_values) {
96 if (!best_value ||
97 this_value->config.isBetterThan(best_value->config,
98 &target_density)) {
99 best_value = this_value;
100 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800101 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700102 CHECK(best_value != nullptr);
Adam Lesinski355f2852016-02-13 20:26:45 -0800103
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700104 // When we select one of these, they are all claimed such that the base
105 // doesn't include any anymore.
106 (*claimed_values)[best_value] = true;
107 selected.push_back(best_value);
108 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800109 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700110 return selected;
111 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800112
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700113 private:
114 DISALLOW_COPY_AND_ASSIGN(SplitValueSelector);
115
116 std::set<ConfigDescription> density_independent_configs_;
117 std::map<ConfigDescription, uint16_t>
118 density_dependent_config_to_density_map_;
Adam Lesinski355f2852016-02-13 20:26:45 -0800119};
120
121/**
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700122 * Marking non-preferred densities as claimed will make sure the base doesn't
123 * include them,
Adam Lesinski355f2852016-02-13 20:26:45 -0800124 * leaving only the preferred density behind.
125 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700126static void MarkNonPreferredDensitiesAsClaimed(
127 uint16_t preferred_density, const ConfigDensityGroups& density_groups,
128 ConfigClaimedMap* config_claimed_map) {
129 for (auto& entry : density_groups) {
130 const ConfigDescription& config = entry.first;
131 const std::vector<ResourceConfigValue*>& related_values = entry.second;
Adam Lesinski355f2852016-02-13 20:26:45 -0800132
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700133 ConfigDescription target_density = config;
134 target_density.density = preferred_density;
135 ResourceConfigValue* best_value = nullptr;
136 for (ResourceConfigValue* this_value : related_values) {
137 if (!best_value) {
138 best_value = this_value;
139 } else if (this_value->config.isBetterThan(best_value->config,
140 &target_density)) {
141 // Claim the previous value so that it is not included in the base.
142 (*config_claimed_map)[best_value] = true;
143 best_value = this_value;
144 } else {
145 // Claim this value so that it is not included in the base.
146 (*config_claimed_map)[this_value] = true;
147 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800148 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700149 CHECK(best_value != nullptr);
150 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800151}
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700152bool TableSplitter::VerifySplitConstraints(IAaptContext* context) {
153 bool error = false;
154 for (size_t i = 0; i < split_constraints_.size(); i++) {
155 for (size_t j = i + 1; j < split_constraints_.size(); j++) {
156 for (const ConfigDescription& config : split_constraints_[i].configs) {
157 if (split_constraints_[j].configs.find(config) !=
158 split_constraints_[j].configs.end()) {
159 context->GetDiagnostics()->Error(DiagMessage()
160 << "config '" << config
161 << "' appears in multiple splits, "
162 << "target split ambiguous");
163 error = true;
Adam Lesinski355f2852016-02-13 20:26:45 -0800164 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700165 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800166 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700167 }
168 return !error;
Adam Lesinski355f2852016-02-13 20:26:45 -0800169}
170
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700171void TableSplitter::SplitTable(ResourceTable* original_table) {
172 const size_t split_count = split_constraints_.size();
173 for (auto& pkg : original_table->packages) {
174 // Initialize all packages for splits.
175 for (size_t idx = 0; idx < split_count; idx++) {
176 ResourceTable* split_table = splits_[idx].get();
177 split_table->CreatePackage(pkg->name, pkg->id);
Adam Lesinski355f2852016-02-13 20:26:45 -0800178 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700179
180 for (auto& type : pkg->types) {
181 if (type->type == ResourceType::kMipmap) {
182 // Always keep mipmaps.
183 continue;
184 }
185
186 for (auto& entry : type->entries) {
187 if (options_.config_filter) {
188 // First eliminate any resource that we definitely don't want.
189 for (std::unique_ptr<ResourceConfigValue>& config_value :
190 entry->values) {
191 if (!options_.config_filter->Match(config_value->config)) {
192 // null out the entry. We will clean up and remove nulls at the
193 // end for performance reasons.
194 config_value.reset();
195 }
196 }
197 }
198
199 // Organize the values into two separate buckets. Those that are
200 // density-dependent
201 // and those that are density-independent.
202 // One density technically matches all density, it's just that some
203 // densities
204 // match better. So we need to be aware of the full set of densities to
205 // make this
206 // decision.
207 ConfigDensityGroups density_groups;
208 ConfigClaimedMap config_claimed_map;
209 for (const std::unique_ptr<ResourceConfigValue>& config_value :
210 entry->values) {
211 if (config_value) {
212 config_claimed_map[config_value.get()] = false;
213
214 if (config_value->config.density != 0) {
215 // Create a bucket for this density-dependent config.
216 density_groups[CopyWithoutDensity(config_value->config)]
217 .push_back(config_value.get());
218 }
219 }
220 }
221
222 // First we check all the splits. If it doesn't match one of the splits,
223 // we
224 // leave it in the base.
225 for (size_t idx = 0; idx < split_count; idx++) {
226 const SplitConstraints& split_constraint = split_constraints_[idx];
227 ResourceTable* split_table = splits_[idx].get();
228
229 // Select the values we want from this entry for this split.
230 SplitValueSelector selector(split_constraint);
231 std::vector<ResourceConfigValue*> selected_values =
232 selector.SelectValues(density_groups, &config_claimed_map);
233
234 // No need to do any work if we selected nothing.
235 if (!selected_values.empty()) {
236 // Create the same resource structure in the split. We do this
237 // lazily because we might not have actual values for each
238 // type/entry.
239 ResourceTablePackage* split_pkg =
240 split_table->FindPackage(pkg->name);
241 ResourceTableType* split_type =
242 split_pkg->FindOrCreateType(type->type);
243 if (!split_type->id) {
244 split_type->id = type->id;
245 split_type->symbol_status = type->symbol_status;
246 }
247
248 ResourceEntry* split_entry =
249 split_type->FindOrCreateEntry(entry->name);
250 if (!split_entry->id) {
251 split_entry->id = entry->id;
252 split_entry->symbol_status = entry->symbol_status;
253 }
254
255 // Copy the selected values into the new Split Entry.
256 for (ResourceConfigValue* config_value : selected_values) {
257 ResourceConfigValue* new_config_value =
258 split_entry->FindOrCreateValue(config_value->config,
259 config_value->product);
260 new_config_value->value = std::unique_ptr<Value>(
261 config_value->value->Clone(&split_table->string_pool));
262 }
263 }
264 }
265
266 if (options_.preferred_density) {
267 MarkNonPreferredDensitiesAsClaimed(options_.preferred_density.value(),
268 density_groups,
269 &config_claimed_map);
270 }
271
272 // All splits are handled, now check to see what wasn't claimed and
273 // remove
274 // whatever exists in other splits.
275 for (std::unique_ptr<ResourceConfigValue>& config_value :
276 entry->values) {
277 if (config_value && config_claimed_map[config_value.get()]) {
278 // Claimed, remove from base.
279 config_value.reset();
280 }
281 }
282
283 // Now erase all nullptrs.
284 entry->values.erase(
285 std::remove(entry->values.begin(), entry->values.end(), nullptr),
286 entry->values.end());
287 }
288 }
289 }
Adam Lesinski355f2852016-02-13 20:26:45 -0800290}
291
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700292} // namespace aapt