1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef AAPT_FORMAT_BINARY_RESENTRY_SERIALIZER_H 18 #define AAPT_FORMAT_BINARY_RESENTRY_SERIALIZER_H 19 20 #include <unordered_map> 21 22 #include "ResourceTable.h" 23 #include "ValueVisitor.h" 24 #include "android-base/macros.h" 25 #include "androidfw/BigBuffer.h" 26 #include "androidfw/ResourceTypes.h" 27 28 namespace aapt { 29 30 using android::BigBuffer; 31 using android::Res_value; 32 using android::ResTable_entry; 33 using android::ResTable_map; 34 35 struct FlatEntry { 36 const ResourceTableEntryView* entry; 37 const Value* value; 38 39 // The entry string pool index to the entry's name. 40 uint32_t entry_key; 41 }; 42 43 // Pair of ResTable_entry and Res_value. These pairs are stored sequentially in values buffer. 44 // We introduce this structure for ResEntryWriter to a have single allocation using 45 // BigBuffer::NextBlock which allows to return it back with BigBuffer::Backup. 46 struct ResEntryValuePair { 47 ResTable_entry entry; 48 Res_value value; 49 }; 50 51 static_assert(sizeof(ResEntryValuePair) == sizeof(ResTable_entry) + sizeof(Res_value), 52 "ResEntryValuePair must not have padding between entry and value."); 53 54 template <bool compact> 55 using ResEntryValue = std::conditional_t<compact, ResTable_entry, ResEntryValuePair>; 56 57 // References ResEntryValue object stored in BigBuffer used as a key in std::unordered_map. 58 // Allows access to memory address where ResEntryValue is stored. 59 template <bool compact> 60 union ResEntryValueRef { 61 using T = ResEntryValue<compact>; 62 const std::reference_wrapper<const T> ref; 63 const u_char* ptr; 64 ResEntryValueRef(const T & rev)65 explicit ResEntryValueRef(const T& rev) : ref(rev) { 66 } 67 }; 68 69 // Hasher which computes hash of ResEntryValue using its bytes representation in memory. 70 struct ResEntryValueContentHasher { 71 template <typename R> operatorResEntryValueContentHasher72 std::size_t operator()(const R& ref) const { 73 return android::JenkinsHashMixBytes(0, ref.ptr, sizeof(typename R::T)); 74 } 75 }; 76 77 // Equaler which compares ResEntryValuePairs using theirs bytes representation in memory. 78 struct ResEntryValueContentEqualTo { 79 template <typename R> operatorResEntryValueContentEqualTo80 bool operator()(const R& a, const R& b) const { 81 return std::memcmp(a.ptr, b.ptr, sizeof(typename R::T)) == 0; 82 } 83 }; 84 85 // Base class that allows to write FlatEntries into entries_buffer. 86 class ResEntryWriter { 87 public: 88 virtual ~ResEntryWriter() = default; 89 90 // Writes resource table entry and its value into 'entries_buffer_' and returns offset 91 // in the buffer where entry was written. Write(const FlatEntry * entry)92 int32_t Write(const FlatEntry* entry) { 93 if (ValueCast<Item>(entry->value) != nullptr) { 94 return WriteItem(entry); 95 } else { 96 return WriteMap(entry); 97 } 98 } 99 100 protected: ResEntryWriter(BigBuffer * entries_buffer)101 ResEntryWriter(BigBuffer* entries_buffer) : entries_buffer_(entries_buffer) { 102 } 103 BigBuffer* entries_buffer_; 104 105 virtual int32_t WriteItem(const FlatEntry* entry) = 0; 106 107 virtual int32_t WriteMap(const FlatEntry* entry) = 0; 108 109 private: 110 DISALLOW_COPY_AND_ASSIGN(ResEntryWriter); 111 }; 112 113 int32_t WriteMapToBuffer(const FlatEntry* map_entry, BigBuffer* buffer); 114 115 template <bool compact_entry, typename T=ResEntryValue<compact_entry>> 116 std::pair<int32_t, T*> WriteItemToBuffer(const FlatEntry* item_entry, BigBuffer* buffer); 117 118 // ResEntryWriter which writes FlatEntries sequentially into entries_buffer. 119 // Next entry is always written right after previous one in the buffer. 120 template <bool compact_entry = false> 121 class SequentialResEntryWriter : public ResEntryWriter { 122 public: SequentialResEntryWriter(BigBuffer * entries_buffer)123 explicit SequentialResEntryWriter(BigBuffer* entries_buffer) 124 : ResEntryWriter(entries_buffer) { 125 } 126 ~SequentialResEntryWriter() override = default; 127 WriteItem(const FlatEntry * entry)128 int32_t WriteItem(const FlatEntry* entry) override { 129 auto result = WriteItemToBuffer<compact_entry>(entry, entries_buffer_); 130 return result.first; 131 } 132 WriteMap(const FlatEntry * entry)133 int32_t WriteMap(const FlatEntry* entry) override { 134 return WriteMapToBuffer(entry, entries_buffer_); 135 } 136 137 private: 138 DISALLOW_COPY_AND_ASSIGN(SequentialResEntryWriter); 139 }; 140 141 // ResEntryWriter that writes only unique entry and value pairs into entries_buffer. 142 // Next entry is written into buffer only if there is no entry with the same bytes representation 143 // in memory written before. Otherwise returns offset of already written entry. 144 template <bool compact_entry = false> 145 class DeduplicateItemsResEntryWriter : public ResEntryWriter { 146 public: DeduplicateItemsResEntryWriter(BigBuffer * entries_buffer)147 explicit DeduplicateItemsResEntryWriter(BigBuffer* entries_buffer) 148 : ResEntryWriter(entries_buffer) { 149 } 150 ~DeduplicateItemsResEntryWriter() override = default; 151 WriteItem(const FlatEntry * entry)152 int32_t WriteItem(const FlatEntry* entry) override { 153 const auto& [offset, out_entry] = WriteItemToBuffer<compact_entry>(entry, entries_buffer_); 154 155 auto [it, inserted] = entry_offsets.insert({Ref{*out_entry}, offset}); 156 if (inserted) { 157 // If inserted just return a new offset as this is a first time we store 158 // this entry 159 return offset; 160 } 161 162 // If not inserted this means that this is a duplicate, backup allocated block to the buffer 163 // and return offset of previously stored entry 164 entries_buffer_->BackUp(sizeof(*out_entry)); 165 return it->second; 166 } 167 WriteMap(const FlatEntry * entry)168 int32_t WriteMap(const FlatEntry* entry) override { 169 return WriteMapToBuffer(entry, entries_buffer_); 170 } 171 172 private: 173 DISALLOW_COPY_AND_ASSIGN(DeduplicateItemsResEntryWriter); 174 175 using Ref = ResEntryValueRef<compact_entry>; 176 using Map = std::unordered_map<Ref, int32_t, 177 ResEntryValueContentHasher, 178 ResEntryValueContentEqualTo>; 179 Map entry_offsets; 180 }; 181 182 } // namespace aapt 183 184 #endif 185