blob: fe4b967221185e08f98fb8340d98cbc10521f1ae [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
Adam Lesinski52364f72016-01-11 13:10:24 -080017#include "StringPool.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070018#include "util/BigBuffer.h"
19#include "util/StringPiece.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070020#include "util/Util.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080021
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
Adam Lesinskid0f116b2016-07-08 15:00:32 -070062const std::string* StringPool::Ref::operator->() const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080063 return &mEntry->value;
64}
65
Adam Lesinskid0f116b2016-07-08 15:00:32 -070066const std::string& StringPool::Ref::operator*() const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067 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
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700127StringPool::Ref StringPool::makeRef(const StringPiece& str) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800128 return makeRefImpl(str, Context{}, true);
129}
130
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700131StringPool::Ref StringPool::makeRef(const StringPiece& str, const Context& context) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800132 return makeRefImpl(str, context, true);
133}
134
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700135StringPool::Ref StringPool::makeRefImpl(const StringPiece& str, const Context& context,
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800136 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);
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700150 mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800151 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);
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700165 mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800166
167 StyleEntry* styleEntry = new StyleEntry();
168 styleEntry->str = Ref(entry);
169 for (const aapt::Span& span : str.spans) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700170 styleEntry->spans.emplace_back(Span{ makeRef(span.name), span.firstChar, span.lastChar });
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800171 }
172 styleEntry->ref = 0;
173 mStyles.emplace_back(styleEntry);
174 return StyleRef(styleEntry);
175}
176
Adam Lesinski769de982015-04-10 19:43:55 -0700177StringPool::StyleRef StringPool::makeRef(const StyleRef& ref) {
178 Entry* entry = new Entry();
179 entry->value = *ref.mEntry->str;
180 entry->context = ref.mEntry->str.mEntry->context;
181 entry->index = mStrings.size();
182 entry->ref = 0;
183 mStrings.emplace_back(entry);
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700184 mIndexedStrings.insert(std::make_pair(StringPiece(entry->value), entry));
Adam Lesinski769de982015-04-10 19:43:55 -0700185
186 StyleEntry* styleEntry = new StyleEntry();
187 styleEntry->str = Ref(entry);
188 for (const Span& span : ref.mEntry->spans) {
189 styleEntry->spans.emplace_back(Span{ makeRef(*span.name), span.firstChar, span.lastChar });
190 }
191 styleEntry->ref = 0;
192 mStyles.emplace_back(styleEntry);
193 return StyleRef(styleEntry);
194}
195
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800196void StringPool::merge(StringPool&& pool) {
197 mIndexedStrings.insert(pool.mIndexedStrings.begin(), pool.mIndexedStrings.end());
198 pool.mIndexedStrings.clear();
199 std::move(pool.mStrings.begin(), pool.mStrings.end(), std::back_inserter(mStrings));
200 pool.mStrings.clear();
201 std::move(pool.mStyles.begin(), pool.mStyles.end(), std::back_inserter(mStyles));
202 pool.mStyles.clear();
203
204 // Assign the indices.
205 const size_t len = mStrings.size();
206 for (size_t index = 0; index < len; index++) {
207 mStrings[index]->index = index;
208 }
209}
210
211void StringPool::hintWillAdd(size_t stringCount, size_t styleCount) {
212 mStrings.reserve(mStrings.size() + stringCount);
213 mStyles.reserve(mStyles.size() + styleCount);
214}
215
216void StringPool::prune() {
217 const auto iterEnd = std::end(mIndexedStrings);
218 auto indexIter = std::begin(mIndexedStrings);
219 while (indexIter != iterEnd) {
220 if (indexIter->second->ref <= 0) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700221 indexIter = mIndexedStrings.erase(indexIter);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800222 } else {
223 ++indexIter;
224 }
225 }
226
227 auto endIter2 = std::remove_if(std::begin(mStrings), std::end(mStrings),
228 [](const std::unique_ptr<Entry>& entry) -> bool {
229 return entry->ref <= 0;
230 }
231 );
232
233 auto endIter3 = std::remove_if(std::begin(mStyles), std::end(mStyles),
234 [](const std::unique_ptr<StyleEntry>& entry) -> bool {
235 return entry->ref <= 0;
236 }
237 );
238
239 // Remove the entries at the end or else we'll be accessing
240 // a deleted string from the StyleEntry.
241 mStrings.erase(endIter2, std::end(mStrings));
242 mStyles.erase(endIter3, std::end(mStyles));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700243
244 // Reassign the indices.
245 const size_t len = mStrings.size();
246 for (size_t index = 0; index < len; index++) {
247 mStrings[index]->index = index;
248 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800249}
250
251void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp) {
252 std::sort(std::begin(mStrings), std::end(mStrings),
253 [&cmp](const std::unique_ptr<Entry>& a, const std::unique_ptr<Entry>& b) -> bool {
254 return cmp(*a, *b);
255 }
256 );
257
258 // Assign the indices.
259 const size_t len = mStrings.size();
260 for (size_t index = 0; index < len; index++) {
261 mStrings[index]->index = index;
262 }
263
264 // Reorder the styles.
265 std::sort(std::begin(mStyles), std::end(mStyles),
266 [](const std::unique_ptr<StyleEntry>& lhs,
267 const std::unique_ptr<StyleEntry>& rhs) -> bool {
268 return lhs->str.getIndex() < rhs->str.getIndex();
269 }
270 );
271}
272
Adam Lesinski24aad162015-04-24 19:19:30 -0700273template <typename T>
274static T* encodeLength(T* data, size_t length) {
275 static_assert(std::is_integral<T>::value, "wat.");
276
277 constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
278 constexpr size_t kMaxSize = kMask - 1;
279 if (length > kMaxSize) {
280 *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800281 }
Adam Lesinski24aad162015-04-24 19:19:30 -0700282 *data++ = length;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800283 return data;
284}
285
Adam Lesinski24aad162015-04-24 19:19:30 -0700286template <typename T>
287static size_t encodedLengthUnits(size_t length) {
288 static_assert(std::is_integral<T>::value, "wat.");
289
290 constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
291 constexpr size_t kMaxSize = kMask - 1;
292 return length > kMaxSize ? 2 : 1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800293}
294
Adam Lesinski24aad162015-04-24 19:19:30 -0700295
296bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800297 const size_t startIndex = out->size();
298 android::ResStringPool_header* header = out->nextBlock<android::ResStringPool_header>();
299 header->header.type = android::RES_STRING_POOL_TYPE;
300 header->header.headerSize = sizeof(*header);
301 header->stringCount = pool.size();
Adam Lesinski24aad162015-04-24 19:19:30 -0700302 if (utf8) {
303 header->flags |= android::ResStringPool_header::UTF8_FLAG;
304 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800305
Adam Lesinski769de982015-04-10 19:43:55 -0700306 uint32_t* indices = pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800307
308 uint32_t* styleIndices = nullptr;
309 if (!pool.mStyles.empty()) {
310 header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
311 styleIndices = out->nextBlock<uint32_t>(header->styleCount);
312 }
313
314 const size_t beforeStringsIndex = out->size();
315 header->stringsStart = beforeStringsIndex - startIndex;
316
317 for (const auto& entry : pool) {
318 *indices = out->size() - beforeStringsIndex;
319 indices++;
320
Adam Lesinski24aad162015-04-24 19:19:30 -0700321 if (utf8) {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700322 const std::string& encoded = entry->value;
323 const ssize_t utf16Length = utf8_to_utf16_length(
324 reinterpret_cast<const uint8_t*>(entry->value.data()), entry->value.size());
325 assert(utf16Length >= 0);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800326
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700327 const size_t totalSize = encodedLengthUnits<char>(utf16Length)
Adam Lesinski24aad162015-04-24 19:19:30 -0700328 + encodedLengthUnits<char>(encoded.length())
329 + encoded.size() + 1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800330
Adam Lesinski24aad162015-04-24 19:19:30 -0700331 char* data = out->nextBlock<char>(totalSize);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800332
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700333 // First encode the UTF16 string length.
334 data = encodeLength(data, utf16Length);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800335
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700336 // Now encode the size of the real UTF8 string.
Adam Lesinski24aad162015-04-24 19:19:30 -0700337 data = encodeLength(data, encoded.length());
338 strncpy(data, encoded.data(), encoded.size());
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700339
Adam Lesinski24aad162015-04-24 19:19:30 -0700340 } else {
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700341 const std::u16string encoded = util::utf8ToUtf16(entry->value);
342 const ssize_t utf16Length = encoded.size();
343
344 // Total number of 16-bit words to write.
345 const size_t totalSize = encodedLengthUnits<char16_t>(utf16Length) + encoded.size() + 1;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800346
Adam Lesinski24aad162015-04-24 19:19:30 -0700347 char16_t* data = out->nextBlock<char16_t>(totalSize);
348
349 // Encode the actual UTF16 string length.
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700350 data = encodeLength(data, utf16Length);
351 const size_t byteLength = encoded.size() * sizeof(char16_t);
Adam Lesinski52364f72016-01-11 13:10:24 -0800352
353 // NOTE: For some reason, strncpy16(data, entry->value.data(), entry->value.size())
354 // truncates the string.
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700355 memcpy(data, encoded.data(), byteLength);
Adam Lesinski52364f72016-01-11 13:10:24 -0800356
357 // The null-terminating character is already here due to the block of data being set
358 // to 0s on allocation.
Adam Lesinski24aad162015-04-24 19:19:30 -0700359 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800360 }
361
362 out->align4();
363
364 if (!pool.mStyles.empty()) {
365 const size_t beforeStylesIndex = out->size();
366 header->stylesStart = beforeStylesIndex - startIndex;
367
368 size_t currentIndex = 0;
369 for (const auto& entry : pool.mStyles) {
370 while (entry->str.getIndex() > currentIndex) {
371 styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
372
373 uint32_t* spanOffset = out->nextBlock<uint32_t>();
374 *spanOffset = android::ResStringPool_span::END;
375 }
376 styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
377
378 android::ResStringPool_span* span =
379 out->nextBlock<android::ResStringPool_span>(entry->spans.size());
380 for (const auto& s : entry->spans) {
381 span->name.index = s.name.getIndex();
382 span->firstChar = s.firstChar;
383 span->lastChar = s.lastChar;
384 span++;
385 }
386
387 uint32_t* spanEnd = out->nextBlock<uint32_t>();
388 *spanEnd = android::ResStringPool_span::END;
389 }
390
391 // The error checking code in the platform looks for an entire
392 // ResStringPool_span structure worth of 0xFFFFFFFF at the end
393 // of the style block, so fill in the remaining 2 32bit words
394 // with 0xFFFFFFFF.
395 const size_t paddingLength = sizeof(android::ResStringPool_span)
396 - sizeof(android::ResStringPool_span::name);
397 uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
398 memset(padding, 0xff, paddingLength);
399 out->align4();
400 }
401 header->header.size = out->size() - startIndex;
402 return true;
403}
404
Adam Lesinski24aad162015-04-24 19:19:30 -0700405bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
406 return flatten(out, pool, true);
407}
408
409bool StringPool::flattenUtf16(BigBuffer* out, const StringPool& pool) {
410 return flatten(out, pool, false);
411}
412
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800413} // namespace aapt