diff options
| author | 2022-09-29 16:45:51 +0000 | |
|---|---|---|
| committer | 2022-10-05 00:04:31 +0000 | |
| commit | cf844a3e8cde155a10eb14f174b57300c732cda5 (patch) | |
| tree | 0c5f867761dd7386d15a80a7b746c6a18921ab00 | |
| parent | 0f4150919e48e60624bfdce942d8027f3d398cba (diff) | |
Intoduce ResEntryWriter unit.
ResEntryWriter is responsible to write pairs of entry+value into binary
buffers and allows easy customization how and where entries are written.
This is no-op refactoring and all existing tests should pass.
Bug: b/249793372
Test: Existing tests
Change-Id: Ica98a25aeb49cc96cad28547c462b36316a8adb6
| -rw-r--r-- | tools/aapt2/Android.bp | 1 | ||||
| -rw-r--r-- | tools/aapt2/format/binary/ResEntryWriter.cpp | 240 | ||||
| -rw-r--r-- | tools/aapt2/format/binary/ResEntryWriter.h | 79 | ||||
| -rw-r--r-- | tools/aapt2/format/binary/TableFlattener.cpp | 216 |
4 files changed, 323 insertions, 213 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 7ddbe95aa79b..aa337e5f9691 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -105,6 +105,7 @@ cc_library_host_static { "format/Container.cpp", "format/binary/BinaryResourceParser.cpp", "format/binary/ResChunkPullParser.cpp", + "format/binary/ResEntryWriter.cpp", "format/binary/TableFlattener.cpp", "format/binary/XmlFlattener.cpp", "format/proto/ProtoDeserialize.cpp", diff --git a/tools/aapt2/format/binary/ResEntryWriter.cpp b/tools/aapt2/format/binary/ResEntryWriter.cpp new file mode 100644 index 000000000000..7b64619be2ba --- /dev/null +++ b/tools/aapt2/format/binary/ResEntryWriter.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2022 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 "format/binary/ResEntryWriter.h" + +#include "ValueVisitor.h" +#include "androidfw/BigBuffer.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/Util.h" +#include "format/binary/ResourceTypeExtensions.h" + +namespace aapt { + +using android::BigBuffer; +using android::Res_value; +using android::ResTable_entry; +using android::ResTable_map; + +struct less_style_entries { + bool operator()(const Style::Entry* a, const Style::Entry* b) const { + if (a->key.id) { + if (b->key.id) { + return cmp_ids_dynamic_after_framework(a->key.id.value(), b->key.id.value()); + } + return true; + } + if (!b->key.id) { + return a->key.name.value() < b->key.name.value(); + } + return false; + } +}; + +class MapFlattenVisitor : public ConstValueVisitor { + public: + using ConstValueVisitor::Visit; + + MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer) + : out_entry_(out_entry), buffer_(buffer) { + } + + void Visit(const Attribute* attr) override { + { + Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE)); + BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->type_mask); + FlattenEntry(&key, &val); + } + + if (attr->min_int != std::numeric_limits<int32_t>::min()) { + Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN)); + BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->min_int)); + FlattenEntry(&key, &val); + } + + if (attr->max_int != std::numeric_limits<int32_t>::max()) { + Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX)); + BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->max_int)); + FlattenEntry(&key, &val); + } + + for (const Attribute::Symbol& s : attr->symbols) { + BinaryPrimitive val(s.type, s.value); + FlattenEntry(&s.symbol, &val); + } + } + + void Visit(const Style* style) override { + if (style->parent) { + const Reference& parent_ref = style->parent.value(); + CHECK(bool(parent_ref.id)) << "parent has no ID"; + out_entry_->parent.ident = android::util::HostToDevice32(parent_ref.id.value().id); + } + + // Sort the style. + std::vector<const Style::Entry*> sorted_entries; + for (const auto& entry : style->entries) { + sorted_entries.emplace_back(&entry); + } + + std::sort(sorted_entries.begin(), sorted_entries.end(), less_style_entries()); + + for (const Style::Entry* entry : sorted_entries) { + FlattenEntry(&entry->key, entry->value.get()); + } + } + + void Visit(const Styleable* styleable) override { + for (auto& attr_ref : styleable->entries) { + BinaryPrimitive val(Res_value{}); + FlattenEntry(&attr_ref, &val); + } + } + + void Visit(const Array* array) override { + const size_t count = array->elements.size(); + for (size_t i = 0; i < count; i++) { + Reference key(android::ResTable_map::ATTR_MIN + i); + FlattenEntry(&key, array->elements[i].get()); + } + } + + void Visit(const Plural* plural) override { + const size_t count = plural->values.size(); + for (size_t i = 0; i < count; i++) { + if (!plural->values[i]) { + continue; + } + + ResourceId q; + switch (i) { + case Plural::Zero: + q.id = android::ResTable_map::ATTR_ZERO; + break; + + case Plural::One: + q.id = android::ResTable_map::ATTR_ONE; + break; + + case Plural::Two: + q.id = android::ResTable_map::ATTR_TWO; + break; + + case Plural::Few: + q.id = android::ResTable_map::ATTR_FEW; + break; + + case Plural::Many: + q.id = android::ResTable_map::ATTR_MANY; + break; + + case Plural::Other: + q.id = android::ResTable_map::ATTR_OTHER; + break; + + default: + LOG(FATAL) << "unhandled plural type"; + break; + } + + Reference key(q); + FlattenEntry(&key, plural->values[i].get()); + } + } + + /** + * Call this after visiting a Value. This will finish any work that + * needs to be done to prepare the entry. + */ + void Finish() { + out_entry_->count = android::util::HostToDevice32(entry_count_); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor); + + void FlattenKey(const Reference* key, ResTable_map* out_entry) { + CHECK(bool(key->id)) << "key has no ID"; + out_entry->name.ident = android::util::HostToDevice32(key->id.value().id); + } + + void FlattenValue(const Item* value, ResTable_map* out_entry) { + CHECK(value->Flatten(&out_entry->value)) << "flatten failed"; + } + + void FlattenEntry(const Reference* key, Item* value) { + ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>(); + FlattenKey(key, out_entry); + FlattenValue(value, out_entry); + out_entry->value.size = android::util::HostToDevice16(sizeof(out_entry->value)); + entry_count_++; + } + + ResTable_entry_ext* out_entry_; + BigBuffer* buffer_; + size_t entry_count_ = 0; +}; + +template <typename T> +void WriteEntry(const FlatEntry* entry, T* out_result) { + static_assert(std::is_same_v<ResTable_entry, T> || std::is_same_v<ResTable_entry_ext, T>, + "T must be ResTable_entry or ResTable_entry_ext"); + + ResTable_entry* out_entry = (ResTable_entry*)out_result; + if (entry->entry->visibility.level == Visibility::Level::kPublic) { + out_entry->flags |= ResTable_entry::FLAG_PUBLIC; + } + + if (entry->value->IsWeak()) { + out_entry->flags |= ResTable_entry::FLAG_WEAK; + } + + if constexpr (std::is_same_v<ResTable_entry_ext, T>) { + out_entry->flags |= ResTable_entry::FLAG_COMPLEX; + } + + out_entry->flags = android::util::HostToDevice16(out_entry->flags); + out_entry->key.index = android::util::HostToDevice32(entry->entry_key); + out_entry->size = android::util::HostToDevice16(sizeof(T)); +} + +int32_t WriteMapToBuffer(const FlatEntry* map_entry, BigBuffer* buffer) { + int32_t offset = buffer->size(); + ResTable_entry_ext* out_entry = buffer->NextBlock<ResTable_entry_ext>(); + WriteEntry<ResTable_entry_ext>(map_entry, out_entry); + + MapFlattenVisitor visitor(out_entry, buffer); + map_entry->value->Accept(&visitor); + visitor.Finish(); + return offset; +} + +int32_t SequentialResEntryWriter::WriteMap(const FlatEntry* entry) { + return WriteMapToBuffer(entry, entries_buffer_); +} + +int32_t SequentialResEntryWriter::WriteItem(const FlatEntry* entry) { + int32_t offset = entries_buffer_->size(); + ResTable_entry* out_entry = entries_buffer_->NextBlock<ResTable_entry>(); + Res_value* out_value = entries_buffer_->NextBlock<Res_value>(); + out_value->size = android::util::HostToDevice16(sizeof(*out_value)); + + WriteEntry<ResTable_entry>(entry, out_entry); + CHECK(ValueCast<Item>(entry->value)->Flatten(out_value)) << "flatten failed"; + return offset; +} + +} // namespace aapt
\ No newline at end of file diff --git a/tools/aapt2/format/binary/ResEntryWriter.h b/tools/aapt2/format/binary/ResEntryWriter.h new file mode 100644 index 000000000000..10bd3125091b --- /dev/null +++ b/tools/aapt2/format/binary/ResEntryWriter.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2022 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. + */ + +#ifndef AAPT_FORMAT_BINARY_RESENTRY_SERIALIZER_H +#define AAPT_FORMAT_BINARY_RESENTRY_SERIALIZER_H + +#include "ResourceTable.h" +#include "ValueVisitor.h" +#include "android-base/macros.h" +#include "androidfw/BigBuffer.h" + +namespace aapt { + +struct FlatEntry { + const ResourceTableEntryView* entry; + const Value* value; + + // The entry string pool index to the entry's name. + uint32_t entry_key; +}; + +class ResEntryWriter { + public: + virtual ~ResEntryWriter() = default; + + // Writes resource table entry and its value into 'entries_buffer_' and returns offset + // in the buffer where entry was written. + int32_t Write(const FlatEntry* entry) { + if (ValueCast<Item>(entry->value) != nullptr) { + return WriteItem(entry); + } else { + return WriteMap(entry); + } + } + + protected: + ResEntryWriter(android::BigBuffer* entries_buffer) : entries_buffer_(entries_buffer) { + } + android::BigBuffer* entries_buffer_; + + virtual int32_t WriteItem(const FlatEntry* entry) = 0; + + virtual int32_t WriteMap(const FlatEntry* entry) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ResEntryWriter); +}; + +class SequentialResEntryWriter : public ResEntryWriter { + public: + explicit SequentialResEntryWriter(android::BigBuffer* entries_buffer) + : ResEntryWriter(entries_buffer) { + } + ~SequentialResEntryWriter() override = default; + + int32_t WriteItem(const FlatEntry* entry) override; + + int32_t WriteMap(const FlatEntry* entry) override; + + private: + DISALLOW_COPY_AND_ASSIGN(SequentialResEntryWriter); +}; + +} // namespace aapt + +#endif
\ No newline at end of file diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 22f278cc0b22..bb3d034b71a3 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -31,6 +31,7 @@ #include "androidfw/BigBuffer.h" #include "androidfw/ResourceUtils.h" #include "format/binary/ChunkWriter.h" +#include "format/binary/ResEntryWriter.h" #include "format/binary/ResourceTypeExtensions.h" #include "trace/TraceBuffer.h" @@ -58,170 +59,6 @@ static void strcpy16_htod(uint16_t* dst, size_t len, const StringPiece16& src) { dst[i] = 0; } -static bool cmp_style_entries(const Style::Entry* a, const Style::Entry* b) { - if (a->key.id) { - if (b->key.id) { - return cmp_ids_dynamic_after_framework(a->key.id.value(), b->key.id.value()); - } - return true; - } else if (!b->key.id) { - return a->key.name.value() < b->key.name.value(); - } - return false; -} - -struct FlatEntry { - const ResourceTableEntryView* entry; - const Value* value; - - // The entry string pool index to the entry's name. - uint32_t entry_key; -}; - -class MapFlattenVisitor : public ConstValueVisitor { - public: - using ConstValueVisitor::Visit; - - MapFlattenVisitor(ResTable_entry_ext* out_entry, BigBuffer* buffer) - : out_entry_(out_entry), buffer_(buffer) { - } - - void Visit(const Attribute* attr) override { - { - Reference key = Reference(ResourceId(ResTable_map::ATTR_TYPE)); - BinaryPrimitive val(Res_value::TYPE_INT_DEC, attr->type_mask); - FlattenEntry(&key, &val); - } - - if (attr->min_int != std::numeric_limits<int32_t>::min()) { - Reference key = Reference(ResourceId(ResTable_map::ATTR_MIN)); - BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->min_int)); - FlattenEntry(&key, &val); - } - - if (attr->max_int != std::numeric_limits<int32_t>::max()) { - Reference key = Reference(ResourceId(ResTable_map::ATTR_MAX)); - BinaryPrimitive val(Res_value::TYPE_INT_DEC, static_cast<uint32_t>(attr->max_int)); - FlattenEntry(&key, &val); - } - - for (const Attribute::Symbol& s : attr->symbols) { - BinaryPrimitive val(s.type, s.value); - FlattenEntry(&s.symbol, &val); - } - } - - void Visit(const Style* style) override { - if (style->parent) { - const Reference& parent_ref = style->parent.value(); - CHECK(bool(parent_ref.id)) << "parent has no ID"; - out_entry_->parent.ident = android::util::HostToDevice32(parent_ref.id.value().id); - } - - // Sort the style. - std::vector<const Style::Entry*> sorted_entries; - for (const auto& entry : style->entries) { - sorted_entries.emplace_back(&entry); - } - - std::sort(sorted_entries.begin(), sorted_entries.end(), cmp_style_entries); - - for (const Style::Entry* entry : sorted_entries) { - FlattenEntry(&entry->key, entry->value.get()); - } - } - - void Visit(const Styleable* styleable) override { - for (auto& attr_ref : styleable->entries) { - BinaryPrimitive val(Res_value{}); - FlattenEntry(&attr_ref, &val); - } - } - - void Visit(const Array* array) override { - const size_t count = array->elements.size(); - for (size_t i = 0; i < count; i++) { - Reference key(android::ResTable_map::ATTR_MIN + i); - FlattenEntry(&key, array->elements[i].get()); - } - } - - void Visit(const Plural* plural) override { - const size_t count = plural->values.size(); - for (size_t i = 0; i < count; i++) { - if (!plural->values[i]) { - continue; - } - - ResourceId q; - switch (i) { - case Plural::Zero: - q.id = android::ResTable_map::ATTR_ZERO; - break; - - case Plural::One: - q.id = android::ResTable_map::ATTR_ONE; - break; - - case Plural::Two: - q.id = android::ResTable_map::ATTR_TWO; - break; - - case Plural::Few: - q.id = android::ResTable_map::ATTR_FEW; - break; - - case Plural::Many: - q.id = android::ResTable_map::ATTR_MANY; - break; - - case Plural::Other: - q.id = android::ResTable_map::ATTR_OTHER; - break; - - default: - LOG(FATAL) << "unhandled plural type"; - break; - } - - Reference key(q); - FlattenEntry(&key, plural->values[i].get()); - } - } - - /** - * Call this after visiting a Value. This will finish any work that - * needs to be done to prepare the entry. - */ - void Finish() { - out_entry_->count = android::util::HostToDevice32(entry_count_); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MapFlattenVisitor); - - void FlattenKey(const Reference* key, ResTable_map* out_entry) { - CHECK(bool(key->id)) << "key has no ID"; - out_entry->name.ident = android::util::HostToDevice32(key->id.value().id); - } - - void FlattenValue(const Item* value, ResTable_map* out_entry) { - CHECK(value->Flatten(&out_entry->value)) << "flatten failed"; - } - - void FlattenEntry(const Reference* key, Item* value) { - ResTable_map* out_entry = buffer_->NextBlock<ResTable_map>(); - FlattenKey(key, out_entry); - FlattenValue(value, out_entry); - out_entry->value.size = android::util::HostToDevice16(sizeof(out_entry->value)); - entry_count_++; - } - - ResTable_entry_ext* out_entry_; - BigBuffer* buffer_; - size_t entry_count_ = 0; -}; - struct OverlayableChunk { std::string actor; android::Source source; @@ -298,47 +135,6 @@ class PackageFlattener { private: DISALLOW_COPY_AND_ASSIGN(PackageFlattener); - template <typename T, bool IsItem> - T* WriteEntry(FlatEntry* entry, BigBuffer* buffer) { - static_assert( - std::is_same<ResTable_entry, T>::value || std::is_same<ResTable_entry_ext, T>::value, - "T must be ResTable_entry or ResTable_entry_ext"); - - T* result = buffer->NextBlock<T>(); - ResTable_entry* out_entry = (ResTable_entry*)result; - if (entry->entry->visibility.level == Visibility::Level::kPublic) { - out_entry->flags |= ResTable_entry::FLAG_PUBLIC; - } - - if (entry->value->IsWeak()) { - out_entry->flags |= ResTable_entry::FLAG_WEAK; - } - - if (!IsItem) { - out_entry->flags |= ResTable_entry::FLAG_COMPLEX; - } - - out_entry->flags = android::util::HostToDevice16(out_entry->flags); - out_entry->key.index = android::util::HostToDevice32(entry->entry_key); - out_entry->size = android::util::HostToDevice16(sizeof(T)); - return result; - } - - bool FlattenValue(FlatEntry* entry, BigBuffer* buffer) { - if (const Item* item = ValueCast<Item>(entry->value)) { - WriteEntry<ResTable_entry, true>(entry, buffer); - Res_value* outValue = buffer->NextBlock<Res_value>(); - CHECK(item->Flatten(outValue)) << "flatten failed"; - outValue->size = android::util::HostToDevice16(sizeof(*outValue)); - } else { - ResTable_entry_ext* out_entry = WriteEntry<ResTable_entry_ext, false>(entry, buffer); - MapFlattenVisitor visitor(out_entry, buffer); - entry->value->Accept(&visitor); - visitor.Finish(); - } - return true; - } - bool FlattenConfig(const ResourceTableTypeView& type, const ConfigDescription& config, const size_t num_total_entries, std::vector<FlatEntry>* entries, BigBuffer* buffer) { @@ -355,16 +151,10 @@ class PackageFlattener { offsets.resize(num_total_entries, 0xffffffffu); android::BigBuffer values_buffer(512); + SequentialResEntryWriter res_entry_writer(&values_buffer); for (FlatEntry& flat_entry : *entries) { CHECK(static_cast<size_t>(flat_entry.entry->id.value()) < num_total_entries); - offsets[flat_entry.entry->id.value()] = values_buffer.size(); - if (!FlattenValue(&flat_entry, &values_buffer)) { - diag_->Error(android::DiagMessage() - << "failed to flatten resource '" - << ResourceNameRef(package_.name, type.named_type, flat_entry.entry->name) - << "' for configuration '" << config << "'"); - return false; - } + offsets[flat_entry.entry->id.value()] = res_entry_writer.Write(&flat_entry); } bool sparse_encode = sparse_entries_ == SparseEntriesMode::Enabled || |