]>
git.saurik.com Git - android/aapt.git/blob - StringPool.cpp
   2 // Copyright 2006 The Android Open Source Project 
   4 // Build resource files from raw assets. 
   7 #include "StringPool.h" 
   8 #include "ResourceTable.h" 
  10 #include <utils/ByteOrder.h> 
  11 #include <utils/SortedVector.h> 
  15 #  define ZD_TYPE ssize_t 
  23 void strcpy16_htod(uint16_t* dst
, const uint16_t* src
) 
  26         char16_t s 
= htods(*src
); 
  33 void printStringPool(const ResStringPool
* pool
) 
  35     SortedVector
<const void*> uniqueStrings
; 
  36     const size_t N 
= pool
->size(); 
  37     for (size_t i
=0; i
<N
; i
++) { 
  40             uniqueStrings
.add(pool
->string8At(i
, &len
)); 
  42             uniqueStrings
.add(pool
->stringAt(i
, &len
)); 
  46     printf("String pool of " ZD 
" unique %s %s strings, " ZD 
" entries and " 
  47             ZD 
" styles using " ZD 
" bytes:\n", 
  48             (ZD_TYPE
)uniqueStrings
.size(), pool
->isUTF8() ? "UTF-8" : "UTF-16", 
  49             pool
->isSorted() ? "sorted" : "non-sorted", 
  50             (ZD_TYPE
)N
, (ZD_TYPE
)pool
->styleCount(), (ZD_TYPE
)pool
->bytes()); 
  52     const size_t NS 
= pool
->size(); 
  53     for (size_t s
=0; s
<NS
; s
++) { 
  54         String8 str 
= pool
->string8ObjectAt(s
); 
  55         printf("String #" ZD 
": %s\n", (ZD_TYPE
) s
, str
.string()); 
  59 String8 
StringPool::entry::makeConfigsString() const { 
  60     String8 
configStr(configTypeName
); 
  61     if (configStr
.size() > 0) configStr
.append(" "); 
  62     if (configs
.size() > 0) { 
  63         for (size_t j
=0; j
<configs
.size(); j
++) { 
  64             if (j 
> 0) configStr
.append(", "); 
  65             configStr
.append(configs
[j
].toString()); 
  73 int StringPool::entry::compare(const entry
& o
) const { 
  74     // Strings with styles go first, to reduce the size of the 
  77         return o
.hasStyles 
? 0 : -1; 
  82     int comp 
= configTypeName
.compare(o
.configTypeName
); 
  86     const size_t LHN 
= configs
.size(); 
  87     const size_t RHN 
= o
.configs
.size(); 
  89     while (i 
< LHN 
&& i 
< RHN
) { 
  90         comp 
= configs
[i
].compareLogical(o
.configs
[i
]); 
  96     if (LHN 
< RHN
) return -1; 
  97     else if (LHN 
> RHN
) return 1; 
 101 StringPool::StringPool(bool utf8
) : 
 102         mUTF8(utf8
), mValues(-1) 
 106 ssize_t 
StringPool::add(const String16
& value
, const Vector
<entry_style_span
>& spans
, 
 107         const String8
* configTypeName
, const ResTable_config
* config
) 
 109     ssize_t res 
= add(value
, false, configTypeName
, config
); 
 111         addStyleSpans(res
, spans
); 
 116 ssize_t 
StringPool::add(const String16
& value
, 
 117         bool mergeDuplicates
, const String8
* configTypeName
, const ResTable_config
* config
) 
 119     ssize_t vidx 
= mValues
.indexOfKey(value
); 
 120     ssize_t pos 
= vidx 
>= 0 ? mValues
.valueAt(vidx
) : -1; 
 121     ssize_t eidx 
= pos 
>= 0 ? mEntryArray
.itemAt(pos
) : -1; 
 123         eidx 
= mEntries
.add(entry(value
)); 
 125             fprintf(stderr
, "Failure adding string %s\n", String8(value
).string()); 
 130     if (configTypeName 
!= NULL
) { 
 131         entry
& ent 
= mEntries
.editItemAt(eidx
); 
 132         NOISY(printf("*** adding config type name %s, was %s\n", 
 133                 configTypeName
->string(), ent
.configTypeName
.string())); 
 134         if (ent
.configTypeName
.size() <= 0) { 
 135             ent
.configTypeName 
= *configTypeName
; 
 136         } else if (ent
.configTypeName 
!= *configTypeName
) { 
 137             ent
.configTypeName 
= " "; 
 141     if (config 
!= NULL
) { 
 142         // Add this to the set of configs associated with the string. 
 143         entry
& ent 
= mEntries
.editItemAt(eidx
); 
 145         for (addPos
=0; addPos
<ent
.configs
.size(); addPos
++) { 
 146             int cmp 
= ent
.configs
.itemAt(addPos
).compareLogical(*config
); 
 149                     NOISY(printf("*** inserting config: %s\n", config
->toString().string())); 
 150                     ent
.configs
.insertAt(*config
, addPos
); 
 155         if (addPos 
>= ent
.configs
.size()) { 
 156             NOISY(printf("*** adding config: %s\n", config
->toString().string())); 
 157             ent
.configs
.add(*config
); 
 161     const bool first 
= vidx 
< 0; 
 162     const bool styled 
= (pos 
>= 0 && (size_t)pos 
< mEntryStyleArray
.size()) ? 
 163         mEntryStyleArray
[pos
].spans
.size() : 0; 
 164     if (first 
|| styled 
|| !mergeDuplicates
) { 
 165         pos 
= mEntryArray
.add(eidx
); 
 167             vidx 
= mValues
.add(value
, pos
); 
 169         entry
& ent 
= mEntries
.editItemAt(eidx
); 
 170         ent
.indices
.add(pos
); 
 173     NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n", 
 174             String8(value
).string(), pos
, eidx
, vidx
)); 
 179 status_t 
StringPool::addStyleSpan(size_t idx
, const String16
& name
, 
 180                                   uint32_t start
, uint32_t end
) 
 182     entry_style_span span
; 
 184     span
.span
.firstChar 
= start
; 
 185     span
.span
.lastChar 
= end
; 
 186     return addStyleSpan(idx
, span
); 
 189 status_t 
StringPool::addStyleSpans(size_t idx
, const Vector
<entry_style_span
>& spans
) 
 191     const size_t N
=spans
.size(); 
 192     for (size_t i
=0; i
<N
; i
++) { 
 193         status_t err 
= addStyleSpan(idx
, spans
[i
]); 
 194         if (err 
!= NO_ERROR
) { 
 201 status_t 
StringPool::addStyleSpan(size_t idx
, const entry_style_span
& span
) 
 203     // Place blank entries in the span array up to this index. 
 204     while (mEntryStyleArray
.size() <= idx
) { 
 205         mEntryStyleArray
.add(); 
 208     entry_style
& style 
= mEntryStyleArray
.editItemAt(idx
); 
 209     style
.spans
.add(span
); 
 210     mEntries
.editItemAt(mEntryArray
[idx
]).hasStyles 
= true; 
 214 int StringPool::config_sort(const size_t* lhs
, const size_t* rhs
, void* state
) 
 216     StringPool
* pool 
= (StringPool
*)state
; 
 217     const entry
& lhe 
= pool
->mEntries
[pool
->mEntryArray
[*lhs
]]; 
 218     const entry
& rhe 
= pool
->mEntries
[pool
->mEntryArray
[*rhs
]]; 
 219     return lhe
.compare(rhe
); 
 222 void StringPool::sortByConfig() 
 224     LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos
.size() > 0, "Can't sort string pool after already sorted."); 
 226     const size_t N 
= mEntryArray
.size(); 
 228     // This is a vector that starts out with a 1:1 mapping to entries 
 229     // in the array, which we will sort to come up with the desired order. 
 230     // At that point it maps from the new position in the array to the 
 231     // original position the entry appeared. 
 232     Vector
<size_t> newPosToOriginalPos
; 
 233     for (size_t i
=0; i
<mEntryArray
.size(); i
++) { 
 234         newPosToOriginalPos
.add(i
); 
 238     NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n")); 
 239     newPosToOriginalPos
.sort(config_sort
, this); 
 240     NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n")); 
 242     // Create the reverse mapping from the original position in the array 
 243     // to the new position where it appears in the sorted array.  This is 
 244     // so that clients can re-map any positions they had previously stored. 
 245     mOriginalPosToNewPos 
= newPosToOriginalPos
; 
 246     for (size_t i
=0; i
<N
; i
++) { 
 247         mOriginalPosToNewPos
.editItemAt(newPosToOriginalPos
[i
]) = i
; 
 251     SortedVector
<entry
> entries
; 
 253     for (size_t i
=0; i
<N
; i
++) { 
 254         printf("#%d was %d: %s\n", i
, newPosToOriginalPos
[i
], 
 255                 mEntries
[mEntryArray
[newPosToOriginalPos
[i
]]].makeConfigsString().string()); 
 256         entries
.add(mEntries
[mEntryArray
[i
]]); 
 259     for (size_t i
=0; i
<entries
.size(); i
++) { 
 260         printf("Sorted config #%d: %s\n", i
, 
 261                 entries
[i
].makeConfigsString().string()); 
 265     // Now we rebuild the arrays. 
 266     Vector
<entry
> newEntries
; 
 267     Vector
<size_t> newEntryArray
; 
 268     Vector
<entry_style
> newEntryStyleArray
; 
 269     DefaultKeyedVector
<size_t, size_t> origOffsetToNewOffset
; 
 271     for (size_t i
=0; i
<N
; i
++) { 
 272         // We are filling in new offset 'i'; oldI is where we can find it 
 273         // in the original data structure. 
 274         size_t oldI 
= newPosToOriginalPos
[i
]; 
 275         // This is the actual entry associated with the old offset. 
 276         const entry
& oldEnt 
= mEntries
[mEntryArray
[oldI
]]; 
 277         // This is the same entry the last time we added it to the 
 278         // new entry array, if any. 
 279         ssize_t newIndexOfOffset 
= origOffsetToNewOffset
.indexOfKey(oldI
); 
 281         if (newIndexOfOffset 
< 0) { 
 282             // This is the first time we have seen the entry, so add 
 284             newOffset 
= newEntries
.add(oldEnt
); 
 285             newEntries
.editItemAt(newOffset
).indices
.clear(); 
 287             // We have seen this entry before, use the existing one 
 288             // instead of adding it again. 
 289             newOffset 
= origOffsetToNewOffset
.valueAt(newIndexOfOffset
); 
 291         // Update the indices to include this new position. 
 292         newEntries
.editItemAt(newOffset
).indices
.add(i
); 
 293         // And add the offset of the entry to the new entry array. 
 294         newEntryArray
.add(newOffset
); 
 295         // Add any old style to the new style array. 
 296         if (mEntryStyleArray
.size() > 0) { 
 297             if (oldI 
< mEntryStyleArray
.size()) { 
 298                 newEntryStyleArray
.add(mEntryStyleArray
[oldI
]); 
 300                 newEntryStyleArray
.add(entry_style()); 
 305     // Now trim any entries at the end of the new style array that are 
 307     for (ssize_t i
=newEntryStyleArray
.size()-1; i
>=0; i
--) { 
 308         const entry_style
& style 
= newEntryStyleArray
[i
]; 
 309         if (style
.spans
.size() > 0) { 
 313         // This one is not needed; remove. 
 314         newEntryStyleArray
.removeAt(i
); 
 317     // All done, install the new data structures and upate mValues with 
 318     // the new positions. 
 319     mEntries 
= newEntries
; 
 320     mEntryArray 
= newEntryArray
; 
 321     mEntryStyleArray 
= newEntryStyleArray
; 
 323     for (size_t i
=0; i
<mEntries
.size(); i
++) { 
 324         const entry
& ent 
= mEntries
[i
]; 
 325         mValues
.add(ent
.value
, ent
.indices
[0]); 
 329     printf("FINAL SORTED STRING CONFIGS:\n"); 
 330     for (size_t i
=0; i
<mEntries
.size(); i
++) { 
 331         const entry
& ent 
= mEntries
[i
]; 
 332         printf("#" ZD 
" %s: %s\n", (ZD_TYPE
)i
, ent
.makeConfigsString().string(), 
 333                 String8(ent
.value
).string()); 
 338 sp
<AaptFile
> StringPool::createStringBlock() 
 340     sp
<AaptFile
> pool 
= new AaptFile(String8(), AaptGroupEntry(), 
 342     status_t err 
= writeStringBlock(pool
); 
 343     return err 
== NO_ERROR 
? pool 
: NULL
; 
 346 #define ENCODE_LENGTH(str, chrsz, strSize) \ 
 348     size_t maxMask = 1 << ((chrsz*8)-1); \ 
 349     size_t maxSize = maxMask-1; \ 
 350     if (strSize > maxSize) { \ 
 351         *str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \ 
 356 status_t 
StringPool::writeStringBlock(const sp
<AaptFile
>& pool
) 
 358     // Allow appending.  Sorry this is a little wacky. 
 359     if (pool
->getSize() > 0) { 
 360         sp
<AaptFile
> block 
= createStringBlock(); 
 362             return UNKNOWN_ERROR
; 
 364         ssize_t res 
= pool
->writeData(block
->getData(), block
->getSize()); 
 365         return (res 
>= 0) ? (status_t
)NO_ERROR 
: res
; 
 368     // First we need to add all style span names to the string pool. 
 369     // We do this now (instead of when the span is added) so that these 
 370     // will appear at the end of the pool, not disrupting the order 
 371     // our client placed their own strings in it. 
 373     const size_t STYLES 
= mEntryStyleArray
.size(); 
 376     for (i
=0; i
<STYLES
; i
++) { 
 377         entry_style
& style 
= mEntryStyleArray
.editItemAt(i
); 
 378         const size_t N 
= style
.spans
.size(); 
 379         for (size_t i
=0; i
<N
; i
++) { 
 380             entry_style_span
& span 
= style
.spans
.editItemAt(i
); 
 381             ssize_t idx 
= add(span
.name
, true); 
 383                 fprintf(stderr
, "Error adding span for style tag '%s'\n", 
 384                         String8(span
.name
).string()); 
 387             span
.span
.name
.index 
= (uint32_t)idx
; 
 391     const size_t ENTRIES 
= mEntryArray
.size(); 
 393     // Now build the pool of unique strings. 
 395     const size_t STRINGS 
= mEntries
.size(); 
 396     const size_t preSize 
= sizeof(ResStringPool_header
) 
 397                          + (sizeof(uint32_t)*ENTRIES
) 
 398                          + (sizeof(uint32_t)*STYLES
); 
 399     if (pool
->editData(preSize
) == NULL
) { 
 400         fprintf(stderr
, "ERROR: Out of memory for string pool\n"); 
 404     const size_t charSize 
= mUTF8 
? sizeof(uint8_t) : sizeof(char16_t); 
 407     for (i
=0; i
<STRINGS
; i
++) { 
 408         entry
& ent 
= mEntries
.editItemAt(i
); 
 409         const size_t strSize 
= (ent
.value
.size()); 
 410         const size_t lenSize 
= strSize 
> (size_t)(1<<((charSize
*8)-1))-1 ? 
 411             charSize
*2 : charSize
; 
 415             encStr 
= String8(ent
.value
); 
 418         const size_t encSize 
= mUTF8 
? encStr
.size() : 0; 
 419         const size_t encLenSize 
= mUTF8 
? 
 420             (encSize 
> (size_t)(1<<((charSize
*8)-1))-1 ? 
 421                 charSize
*2 : charSize
) : 0; 
 425         const size_t totalSize 
= lenSize 
+ encLenSize 
+ 
 426             ((mUTF8 
? encSize 
: strSize
)+1)*charSize
; 
 428         void* dat 
= (void*)pool
->editData(preSize 
+ strPos 
+ totalSize
); 
 430             fprintf(stderr
, "ERROR: Out of memory for string pool\n"); 
 433         dat 
= (uint8_t*)dat 
+ preSize 
+ strPos
; 
 435             uint8_t* strings 
= (uint8_t*)dat
; 
 437             ENCODE_LENGTH(strings
, sizeof(uint8_t), strSize
) 
 439             ENCODE_LENGTH(strings
, sizeof(uint8_t), encSize
) 
 441             strncpy((char*)strings
, encStr
, encSize
+1); 
 443             uint16_t* strings 
= (uint16_t*)dat
; 
 445             ENCODE_LENGTH(strings
, sizeof(uint16_t), strSize
) 
 447             strcpy16_htod(strings
, ent
.value
); 
 453     // Pad ending string position up to a uint32_t boundary. 
 456         size_t padPos 
= ((strPos
+3)&~0x3); 
 457         uint8_t* dat 
= (uint8_t*)pool
->editData(preSize 
+ padPos
); 
 459             fprintf(stderr
, "ERROR: Out of memory padding string pool\n"); 
 462         memset(dat
+preSize
+strPos
, 0, padPos
-strPos
); 
 466     // Build the pool of style spans. 
 468     size_t styPos 
= strPos
; 
 469     for (i
=0; i
<STYLES
; i
++) { 
 470         entry_style
& ent 
= mEntryStyleArray
.editItemAt(i
); 
 471         const size_t N 
= ent
.spans
.size(); 
 472         const size_t totalSize 
= (N
*sizeof(ResStringPool_span
)) 
 473                                + sizeof(ResStringPool_ref
); 
 475         ent
.offset 
= styPos
-strPos
; 
 476         uint8_t* dat 
= (uint8_t*)pool
->editData(preSize 
+ styPos 
+ totalSize
); 
 478             fprintf(stderr
, "ERROR: Out of memory for string styles\n"); 
 481         ResStringPool_span
* span 
= (ResStringPool_span
*)(dat
+preSize
+styPos
); 
 482         for (size_t i
=0; i
<N
; i
++) { 
 483             span
->name
.index 
= htodl(ent
.spans
[i
].span
.name
.index
); 
 484             span
->firstChar 
= htodl(ent
.spans
[i
].span
.firstChar
); 
 485             span
->lastChar 
= htodl(ent
.spans
[i
].span
.lastChar
); 
 488         span
->name
.index 
= htodl(ResStringPool_span::END
); 
 494         // Add full terminator at the end (when reading we validate that 
 495         // the end of the pool is fully terminated to simplify error 
 497         size_t extra 
= sizeof(ResStringPool_span
)-sizeof(ResStringPool_ref
); 
 498         uint8_t* dat 
= (uint8_t*)pool
->editData(preSize 
+ styPos 
+ extra
); 
 500             fprintf(stderr
, "ERROR: Out of memory for string styles\n"); 
 503         uint32_t* p 
= (uint32_t*)(dat
+preSize
+styPos
); 
 505             *p
++ = htodl(ResStringPool_span::END
); 
 506             extra 
-= sizeof(uint32_t); 
 513     ResStringPool_header
* header 
= 
 514         (ResStringPool_header
*)pool
->padData(sizeof(uint32_t)); 
 515     if (header 
== NULL
) { 
 516         fprintf(stderr
, "ERROR: Out of memory for string pool\n"); 
 519     memset(header
, 0, sizeof(*header
)); 
 520     header
->header
.type 
= htods(RES_STRING_POOL_TYPE
); 
 521     header
->header
.headerSize 
= htods(sizeof(*header
)); 
 522     header
->header
.size 
= htodl(pool
->getSize()); 
 523     header
->stringCount 
= htodl(ENTRIES
); 
 524     header
->styleCount 
= htodl(STYLES
); 
 526         header
->flags 
|= htodl(ResStringPool_header::UTF8_FLAG
); 
 528     header
->stringsStart 
= htodl(preSize
); 
 529     header
->stylesStart 
= htodl(STYLES 
> 0 ? (preSize
+strPos
) : 0); 
 531     // Write string index array. 
 533     uint32_t* index 
= (uint32_t*)(header
+1); 
 534     for (i
=0; i
<ENTRIES
; i
++) { 
 535         entry
& ent 
= mEntries
.editItemAt(mEntryArray
[i
]); 
 536         *index
++ = htodl(ent
.offset
); 
 537         NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i
, 
 538                 String8(ent
.value
).string(), 
 539                 mEntryArray
[i
], ent
.offset
)); 
 542     // Write style index array. 
 544     for (i
=0; i
<STYLES
; i
++) { 
 545         *index
++ = htodl(mEntryStyleArray
[i
].offset
); 
 551 ssize_t 
StringPool::offsetForString(const String16
& val
) const 
 553     const Vector
<size_t>* indices 
= offsetsForString(val
); 
 554     ssize_t res 
= indices 
!= NULL 
&& indices
->size() > 0 ? indices
->itemAt(0) : -1; 
 555     NOISY(printf("Offset for string %s: %d (%s)\n", String8(val
).string(), res
, 
 556             res 
>= 0 ? String8(mEntries
[mEntryArray
[res
]].value
).string() : String8())); 
 560 const Vector
<size_t>* StringPool::offsetsForString(const String16
& val
) const 
 562     ssize_t pos 
= mValues
.valueFor(val
); 
 566     return &mEntries
[mEntryArray
[pos
]].indices
;