StringPool.cpp revision 345b7eb8749d6954942fd4e961fff9f2f854934c
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com//
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Copyright 2006 The Android Open Source Project
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com//
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com// Build resource files from raw assets.
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com//
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "StringPool.h"
8ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include "ResourceTable.h"
98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
10d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org#include <utils/ByteOrder.h>
118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include <utils/SortedVector.h>
128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
13f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com#if HAVE_PRINTF_ZD
148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#  define ZD "%zd"
150456e0b7b85060e9b9597ce414c4c2b19aff4f58robertphillips@google.com#  define ZD_TYPE ssize_t
160456e0b7b85060e9b9597ce414c4c2b19aff4f58robertphillips@google.com#else
178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#  define ZD "%ld"
188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#  define ZD_TYPE long
198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif
208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#define NOISY(x) //x
228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid strcpy16_htod(uint16_t* dst, const uint16_t* src)
248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
252b2ede3e713065e1bac461787b0aafb03eaf871fdjsollen@google.com    while (*src) {
268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        char16_t s = htods(*src);
278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        *dst++ = s;
288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        src++;
298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    *dst = 0;
318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comvoid printStringPool(const ResStringPool* pool)
348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SortedVector<const void*> uniqueStrings;
368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const size_t N = pool->size();
378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (size_t i=0; i<N; i++) {
388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        size_t len;
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (pool->isUTF8()) {
408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            uniqueStrings.add(pool->string8At(i, &len));
418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        } else {
428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            uniqueStrings.add(pool->stringAt(i, &len));
438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
4654924243c1b65b3ee6d8fa064b50a9b1bb2a19a5djsollen@google.com    printf("String pool of " ZD " unique %s %s strings, " ZD " entries and "
478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            ZD " styles using " ZD " bytes:\n",
488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            (ZD_TYPE)uniqueStrings.size(), pool->isUTF8() ? "UTF-8" : "UTF-16",
498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            pool->isSorted() ? "sorted" : "non-sorted",
502b2ede3e713065e1bac461787b0aafb03eaf871fdjsollen@google.com            (ZD_TYPE)N, (ZD_TYPE)pool->styleCount(), (ZD_TYPE)pool->bytes());
518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const size_t NS = pool->size();
538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (size_t s=0; s<NS; s++) {
548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        String8 str = pool->string8ObjectAt(s);
558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string());
568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comString8 StringPool::entry::makeConfigsString() const {
608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    String8 configStr(configTypeName);
618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (configStr.size() > 0) configStr.append(" ");
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (configs.size() > 0) {
638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        for (size_t j=0; j<configs.size(); j++) {
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (j > 0) configStr.append(", ");
658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            configStr.append(configs[j].toString());
668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        configStr = "(none)";
698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return configStr;
718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comint StringPool::entry::compare(const entry& o) const {
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // Strings with styles go first, to reduce the size of the
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // styles array.
768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (hasStyles) {
778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return o.hasStyles ? 0 : -1;
788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (o.hasStyles) {
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return 1;
818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int comp = configTypeName.compare(o.configTypeName);
838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (comp != 0) {
848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return comp;
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const size_t LHN = configs.size();
878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const size_t RHN = o.configs.size();
888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    size_t i=0;
898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    while (i < LHN && i < RHN) {
908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        comp = configs[i].compareLogical(o.configs[i]);
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (comp != 0) {
928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            return comp;
938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        i++;
958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (LHN < RHN) return -1;
978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    else if (LHN > RHN) return 1;
988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return 0;
998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comStringPool::StringPool(bool utf8) :
1028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        mUTF8(utf8), mValues(-1)
1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comssize_t StringPool::add(const String16& value, const Vector<entry_style_span>& spans,
1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        const String8* configTypeName, const ResTable_config* config)
1088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
1098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    ssize_t res = add(value, false, configTypeName, config);
1108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (res >= 0) {
1118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        addStyleSpans(res, spans);
1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1130c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com    return res;
1148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comssize_t StringPool::add(const String16& value,
1177c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com        bool mergeDuplicates, const String8* configTypeName, const ResTable_config* config)
1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    ssize_t vidx = mValues.indexOfKey(value);
1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    ssize_t pos = vidx >= 0 ? mValues.valueAt(vidx) : -1;
1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    ssize_t eidx = pos >= 0 ? mEntryArray.itemAt(pos) : -1;
1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (eidx < 0) {
1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        eidx = mEntries.add(entry(value));
1248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (eidx < 0) {
1258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            fprintf(stderr, "Failure adding string %s\n", String8(value).string());
1268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            return eidx;
1278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (configTypeName != NULL) {
1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        entry& ent = mEntries.editItemAt(eidx);
1328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        NOISY(printf("*** adding config type name %s, was %s\n",
1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                configTypeName->string(), ent.configTypeName.string()));
1348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (ent.configTypeName.size() <= 0) {
1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            ent.configTypeName = *configTypeName;
1368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        } else if (ent.configTypeName != *configTypeName) {
1378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            ent.configTypeName = " ";
1388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (config != NULL) {
1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // Add this to the set of configs associated with the string.
1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        entry& ent = mEntries.editItemAt(eidx);
1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        size_t addPos;
1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        for (addPos=0; addPos<ent.configs.size(); addPos++) {
1468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            int cmp = ent.configs.itemAt(addPos).compareLogical(*config);
1478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (cmp >= 0) {
1488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                if (cmp > 0) {
1498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    NOISY(printf("*** inserting config: %s\n", config->toString().string()));
1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    ent.configs.insertAt(*config, addPos);
1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                }
1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
1538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (addPos >= ent.configs.size()) {
1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            NOISY(printf("*** adding config: %s\n", config->toString().string()));
1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            ent.configs.add(*config);
1588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const bool first = vidx < 0;
1628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const bool styled = (pos >= 0 && (size_t)pos < mEntryStyleArray.size()) ?
1638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        mEntryStyleArray[pos].spans.size() : 0;
1648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (first || styled || !mergeDuplicates) {
1658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        pos = mEntryArray.add(eidx);
1668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (first) {
1678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            vidx = mValues.add(value, pos);
1688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        entry& ent = mEntries.editItemAt(eidx);
1708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        ent.indices.add(pos);
1718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n",
1748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            String8(value).string(), pos, eidx, vidx));
1758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return pos;
1778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatus_t StringPool::addStyleSpan(size_t idx, const String16& name,
1808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                                  uint32_t start, uint32_t end)
1818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
1828d430185e08d2067584837a76b7193b803fee7a0tomhudson@google.com    entry_style_span span;
1838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    span.name = name;
1848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    span.span.firstChar = start;
1858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    span.span.lastChar = end;
1868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return addStyleSpan(idx, span);
1878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatus_t StringPool::addStyleSpans(size_t idx, const Vector<entry_style_span>& spans)
1908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
1918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const size_t N=spans.size();
1928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (size_t i=0; i<N; i++) {
1938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        status_t err = addStyleSpan(idx, spans[i]);
194d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        if (err != NO_ERROR) {
1957c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com            return err;
196f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com        }
1978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return NO_ERROR;
199d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org}
200d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org
201d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.orgstatus_t StringPool::addStyleSpan(size_t idx, const entry_style_span& span)
202d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org{
20303c1c359b336ad20d23ab07004cdafafd14c90a5rileya@google.com    // Place blank entries in the span array up to this index.
20403c1c359b336ad20d23ab07004cdafafd14c90a5rileya@google.com    while (mEntryStyleArray.size() <= idx) {
20503c1c359b336ad20d23ab07004cdafafd14c90a5rileya@google.com        mEntryStyleArray.add();
20603c1c359b336ad20d23ab07004cdafafd14c90a5rileya@google.com    }
20703c1c359b336ad20d23ab07004cdafafd14c90a5rileya@google.com
2088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    entry_style& style = mEntryStyleArray.editItemAt(idx);
2098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    style.spans.add(span);
2108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    mEntries.editItemAt(mEntryArray[idx]).hasStyles = true;
2118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return NO_ERROR;
2128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
2138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comint StringPool::config_sort(const size_t* lhs, const size_t* rhs, void* state)
2158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
2168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    StringPool* pool = (StringPool*)state;
2178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const entry& lhe = pool->mEntries[pool->mEntryArray[*lhs]];
218f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    const entry& rhe = pool->mEntries[pool->mEntryArray[*rhs]];
219f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    return lhe.compare(rhe);
220f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com}
221f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com
222f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.comvoid StringPool::sortByConfig()
223f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com{
224f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos.size() > 0, "Can't sort string pool after already sorted.");
225f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com
226f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    const size_t N = mEntryArray.size();
227f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com
228f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    // This is a vector that starts out with a 1:1 mapping to entries
2292be9e8b407624fa696854b78b407b97a01dbb703reed@google.com    // in the array, which we will sort to come up with the desired order.
230f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    // At that point it maps from the new position in the array to the
231b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org    // original position the entry appeared.
232b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org    Vector<size_t> newPosToOriginalPos;
233b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org    for (size_t i=0; i<mEntryArray.size(); i++) {
234b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org        newPosToOriginalPos.add(i);
235b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org    }
236b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org
237b6e161937bc890f0aa12ac5e27415d4d260ea6e0junov@chromium.org    // Sort the array.
2388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n"));
2395119bdb952025a30f115b9c6a187173956e55097reed@android.com    newPosToOriginalPos.sort(config_sort, this);
240f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n"));
241f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com
2428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // Create the reverse mapping from the original position in the array
2438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // to the new position where it appears in the sorted array.  This is
2448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // so that clients can re-map any positions they had previously stored.
2458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    mOriginalPosToNewPos = newPosToOriginalPos;
2468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (size_t i=0; i<N; i++) {
2478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        mOriginalPosToNewPos.editItemAt(newPosToOriginalPos[i]) = i;
24854924243c1b65b3ee6d8fa064b50a9b1bb2a19a5djsollen@google.com    }
2498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if 0
2518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SortedVector<entry> entries;
2528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (size_t i=0; i<N; i++) {
2548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        printf("#%d was %d: %s\n", i, newPosToOriginalPos[i],
2558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                mEntries[mEntryArray[newPosToOriginalPos[i]]].makeConfigsString().string());
2568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        entries.add(mEntries[mEntryArray[i]]);
25759ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com    }
25859ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com
25959ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com    for (size_t i=0; i<entries.size(); i++) {
26059ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com        printf("Sorted config #%d: %s\n", i,
2618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                entries[i].makeConfigsString().string());
2628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
2638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif
2648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // Now we rebuild the arrays.
2668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Vector<entry> newEntries;
2678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Vector<size_t> newEntryArray;
2688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Vector<entry_style> newEntryStyleArray;
2698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    DefaultKeyedVector<size_t, size_t> origOffsetToNewOffset;
2708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
2718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (size_t i=0; i<N; i++) {
272d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        // We are filling in new offset 'i'; oldI is where we can find it
2738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // in the original data structure.
274d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        size_t oldI = newPosToOriginalPos[i];
275d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        // This is the actual entry associated with the old offset.
2768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        const entry& oldEnt = mEntries[mEntryArray[oldI]];
277d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        // This is the same entry the last time we added it to the
2788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // new entry array, if any.
2798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        ssize_t newIndexOfOffset = origOffsetToNewOffset.indexOfKey(oldI);
280d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        size_t newOffset;
281d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        if (newIndexOfOffset < 0) {
282d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org            // This is the first time we have seen the entry, so add
2838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // it.
2848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            newOffset = newEntries.add(oldEnt);
2858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            newEntries.editItemAt(newOffset).indices.clear();
2868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        } else {
2878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // We have seen this entry before, use the existing one
2888f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com            // instead of adding it again.
2898f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com            newOffset = origOffsetToNewOffset.valueAt(newIndexOfOffset);
2908f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com        }
2918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // Update the indices to include this new position.
2928a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        newEntries.editItemAt(newOffset).indices.add(i);
2938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // And add the offset of the entry to the new entry array.
2948f073382bb6a9b3998a74e6b58654476b77b4c86reed@android.com        newEntryArray.add(newOffset);
2955b81535014f545f6498f5c8721723b81576989b1reed@android.com        // Add any old style to the new style array.
2965119bdb952025a30f115b9c6a187173956e55097reed@android.com        if (mEntryStyleArray.size() > 0) {
2975b81535014f545f6498f5c8721723b81576989b1reed@android.com            if (oldI < mEntryStyleArray.size()) {
2985b81535014f545f6498f5c8721723b81576989b1reed@android.com                newEntryStyleArray.add(mEntryStyleArray[oldI]);
2995b81535014f545f6498f5c8721723b81576989b1reed@android.com            } else {
3005119bdb952025a30f115b9c6a187173956e55097reed@android.com                newEntryStyleArray.add(entry_style());
3015119bdb952025a30f115b9c6a187173956e55097reed@android.com            }
3028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
3038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
3048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
3058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // Now trim any entries at the end of the new style array that are
3068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // not needed.
3078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    for (ssize_t i=newEntryStyleArray.size()-1; i>=0; i--) {
3088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        const entry_style& style = newEntryStyleArray[i];
3098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (style.spans.size() > 0) {
3108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // That's it.
3118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
3128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
3138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // This one is not needed; remove.
3148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        newEntryStyleArray.removeAt(i);
3158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
3168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
317f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    // All done, install the new data structures and upate mValues with
318f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    // the new positions.
3197c2f27d788fff9dbf66a6d52753e47f786a313c0reed@google.com    mEntries = newEntries;
3208cad58624bc194390b14a21d0578dfcdd6fbad6freed@google.com    mEntryArray = newEntryArray;
3212be9e8b407624fa696854b78b407b97a01dbb703reed@google.com    mEntryStyleArray = newEntryStyleArray;
322f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    mValues.clear();
323f2b98d67dcb6fcb3120feede9c72016fc7b3ead8reed@android.com    for (size_t i=0; i<mEntries.size(); i++) {
324d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        const entry& ent = mEntries[i];
325d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        mValues.add(ent.value, ent.indices[0]);
326d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org    }
327d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org
328d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org#if 0
329d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org    printf("FINAL SORTED STRING CONFIGS:\n");
330d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org    for (size_t i=0; i<mEntries.size(); i++) {
331d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        const entry& ent = mEntries[i];
332d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org        printf("#" ZD " %s: %s\n", (ZD_TYPE)i, ent.makeConfigsString().string(),
333d3ae77965e94e0efda496f5461cbec4533cb5b16vandebo@chromium.org                String8(ent.value).string());
33437a201231b8f6381938282675eb9abb50ab3b389reed@google.com    }
335a2ca41e3afdd8fad5e0e924dec029f33918e0a67djsollen@google.com#endif
336a2ca41e3afdd8fad5e0e924dec029f33918e0a67djsollen@google.com}
33737a201231b8f6381938282675eb9abb50ab3b389reed@google.com
33837a201231b8f6381938282675eb9abb50ab3b389reed@google.comsp<AaptFile> StringPool::createStringBlock()
33937a201231b8f6381938282675eb9abb50ab3b389reed@google.com{
34037a201231b8f6381938282675eb9abb50ab3b389reed@google.com    sp<AaptFile> pool = new AaptFile(String8(), AaptGroupEntry(),
34137a201231b8f6381938282675eb9abb50ab3b389reed@google.com                                     String8());
34237a201231b8f6381938282675eb9abb50ab3b389reed@google.com    status_t err = writeStringBlock(pool);
34359ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com    return err == NO_ERROR ? pool : NULL;
34459ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com}
34559ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com
34659ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com#define ENCODE_LENGTH(str, chrsz, strSize) \
34759ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com{ \
3480c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com    size_t maxMask = 1 << ((chrsz*8)-1); \
34959ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com    size_t maxSize = maxMask-1; \
35059ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com    if (strSize > maxSize) { \
35159ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com        *str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \
3520c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.com    } \
35359ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com    *str++ = strSize; \
35459ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com}
35559ccef695cef28a74ab2ea13d5a6c9017af45402reed@google.com
3560c00f21fee3f5cfa3aa7e5d46ff94cb8cf340451tomhudson@google.comstatus_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
35737a201231b8f6381938282675eb9abb50ab3b389reed@google.com{
35837a201231b8f6381938282675eb9abb50ab3b389reed@google.com    // Allow appending.  Sorry this is a little wacky.
359a2ca41e3afdd8fad5e0e924dec029f33918e0a67djsollen@google.com    if (pool->getSize() > 0) {
360        sp<AaptFile> block = createStringBlock();
361        if (block == NULL) {
362            return UNKNOWN_ERROR;
363        }
364        ssize_t res = pool->writeData(block->getData(), block->getSize());
365        return (res >= 0) ? (status_t)NO_ERROR : res;
366    }
367
368    // First we need to add all style span names to the string pool.
369    // We do this now (instead of when the span is added) so that these
370    // will appear at the end of the pool, not disrupting the order
371    // our client placed their own strings in it.
372
373    const size_t STYLES = mEntryStyleArray.size();
374    size_t i;
375
376    for (i=0; i<STYLES; i++) {
377        entry_style& style = mEntryStyleArray.editItemAt(i);
378        const size_t N = style.spans.size();
379        for (size_t i=0; i<N; i++) {
380            entry_style_span& span = style.spans.editItemAt(i);
381            ssize_t idx = add(span.name, true);
382            if (idx < 0) {
383                fprintf(stderr, "Error adding span for style tag '%s'\n",
384                        String8(span.name).string());
385                return idx;
386            }
387            span.span.name.index = (uint32_t)idx;
388        }
389    }
390
391    const size_t ENTRIES = mEntryArray.size();
392
393    // Now build the pool of unique strings.
394
395    const size_t STRINGS = mEntries.size();
396    const size_t preSize = sizeof(ResStringPool_header)
397                         + (sizeof(uint32_t)*ENTRIES)
398                         + (sizeof(uint32_t)*STYLES);
399    if (pool->editData(preSize) == NULL) {
400        fprintf(stderr, "ERROR: Out of memory for string pool\n");
401        return NO_MEMORY;
402    }
403
404    const size_t charSize = mUTF8 ? sizeof(uint8_t) : sizeof(char16_t);
405
406    size_t strPos = 0;
407    for (i=0; i<STRINGS; i++) {
408        entry& ent = mEntries.editItemAt(i);
409        const size_t strSize = (ent.value.size());
410        const size_t lenSize = strSize > (size_t)(1<<((charSize*8)-1))-1 ?
411            charSize*2 : charSize;
412
413        String8 encStr;
414        if (mUTF8) {
415            encStr = String8(ent.value);
416        }
417
418        const size_t encSize = mUTF8 ? encStr.size() : 0;
419        const size_t encLenSize = mUTF8 ?
420            (encSize > (size_t)(1<<((charSize*8)-1))-1 ?
421                charSize*2 : charSize) : 0;
422
423        ent.offset = strPos;
424
425        const size_t totalSize = lenSize + encLenSize +
426            ((mUTF8 ? encSize : strSize)+1)*charSize;
427
428        void* dat = (void*)pool->editData(preSize + strPos + totalSize);
429        if (dat == NULL) {
430            fprintf(stderr, "ERROR: Out of memory for string pool\n");
431            return NO_MEMORY;
432        }
433        dat = (uint8_t*)dat + preSize + strPos;
434        if (mUTF8) {
435            uint8_t* strings = (uint8_t*)dat;
436
437            ENCODE_LENGTH(strings, sizeof(uint8_t), strSize)
438
439            ENCODE_LENGTH(strings, sizeof(uint8_t), encSize)
440
441            strncpy((char*)strings, encStr, encSize+1);
442        } else {
443            uint16_t* strings = (uint16_t*)dat;
444
445            ENCODE_LENGTH(strings, sizeof(uint16_t), strSize)
446
447            strcpy16_htod(strings, ent.value);
448        }
449
450        strPos += totalSize;
451    }
452
453    // Pad ending string position up to a uint32_t boundary.
454
455    if (strPos&0x3) {
456        size_t padPos = ((strPos+3)&~0x3);
457        uint8_t* dat = (uint8_t*)pool->editData(preSize + padPos);
458        if (dat == NULL) {
459            fprintf(stderr, "ERROR: Out of memory padding string pool\n");
460            return NO_MEMORY;
461        }
462        memset(dat+preSize+strPos, 0, padPos-strPos);
463        strPos = padPos;
464    }
465
466    // Build the pool of style spans.
467
468    size_t styPos = strPos;
469    for (i=0; i<STYLES; i++) {
470        entry_style& ent = mEntryStyleArray.editItemAt(i);
471        const size_t N = ent.spans.size();
472        const size_t totalSize = (N*sizeof(ResStringPool_span))
473                               + sizeof(ResStringPool_ref);
474
475        ent.offset = styPos-strPos;
476        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + totalSize);
477        if (dat == NULL) {
478            fprintf(stderr, "ERROR: Out of memory for string styles\n");
479            return NO_MEMORY;
480        }
481        ResStringPool_span* span = (ResStringPool_span*)(dat+preSize+styPos);
482        for (size_t i=0; i<N; i++) {
483            span->name.index = htodl(ent.spans[i].span.name.index);
484            span->firstChar = htodl(ent.spans[i].span.firstChar);
485            span->lastChar = htodl(ent.spans[i].span.lastChar);
486            span++;
487        }
488        span->name.index = htodl(ResStringPool_span::END);
489
490        styPos += totalSize;
491    }
492
493    if (STYLES > 0) {
494        // Add full terminator at the end (when reading we validate that
495        // the end of the pool is fully terminated to simplify error
496        // checking).
497        size_t extra = sizeof(ResStringPool_span)-sizeof(ResStringPool_ref);
498        uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + extra);
499        if (dat == NULL) {
500            fprintf(stderr, "ERROR: Out of memory for string styles\n");
501            return NO_MEMORY;
502        }
503        uint32_t* p = (uint32_t*)(dat+preSize+styPos);
504        while (extra > 0) {
505            *p++ = htodl(ResStringPool_span::END);
506            extra -= sizeof(uint32_t);
507        }
508        styPos += extra;
509    }
510
511    // Write header.
512
513    ResStringPool_header* header =
514        (ResStringPool_header*)pool->padData(sizeof(uint32_t));
515    if (header == NULL) {
516        fprintf(stderr, "ERROR: Out of memory for string pool\n");
517        return NO_MEMORY;
518    }
519    memset(header, 0, sizeof(*header));
520    header->header.type = htods(RES_STRING_POOL_TYPE);
521    header->header.headerSize = htods(sizeof(*header));
522    header->header.size = htodl(pool->getSize());
523    header->stringCount = htodl(ENTRIES);
524    header->styleCount = htodl(STYLES);
525    if (mUTF8) {
526        header->flags |= htodl(ResStringPool_header::UTF8_FLAG);
527    }
528    header->stringsStart = htodl(preSize);
529    header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0);
530
531    // Write string index array.
532
533    uint32_t* index = (uint32_t*)(header+1);
534    for (i=0; i<ENTRIES; i++) {
535        entry& ent = mEntries.editItemAt(mEntryArray[i]);
536        *index++ = htodl(ent.offset);
537        NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i,
538                String8(ent.value).string(),
539                mEntryArray[i], ent.offset));
540    }
541
542    // Write style index array.
543
544    for (i=0; i<STYLES; i++) {
545        *index++ = htodl(mEntryStyleArray[i].offset);
546    }
547
548    return NO_ERROR;
549}
550
551ssize_t StringPool::offsetForString(const String16& val) const
552{
553    const Vector<size_t>* indices = offsetsForString(val);
554    ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1;
555    NOISY(printf("Offset for string %s: %d (%s)\n", String8(val).string(), res,
556            res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8()));
557    return res;
558}
559
560const Vector<size_t>* StringPool::offsetsForString(const String16& val) const
561{
562    ssize_t pos = mValues.valueFor(val);
563    if (pos < 0) {
564        return NULL;
565    }
566    return &mEntries[mEntryArray[pos]].indices;
567}
568