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