Home | History | Annotate | Download | only in aapt
      1 //
      2 // Copyright 2006 The Android Open Source Project
      3 //
      4 // Build resource files from raw assets.
      5 //
      6 
      7 #include "StringPool.h"
      8 
      9 #include <utils/ByteOrder.h>
     10 
     11 #if HAVE_PRINTF_ZD
     12 #  define ZD "%zd"
     13 #  define ZD_TYPE ssize_t
     14 #else
     15 #  define ZD "%ld"
     16 #  define ZD_TYPE long
     17 #endif
     18 
     19 #define NOISY(x) //x
     20 
     21 void strcpy16_htod(uint16_t* dst, const uint16_t* src)
     22 {
     23     while (*src) {
     24         char16_t s = htods(*src);
     25         *dst++ = s;
     26         src++;
     27     }
     28     *dst = 0;
     29 }
     30 
     31 void printStringPool(const ResStringPool* pool)
     32 {
     33     const size_t NS = pool->size();
     34     for (size_t s=0; s<NS; s++) {
     35         size_t len;
     36         const char *str = (const char*)pool->string8At(s, &len);
     37         if (str == NULL) {
     38             str = String8(pool->stringAt(s, &len)).string();
     39         }
     40 
     41         printf("String #" ZD ": %s\n", (ZD_TYPE) s, str);
     42     }
     43 }
     44 
     45 StringPool::StringPool(bool sorted, bool utf8)
     46     : mSorted(sorted), mUTF8(utf8), mValues(-1), mIdents(-1)
     47 {
     48 }
     49 
     50 ssize_t StringPool::add(const String16& value, bool mergeDuplicates)
     51 {
     52     return add(String16(), value, mergeDuplicates);
     53 }
     54 
     55 ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans)
     56 {
     57     ssize_t res = add(String16(), value, false);
     58     if (res >= 0) {
     59         addStyleSpans(res, spans);
     60     }
     61     return res;
     62 }
     63 
     64 ssize_t StringPool::add(const String16& ident, const String16& value,
     65                         bool mergeDuplicates)
     66 {
     67     if (ident.size() > 0) {
     68         ssize_t idx = mIdents.valueFor(ident);
     69         if (idx >= 0) {
     70             fprintf(stderr, "ERROR: Duplicate string identifier %s\n",
     71                     String8(mEntries[idx].value).string());
     72             return UNKNOWN_ERROR;
     73         }
     74     }
     75 
     76     ssize_t vidx = mValues.indexOfKey(value);
     77     ssize_t pos = vidx >= 0 ? mValues.valueAt(vidx) : -1;
     78     ssize_t eidx = pos >= 0 ? mEntryArray.itemAt(pos) : -1;
     79     if (eidx < 0) {
     80         eidx = mEntries.add(entry(value));
     81         if (eidx < 0) {
     82             fprintf(stderr, "Failure adding string %s\n", String8(value).string());
     83             return eidx;
     84         }
     85     }
     86 
     87     const bool first = vidx < 0;
     88     if (first || !mergeDuplicates) {
     89         pos = mEntryArray.add(eidx);
     90         if (first) {
     91             vidx = mValues.add(value, pos);
     92             const size_t N = mEntryArrayToValues.size();
     93             for (size_t i=0; i<N; i++) {
     94                 size_t& e = mEntryArrayToValues.editItemAt(i);
     95                 if ((ssize_t)e >= vidx) {
     96                     e++;
     97                 }
     98             }
     99         }
    100         mEntryArrayToValues.add(vidx);
    101         if (!mSorted) {
    102             entry& ent = mEntries.editItemAt(eidx);
    103             ent.indices.add(pos);
    104         }
    105     }
    106 
    107     if (ident.size() > 0) {
    108         mIdents.add(ident, vidx);
    109     }
    110 
    111     NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n",
    112             String8(value).string(), pos, eidx, vidx));
    113 
    114     return pos;
    115 }
    116 
    117 status_t StringPool::addStyleSpan(size_t idx, const String16& name,
    118                                   uint32_t start, uint32_t end)
    119 {
    120     entry_style_span span;
    121     span.name = name;
    122     span.span.firstChar = start;
    123     span.span.lastChar = end;
    124     return addStyleSpan(idx, span);
    125 }
    126 
    127 status_t StringPool::addStyleSpans(size_t idx, const Vector<entry_style_span>& spans)
    128 {
    129     const size_t N=spans.size();
    130     for (size_t i=0; i<N; i++) {
    131         status_t err = addStyleSpan(idx, spans[i]);
    132         if (err != NO_ERROR) {
    133             return err;
    134         }
    135     }
    136     return NO_ERROR;
    137 }
    138 
    139 status_t StringPool::addStyleSpan(size_t idx, const entry_style_span& span)
    140 {
    141     LOG_ALWAYS_FATAL_IF(mSorted, "Can't use styles with sorted string pools.");
    142 
    143     // Place blank entries in the span array up to this index.
    144     while (mEntryStyleArray.size() <= idx) {
    145         mEntryStyleArray.add();
    146     }
    147 
    148     entry_style& style = mEntryStyleArray.editItemAt(idx);
    149     style.spans.add(span);
    150     return NO_ERROR;
    151 }
    152 
    153 size_t StringPool::size() const
    154 {
    155     return mSorted ? mValues.size() : mEntryArray.size();
    156 }
    157 
    158 const StringPool::entry& StringPool::entryAt(size_t idx) const
    159 {
    160     if (!mSorted) {
    161         return mEntries[mEntryArray[idx]];
    162     } else {
    163         return mEntries[mEntryArray[mValues.valueAt(idx)]];
    164     }
    165 }
    166 
    167 size_t StringPool::countIdentifiers() const
    168 {
    169     return mIdents.size();
    170 }
    171 
    172 sp<AaptFile> StringPool::createStringBlock()
    173 {
    174     sp<AaptFile> pool = new AaptFile(String8(), AaptGroupEntry(),
    175                                      String8());
    176     status_t err = writeStringBlock(pool);
    177     return err == NO_ERROR ? pool : NULL;
    178 }
    179 
    180 #define ENCODE_LENGTH(str, chrsz, strSize) \
    181 { \
    182     size_t maxMask = 1 << ((chrsz*8)-1); \
    183     size_t maxSize = maxMask-1; \
    184     if (strSize > maxSize) { \
    185         *str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \
    186     } \
    187     *str++ = strSize; \
    188 }
    189 
    190 status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
    191 {
    192     // Allow appending.  Sorry this is a little wacky.
    193     if (pool->getSize() > 0) {
    194         sp<AaptFile> block = createStringBlock();
    195         if (block == NULL) {
    196             return UNKNOWN_ERROR;
    197         }
    198         ssize_t res = pool->writeData(block->getData(), block->getSize());
    199         return (res >= 0) ? (status_t)NO_ERROR : res;
    200     }
    201 
    202     // First we need to add all style span names to the string pool.
    203     // We do this now (instead of when the span is added) so that these
    204     // will appear at the end of the pool, not disrupting the order
    205     // our client placed their own strings in it.
    206 
    207     const size_t STYLES = mEntryStyleArray.size();
    208     size_t i;
    209 
    210     for (i=0; i<STYLES; i++) {
    211         entry_style& style = mEntryStyleArray.editItemAt(i);
    212         const size_t N = style.spans.size();
    213         for (size_t i=0; i<N; i++) {
    214             entry_style_span& span = style.spans.editItemAt(i);
    215             ssize_t idx = add(span.name, true);
    216             if (idx < 0) {
    217                 fprintf(stderr, "Error adding span for style tag '%s'\n",
    218                         String8(span.name).string());
    219                 return idx;
    220             }
    221             span.span.name.index = (uint32_t)idx;
    222         }
    223     }
    224 
    225     const size_t ENTRIES = size();
    226 
    227     // Now build the pool of unique strings.
    228 
    229     const size_t STRINGS = mEntries.size();
    230     const size_t preSize = sizeof(ResStringPool_header)
    231                          + (sizeof(uint32_t)*ENTRIES)
    232                          + (sizeof(uint32_t)*STYLES);
    233     if (pool->editData(preSize) == NULL) {
    234         fprintf(stderr, "ERROR: Out of memory for string pool\n");
    235         return NO_MEMORY;
    236     }
    237 
    238     const size_t charSize = mUTF8 ? sizeof(uint8_t) : sizeof(char16_t);
    239 
    240     size_t strPos = 0;
    241     for (i=0; i<STRINGS; i++) {
    242         entry& ent = mEntries.editItemAt(i);
    243         const size_t strSize = (ent.value.size());
    244         const size_t lenSize = strSize > (size_t)(1<<((charSize*8)-1))-1 ?
    245             charSize*2 : charSize;
    246 
    247         String8 encStr;
    248         if (mUTF8) {
    249             encStr = String8(ent.value);
    250         }
    251 
    252         const size_t encSize = mUTF8 ? encStr.size() : 0;
    253         const size_t encLenSize = mUTF8 ?
    254             (encSize > (size_t)(1<<((charSize*8)-1))-1 ?
    255                 charSize*2 : charSize) : 0;
    256 
    257         ent.offset = strPos;
    258 
    259         const size_t totalSize = lenSize + encLenSize +
    260             ((mUTF8 ? encSize : strSize)+1)*charSize;
    261 
    262         void* dat = (void*)pool->editData(preSize + strPos + totalSize);
    263         if (dat == NULL) {
    264             fprintf(stderr, "ERROR: Out of memory for string pool\n");
    265             return NO_MEMORY;
    266         }
    267         dat = (uint8_t*)dat + preSize + strPos;
    268         if (mUTF8) {
    269             uint8_t* strings = (uint8_t*)dat;
    270 
    271             ENCODE_LENGTH(strings, sizeof(uint8_t), strSize)
    272 
    273             ENCODE_LENGTH(strings, sizeof(uint8_t), encSize)
    274 
    275             strncpy((char*)strings, encStr, encSize+1);
    276         } else {
    277             uint16_t* strings = (uint16_t*)dat;
    278 
    279             ENCODE_LENGTH(strings, sizeof(uint16_t), strSize)
    280 
    281             strcpy16_htod(strings, ent.value);
    282         }
    283 
    284         strPos += totalSize;
    285     }
    286 
    287     // Pad ending string position up to a uint32_t boundary.
    288 
    289     if (strPos&0x3) {
    290         size_t padPos = ((strPos+3)&~0x3);
    291         uint8_t* dat = (uint8_t*)pool->editData(preSize + padPos);
    292         if (dat == NULL) {
    293             fprintf(stderr, "ERROR: Out of memory padding string pool\n");
    294             return NO_MEMORY;
    295         }
    296         memset(dat+preSize+strPos, 0, padPos-strPos);
    297         strPos = padPos;
    298     }
    299 
    300     // Build the pool of style spans.
    301 
    302     size_t styPos = strPos;
    303     for (i=0; i<STYLES; i++) {
    304         entry_style& ent = mEntryStyleArray.editItemAt(i);
    305         const size_t N = ent.spans.size();
    306         const size_t totalSize = (N*sizeof(ResStringPool_span))
    307                                + sizeof(ResStringPool_ref);
    308 
    309         ent.offset = styPos-strPos;
    310         uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + totalSize);
    311         if (dat == NULL) {
    312             fprintf(stderr, "ERROR: Out of memory for string styles\n");
    313             return NO_MEMORY;
    314         }
    315         ResStringPool_span* span = (ResStringPool_span*)(dat+preSize+styPos);
    316         for (size_t i=0; i<N; i++) {
    317             span->name.index = htodl(ent.spans[i].span.name.index);
    318             span->firstChar = htodl(ent.spans[i].span.firstChar);
    319             span->lastChar = htodl(ent.spans[i].span.lastChar);
    320             span++;
    321         }
    322         span->name.index = htodl(ResStringPool_span::END);
    323 
    324         styPos += totalSize;
    325     }
    326 
    327     if (STYLES > 0) {
    328         // Add full terminator at the end (when reading we validate that
    329         // the end of the pool is fully terminated to simplify error
    330         // checking).
    331         size_t extra = sizeof(ResStringPool_span)-sizeof(ResStringPool_ref);
    332         uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + extra);
    333         if (dat == NULL) {
    334             fprintf(stderr, "ERROR: Out of memory for string styles\n");
    335             return NO_MEMORY;
    336         }
    337         uint32_t* p = (uint32_t*)(dat+preSize+styPos);
    338         while (extra > 0) {
    339             *p++ = htodl(ResStringPool_span::END);
    340             extra -= sizeof(uint32_t);
    341         }
    342         styPos += extra;
    343     }
    344 
    345     // Write header.
    346 
    347     ResStringPool_header* header =
    348         (ResStringPool_header*)pool->padData(sizeof(uint32_t));
    349     if (header == NULL) {
    350         fprintf(stderr, "ERROR: Out of memory for string pool\n");
    351         return NO_MEMORY;
    352     }
    353     memset(header, 0, sizeof(*header));
    354     header->header.type = htods(RES_STRING_POOL_TYPE);
    355     header->header.headerSize = htods(sizeof(*header));
    356     header->header.size = htodl(pool->getSize());
    357     header->stringCount = htodl(ENTRIES);
    358     header->styleCount = htodl(STYLES);
    359     if (mSorted) {
    360         header->flags |= htodl(ResStringPool_header::SORTED_FLAG);
    361     }
    362     if (mUTF8) {
    363         header->flags |= htodl(ResStringPool_header::UTF8_FLAG);
    364     }
    365     header->stringsStart = htodl(preSize);
    366     header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0);
    367 
    368     // Write string index array.
    369 
    370     uint32_t* index = (uint32_t*)(header+1);
    371     if (mSorted) {
    372         for (i=0; i<ENTRIES; i++) {
    373             entry& ent = const_cast<entry&>(entryAt(i));
    374             ent.indices.clear();
    375             ent.indices.add(i);
    376             *index++ = htodl(ent.offset);
    377         }
    378     } else {
    379         for (i=0; i<ENTRIES; i++) {
    380             entry& ent = mEntries.editItemAt(mEntryArray[i]);
    381             *index++ = htodl(ent.offset);
    382             NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i,
    383                     String8(ent.value).string(),
    384                     mEntryArray[i], ent.offset));
    385         }
    386     }
    387 
    388     // Write style index array.
    389 
    390     if (mSorted) {
    391         for (i=0; i<STYLES; i++) {
    392             LOG_ALWAYS_FATAL("Shouldn't be here!");
    393         }
    394     } else {
    395         for (i=0; i<STYLES; i++) {
    396             *index++ = htodl(mEntryStyleArray[i].offset);
    397         }
    398     }
    399 
    400     return NO_ERROR;
    401 }
    402 
    403 ssize_t StringPool::offsetForString(const String16& val) const
    404 {
    405     const Vector<size_t>* indices = offsetsForString(val);
    406     ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1;
    407     NOISY(printf("Offset for string %s: %d (%s)\n", String8(val).string(), res,
    408             res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8()));
    409     return res;
    410 }
    411 
    412 const Vector<size_t>* StringPool::offsetsForString(const String16& val) const
    413 {
    414     ssize_t pos = mValues.valueFor(val);
    415     if (pos < 0) {
    416         return NULL;
    417     }
    418     return &mEntries[mEntryArray[pos]].indices;
    419 }
    420