1//
2// Copyright 2006 The Android Open Source Project
3//
4// Build resource files from raw assets.
5//
6#include "StringPool.h"
7
8#include <utils/ByteOrder.h>
9#include <utils/SortedVector.h>
10
11#include <algorithm>
12
13#include "ResourceTable.h"
14
15// SSIZE: mingw does not have signed size_t == ssize_t.
16#if !defined(_WIN32)
17#  define ZD "%zd"
18#  define ZD_TYPE ssize_t
19#  define SSIZE(x) x
20#else
21#  define ZD "%ld"
22#  define ZD_TYPE long
23#  define SSIZE(x) (signed size_t)x
24#endif
25
26// Set to true for noisy debug output.
27static const bool kIsDebug = false;
28
29#if __cplusplus >= 201103L
30void strcpy16_htod(char16_t* dst, const char16_t* src)
31{
32    while (*src) {
33        char16_t s = htods(*src);
34        *dst++ = s;
35        src++;
36    }
37    *dst = 0;
38}
39#endif
40
41void strcpy16_htod(uint16_t* dst, const char16_t* src)
42{
43    while (*src) {
44        uint16_t s = htods(static_cast<uint16_t>(*src));
45        *dst++ = s;
46        src++;
47    }
48    *dst = 0;
49}
50
51void printStringPool(const ResStringPool* pool)
52{
53    if (pool->getError() == NO_INIT) {
54        printf("String pool is unitialized.\n");
55        return;
56    } else if (pool->getError() != NO_ERROR) {
57        printf("String pool is corrupt/invalid.\n");
58        return;
59    }
60
61    SortedVector<const void*> uniqueStrings;
62    const size_t N = pool->size();
63    for (size_t i=0; i<N; i++) {
64        size_t len;
65        if (pool->isUTF8()) {
66            uniqueStrings.add(pool->string8At(i, &len));
67        } else {
68            uniqueStrings.add(pool->stringAt(i, &len));
69        }
70    }
71
72    printf("String pool of " ZD " unique %s %s strings, " ZD " entries and "
73            ZD " styles using " ZD " bytes:\n",
74            (ZD_TYPE)uniqueStrings.size(), pool->isUTF8() ? "UTF-8" : "UTF-16",
75            pool->isSorted() ? "sorted" : "non-sorted",
76            (ZD_TYPE)N, (ZD_TYPE)pool->styleCount(), (ZD_TYPE)pool->bytes());
77
78    const size_t NS = pool->size();
79    for (size_t s=0; s<NS; s++) {
80        String8 str = pool->string8ObjectAt(s);
81        printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string());
82    }
83}
84
85String8 StringPool::entry::makeConfigsString() const {
86    String8 configStr(configTypeName);
87    if (configStr.size() > 0) configStr.append(" ");
88    if (configs.size() > 0) {
89        for (size_t j=0; j<configs.size(); j++) {
90            if (j > 0) configStr.append(", ");
91            configStr.append(configs[j].toString());
92        }
93    } else {
94        configStr = "(none)";
95    }
96    return configStr;
97}
98
99int StringPool::entry::compare(const entry& o) const {
100    // Strings with styles go first, to reduce the size of the styles array.
101    // We don't care about the relative order of these strings.
102    if (hasStyles) {
103        return o.hasStyles ? 0 : -1;
104    }
105    if (o.hasStyles) {
106        return 1;
107    }
108
109    // Sort unstyled strings by type, then by logical configuration.
110    int comp = configTypeName.compare(o.configTypeName);
111    if (comp != 0) {
112        return comp;
113    }
114    const size_t LHN = configs.size();
115    const size_t RHN = o.configs.size();
116    size_t i=0;
117    while (i < LHN && i < RHN) {
118        comp = configs[i].compareLogical(o.configs[i]);
119        if (comp != 0) {
120            return comp;
121        }
122        i++;
123    }
124    if (LHN < RHN) return -1;
125    else if (LHN > RHN) return 1;
126    return 0;
127}
128
129StringPool::StringPool(bool utf8) :
130        mUTF8(utf8), mValues(-1)
131{
132}
133
134ssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans,
135        const String8* configTypeName, const ResTable_config* config)
136{
137    ssize_t res = add(value, false, configTypeName, config);
138    if (res >= 0) {
139        addStyleSpans(res, spans);
140    }
141    return res;
142}
143
144ssize_t StringPool::add(const String16& value,
145        bool mergeDuplicates, const String8* configTypeName, const ResTable_config* config)
146{
147    ssize_t vidx = mValues.indexOfKey(value);
148    ssize_t pos = vidx >= 0 ? mValues.valueAt(vidx) : -1;
149    ssize_t eidx = pos >= 0 ? mEntryArray.itemAt(pos) : -1;
150    if (eidx < 0) {
151        eidx = mEntries.add(entry(value));
152        if (eidx < 0) {
153            fprintf(stderr, "Failure adding string %s\n", String8(value).string());
154            return eidx;
155        }
156    }
157
158    if (configTypeName != NULL) {
159        entry& ent = mEntries.editItemAt(eidx);
160        if (kIsDebug) {
161            printf("*** adding config type name %s, was %s\n",
162                    configTypeName->string(), ent.configTypeName.string());
163        }
164        if (ent.configTypeName.size() <= 0) {
165            ent.configTypeName = *configTypeName;
166        } else if (ent.configTypeName != *configTypeName) {
167            ent.configTypeName = " ";
168        }
169    }
170
171    if (config != NULL) {
172        // Add this to the set of configs associated with the string.
173        entry& ent = mEntries.editItemAt(eidx);
174        size_t addPos;
175        for (addPos=0; addPos<ent.configs.size(); addPos++) {
176            int cmp = ent.configs.itemAt(addPos).compareLogical(*config);
177            if (cmp >= 0) {
178                if (cmp > 0) {
179                    if (kIsDebug) {
180                        printf("*** inserting config: %s\n", config->toString().string());
181                    }
182                    ent.configs.insertAt(*config, addPos);
183                }
184                break;
185            }
186        }
187        if (addPos >= ent.configs.size()) {
188            if (kIsDebug) {
189                printf("*** adding config: %s\n", config->toString().string());
190            }
191            ent.configs.add(*config);
192        }
193    }
194
195    const bool first = vidx < 0;
196    const bool styled = (pos >= 0 && (size_t)pos < mEntryStyleArray.size()) ?
197        mEntryStyleArray[pos].spans.size() : 0;
198    if (first || styled || !mergeDuplicates) {
199        pos = mEntryArray.add(eidx);
200        if (first) {
201            vidx = mValues.add(value, pos);
202        }
203        entry& ent = mEntries.editItemAt(eidx);
204        ent.indices.add(pos);
205    }
206
207    if (kIsDebug) {
208        printf("Adding string %s to pool: pos=%zd eidx=%zd vidx=%zd\n",
209                String8(value).string(), SSIZE(pos), SSIZE(eidx), SSIZE(vidx));
210    }
211
212    return pos;
213}
214
215status_t StringPool::addStyleSpan(size_t idx, const String16& name,
216                                  uint32_t start, uint32_t end)
217{
218    entry_style_span span;
219    span.name = name;
220    span.span.firstChar = start;
221    span.span.lastChar = end;
222    return addStyleSpan(idx, span);
223}
224
225status_t StringPool::addStyleSpans(size_t idx, const Vector<entry_style_span>& spans)
226{
227    const size_t N=spans.size();
228    for (size_t i=0; i<N; i++) {
229        status_t err = addStyleSpan(idx, spans[i]);
230        if (err != NO_ERROR) {
231            return err;
232        }
233    }
234    return NO_ERROR;
235}
236
237status_t StringPool::addStyleSpan(size_t idx, const entry_style_span& span)
238{
239    // Place blank entries in the span array up to this index.
240    while (mEntryStyleArray.size() <= idx) {
241        mEntryStyleArray.add();
242    }
243
244    entry_style& style = mEntryStyleArray.editItemAt(idx);
245    style.spans.add(span);
246    mEntries.editItemAt(mEntryArray[idx]).hasStyles = true;
247    return NO_ERROR;
248}
249
250StringPool::ConfigSorter::ConfigSorter(const StringPool& pool) : pool(pool)
251{
252}
253
254bool StringPool::ConfigSorter::operator()(size_t l, size_t r)
255{
256    const StringPool::entry& lhe = pool.mEntries[pool.mEntryArray[l]];
257    const StringPool::entry& rhe = pool.mEntries[pool.mEntryArray[r]];
258    return lhe.compare(rhe) < 0;
259}
260
261void StringPool::sortByConfig()
262{
263    LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos.size() > 0, "Can't sort string pool after already sorted.");
264
265    const size_t N = mEntryArray.size();
266
267    // This is a vector that starts out with a 1:1 mapping to entries
268    // in the array, which we will sort to come up with the desired order.
269    // At that point it maps from the new position in the array to the
270    // original position the entry appeared.
271    Vector<size_t> newPosToOriginalPos;
272    newPosToOriginalPos.setCapacity(N);
273    for (size_t i=0; i < N; i++) {
274        newPosToOriginalPos.add(i);
275    }
276
277    // Sort the array.
278    if (kIsDebug) {
279        printf("SORTING STRINGS BY CONFIGURATION...\n");
280    }
281    ConfigSorter sorter(*this);
282    std::sort(newPosToOriginalPos.begin(), newPosToOriginalPos.end(), sorter);
283    if (kIsDebug) {
284        printf("DONE SORTING STRINGS BY CONFIGURATION.\n");
285    }
286
287    // Create the reverse mapping from the original position in the array
288    // to the new position where it appears in the sorted array.  This is
289    // so that clients can re-map any positions they had previously stored.
290    mOriginalPosToNewPos = newPosToOriginalPos;
291    for (size_t i=0; i<N; i++) {
292        mOriginalPosToNewPos.editItemAt(newPosToOriginalPos[i]) = i;
293    }
294
295#if 0
296    SortedVector<entry> entries;
297
298    for (size_t i=0; i<N; i++) {
299        printf("#%d was %d: %s\n", i, newPosToOriginalPos[i],
300                mEntries[mEntryArray[newPosToOriginalPos[i]]].makeConfigsString().string());
301        entries.add(mEntries[mEntryArray[i]]);
302    }
303
304    for (size_t i=0; i<entries.size(); i++) {
305        printf("Sorted config #%d: %s\n", i,
306                entries[i].makeConfigsString().string());
307    }
308#endif
309
310    // Now we rebuild the arrays.
311    Vector<entry> newEntries;
312    Vector<size_t> newEntryArray;
313    Vector<entry_style> newEntryStyleArray;
314    DefaultKeyedVector<size_t, size_t> origOffsetToNewOffset;
315
316    for (size_t i=0; i<N; i++) {
317        // We are filling in new offset 'i'; oldI is where we can find it
318        // in the original data structure.
319        size_t oldI = newPosToOriginalPos[i];
320        // This is the actual entry associated with the old offset.
321        const entry& oldEnt = mEntries[mEntryArray[oldI]];
322        // This is the same entry the last time we added it to the
323        // new entry array, if any.
324        ssize_t newIndexOfOffset = origOffsetToNewOffset.indexOfKey(oldI);
325        size_t newOffset;
326        if (newIndexOfOffset < 0) {
327            // This is the first time we have seen the entry, so add
328            // it.
329            newOffset = newEntries.add(oldEnt);
330            newEntries.editItemAt(newOffset).indices.clear();
331        } else {
332            // We have seen this entry before, use the existing one
333            // instead of adding it again.
334            newOffset = origOffsetToNewOffset.valueAt(newIndexOfOffset);
335        }
336        // Update the indices to include this new position.
337        newEntries.editItemAt(newOffset).indices.add(i);
338        // And add the offset of the entry to the new entry array.
339        newEntryArray.add(newOffset);
340        // Add any old style to the new style array.
341        if (mEntryStyleArray.size() > 0) {
342            if (oldI < mEntryStyleArray.size()) {
343                newEntryStyleArray.add(mEntryStyleArray[oldI]);
344            } else {
345                newEntryStyleArray.add(entry_style());
346            }
347        }
348    }
349
350    // Now trim any entries at the end of the new style array that are
351    // not needed.
352    for (ssize_t i=newEntryStyleArray.size()-1; i>=0; i--) {
353        const entry_style& style = newEntryStyleArray[i];
354        if (style.spans.size() > 0) {
355            // That's it.
356            break;
357        }
358        // This one is not needed; remove.
359        newEntryStyleArray.removeAt(i);
360    }
361
362    // All done, install the new data structures and upate mValues with
363    // the new positions.
364    mEntries = newEntries;
365    mEntryArray = newEntryArray;
366    mEntryStyleArray = newEntryStyleArray;
367    mValues.clear();
368    for (size_t i=0; i<mEntries.size(); i++) {
369        const entry& ent = mEntries[i];
370        mValues.add(ent.value, ent.indices[0]);
371    }
372
373#if 0
374    printf("FINAL SORTED STRING CONFIGS:\n");
375    for (size_t i=0; i<mEntries.size(); i++) {
376        const entry& ent = mEntries[i];
377        printf("#" ZD " %s: %s\n", (ZD_TYPE)i, ent.makeConfigsString().string(),
378                String8(ent.value).string());
379    }
380#endif
381}
382
383sp<AaptFile> StringPool::createStringBlock()
384{
385    sp<AaptFile> pool = new AaptFile(String8(), AaptGroupEntry(),
386                                     String8());
387    status_t err = writeStringBlock(pool);
388    return err == NO_ERROR ? pool : NULL;
389}
390
391#define ENCODE_LENGTH(str, chrsz, strSize) \
392{ \
393    size_t maxMask = 1 << ((chrsz*8)-1); \
394    size_t maxSize = maxMask-1; \
395    if (strSize > maxSize) { \
396        *str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \
397    } \
398    *str++ = strSize; \
399}
400
401status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
402{
403    // Allow appending.  Sorry this is a little wacky.
404    if (pool->getSize() > 0) {
405        sp<AaptFile> block = createStringBlock();
406        if (block == NULL) {
407            return UNKNOWN_ERROR;
408        }
409        ssize_t res = pool->writeData(block->getData(), block->getSize());
410        return (res >= 0) ? (status_t)NO_ERROR : res;
411    }
412
413    // First we need to add all style span names to the string pool.
414    // We do this now (instead of when the span is added) so that these
415    // will appear at the end of the pool, not disrupting the order
416    // our client placed their own strings in it.
417
418    const size_t STYLES = mEntryStyleArray.size();
419    size_t i;
420
421    for (i=0; i<STYLES; i++) {
422        entry_style& style = mEntryStyleArray.editItemAt(i);
423        const size_t N = style.spans.size();
424        for (size_t i=0; i<N; i++) {
425            entry_style_span& span = style.spans.editItemAt(i);
426            ssize_t idx = add(span.name, true);
427            if (idx < 0) {
428                fprintf(stderr, "Error adding span for style tag '%s'\n",
429                        String8(span.name).string());
430                return idx;
431            }
432            span.span.name.index = (uint32_t)idx;
433        }
434    }
435
436    const size_t ENTRIES = mEntryArray.size();
437
438    // Now build the pool of unique strings.
439
440    const size_t STRINGS = mEntries.size();
441    const size_t preSize = sizeof(ResStringPool_header)
442                         + (sizeof(uint32_t)*ENTRIES)
443                         + (sizeof(uint32_t)*STYLES);
444    if (pool->editData(preSize) == NULL) {
445        fprintf(stderr, "ERROR: Out of memory for string pool\n");
446        return NO_MEMORY;
447    }
448
449    const size_t charSize = mUTF8 ? sizeof(uint8_t) : sizeof(uint16_t);
450
451    size_t strPos = 0;
452    for (i=0; i<STRINGS; i++) {
453        entry& ent = mEntries.editItemAt(i);
454        const size_t strSize = (ent.value.size());
455        const size_t lenSize = strSize > (size_t)(1<<((charSize*8)-1))-1 ?
456            charSize*2 : charSize;
457
458        String8 encStr;
459        if (mUTF8) {
460            encStr = String8(ent.value);
461        }
462
463        const size_t encSize = mUTF8 ? encStr.size() : 0;
464        const size_t encLenSize = mUTF8 ?
465            (encSize > (size_t)(1<<((charSize*8)-1))-1 ?
466                charSize*2 : charSize) : 0;
467
468        ent.offset = strPos;
469
470        const size_t totalSize = lenSize + encLenSize +
471            ((mUTF8 ? encSize : strSize)+1)*charSize;
472
473        void* dat = (void*)pool->editData(preSize + strPos + totalSize);
474        if (dat == NULL) {
475            fprintf(stderr, "ERROR: Out of memory for string pool\n");
476            return NO_MEMORY;
477        }
478        dat = (uint8_t*)dat + preSize + strPos;
479        if (mUTF8) {
480            uint8_t* strings = (uint8_t*)dat;
481
482            ENCODE_LENGTH(strings, sizeof(uint8_t), strSize)
483
484            ENCODE_LENGTH(strings, sizeof(uint8_t), encSize)
485
486            strncpy((char*)strings, encStr, encSize+1);
487        } else {
488            char16_t* strings = (char16_t*)dat;
489
490            ENCODE_LENGTH(strings, sizeof(char16_t), strSize)
491
492            strcpy16_htod(strings, ent.value);
493        }
494
495        strPos += totalSize;
496    }
497
498    // Pad ending string position up to a uint32_t boundary.
499
500    if (strPos&0x3) {
501        size_t padPos = ((strPos+3)&~0x3);
502        uint8_t* dat = (uint8_t*)pool->editData(preSize + padPos);
503        if (dat == NULL) {
504            fprintf(stderr, "ERROR: Out of memory padding string pool\n");
505            return NO_MEMORY;
506        }
507        memset(dat+preSize+strPos, 0, padPos-strPos);
508        strPos = padPos;
509    }
510
511    // Build the pool of style spans.
512
513    size_t styPos = strPos;
514    for (i=0; i<STYLES; i++) {
515        entry_style& ent = mEntryStyleArray.editItemAt(i);
516        const size_t N = ent.spans.size();
517        const size_t totalSize = (N*sizeof(ResStringPool_span))
518                               + sizeof(ResStringPool_ref);
519
520        ent.offset = styPos-strPos;
521        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + totalSize);
522        if (dat == NULL) {
523            fprintf(stderr, "ERROR: Out of memory for string styles\n");
524            return NO_MEMORY;
525        }
526        ResStringPool_span* span = (ResStringPool_span*)(dat+preSize+styPos);
527        for (size_t i=0; i<N; i++) {
528            span->name.index = htodl(ent.spans[i].span.name.index);
529            span->firstChar = htodl(ent.spans[i].span.firstChar);
530            span->lastChar = htodl(ent.spans[i].span.lastChar);
531            span++;
532        }
533        span->name.index = htodl(ResStringPool_span::END);
534
535        styPos += totalSize;
536    }
537
538    if (STYLES > 0) {
539        // Add full terminator at the end (when reading we validate that
540        // the end of the pool is fully terminated to simplify error
541        // checking).
542        size_t extra = sizeof(ResStringPool_span)-sizeof(ResStringPool_ref);
543        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + extra);
544        if (dat == NULL) {
545            fprintf(stderr, "ERROR: Out of memory for string styles\n");
546            return NO_MEMORY;
547        }
548        uint32_t* p = (uint32_t*)(dat+preSize+styPos);
549        while (extra > 0) {
550            *p++ = htodl(ResStringPool_span::END);
551            extra -= sizeof(uint32_t);
552        }
553        styPos += extra;
554    }
555
556    // Write header.
557
558    ResStringPool_header* header =
559        (ResStringPool_header*)pool->padData(sizeof(uint32_t));
560    if (header == NULL) {
561        fprintf(stderr, "ERROR: Out of memory for string pool\n");
562        return NO_MEMORY;
563    }
564    memset(header, 0, sizeof(*header));
565    header->header.type = htods(RES_STRING_POOL_TYPE);
566    header->header.headerSize = htods(sizeof(*header));
567    header->header.size = htodl(pool->getSize());
568    header->stringCount = htodl(ENTRIES);
569    header->styleCount = htodl(STYLES);
570    if (mUTF8) {
571        header->flags |= htodl(ResStringPool_header::UTF8_FLAG);
572    }
573    header->stringsStart = htodl(preSize);
574    header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0);
575
576    // Write string index array.
577
578    uint32_t* index = (uint32_t*)(header+1);
579    for (i=0; i<ENTRIES; i++) {
580        entry& ent = mEntries.editItemAt(mEntryArray[i]);
581        *index++ = htodl(ent.offset);
582        if (kIsDebug) {
583            printf("Writing entry #%zu: \"%s\" ent=%zu off=%zu\n",
584                    i,
585                    String8(ent.value).string(),
586                    mEntryArray[i],
587                    ent.offset);
588        }
589    }
590
591    // Write style index array.
592
593    for (i=0; i<STYLES; i++) {
594        *index++ = htodl(mEntryStyleArray[i].offset);
595    }
596
597    return NO_ERROR;
598}
599
600ssize_t StringPool::offsetForString(const String16& val) const
601{
602    const Vector<size_t>* indices = offsetsForString(val);
603    ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1;
604    if (kIsDebug) {
605        printf("Offset for string %s: %zd (%s)\n", String8(val).string(), SSIZE(res),
606                res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8());
607    }
608    return res;
609}
610
611const Vector<size_t>* StringPool::offsetsForString(const String16& val) const
612{
613    ssize_t pos = mValues.valueFor(val);
614    if (pos < 0) {
615        return NULL;
616    }
617    return &mEntries[mEntryArray[pos]].indices;
618}
619