StringPool.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 #%d: %s\n", s, 29 String8(pool->stringAt(s, &len)).string()); 30 } 31} 32 33StringPool::StringPool(bool sorted) 34 : mSorted(sorted), 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 168status_t StringPool::writeStringBlock(const sp<AaptFile>& pool) 169{ 170 // Allow appending. Sorry this is a little wacky. 171 if (pool->getSize() > 0) { 172 sp<AaptFile> block = createStringBlock(); 173 if (block == NULL) { 174 return UNKNOWN_ERROR; 175 } 176 ssize_t res = pool->writeData(block->getData(), block->getSize()); 177 return (res >= 0) ? (status_t)NO_ERROR : res; 178 } 179 180 // First we need to add all style span names to the string pool. 181 // We do this now (instead of when the span is added) so that these 182 // will appear at the end of the pool, not disrupting the order 183 // our client placed their own strings in it. 184 185 const size_t STYLES = mEntryStyleArray.size(); 186 size_t i; 187 188 for (i=0; i<STYLES; i++) { 189 entry_style& style = mEntryStyleArray.editItemAt(i); 190 const size_t N = style.spans.size(); 191 for (size_t i=0; i<N; i++) { 192 entry_style_span& span = style.spans.editItemAt(i); 193 ssize_t idx = add(span.name, true); 194 if (idx < 0) { 195 fprintf(stderr, "Error adding span for style tag '%s'\n", 196 String8(span.name).string()); 197 return idx; 198 } 199 span.span.name.index = (uint32_t)idx; 200 } 201 } 202 203 const size_t ENTRIES = size(); 204 205 // Now build the pool of unique strings. 206 207 const size_t STRINGS = mEntries.size(); 208 const size_t preSize = sizeof(ResStringPool_header) 209 + (sizeof(uint32_t)*ENTRIES) 210 + (sizeof(uint32_t)*STYLES); 211 if (pool->editData(preSize) == NULL) { 212 fprintf(stderr, "ERROR: Out of memory for string pool\n"); 213 return NO_MEMORY; 214 } 215 216 size_t strPos = 0; 217 for (i=0; i<STRINGS; i++) { 218 entry& ent = mEntries.editItemAt(i); 219 const size_t strSize = (ent.value.size()); 220 const size_t lenSize = strSize > 0x7fff ? sizeof(uint32_t) : sizeof(uint16_t); 221 const size_t totalSize = lenSize + ((strSize+1)*sizeof(uint16_t)); 222 223 ent.offset = strPos; 224 uint16_t* dat = (uint16_t*)pool->editData(preSize + strPos + totalSize); 225 if (dat == NULL) { 226 fprintf(stderr, "ERROR: Out of memory for string pool\n"); 227 return NO_MEMORY; 228 } 229 dat += (preSize+strPos)/sizeof(uint16_t); 230 if (lenSize > sizeof(uint16_t)) { 231 *dat = htods(0x8000 | ((strSize>>16)&0x7ffff)); 232 dat++; 233 } 234 *dat++ = htods(strSize); 235 strcpy16_htod(dat, ent.value); 236 237 strPos += lenSize + (strSize+1)*sizeof(uint16_t); 238 } 239 240 // Pad ending string position up to a uint32_t boundary. 241 242 if (strPos&0x3) { 243 size_t padPos = ((strPos+3)&~0x3); 244 uint8_t* dat = (uint8_t*)pool->editData(preSize + padPos); 245 if (dat == NULL) { 246 fprintf(stderr, "ERROR: Out of memory padding string pool\n"); 247 return NO_MEMORY; 248 } 249 memset(dat+preSize+strPos, 0, padPos-strPos); 250 strPos = padPos; 251 } 252 253 // Build the pool of style spans. 254 255 size_t styPos = strPos; 256 for (i=0; i<STYLES; i++) { 257 entry_style& ent = mEntryStyleArray.editItemAt(i); 258 const size_t N = ent.spans.size(); 259 const size_t totalSize = (N*sizeof(ResStringPool_span)) 260 + sizeof(ResStringPool_ref); 261 262 ent.offset = styPos-strPos; 263 uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + totalSize); 264 if (dat == NULL) { 265 fprintf(stderr, "ERROR: Out of memory for string styles\n"); 266 return NO_MEMORY; 267 } 268 ResStringPool_span* span = (ResStringPool_span*)(dat+preSize+styPos); 269 for (size_t i=0; i<N; i++) { 270 span->name.index = htodl(ent.spans[i].span.name.index); 271 span->firstChar = htodl(ent.spans[i].span.firstChar); 272 span->lastChar = htodl(ent.spans[i].span.lastChar); 273 span++; 274 } 275 span->name.index = htodl(ResStringPool_span::END); 276 277 styPos += totalSize; 278 } 279 280 if (STYLES > 0) { 281 // Add full terminator at the end (when reading we validate that 282 // the end of the pool is fully terminated to simplify error 283 // checking). 284 size_t extra = sizeof(ResStringPool_span)-sizeof(ResStringPool_ref); 285 uint8_t* dat = (uint8_t*)pool->editData(preSize + styPos + extra); 286 if (dat == NULL) { 287 fprintf(stderr, "ERROR: Out of memory for string styles\n"); 288 return NO_MEMORY; 289 } 290 uint32_t* p = (uint32_t*)(dat+preSize+styPos); 291 while (extra > 0) { 292 *p++ = htodl(ResStringPool_span::END); 293 extra -= sizeof(uint32_t); 294 } 295 styPos += extra; 296 } 297 298 // Write header. 299 300 ResStringPool_header* header = 301 (ResStringPool_header*)pool->padData(sizeof(uint32_t)); 302 if (header == NULL) { 303 fprintf(stderr, "ERROR: Out of memory for string pool\n"); 304 return NO_MEMORY; 305 } 306 memset(header, 0, sizeof(*header)); 307 header->header.type = htods(RES_STRING_POOL_TYPE); 308 header->header.headerSize = htods(sizeof(*header)); 309 header->header.size = htodl(pool->getSize()); 310 header->stringCount = htodl(ENTRIES); 311 header->styleCount = htodl(STYLES); 312 if (mSorted) { 313 header->flags |= htodl(ResStringPool_header::SORTED_FLAG); 314 } 315 header->stringsStart = htodl(preSize); 316 header->stylesStart = htodl(STYLES > 0 ? (preSize+strPos) : 0); 317 318 // Write string index array. 319 320 uint32_t* index = (uint32_t*)(header+1); 321 if (mSorted) { 322 for (i=0; i<ENTRIES; i++) { 323 entry& ent = const_cast<entry&>(entryAt(i)); 324 ent.indices.clear(); 325 ent.indices.add(i); 326 *index++ = htodl(ent.offset); 327 } 328 } else { 329 for (i=0; i<ENTRIES; i++) { 330 entry& ent = mEntries.editItemAt(mEntryArray[i]); 331 *index++ = htodl(ent.offset); 332 NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i, 333 String8(ent.value).string(), 334 mEntryArray[i], ent.offset)); 335 } 336 } 337 338 // Write style index array. 339 340 if (mSorted) { 341 for (i=0; i<STYLES; i++) { 342 LOG_ALWAYS_FATAL("Shouldn't be here!"); 343 } 344 } else { 345 for (i=0; i<STYLES; i++) { 346 *index++ = htodl(mEntryStyleArray[i].offset); 347 } 348 } 349 350 return NO_ERROR; 351} 352 353ssize_t StringPool::offsetForString(const String16& val) const 354{ 355 const Vector<size_t>* indices = offsetsForString(val); 356 ssize_t res = indices != NULL && indices->size() > 0 ? indices->itemAt(0) : -1; 357 NOISY(printf("Offset for string %s: %d (%s)\n", String8(val).string(), res, 358 res >= 0 ? String8(mEntries[mEntryArray[res]].value).string() : String8())); 359 return res; 360} 361 362const Vector<size_t>* StringPool::offsetsForString(const String16& val) const 363{ 364 ssize_t pos = mValues.valueFor(val); 365 if (pos < 0) { 366 return NULL; 367 } 368 return &mEntries[mEntryArray[pos]].indices; 369} 370