1fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski/* 2fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * Copyright (C) 2016 The Android Open Source Project 3fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * 4fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); 5fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * you may not use this file except in compliance with the License. 6fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * You may obtain a copy of the License at 7fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * 8fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * http://www.apache.org/licenses/LICENSE-2.0 9fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * 10fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * Unless required by applicable law or agreed to in writing, software 11fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, 12fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * See the License for the specific language governing permissions and 14fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * limitations under the License. 15fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski */ 16fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 17d48944a745f9ed121e6bde22ef6feb3a44fbec39Adam Lesinski#include "optimize/VersionCollapser.h" 18fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 19fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski#include <algorithm> 20fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski#include <vector> 21fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 22ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "ResourceTable.h" 23ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski 24fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskinamespace aapt { 25fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 26fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskitemplate <typename Iterator, typename Pred> 27fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskiclass FilterIterator { 28cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public: 29cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski FilterIterator(Iterator begin, Iterator end, Pred pred = Pred()) 30ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski : current_(begin), end_(end), pred_(pred) { 31ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Advance(); 32cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 33cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 34ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski bool HasNext() { return current_ != end_; } 35cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 36ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Iterator NextIter() { 37ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Iterator iter = current_; 38ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski ++current_; 39ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Advance(); 40cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return iter; 41cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 42cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 43ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski typename Iterator::reference Next() { return *NextIter(); } 44cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 45cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private: 46ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski void Advance() { 47ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski for (; current_ != end_; ++current_) { 48ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski if (pred_(*current_)) { 49cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return; 50cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 51fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski } 52cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 53fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 54ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Iterator current_, end_; 55ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Pred pred_; 56fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski}; 57fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 58fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskitemplate <typename Iterator, typename Pred> 59ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiFilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin, 60ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Iterator end = Iterator(), 61ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski Pred pred = Pred()) { 62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return FilterIterator<Iterator, Pred>(begin, end, pred); 63fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski} 64fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 65fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski/** 66cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * Every Configuration with an SDK version specified that is less than minSdk 67cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * will be removed. 68cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * The exception is when there is no exact matching resource for the minSdk. The 69cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * next smallest 70fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * one will be kept. 71fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski */ 72ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic void CollapseVersions(int min_sdk, ResourceEntry* entry) { 73cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // First look for all sdks less than minSdk. 74cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (auto iter = entry->values.rbegin(); iter != entry->values.rend(); 75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski ++iter) { 76cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Check if the item was already marked for removal. 77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!(*iter)) { 78cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski continue; 79fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski } 80fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const ConfigDescription& config = (*iter)->config; 82ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski if (config.sdkVersion <= min_sdk) { 83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // This is the first configuration we've found with a smaller or equal SDK 84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // level 85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // to the minimum. We MUST keep this one, but remove all others we find, 86cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // which get 87cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // overridden by this one. 88cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 89ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion(); 90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool { 91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Check that the value hasn't already been marked for removal. 92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!val) { 93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 9487675ada47fabd1d751dc35990a95d7682b19c5bAdam Lesinski } 9587675ada47fabd1d751dc35990a95d7682b19c5bAdam Lesinski 96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Only return Configs that differ in SDK version. 97ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski config_without_sdk.sdkVersion = val->config.sdkVersion; 98ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski return config_without_sdk == val->config && 99ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski val->config.sdkVersion <= min_sdk; 100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski }; 101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Remove the rest that match. 103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski auto filter_iter = 104ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski make_filter_iterator(iter + 1, entry->values.rend(), pred); 105ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski while (filter_iter.HasNext()) { 106ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski filter_iter.Next() = {}; 107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 109cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 111cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Now erase the nullptr values. 112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski entry->values.erase( 113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::remove_if(entry->values.begin(), entry->values.end(), 114cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski [](const std::unique_ptr<ResourceConfigValue>& val) 115cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski -> bool { return val == nullptr; }), 116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski entry->values.end()); 117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Strip the version qualifiers for every resource with version <= minSdk. 119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // This will ensure 120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // that the resource entries are all packed together in the same ResTable_type 121cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // struct 122cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // and take up less space in the resources.arsc table. 123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool modified = false; 124ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) { 125ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski if (config_value->config.sdkVersion != 0 && 126ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski config_value->config.sdkVersion <= min_sdk) { 127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Override the resource with a Configuration without an SDK. 128ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski std::unique_ptr<ResourceConfigValue> new_value = 129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski util::make_unique<ResourceConfigValue>( 130ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski config_value->config.CopyWithoutSdkVersion(), 131ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski config_value->product); 132ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski new_value->value = std::move(config_value->value); 133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski config_value = std::move(new_value); 134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski modified = true; 13687675ada47fabd1d751dc35990a95d7682b19c5bAdam Lesinski } 137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 138cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (modified) { 140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // We've modified the keys (ConfigDescription) by changing the sdkVersion to 141ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski // 0. We MUST re-sort to ensure ordering guarantees hold. 142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::sort(entry->values.begin(), entry->values.end(), 143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski [](const std::unique_ptr<ResourceConfigValue>& a, 144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const std::unique_ptr<ResourceConfigValue>& b) -> bool { 145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return a->config.compare(b->config) < 0; 146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski }); 147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 148fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski} 149fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 150ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) { 151ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski const int min_sdk = context->GetMinSdkVersion(); 152cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (auto& package : table->packages) { 153cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (auto& type : package->types) { 154cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (auto& entry : type->entries) { 155ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski CollapseVersions(min_sdk, entry.get()); 156cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 157fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski } 158cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 160fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski} 161fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski 162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski} // namespace aapt 163