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