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