| /* | |
| * 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 <unordered_map> | |
| #include "ResourceTable.h" | |
| #include "ValueVisitor.h" | |
| #include "android-base/macros.h" | |
| #include "androidfw/BigBuffer.h" | |
| #include "androidfw/ResourceTypes.h" | |
| namespace aapt { | |
| using android::BigBuffer; | |
| using android::Res_value; | |
| using android::ResTable_entry; | |
| using android::ResTable_map; | |
| struct FlatEntry { | |
| const ResourceTableEntryView* entry; | |
| const Value* value; | |
| // The entry string pool index to the entry's name. | |
| uint32_t entry_key; | |
| bool uses_readwrite_feature_flags; | |
| }; | |
| // Pair of ResTable_entry and Res_value. These pairs are stored sequentially in values buffer. | |
| // We introduce this structure for ResEntryWriter to a have single allocation using | |
| // BigBuffer::NextBlock which allows to return it back with BigBuffer::Backup. | |
| struct ResEntryValuePair { | |
| ResTable_entry entry; | |
| Res_value value; | |
| }; | |
| static_assert(sizeof(ResEntryValuePair) == sizeof(ResTable_entry) + sizeof(Res_value), | |
| "ResEntryValuePair must not have padding between entry and value."); | |
| template <bool compact> | |
| using ResEntryValue = std::conditional_t<compact, ResTable_entry, ResEntryValuePair>; | |
| // References ResEntryValue object stored in BigBuffer used as a key in std::unordered_map. | |
| // Allows access to memory address where ResEntryValue is stored. | |
| template <bool compact> | |
| union ResEntryValueRef { | |
| using T = ResEntryValue<compact>; | |
| const std::reference_wrapper<const T> ref; | |
| const u_char* ptr; | |
| explicit ResEntryValueRef(const T& rev) : ref(rev) { | |
| } | |
| }; | |
| // Hasher which computes hash of ResEntryValue using its bytes representation in memory. | |
| struct ResEntryValueContentHasher { | |
| template <typename R> | |
| std::size_t operator()(const R& ref) const { | |
| return android::JenkinsHashMixBytes(0, ref.ptr, sizeof(typename R::T)); | |
| } | |
| }; | |
| // Equaler which compares ResEntryValuePairs using theirs bytes representation in memory. | |
| struct ResEntryValueContentEqualTo { | |
| template <typename R> | |
| bool operator()(const R& a, const R& b) const { | |
| return std::memcmp(a.ptr, b.ptr, sizeof(typename R::T)) == 0; | |
| } | |
| }; | |
| // Base class that allows to write FlatEntries into entries_buffer. | |
| 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(BigBuffer* entries_buffer) : entries_buffer_(entries_buffer) { | |
| } | |
| 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); | |
| }; | |
| int32_t WriteMapToBuffer(const FlatEntry* map_entry, BigBuffer* buffer); | |
| template <bool compact_entry, typename T=ResEntryValue<compact_entry>> | |
| std::pair<int32_t, T*> WriteItemToBuffer(const FlatEntry* item_entry, BigBuffer* buffer); | |
| // ResEntryWriter which writes FlatEntries sequentially into entries_buffer. | |
| // Next entry is always written right after previous one in the buffer. | |
| template <bool compact_entry = false> | |
| class SequentialResEntryWriter : public ResEntryWriter { | |
| public: | |
| explicit SequentialResEntryWriter(BigBuffer* entries_buffer) | |
| : ResEntryWriter(entries_buffer) { | |
| } | |
| ~SequentialResEntryWriter() override = default; | |
| int32_t WriteItem(const FlatEntry* entry) override { | |
| auto result = WriteItemToBuffer<compact_entry>(entry, entries_buffer_); | |
| return result.first; | |
| } | |
| int32_t WriteMap(const FlatEntry* entry) override { | |
| return WriteMapToBuffer(entry, entries_buffer_); | |
| } | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(SequentialResEntryWriter); | |
| }; | |
| // ResEntryWriter that writes only unique entry and value pairs into entries_buffer. | |
| // Next entry is written into buffer only if there is no entry with the same bytes representation | |
| // in memory written before. Otherwise returns offset of already written entry. | |
| template <bool compact_entry = false> | |
| class DeduplicateItemsResEntryWriter : public ResEntryWriter { | |
| public: | |
| explicit DeduplicateItemsResEntryWriter(BigBuffer* entries_buffer) | |
| : ResEntryWriter(entries_buffer) { | |
| } | |
| ~DeduplicateItemsResEntryWriter() override = default; | |
| int32_t WriteItem(const FlatEntry* entry) override { | |
| const auto& [offset, out_entry] = WriteItemToBuffer<compact_entry>(entry, entries_buffer_); | |
| auto [it, inserted] = entry_offsets.insert({Ref{*out_entry}, offset}); | |
| if (inserted) { | |
| // If inserted just return a new offset as this is a first time we store | |
| // this entry | |
| return offset; | |
| } | |
| // If not inserted this means that this is a duplicate, backup allocated block to the buffer | |
| // and return offset of previously stored entry | |
| entries_buffer_->BackUp(sizeof(*out_entry)); | |
| return it->second; | |
| } | |
| int32_t WriteMap(const FlatEntry* entry) override { | |
| return WriteMapToBuffer(entry, entries_buffer_); | |
| } | |
| private: | |
| DISALLOW_COPY_AND_ASSIGN(DeduplicateItemsResEntryWriter); | |
| using Ref = ResEntryValueRef<compact_entry>; | |
| using Map = std::unordered_map<Ref, int32_t, | |
| ResEntryValueContentHasher, | |
| ResEntryValueContentEqualTo>; | |
| Map entry_offsets; | |
| }; | |
| } // namespace aapt | |
| #endif |