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
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7ddbe95..aa337e5 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -105,6 +105,7 @@
"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 0000000..7b64619
--- /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 0000000..10bd312
--- /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 22f278c..bb3d034 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 @@
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 @@
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 @@
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 ||