Home | History | Annotate | Download | only in split
      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 
     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:
     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 
     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  */
    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 
    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 
    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