• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
21 #include <algorithm>
22 #include <map>
23 #include <set>
24 #include <unordered_map>
25 #include <vector>
26 
27 namespace aapt {
28 
29 using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>;
30 using ConfigDensityGroups = std::map<ConfigDescription, std::vector<ResourceConfigValue*>>;
31 
copyWithoutDensity(const ConfigDescription & config)32 static 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  */
41 class SplitValueSelector {
42 public:
SplitValueSelector(const SplitConstraints & constraints)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 
selectValues(const ConfigDensityGroups & densityGroups,ConfigClaimedMap * claimedValues)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 
104 private:
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  */
markNonPreferredDensitiesAsClaimed(uint16_t preferredDensity,const ConfigDensityGroups & densityGroups,ConfigClaimedMap * configClaimedMap)113 static 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 
verifySplitConstraints(IAaptContext * context)139 bool 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 
splitTable(ResourceTable * originalTable)157 void 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
266