• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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