blob: b159a0076b02dfa44b1451697d52f73643b3680d [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
2 * Copyright (C) 2015 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#include "BigBuffer.h"
18#include "StringPiece.h"
19#include "StringPool.h"
20#include "Util.h"
21
22#include <algorithm>
23#include <androidfw/ResourceTypes.h>
24#include <memory>
25#include <string>
26
27namespace aapt {
28
29StringPool::Ref::Ref() : mEntry(nullptr) {
30}
31
32StringPool::Ref::Ref(const StringPool::Ref& rhs) : mEntry(rhs.mEntry) {
33 if (mEntry != nullptr) {
34 mEntry->ref++;
35 }
36}
37
38StringPool::Ref::Ref(StringPool::Entry* entry) : mEntry(entry) {
39 if (mEntry != nullptr) {
40 mEntry->ref++;
41 }
42}
43
44StringPool::Ref::~Ref() {
45 if (mEntry != nullptr) {
46 mEntry->ref--;
47 }
48}
49
50StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) {
51 if (rhs.mEntry != nullptr) {
52 rhs.mEntry->ref++;
53 }
54
55 if (mEntry != nullptr) {
56 mEntry->ref--;
57 }
58 mEntry = rhs.mEntry;
59 return *this;
60}
61
62const std::u16string* StringPool::Ref::operator->() const {
63 return &mEntry->value;
64}
65
66const std::u16string& StringPool::Ref::operator*() const {
67 return mEntry->value;
68}
69
70size_t StringPool::Ref::getIndex() const {
71 return mEntry->index;
72}
73
74const StringPool::Context& StringPool::Ref::getContext() const {
75 return mEntry->context;
76}
77
78StringPool::StyleRef::StyleRef() : mEntry(nullptr) {
79}
80
81StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : mEntry(rhs.mEntry) {
82 if (mEntry != nullptr) {
83 mEntry->ref++;
84 }
85}
86
87StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : mEntry(entry) {
88 if (mEntry != nullptr) {
89 mEntry->ref++;
90 }
91}
92
93StringPool::StyleRef::~StyleRef() {
94 if (mEntry != nullptr) {
95 mEntry->ref--;
96 }
97}
98
99StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) {
100 if (rhs.mEntry != nullptr) {
101 rhs.mEntry->ref++;
102 }
103
104 if (mEntry != nullptr) {
105 mEntry->ref--;
106 }
107 mEntry = rhs.mEntry;
108 return *this;
109}
110
111const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
112 return mEntry;
113}
114
115const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
116 return *mEntry;
117}
118
119size_t StringPool::StyleRef::getIndex() const {
120 return mEntry->str.getIndex();
121}
122
123const StringPool::Context& StringPool::StyleRef::getContext() const {
124 return mEntry->str.getContext();
125}
126
127StringPool::Ref StringPool::makeRef(const StringPiece16& str) {
128 return makeRefImpl(str, Context{}, true);
129}
130
131StringPool::Ref StringPool::makeRef(const StringPiece16& str, const Context& context) {
132 return makeRefImpl(str, context, true);
133}
134
135StringPool::Ref StringPool::makeRefImpl(const StringPiece16& str, const Context& context,
136 bool unique) {
137 if (unique) {
138 auto iter = mIndexedStrings.find(str);
139 if (iter != std::end(mIndexedStrings)) {
140 return Ref(iter->second);
141 }
142 }
143
144 Entry* entry = new Entry();
145 entry->value = str.toString();
146 entry->context = context;
147 entry->index = mStrings.size();
148 entry->ref = 0;
149 mStrings.emplace_back(entry);
150 mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry));
151 return Ref(entry);
152}
153
154StringPool::StyleRef StringPool::makeRef(const StyleString& str) {
155 return makeRef(str, Context{});
156}
157
158StringPool::StyleRef StringPool::makeRef(const StyleString& str, const Context& context) {
159 Entry* entry = new Entry();
160 entry->value = str.str;
161 entry->context = context;
162 entry->index = mStrings.size();
163 entry->ref = 0;
164 mStrings.emplace_back(entry);
165 mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry));
166
167 StyleEntry* styleEntry = new StyleEntry();
168 styleEntry->str = Ref(entry);
169 for (const aapt::Span& span : str.spans) {
170 styleEntry->spans.emplace_back(Span{makeRef(span.name),
171 span.firstChar, span.lastChar});
172 }
173 styleEntry->ref = 0;
174 mStyles.emplace_back(styleEntry);
175 return StyleRef(styleEntry);
176}
177
178void StringPool::merge(StringPool&& pool) {
179 mIndexedStrings.insert(pool.mIndexedStrings.begin(), pool.mIndexedStrings.end());
180 pool.mIndexedStrings.clear();
181 std::move(pool.mStrings.begin(), pool.mStrings.end(), std::back_inserter(mStrings));
182 pool.mStrings.clear();
183 std::move(pool.mStyles.begin(), pool.mStyles.end(), std::back_inserter(mStyles));
184 pool.mStyles.clear();
185
186 // Assign the indices.
187 const size_t len = mStrings.size();
188 for (size_t index = 0; index < len; index++) {
189 mStrings[index]->index = index;
190 }
191}
192
193void StringPool::hintWillAdd(size_t stringCount, size_t styleCount) {
194 mStrings.reserve(mStrings.size() + stringCount);
195 mStyles.reserve(mStyles.size() + styleCount);
196}
197
198void StringPool::prune() {
199 const auto iterEnd = std::end(mIndexedStrings);
200 auto indexIter = std::begin(mIndexedStrings);
201 while (indexIter != iterEnd) {
202 if (indexIter->second->ref <= 0) {
203 mIndexedStrings.erase(indexIter++);
204 } else {
205 ++indexIter;
206 }
207 }
208
209 auto endIter2 = std::remove_if(std::begin(mStrings), std::end(mStrings),
210 [](const std::unique_ptr<Entry>& entry) -> bool {
211 return entry->ref <= 0;
212 }
213 );
214
215 auto endIter3 = std::remove_if(std::begin(mStyles), std::end(mStyles),
216 [](const std::unique_ptr<StyleEntry>& entry) -> bool {
217 return entry->ref <= 0;
218 }
219 );
220
221 // Remove the entries at the end or else we'll be accessing
222 // a deleted string from the StyleEntry.
223 mStrings.erase(endIter2, std::end(mStrings));
224 mStyles.erase(endIter3, std::end(mStyles));
225}
226
227void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp) {
228 std::sort(std::begin(mStrings), std::end(mStrings),
229 [&cmp](const std::unique_ptr<Entry>& a, const std::unique_ptr<Entry>& b) -> bool {
230 return cmp(*a, *b);
231 }
232 );
233
234 // Assign the indices.
235 const size_t len = mStrings.size();
236 for (size_t index = 0; index < len; index++) {
237 mStrings[index]->index = index;
238 }
239
240 // Reorder the styles.
241 std::sort(std::begin(mStyles), std::end(mStyles),
242 [](const std::unique_ptr<StyleEntry>& lhs,
243 const std::unique_ptr<StyleEntry>& rhs) -> bool {
244 return lhs->str.getIndex() < rhs->str.getIndex();
245 }
246 );
247}
248
249static uint8_t* encodeLength(uint8_t* data, size_t length) {
250 if (length > 0x7fu) {
251 *data++ = 0x80u | (0x000000ffu & (length >> 8));
252 }
253 *data++ = 0x000000ffu & length;
254 return data;
255}
256
257static size_t encodedLengthByteCount(size_t length) {
258 return length > 0x7fu ? 2 : 1;
259}
260
261bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
262 const size_t startIndex = out->size();
263 android::ResStringPool_header* header = out->nextBlock<android::ResStringPool_header>();
264 header->header.type = android::RES_STRING_POOL_TYPE;
265 header->header.headerSize = sizeof(*header);
266 header->stringCount = pool.size();
267 header->flags |= android::ResStringPool_header::UTF8_FLAG;
268
269 uint32_t* indices = out->nextBlock<uint32_t>(pool.size());
270
271 uint32_t* styleIndices = nullptr;
272 if (!pool.mStyles.empty()) {
273 header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
274 styleIndices = out->nextBlock<uint32_t>(header->styleCount);
275 }
276
277 const size_t beforeStringsIndex = out->size();
278 header->stringsStart = beforeStringsIndex - startIndex;
279
280 for (const auto& entry : pool) {
281 *indices = out->size() - beforeStringsIndex;
282 indices++;
283
284 std::string encoded = util::utf16ToUtf8(entry->value);
285
286 const size_t stringByteLength = sizeof(char) * encoded.length();
287 const size_t totalSize = encodedLengthByteCount(entry->value.size())
288 + encodedLengthByteCount(encoded.length())
289 + stringByteLength
290 + sizeof(char);
291
292 uint8_t* data = out->nextBlock<uint8_t>(totalSize);
293
294 // First encode the actual UTF16 string length.
295 data = encodeLength(data, entry->value.size());
296
297 // Now encode the size of the converted UTF8 string.
298 data = encodeLength(data, encoded.length());
299
300 memcpy(data, encoded.data(), stringByteLength);
301 data += stringByteLength;
302 *data = 0;
303 }
304
305 out->align4();
306
307 if (!pool.mStyles.empty()) {
308 const size_t beforeStylesIndex = out->size();
309 header->stylesStart = beforeStylesIndex - startIndex;
310
311 size_t currentIndex = 0;
312 for (const auto& entry : pool.mStyles) {
313 while (entry->str.getIndex() > currentIndex) {
314 styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
315
316 uint32_t* spanOffset = out->nextBlock<uint32_t>();
317 *spanOffset = android::ResStringPool_span::END;
318 }
319 styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
320
321 android::ResStringPool_span* span =
322 out->nextBlock<android::ResStringPool_span>(entry->spans.size());
323 for (const auto& s : entry->spans) {
324 span->name.index = s.name.getIndex();
325 span->firstChar = s.firstChar;
326 span->lastChar = s.lastChar;
327 span++;
328 }
329
330 uint32_t* spanEnd = out->nextBlock<uint32_t>();
331 *spanEnd = android::ResStringPool_span::END;
332 }
333
334 // The error checking code in the platform looks for an entire
335 // ResStringPool_span structure worth of 0xFFFFFFFF at the end
336 // of the style block, so fill in the remaining 2 32bit words
337 // with 0xFFFFFFFF.
338 const size_t paddingLength = sizeof(android::ResStringPool_span)
339 - sizeof(android::ResStringPool_span::name);
340 uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
341 memset(padding, 0xff, paddingLength);
342 out->align4();
343 }
344 header->header.size = out->size() - startIndex;
345 return true;
346}
347
348} // namespace aapt