am e6c0e993
: Merge change I129483f8 into eclair-mr2
Merge commit 'e6c0e99334bc2f47e5d36db253ac8f166047c03b' into eclair-mr2-plus-aosp * commit 'e6c0e99334bc2f47e5d36db253ac8f166047c03b': Optional use of UTF-8 strings in resource bundles
This commit is contained in:
@ -393,7 +393,10 @@ struct ResStringPool_header
|
||||
enum {
|
||||
// If set, the string index is sorted by the string values (based
|
||||
// on strcmp16()).
|
||||
SORTED_FLAG = 1<<0
|
||||
SORTED_FLAG = 1<<0,
|
||||
|
||||
// String pool is encoded in UTF-8
|
||||
UTF8_FLAG = 1<<8
|
||||
};
|
||||
uint32_t flags;
|
||||
|
||||
@ -456,9 +459,11 @@ private:
|
||||
void* mOwnedData;
|
||||
const ResStringPool_header* mHeader;
|
||||
size_t mSize;
|
||||
mutable Mutex mDecodeLock;
|
||||
const uint32_t* mEntries;
|
||||
const uint32_t* mEntryStyles;
|
||||
const char16_t* mStrings;
|
||||
const void* mStrings;
|
||||
char16_t** mCache;
|
||||
uint32_t mStringPoolSize; // number of uint16_t
|
||||
const uint32_t* mStyles;
|
||||
uint32_t mStylePoolSize; // number of uint32_t
|
||||
|
@ -49,12 +49,17 @@ int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);
|
||||
// Version of strzcmp16 for comparing strings in different endianness.
|
||||
int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2);
|
||||
|
||||
// Convert UTF-8 to UTF-16 including surrogate pairs
|
||||
void utf8_to_utf16(const uint8_t *src, size_t srcLen, char16_t* dst, const size_t dstLen);
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class String8;
|
||||
class TextOutput;
|
||||
|
||||
|
@ -57,6 +57,11 @@ size_t utf8_length(const char *src);
|
||||
*/
|
||||
size_t utf32_length(const char *src, size_t src_len);
|
||||
|
||||
/*
|
||||
* Returns the UTF-8 length of "src".
|
||||
*/
|
||||
size_t utf8_length_from_utf16(const char16_t *src, size_t src_len);
|
||||
|
||||
/*
|
||||
* Returns the UTF-8 length of "src".
|
||||
*/
|
||||
@ -120,6 +125,9 @@ size_t utf8_to_utf32(const char* src, size_t src_len,
|
||||
size_t utf32_to_utf8(const char32_t* src, size_t src_len,
|
||||
char* dst, size_t dst_len);
|
||||
|
||||
size_t utf16_to_utf8(const char16_t* src, size_t src_len,
|
||||
char* dst, size_t dst_len);
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
@ -229,12 +229,12 @@ Res_png_9patch* Res_png_9patch::deserialize(const void* inData)
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
ResStringPool::ResStringPool()
|
||||
: mError(NO_INIT), mOwnedData(NULL)
|
||||
: mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ResStringPool::ResStringPool(const void* data, size_t size, bool copyData)
|
||||
: mError(NO_INIT), mOwnedData(NULL)
|
||||
: mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL)
|
||||
{
|
||||
setTo(data, size, copyData);
|
||||
}
|
||||
@ -296,7 +296,17 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
|
||||
(int)size);
|
||||
return (mError=BAD_TYPE);
|
||||
}
|
||||
mStrings = (const char16_t*)
|
||||
|
||||
size_t charSize;
|
||||
if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
|
||||
charSize = sizeof(uint8_t);
|
||||
mCache = (char16_t**)malloc(sizeof(char16_t**)*mHeader->stringCount);
|
||||
memset(mCache, 0, sizeof(char16_t**)*mHeader->stringCount);
|
||||
} else {
|
||||
charSize = sizeof(char16_t);
|
||||
}
|
||||
|
||||
mStrings = (const void*)
|
||||
(((const uint8_t*)data)+mHeader->stringsStart);
|
||||
if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) {
|
||||
LOGW("Bad string block: string pool starts at %d, after total size %d\n",
|
||||
@ -305,7 +315,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
|
||||
}
|
||||
if (mHeader->styleCount == 0) {
|
||||
mStringPoolSize =
|
||||
(mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t);
|
||||
(mHeader->header.size-mHeader->stringsStart)/charSize;
|
||||
} else {
|
||||
// check invariant: styles follow the strings
|
||||
if (mHeader->stylesStart <= mHeader->stringsStart) {
|
||||
@ -314,7 +324,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
|
||||
return (mError=BAD_TYPE);
|
||||
}
|
||||
mStringPoolSize =
|
||||
(mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t);
|
||||
(mHeader->stylesStart-mHeader->stringsStart)/charSize;
|
||||
}
|
||||
|
||||
// check invariant: stringCount > 0 requires a string pool to exist
|
||||
@ -329,13 +339,19 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
|
||||
for (i=0; i<mHeader->stringCount; i++) {
|
||||
e[i] = dtohl(mEntries[i]);
|
||||
}
|
||||
char16_t* s = const_cast<char16_t*>(mStrings);
|
||||
for (i=0; i<mStringPoolSize; i++) {
|
||||
s[i] = dtohs(mStrings[i]);
|
||||
if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
|
||||
const char16_t* strings = (const char16_t*)mStrings;
|
||||
char16_t* s = const_cast<char16_t*>(strings);
|
||||
for (i=0; i<mStringPoolSize; i++) {
|
||||
s[i] = dtohs(strings[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mStrings[mStringPoolSize-1] != 0) {
|
||||
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
|
||||
((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
|
||||
(!mHeader->flags&ResStringPool_header::UTF8_FLAG &&
|
||||
((char16_t*)mStrings)[mStringPoolSize-1] != 0)) {
|
||||
LOGW("Bad string block: last string is not 0-terminated\n");
|
||||
return (mError=BAD_TYPE);
|
||||
}
|
||||
@ -410,24 +426,67 @@ void ResStringPool::uninit()
|
||||
free(mOwnedData);
|
||||
mOwnedData = NULL;
|
||||
}
|
||||
if (mHeader != NULL && mCache != NULL) {
|
||||
for (size_t x = 0; x < mHeader->stringCount; x++) {
|
||||
if (mCache[x] != NULL) {
|
||||
free(mCache[x]);
|
||||
mCache[x] = NULL;
|
||||
}
|
||||
}
|
||||
free(mCache);
|
||||
mCache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define DECODE_LENGTH(str, chrsz, len) \
|
||||
len = *(str); \
|
||||
if (*(str)&(1<<(chrsz*8-1))) { \
|
||||
(str)++; \
|
||||
len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \
|
||||
} \
|
||||
(str)++;
|
||||
|
||||
const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const
|
||||
{
|
||||
if (mError == NO_ERROR && idx < mHeader->stringCount) {
|
||||
const uint32_t off = (mEntries[idx]/sizeof(uint16_t));
|
||||
const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
|
||||
const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t));
|
||||
if (off < (mStringPoolSize-1)) {
|
||||
const char16_t* str = mStrings+off;
|
||||
*outLen = *str;
|
||||
if ((*str)&0x8000) {
|
||||
str++;
|
||||
*outLen = (((*outLen)&0x7fff)<<16) + *str;
|
||||
}
|
||||
if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) {
|
||||
return str+1;
|
||||
if (!isUTF8) {
|
||||
const char16_t* strings = (char16_t*)mStrings;
|
||||
const char16_t* str = strings+off;
|
||||
DECODE_LENGTH(str, sizeof(char16_t), *outLen)
|
||||
if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) {
|
||||
return str;
|
||||
} else {
|
||||
LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
|
||||
(int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize);
|
||||
}
|
||||
} else {
|
||||
LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
|
||||
(int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize);
|
||||
const uint8_t* strings = (uint8_t*)mStrings;
|
||||
const uint8_t* str = strings+off;
|
||||
DECODE_LENGTH(str, sizeof(uint8_t), *outLen)
|
||||
size_t encLen;
|
||||
DECODE_LENGTH(str, sizeof(uint8_t), encLen)
|
||||
if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
|
||||
AutoMutex lock(mDecodeLock);
|
||||
if (mCache[idx] != NULL) {
|
||||
return mCache[idx];
|
||||
}
|
||||
char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t));
|
||||
if (!u16str) {
|
||||
LOGW("No memory when trying to allocate decode cache for string #%d\n",
|
||||
(int)idx);
|
||||
return NULL;
|
||||
}
|
||||
const unsigned char *u8src = reinterpret_cast<const unsigned char *>(str);
|
||||
utf8_to_utf16(u8src, encLen, u16str, *outLen);
|
||||
mCache[idx] = u16str;
|
||||
return u16str;
|
||||
} else {
|
||||
LOGW("Bad string block: string #%d extends to %d, past end at %d\n",
|
||||
(int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGW("Bad string block: string #%d entry is at %d, past end at %d\n",
|
||||
@ -466,6 +525,10 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const
|
||||
|
||||
size_t len;
|
||||
|
||||
// TODO optimize searching for UTF-8 strings taking into account
|
||||
// the cache fill to determine when to convert the searched-for
|
||||
// string key to UTF-8.
|
||||
|
||||
if (mHeader->flags&ResStringPool_header::SORTED_FLAG) {
|
||||
// Do a binary search for the string...
|
||||
ssize_t l = 0;
|
||||
@ -1043,6 +1106,7 @@ status_t ResXMLTree::getError() const
|
||||
void ResXMLTree::uninit()
|
||||
{
|
||||
mError = NO_INIT;
|
||||
mStrings.uninit();
|
||||
if (mOwnedData) {
|
||||
free(mOwnedData);
|
||||
mOwnedData = NULL;
|
||||
|
@ -172,10 +172,6 @@ int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2
|
||||
: 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
static inline size_t
|
||||
utf8_char_len(uint8_t ch)
|
||||
{
|
||||
@ -215,8 +211,38 @@ utf8_to_utf32(const uint8_t *src, size_t length)
|
||||
//printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
|
||||
}
|
||||
|
||||
void
|
||||
utf8_to_utf16(const uint8_t *src, size_t srcLen,
|
||||
char16_t* dst, const size_t dstLen)
|
||||
{
|
||||
const uint8_t* const end = src + srcLen;
|
||||
const char16_t* const dstEnd = dst + dstLen;
|
||||
while (src < end && dst < dstEnd) {
|
||||
size_t len = utf8_char_len(*src);
|
||||
uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len);
|
||||
|
||||
// Convert the UTF32 codepoint to one or more UTF16 codepoints
|
||||
if (codepoint <= 0xFFFF) {
|
||||
// Single UTF16 character
|
||||
*dst++ = (char16_t) codepoint;
|
||||
} else {
|
||||
// Multiple UTF16 characters with surrogates
|
||||
codepoint = codepoint - 0x10000;
|
||||
*dst++ = (char16_t) ((codepoint >> 10) + 0xD800);
|
||||
*dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
|
||||
}
|
||||
|
||||
src += len;
|
||||
}
|
||||
if (dst < dstEnd) {
|
||||
*dst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace android {
|
||||
|
||||
static SharedBuffer* gEmptyStringBuf = NULL;
|
||||
static char16_t* gEmptyString = NULL;
|
||||
|
||||
@ -260,30 +286,14 @@ static char16_t* allocFromUTF8(const char* in, size_t len)
|
||||
p += utf8len;
|
||||
}
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t));
|
||||
size_t bufSize = (chars+1)*sizeof(char16_t);
|
||||
SharedBuffer* buf = SharedBuffer::alloc(bufSize);
|
||||
if (buf) {
|
||||
p = in;
|
||||
char16_t* str = (char16_t*)buf->data();
|
||||
char16_t* d = str;
|
||||
while (p < end) {
|
||||
size_t len = utf8_char_len(*p);
|
||||
uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, len);
|
||||
|
||||
// Convert the UTF32 codepoint to one or more UTF16 codepoints
|
||||
if (codepoint <= 0xFFFF) {
|
||||
// Single UTF16 character
|
||||
*d++ = (char16_t) codepoint;
|
||||
} else {
|
||||
// Multiple UTF16 characters with surrogates
|
||||
codepoint = codepoint - 0x10000;
|
||||
*d++ = (char16_t) ((codepoint >> 10) + 0xD800);
|
||||
*d++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
|
||||
}
|
||||
|
||||
p += len;
|
||||
}
|
||||
*d = 0;
|
||||
|
||||
utf8_to_utf16((const uint8_t*)p, len, str, bufSize);
|
||||
|
||||
//printf("Created UTF-16 string from UTF-8 \"%s\":", in);
|
||||
//printHexData(1, str, buf->size(), 16, 1);
|
||||
//printf("\n");
|
||||
|
@ -208,10 +208,23 @@ static char* allocFromUTF16OrUTF32(const T* in, L len)
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
// Note: not dealing with expanding surrogate pairs.
|
||||
static char* allocFromUTF16(const char16_t* in, size_t len)
|
||||
{
|
||||
return allocFromUTF16OrUTF32<char16_t, size_t>(in, len);
|
||||
if (len == 0) return getEmptyString();
|
||||
|
||||
const size_t bytes = utf8_length_from_utf16(in, len);
|
||||
|
||||
SharedBuffer* buf = SharedBuffer::alloc(bytes+1);
|
||||
LOG_ASSERT(buf, "Unable to allocate shared buffer");
|
||||
if (buf) {
|
||||
char* str = (char*)buf->data();
|
||||
|
||||
utf16_to_utf8(in, len, str, bytes+1);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
return getEmptyString();
|
||||
}
|
||||
|
||||
static char* allocFromUTF32(const char32_t* in, size_t len)
|
||||
@ -762,6 +775,26 @@ size_t utf8_length_from_utf32(const char32_t *src, size_t src_len)
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t utf8_length_from_utf16(const char16_t *src, size_t src_len)
|
||||
{
|
||||
if (src == NULL || src_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t ret = 0;
|
||||
const char16_t* const end = src + src_len;
|
||||
while (src < end) {
|
||||
if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
|
||||
&& (*++src & 0xFC00) == 0xDC00) {
|
||||
// surrogate pairs are always 4 bytes.
|
||||
ret += 4;
|
||||
src++;
|
||||
} else {
|
||||
ret += android::utf32_to_utf8_bytes((char32_t) *src++);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t utf32_at_internal(const char* cur, size_t *num_read)
|
||||
{
|
||||
const char first_char = *cur;
|
||||
@ -848,3 +881,33 @@ size_t utf32_to_utf8(const char32_t* src, size_t src_len,
|
||||
}
|
||||
return cur - dst;
|
||||
}
|
||||
|
||||
size_t utf16_to_utf8(const char16_t* src, size_t src_len,
|
||||
char* dst, size_t dst_len)
|
||||
{
|
||||
if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
const char16_t* cur_utf16 = src;
|
||||
const char16_t* const end_utf16 = src + src_len;
|
||||
char *cur = dst;
|
||||
const char* const end = dst + dst_len;
|
||||
while (cur_utf16 < end_utf16 && cur < end) {
|
||||
char32_t utf32;
|
||||
// surrogate pairs
|
||||
if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) {
|
||||
utf32 = (*cur_utf16++ - 0xD800) << 10;
|
||||
utf32 |= *cur_utf16++ - 0xDC00;
|
||||
utf32 += 0x10000;
|
||||
} else {
|
||||
utf32 = (char32_t) *cur_utf16++;
|
||||
}
|
||||
size_t len = android::utf32_to_utf8_bytes(utf32);
|
||||
android::utf32_to_utf8((uint8_t*)cur, utf32, len);
|
||||
cur += len;
|
||||
}
|
||||
if (cur < end) {
|
||||
*cur = '\0';
|
||||
}
|
||||
return cur - dst;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false),
|
||||
mUpdate(false), mExtending(false),
|
||||
mRequireLocalization(false), mPseudolocalize(false),
|
||||
mValues(false),
|
||||
mUTF8(false), mValues(false),
|
||||
mCompressionMethod(0), mOutputAPKFile(NULL),
|
||||
mAssetSourceDir(NULL), mProguardFile(NULL),
|
||||
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
|
||||
@ -76,6 +76,8 @@ public:
|
||||
void setRequireLocalization(bool val) { mRequireLocalization = val; }
|
||||
bool getPseudolocalize(void) const { return mPseudolocalize; }
|
||||
void setPseudolocalize(bool val) { mPseudolocalize = val; }
|
||||
bool getUTF8(void) const { return mUTF8; }
|
||||
void setUTF8(bool val) { mUTF8 = val; }
|
||||
bool getValues(void) const { return mValues; }
|
||||
void setValues(bool val) { mValues = val; }
|
||||
int getCompressionMethod(void) const { return mCompressionMethod; }
|
||||
@ -161,6 +163,7 @@ private:
|
||||
bool mExtending;
|
||||
bool mRequireLocalization;
|
||||
bool mPseudolocalize;
|
||||
bool mUTF8;
|
||||
bool mValues;
|
||||
int mCompressionMethod;
|
||||
bool mJunkPath;
|
||||
|
@ -412,6 +412,7 @@ int doDump(Bundle* bundle)
|
||||
}
|
||||
tree.restart();
|
||||
printXMLBlock(&tree);
|
||||
tree.uninit();
|
||||
delete asset;
|
||||
asset = NULL;
|
||||
}
|
||||
|
@ -118,6 +118,7 @@ void usage(void)
|
||||
" -P specify where to output public resource definitions\n"
|
||||
" -S directory in which to find resources. Multiple directories will be scanned"
|
||||
" and the first match found (left to right) will take precedence."
|
||||
" -8 Encode string resources in UTF-8.\n"
|
||||
" -0 specifies an additional extension for which such files will not\n"
|
||||
" be stored compressed in the .apk. An empty string means to not\n"
|
||||
" compress any files at all.\n"
|
||||
@ -370,6 +371,9 @@ int main(int argc, char* const argv[])
|
||||
bundle.setCompressionMethod(ZipEntry::kCompressStored);
|
||||
}
|
||||
break;
|
||||
case '8':
|
||||
bundle.setUTF8(true);
|
||||
break;
|
||||
case '-':
|
||||
if (strcmp(cp, "-min-sdk-version") == 0) {
|
||||
argc--;
|
||||
|
@ -613,6 +613,12 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
|
||||
NOISY(printf("Found %d included resource packages\n", (int)table.size()));
|
||||
|
||||
// Standard flags for compiled XML and optional UTF-8 encoding
|
||||
int xmlFlags = XML_COMPILE_STANDARD_RESOURCE;
|
||||
if (bundle->getUTF8()) {
|
||||
xmlFlags |= XML_COMPILE_UTF8;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// First, gather all resource information.
|
||||
// --------------------------------------------------------------
|
||||
@ -763,7 +769,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
ResourceDirIterator it(layouts, String8("layout"));
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
String8 src = it.getFile()->getPrintableSource();
|
||||
err = compileXmlFile(assets, it.getFile(), &table);
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
if (err == NO_ERROR) {
|
||||
ResXMLTree block;
|
||||
block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
|
||||
@ -782,7 +788,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
if (anims != NULL) {
|
||||
ResourceDirIterator it(anims, String8("anim"));
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
err = compileXmlFile(assets, it.getFile(), &table);
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
if (err != NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
@ -797,7 +803,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
if (xmls != NULL) {
|
||||
ResourceDirIterator it(xmls, String8("xml"));
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
err = compileXmlFile(assets, it.getFile(), &table);
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
if (err != NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
@ -819,7 +825,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
if (colors != NULL) {
|
||||
ResourceDirIterator it(colors, String8("color"));
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
err = compileXmlFile(assets, it.getFile(), &table);
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
if (err != NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
@ -835,7 +841,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
|
||||
ResourceDirIterator it(menus, String8("menu"));
|
||||
while ((err=it.next()) == NO_ERROR) {
|
||||
String8 src = it.getFile()->getPrintableSource();
|
||||
err = compileXmlFile(assets, it.getFile(), &table);
|
||||
err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
|
||||
if (err != NO_ERROR) {
|
||||
hasErrors = true;
|
||||
}
|
||||
|
@ -39,6 +39,10 @@ status_t compileXmlFile(const sp<AaptAssets>& assets,
|
||||
root->removeWhitespace(false, NULL);
|
||||
}
|
||||
|
||||
if ((options&XML_COMPILE_UTF8) != 0) {
|
||||
root->setUTF8(true);
|
||||
}
|
||||
|
||||
bool hasErrors = false;
|
||||
|
||||
if ((options&XML_COMPILE_ASSIGN_ATTRIBUTE_IDS) != 0) {
|
||||
@ -2505,7 +2509,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
|
||||
// Iterate through all data, collecting all values (strings,
|
||||
// references, etc).
|
||||
StringPool valueStrings;
|
||||
StringPool valueStrings = StringPool(false, bundle->getUTF8());
|
||||
for (pi=0; pi<N; pi++) {
|
||||
sp<Package> p = mOrderedPackages.itemAt(pi);
|
||||
if (p->getTypes().size() == 0) {
|
||||
@ -2513,8 +2517,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
|
||||
continue;
|
||||
}
|
||||
|
||||
StringPool typeStrings;
|
||||
StringPool keyStrings;
|
||||
StringPool typeStrings = StringPool(false, bundle->getUTF8());
|
||||
StringPool keyStrings = StringPool(false, bundle->getUTF8());
|
||||
|
||||
const size_t N = p->getOrderedTypes().size();
|
||||
for (size_t ti=0; ti<N; ti++) {
|
||||
|
@ -24,6 +24,7 @@ enum {
|
||||
XML_COMPILE_COMPACT_WHITESPACE = 1<<2,
|
||||
XML_COMPILE_STRIP_WHITESPACE = 1<<3,
|
||||
XML_COMPILE_STRIP_RAW_VALUES = 1<<4,
|
||||
XML_COMPILE_UTF8 = 1<<5,
|
||||
|
||||
XML_COMPILE_STANDARD_RESOURCE =
|
||||
XML_COMPILE_STRIP_COMMENTS | XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
|
||||
|
@ -30,8 +30,8 @@ void printStringPool(const ResStringPool* pool)
|
||||
}
|
||||
}
|
||||
|
||||
StringPool::StringPool(bool sorted)
|
||||
: mSorted(sorted), mValues(-1), mIdents(-1)
|
||||
StringPool::StringPool(bool sorted, bool utf8)
|
||||
: mSorted(sorted), mUTF8(utf8), mValues(-1), mIdents(-1)
|
||||
{
|
||||
}
|
||||
|
||||
@ -165,6 +165,16 @@ sp<AaptFile> StringPool::createStringBlock()
|
||||
return err == NO_ERROR ? pool : NULL;
|
||||
}
|
||||
|
||||
#define ENCODE_LENGTH(str, chrsz, strSize) \
|
||||
{ \
|
||||
size_t maxMask = 1 << ((chrsz*8)-1); \
|
||||
size_t maxSize = maxMask-1; \
|
||||
if (strSize > maxSize) { \
|
||||
*str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \
|
||||
} \
|
||||
*str++ = strSize; \
|
||||
}
|
||||
|
||||
status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
|
||||
{
|
||||
// Allow appending. Sorry this is a little wacky.
|
||||
@ -213,28 +223,53 @@ status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
|
||||
return NO_MEMORY;
|
||||
}
|
||||
|
||||
const size_t charSize = mUTF8 ? sizeof(uint8_t) : sizeof(char16_t);
|
||||
|
||||
size_t strPos = 0;
|
||||
for (i=0; i<STRINGS; i++) {
|
||||
entry& ent = mEntries.editItemAt(i);
|
||||
const size_t strSize = (ent.value.size());
|
||||
const size_t lenSize = strSize > 0x7fff ? sizeof(uint32_t) : sizeof(uint16_t);
|
||||
const size_t totalSize = lenSize + ((strSize+1)*sizeof(uint16_t));
|
||||
const size_t lenSize = strSize > (size_t)(1<<((charSize*8)-1))-1 ?
|
||||
charSize*2 : charSize;
|
||||
|
||||
String8 encStr;
|
||||
if (mUTF8) {
|
||||
encStr = String8(ent.value);
|
||||
}
|
||||
|
||||
const size_t encSize = mUTF8 ? encStr.size() : 0;
|
||||
const size_t encLenSize = mUTF8 ?
|
||||
(encSize > (size_t)(1<<((charSize*8)-1))-1 ?
|
||||
charSize*2 : charSize) : 0;
|
||||
|
||||
ent.offset = strPos;
|
||||
uint16_t* dat = (uint16_t*)pool->editData(preSize + strPos + totalSize);
|
||||
|
||||
const size_t totalSize = lenSize + encLenSize +
|
||||
((mUTF8 ? encSize : strSize)+1)*charSize;
|
||||
|
||||
void* dat = (void*)pool->editData(preSize + strPos + totalSize);
|
||||
if (dat == NULL) {
|
||||
fprintf(stderr, "ERROR: Out of memory for string pool\n");
|
||||
return NO_MEMORY;
|
||||
}
|
||||
dat += (preSize+strPos)/sizeof(uint16_t);
|
||||
if (lenSize > sizeof(uint16_t)) {
|
||||
*dat = htods(0x8000 | ((strSize>>16)&0x7fff));
|
||||
dat++;
|
||||
}
|
||||
*dat++ = htods(strSize);
|
||||
strcpy16_htod(dat, ent.value);
|
||||
dat = (uint8_t*)dat + preSize + strPos;
|
||||
if (mUTF8) {
|
||||
uint8_t* strings = (uint8_t*)dat;
|
||||
|
||||
strPos += lenSize + (strSize+1)*sizeof(uint16_t);
|
||||
ENCODE_LENGTH(strings, sizeof(uint8_t), strSize)
|
||||
|
||||
ENCODE_LENGTH(strings, sizeof(uint8_t), encSize)
|
||||
|
||||
strncpy((char*)strings, encStr, encSize+1);
|
||||
} else {
|
||||
uint16_t* strings = (uint16_t*)dat;
|
||||
|
||||
ENCODE_LENGTH(strings, sizeof(uint16_t), strSize)
|
||||
|
||||
strcpy16_htod(strings, ent.value);
|
||||
}
|
||||
|
||||
strPos += totalSize;
|
||||
}
|
||||
|
||||
// Pad ending string position up to a uint32_t boundary.
|
||||
@ -312,6 +347,9 @@ status_t StringPool::writeStringBlock(const sp<AaptFile>& pool)
|
||||
if (mSorted) {
|
||||
header->flags |= htodl(ResStringPool_header::SORTED_FLAG);
|
||||
}
|
||||
if (mUTF8) {
|
||||
header->flags |= htodl(ResStringPool_header::UTF8_FLAG);
|
||||
}
|
||||
header->stringsStart = htodl(preSize);
|
||||
header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0);
|
||||
|
||||
|
@ -68,8 +68,11 @@ public:
|
||||
* lookup with ResStringPool::indexOfString() (O(log n)), at the expense
|
||||
* of support for styled string entries (which requires the same string
|
||||
* be included multiple times in the pool).
|
||||
*
|
||||
* If 'utf8' is true, strings will be encoded with UTF-8 instead of
|
||||
* left in Java's native UTF-16.
|
||||
*/
|
||||
explicit StringPool(bool sorted = false);
|
||||
explicit StringPool(bool sorted = false, bool utf8 = false);
|
||||
|
||||
/**
|
||||
* Add a new string to the pool. If mergeDuplicates is true, thenif
|
||||
@ -123,6 +126,7 @@ public:
|
||||
|
||||
private:
|
||||
const bool mSorted;
|
||||
const bool mUTF8;
|
||||
// Raw array of unique strings, in some arbitrary order.
|
||||
Vector<entry> mEntries;
|
||||
// Array of indices into mEntries, in the order they were
|
||||
|
@ -478,6 +478,7 @@ XMLNode::XMLNode(const String8& filename, const String16& s1, const String16& s2
|
||||
, mFilename(filename)
|
||||
, mStartLineNumber(0)
|
||||
, mEndLineNumber(0)
|
||||
, mUTF8(false)
|
||||
{
|
||||
if (isNamespace) {
|
||||
mNamespacePrefix = s1;
|
||||
@ -837,7 +838,7 @@ status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets,
|
||||
status_t XMLNode::flatten(const sp<AaptFile>& dest,
|
||||
bool stripComments, bool stripRawValues) const
|
||||
{
|
||||
StringPool strings;
|
||||
StringPool strings = StringPool(false, mUTF8);
|
||||
Vector<uint32_t> resids;
|
||||
|
||||
// First collect just the strings for attribute names that have a
|
||||
|
@ -124,6 +124,8 @@ public:
|
||||
|
||||
void removeWhitespace(bool stripAll=true, const char** cDataTags=NULL);
|
||||
|
||||
void setUTF8(bool val) { mUTF8 = val; }
|
||||
|
||||
status_t parseValues(const sp<AaptAssets>& assets, ResourceTable* table);
|
||||
|
||||
status_t assignResourceIds(const sp<AaptAssets>& assets,
|
||||
@ -189,6 +191,9 @@ private:
|
||||
String8 mFilename;
|
||||
int32_t mStartLineNumber;
|
||||
int32_t mEndLineNumber;
|
||||
|
||||
// Encode compiled XML with UTF-8 StringPools?
|
||||
bool mUTF8;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user