diff options
author | Adam Lesinski <adamlesinski@google.com> | 2017-02-21 14:22:30 -0800 |
---|---|---|
committer | Adam Lesinski <adamlesinski@google.com> | 2017-02-22 11:41:55 -0800 |
commit | d48944a745f9ed121e6bde22ef6feb3a44fbec39 (patch) | |
tree | 349c152caa68f1283535b2f9c49f5cd8a50eb59b /tools/aapt2/optimize/VersionCollapser.cpp | |
parent | ceb9b2f80f853059233cdd29057f39a5960a74ae (diff) |
AAPT2: Rename strip phase to optimize
- Allow resource deduping, version collapsing, and sparse resource
encoding.
Test: manual
Change-Id: Ia4aa892ab5b06ba1d5ea4a6efb51b00bc3a980c4
Diffstat (limited to 'tools/aapt2/optimize/VersionCollapser.cpp')
-rw-r--r-- | tools/aapt2/optimize/VersionCollapser.cpp | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/tools/aapt2/optimize/VersionCollapser.cpp b/tools/aapt2/optimize/VersionCollapser.cpp new file mode 100644 index 000000000000..d941b487e439 --- /dev/null +++ b/tools/aapt2/optimize/VersionCollapser.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "optimize/VersionCollapser.h" + +#include <algorithm> +#include <vector> + +#include "ResourceTable.h" + +namespace aapt { + +template <typename Iterator, typename Pred> +class FilterIterator { + public: + FilterIterator(Iterator begin, Iterator end, Pred pred = Pred()) + : current_(begin), end_(end), pred_(pred) { + Advance(); + } + + bool HasNext() { return current_ != end_; } + + Iterator NextIter() { + Iterator iter = current_; + ++current_; + Advance(); + return iter; + } + + typename Iterator::reference Next() { return *NextIter(); } + + private: + void Advance() { + for (; current_ != end_; ++current_) { + if (pred_(*current_)) { + return; + } + } + } + + Iterator current_, end_; + Pred pred_; +}; + +template <typename Iterator, typename Pred> +FilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin, + Iterator end = Iterator(), + Pred pred = Pred()) { + return FilterIterator<Iterator, Pred>(begin, end, pred); +} + +/** + * Every Configuration with an SDK version specified that is less than minSdk + * will be removed. + * The exception is when there is no exact matching resource for the minSdk. The + * next smallest + * one will be kept. + */ +static void CollapseVersions(int min_sdk, ResourceEntry* entry) { + // First look for all sdks less than minSdk. + for (auto iter = entry->values.rbegin(); iter != entry->values.rend(); + ++iter) { + // Check if the item was already marked for removal. + if (!(*iter)) { + continue; + } + + const ConfigDescription& config = (*iter)->config; + if (config.sdkVersion <= min_sdk) { + // This is the first configuration we've found with a smaller or equal SDK + // level + // to the minimum. We MUST keep this one, but remove all others we find, + // which get + // overridden by this one. + + ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion(); + auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool { + // Check that the value hasn't already been marked for removal. + if (!val) { + return false; + } + + // Only return Configs that differ in SDK version. + config_without_sdk.sdkVersion = val->config.sdkVersion; + return config_without_sdk == val->config && + val->config.sdkVersion <= min_sdk; + }; + + // Remove the rest that match. + auto filter_iter = + make_filter_iterator(iter + 1, entry->values.rend(), pred); + while (filter_iter.HasNext()) { + filter_iter.Next() = {}; + } + } + } + + // Now erase the nullptr values. + entry->values.erase( + std::remove_if(entry->values.begin(), entry->values.end(), + [](const std::unique_ptr<ResourceConfigValue>& val) + -> bool { return val == nullptr; }), + entry->values.end()); + + // Strip the version qualifiers for every resource with version <= minSdk. + // This will ensure + // that the resource entries are all packed together in the same ResTable_type + // struct + // and take up less space in the resources.arsc table. + bool modified = false; + for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) { + if (config_value->config.sdkVersion != 0 && + config_value->config.sdkVersion <= min_sdk) { + // Override the resource with a Configuration without an SDK. + std::unique_ptr<ResourceConfigValue> new_value = + util::make_unique<ResourceConfigValue>( + config_value->config.CopyWithoutSdkVersion(), + config_value->product); + new_value->value = std::move(config_value->value); + config_value = std::move(new_value); + + modified = true; + } + } + + if (modified) { + // We've modified the keys (ConfigDescription) by changing the sdkVersion to + // 0. We MUST re-sort to ensure ordering guarantees hold. + std::sort(entry->values.begin(), entry->values.end(), + [](const std::unique_ptr<ResourceConfigValue>& a, + const std::unique_ptr<ResourceConfigValue>& b) -> bool { + return a->config.compare(b->config) < 0; + }); + } +} + +bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) { + const int min_sdk = context->GetMinSdkVersion(); + for (auto& package : table->packages) { + for (auto& type : package->types) { + for (auto& entry : type->entries) { + CollapseVersions(min_sdk, entry.get()); + } + } + } + return true; +} + +} // namespace aapt |