b2bac12ce8
ResourcePathShortener appends one digit at the end of colliding short file names to disambiguate them. This cl sorts the list of file paths so that colliding files always get the same disambiguating digit from one run to the next rather than possibly alternating. Test: make aapt2_tests Change-Id: I983a1448c21f2c79d7cdb1de232e7758c04fc256
124 lines
4.1 KiB
C++
124 lines
4.1 KiB
C++
/*
|
|
* Copyright (C) 2018 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/ResourcePathShortener.h"
|
|
|
|
#include <set>
|
|
#include <unordered_set>
|
|
|
|
#include "androidfw/StringPiece.h"
|
|
|
|
#include "ResourceTable.h"
|
|
#include "ValueVisitor.h"
|
|
#include "util/Util.h"
|
|
|
|
|
|
static const std::string base64_chars =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"0123456789-_";
|
|
|
|
namespace aapt {
|
|
|
|
ResourcePathShortener::ResourcePathShortener(
|
|
std::map<std::string, std::string>& path_map_out)
|
|
: path_map_(path_map_out) {
|
|
}
|
|
|
|
std::string ShortenFileName(const android::StringPiece& file_path, int output_length) {
|
|
std::size_t hash_num = std::hash<android::StringPiece>{}(file_path);
|
|
std::string result = "";
|
|
// Convert to (modified) base64 so that it is a proper file path.
|
|
for (int i = 0; i < output_length; i++) {
|
|
uint8_t sextet = hash_num & 0x3f;
|
|
hash_num >>= 6;
|
|
result += base64_chars[sextet];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// Return the optimal hash length such that at most 10% of resources collide in
|
|
// their shortened path.
|
|
// Reference: http://matt.might.net/articles/counting-hash-collisions/
|
|
int OptimalShortenedLength(int num_resources) {
|
|
if (num_resources > 4000) {
|
|
return 3;
|
|
} else {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
std::string GetShortenedPath(const android::StringPiece& shortened_filename,
|
|
const android::StringPiece& extension, int collision_count) {
|
|
std::string shortened_path = "res/" + shortened_filename.to_string();
|
|
if (collision_count > 0) {
|
|
shortened_path += std::to_string(collision_count);
|
|
}
|
|
shortened_path += extension;
|
|
return shortened_path;
|
|
}
|
|
|
|
// implement custom comparator of FileReference pointers so as to use the
|
|
// underlying filepath as key rather than the integer address. This is to ensure
|
|
// determinism of output for colliding files.
|
|
struct PathComparator {
|
|
bool operator() (const FileReference* lhs, const FileReference* rhs) const {
|
|
return lhs->path->compare(*rhs->path);
|
|
}
|
|
};
|
|
|
|
bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table) {
|
|
// used to detect collisions
|
|
std::unordered_set<std::string> shortened_paths;
|
|
std::set<FileReference*, PathComparator> file_refs;
|
|
for (auto& package : table->packages) {
|
|
for (auto& type : package->types) {
|
|
for (auto& entry : type->entries) {
|
|
for (auto& config_value : entry->values) {
|
|
FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
|
|
if (file_ref) {
|
|
file_refs.insert(file_ref);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
int num_chars = OptimalShortenedLength(file_refs.size());
|
|
for (auto& file_ref : file_refs) {
|
|
android::StringPiece res_subdir, actual_filename, extension;
|
|
util::ExtractResFilePathParts(*file_ref->path, &res_subdir, &actual_filename, &extension);
|
|
|
|
// Android detects ColorStateLists via pathname, skip res/color*
|
|
if (util::StartsWith(res_subdir, "res/color"))
|
|
continue;
|
|
|
|
std::string shortened_filename = ShortenFileName(*file_ref->path, num_chars);
|
|
int collision_count = 0;
|
|
std::string shortened_path = GetShortenedPath(shortened_filename, extension, collision_count);
|
|
while (shortened_paths.find(shortened_path) != shortened_paths.end()) {
|
|
collision_count++;
|
|
shortened_path = GetShortenedPath(shortened_filename, extension, collision_count);
|
|
}
|
|
shortened_paths.insert(shortened_path);
|
|
path_map_.insert({*file_ref->path, shortened_path});
|
|
file_ref->path = table->string_pool.MakeRef(shortened_path, file_ref->path.GetContext());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace aapt
|