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 "split/TableSplitter.h" 18 19 #include <algorithm> 20 #include <map> 21 #include <set> 22 #include <unordered_set> 23 #include <unordered_map> 24 #include <vector> 25 26 #include "android-base/logging.h" 27 28 #include "ConfigDescription.h" 29 #include "ResourceTable.h" 30 #include "util/Util.h" 31 32 namespace aapt { 33 34 using ConfigClaimedMap = std::unordered_map<ResourceConfigValue*, bool>; 35 using ConfigDensityGroups = 36 std::map<ConfigDescription, std::vector<ResourceConfigValue*>>; 37 38 static ConfigDescription CopyWithoutDensity(const ConfigDescription& config) { 39 ConfigDescription without_density = config; 40 without_density.density = 0; 41 return without_density; 42 } 43 44 /** 45 * Selects values that match exactly the constraints given. 46 */ 47 class SplitValueSelector { 48 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; 77 } 78 } 79 } 80 81 // 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; 94 95 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 } 102 } 103 CHECK(best_value != nullptr); 104 105 // 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 } 110 } 111 return selected; 112 } 113 114 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_; 120 }; 121 122 /** 123 * Marking non-preferred densities as claimed will make sure the base doesn't 124 * include them, 125 * leaving only the preferred density behind. 126 */ 127 static void MarkNonPreferredDensitiesAsClaimed( 128 const std::vector<uint16_t>& preferred_densities, const ConfigDensityGroups& density_groups, 129 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; 133 134 // 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. 152 for (ResourceConfigValue* this_value : related_values) { 153 if (best_values.find(this_value) == best_values.end()) { 154 (*config_claimed_map)[this_value] = true; 155 } 156 } 157 } 158 } 159 bool 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; 171 } 172 } 173 } 174 } 175 return !error; 176 } 177 178 void 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); 185 } 186 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 273 if (!options_.preferred_densities.empty()) { 274 MarkNonPreferredDensitiesAsClaimed(options_.preferred_densities, 275 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 } 297 } 298 299 } // namespace aapt 300