Home | History | Annotate | Download | only in aapt2
      1 /*
      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 "StringPool.h"
     18 #include "util/BigBuffer.h"
     19 #include "util/StringPiece.h"
     20 #include "util/Util.h"
     21 
     22 #include <algorithm>
     23 #include <androidfw/ResourceTypes.h>
     24 #include <memory>
     25 #include <string>
     26 
     27 namespace aapt {
     28 
     29 StringPool::Ref::Ref() : mEntry(nullptr) {
     30 }
     31 
     32 StringPool::Ref::Ref(const StringPool::Ref& rhs) : mEntry(rhs.mEntry) {
     33     if (mEntry != nullptr) {
     34         mEntry->ref++;
     35     }
     36 }
     37 
     38 StringPool::Ref::Ref(StringPool::Entry* entry) : mEntry(entry) {
     39     if (mEntry != nullptr) {
     40         mEntry->ref++;
     41     }
     42 }
     43 
     44 StringPool::Ref::~Ref() {
     45     if (mEntry != nullptr) {
     46         mEntry->ref--;
     47     }
     48 }
     49 
     50 StringPool::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 
     62 const std::u16string* StringPool::Ref::operator->() const {
     63     return &mEntry->value;
     64 }
     65 
     66 const std::u16string& StringPool::Ref::operator*() const {
     67     return mEntry->value;
     68 }
     69 
     70 size_t StringPool::Ref::getIndex() const {
     71     return mEntry->index;
     72 }
     73 
     74 const StringPool::Context& StringPool::Ref::getContext() const {
     75     return mEntry->context;
     76 }
     77 
     78 StringPool::StyleRef::StyleRef() : mEntry(nullptr) {
     79 }
     80 
     81 StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : mEntry(rhs.mEntry) {
     82     if (mEntry != nullptr) {
     83         mEntry->ref++;
     84     }
     85 }
     86 
     87 StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : mEntry(entry) {
     88     if (mEntry != nullptr) {
     89         mEntry->ref++;
     90     }
     91 }
     92 
     93 StringPool::StyleRef::~StyleRef() {
     94     if (mEntry != nullptr) {
     95         mEntry->ref--;
     96     }
     97 }
     98 
     99 StringPool::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 
    111 const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
    112     return mEntry;
    113 }
    114 
    115 const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
    116     return *mEntry;
    117 }
    118 
    119 size_t StringPool::StyleRef::getIndex() const {
    120     return mEntry->str.getIndex();
    121 }
    122 
    123 const StringPool::Context& StringPool::StyleRef::getContext() const {
    124     return mEntry->str.getContext();
    125 }
    126 
    127 StringPool::Ref StringPool::makeRef(const StringPiece16& str) {
    128     return makeRefImpl(str, Context{}, true);
    129 }
    130 
    131 StringPool::Ref StringPool::makeRef(const StringPiece16& str, const Context& context) {
    132     return makeRefImpl(str, context, true);
    133 }
    134 
    135 StringPool::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 
    154 StringPool::StyleRef StringPool::makeRef(const StyleString& str) {
    155     return makeRef(str, Context{});
    156 }
    157 
    158 StringPool::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 
    178 StringPool::StyleRef StringPool::makeRef(const StyleRef& ref) {
    179     Entry* entry = new Entry();
    180     entry->value = *ref.mEntry->str;
    181     entry->context = ref.mEntry->str.mEntry->context;
    182     entry->index = mStrings.size();
    183     entry->ref = 0;
    184     mStrings.emplace_back(entry);
    185     mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry));
    186 
    187     StyleEntry* styleEntry = new StyleEntry();
    188     styleEntry->str = Ref(entry);
    189     for (const Span& span : ref.mEntry->spans) {
    190         styleEntry->spans.emplace_back(Span{ makeRef(*span.name), span.firstChar, span.lastChar });
    191     }
    192     styleEntry->ref = 0;
    193     mStyles.emplace_back(styleEntry);
    194     return StyleRef(styleEntry);
    195 }
    196 
    197 void StringPool::merge(StringPool&& pool) {
    198     mIndexedStrings.insert(pool.mIndexedStrings.begin(), pool.mIndexedStrings.end());
    199     pool.mIndexedStrings.clear();
    200     std::move(pool.mStrings.begin(), pool.mStrings.end(), std::back_inserter(mStrings));
    201     pool.mStrings.clear();
    202     std::move(pool.mStyles.begin(), pool.mStyles.end(), std::back_inserter(mStyles));
    203     pool.mStyles.clear();
    204 
    205     // Assign the indices.
    206     const size_t len = mStrings.size();
    207     for (size_t index = 0; index < len; index++) {
    208         mStrings[index]->index = index;
    209     }
    210 }
    211 
    212 void StringPool::hintWillAdd(size_t stringCount, size_t styleCount) {
    213     mStrings.reserve(mStrings.size() + stringCount);
    214     mStyles.reserve(mStyles.size() + styleCount);
    215 }
    216 
    217 void StringPool::prune() {
    218     const auto iterEnd = std::end(mIndexedStrings);
    219     auto indexIter = std::begin(mIndexedStrings);
    220     while (indexIter != iterEnd) {
    221         if (indexIter->second->ref <= 0) {
    222             indexIter = mIndexedStrings.erase(indexIter);
    223         } else {
    224             ++indexIter;
    225         }
    226     }
    227 
    228     auto endIter2 = std::remove_if(std::begin(mStrings), std::end(mStrings),
    229             [](const std::unique_ptr<Entry>& entry) -> bool {
    230                 return entry->ref <= 0;
    231             }
    232     );
    233 
    234     auto endIter3 = std::remove_if(std::begin(mStyles), std::end(mStyles),
    235             [](const std::unique_ptr<StyleEntry>& entry) -> bool {
    236                 return entry->ref <= 0;
    237             }
    238     );
    239 
    240     // Remove the entries at the end or else we'll be accessing
    241     // a deleted string from the StyleEntry.
    242     mStrings.erase(endIter2, std::end(mStrings));
    243     mStyles.erase(endIter3, std::end(mStyles));
    244 
    245     // Reassign the indices.
    246     const size_t len = mStrings.size();
    247     for (size_t index = 0; index < len; index++) {
    248         mStrings[index]->index = index;
    249     }
    250 }
    251 
    252 void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp) {
    253     std::sort(std::begin(mStrings), std::end(mStrings),
    254             [&cmp](const std::unique_ptr<Entry>& a, const std::unique_ptr<Entry>& b) -> bool {
    255                 return cmp(*a, *b);
    256             }
    257     );
    258 
    259     // Assign the indices.
    260     const size_t len = mStrings.size();
    261     for (size_t index = 0; index < len; index++) {
    262         mStrings[index]->index = index;
    263     }
    264 
    265     // Reorder the styles.
    266     std::sort(std::begin(mStyles), std::end(mStyles),
    267             [](const std::unique_ptr<StyleEntry>& lhs,
    268                const std::unique_ptr<StyleEntry>& rhs) -> bool {
    269                 return lhs->str.getIndex() < rhs->str.getIndex();
    270             }
    271     );
    272 }
    273 
    274 template <typename T>
    275 static T* encodeLength(T* data, size_t length) {
    276     static_assert(std::is_integral<T>::value, "wat.");
    277 
    278     constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
    279     constexpr size_t kMaxSize = kMask - 1;
    280     if (length > kMaxSize) {
    281         *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8)));
    282     }
    283     *data++ = length;
    284     return data;
    285 }
    286 
    287 template <typename T>
    288 static size_t encodedLengthUnits(size_t length) {
    289     static_assert(std::is_integral<T>::value, "wat.");
    290 
    291     constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1);
    292     constexpr size_t kMaxSize = kMask - 1;
    293     return length > kMaxSize ? 2 : 1;
    294 }
    295 
    296 
    297 bool StringPool::flatten(BigBuffer* out, const StringPool& pool, bool utf8) {
    298     const size_t startIndex = out->size();
    299     android::ResStringPool_header* header = out->nextBlock<android::ResStringPool_header>();
    300     header->header.type = android::RES_STRING_POOL_TYPE;
    301     header->header.headerSize = sizeof(*header);
    302     header->stringCount = pool.size();
    303     if (utf8) {
    304         header->flags |= android::ResStringPool_header::UTF8_FLAG;
    305     }
    306 
    307     uint32_t* indices = pool.size() != 0 ? out->nextBlock<uint32_t>(pool.size()) : nullptr;
    308 
    309     uint32_t* styleIndices = nullptr;
    310     if (!pool.mStyles.empty()) {
    311         header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
    312         styleIndices = out->nextBlock<uint32_t>(header->styleCount);
    313     }
    314 
    315     const size_t beforeStringsIndex = out->size();
    316     header->stringsStart = beforeStringsIndex - startIndex;
    317 
    318     for (const auto& entry : pool) {
    319         *indices = out->size() - beforeStringsIndex;
    320         indices++;
    321 
    322         if (utf8) {
    323             std::string encoded = util::utf16ToUtf8(entry->value);
    324 
    325             const size_t totalSize = encodedLengthUnits<char>(entry->value.size())
    326                     + encodedLengthUnits<char>(encoded.length())
    327                     + encoded.size() + 1;
    328 
    329             char* data = out->nextBlock<char>(totalSize);
    330 
    331             // First encode the actual UTF16 string length.
    332             data = encodeLength(data, entry->value.size());
    333 
    334             // Now encode the size of the converted UTF8 string.
    335             data = encodeLength(data, encoded.length());
    336             strncpy(data, encoded.data(), encoded.size());
    337         } else {
    338             const size_t totalSize = encodedLengthUnits<char16_t>(entry->value.size())
    339                     + entry->value.size() + 1;
    340 
    341             char16_t* data = out->nextBlock<char16_t>(totalSize);
    342 
    343             // Encode the actual UTF16 string length.
    344             data = encodeLength(data, entry->value.size());
    345             const size_t byteLength = entry->value.size() * sizeof(char16_t);
    346 
    347             // NOTE: For some reason, strncpy16(data, entry->value.data(), entry->value.size())
    348             // truncates the string.
    349             memcpy(data, entry->value.data(), byteLength);
    350 
    351             // The null-terminating character is already here due to the block of data being set
    352             // to 0s on allocation.
    353         }
    354     }
    355 
    356     out->align4();
    357 
    358     if (!pool.mStyles.empty()) {
    359         const size_t beforeStylesIndex = out->size();
    360         header->stylesStart = beforeStylesIndex - startIndex;
    361 
    362         size_t currentIndex = 0;
    363         for (const auto& entry : pool.mStyles) {
    364             while (entry->str.getIndex() > currentIndex) {
    365                 styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
    366 
    367                 uint32_t* spanOffset = out->nextBlock<uint32_t>();
    368                 *spanOffset = android::ResStringPool_span::END;
    369             }
    370             styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
    371 
    372             android::ResStringPool_span* span =
    373                     out->nextBlock<android::ResStringPool_span>(entry->spans.size());
    374             for (const auto& s : entry->spans) {
    375                 span->name.index = s.name.getIndex();
    376                 span->firstChar = s.firstChar;
    377                 span->lastChar = s.lastChar;
    378                 span++;
    379             }
    380 
    381             uint32_t* spanEnd = out->nextBlock<uint32_t>();
    382             *spanEnd = android::ResStringPool_span::END;
    383         }
    384 
    385         // The error checking code in the platform looks for an entire
    386         // ResStringPool_span structure worth of 0xFFFFFFFF at the end
    387         // of the style block, so fill in the remaining 2 32bit words
    388         // with 0xFFFFFFFF.
    389         const size_t paddingLength = sizeof(android::ResStringPool_span)
    390                 - sizeof(android::ResStringPool_span::name);
    391         uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
    392         memset(padding, 0xff, paddingLength);
    393         out->align4();
    394     }
    395     header->header.size = out->size() - startIndex;
    396     return true;
    397 }
    398 
    399 bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
    400     return flatten(out, pool, true);
    401 }
    402 
    403 bool StringPool::flattenUtf16(BigBuffer* out, const StringPool& pool) {
    404     return flatten(out, pool, false);
    405 }
    406 
    407 } // namespace aapt
    408