]>
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 styles array. 
  75     // We don't care about the relative order of these strings. 
  77         return o
.hasStyles 
? 0 : -1; 
  83     // Sort unstyled strings by type, then by logical configuration. 
  84     int comp 
= configTypeName
.compare(o
.configTypeName
); 
  88     const size_t LHN 
= configs
.size(); 
  89     const size_t RHN 
= o
.configs
.size(); 
  91     while (i 
< LHN 
&& i 
< RHN
) { 
  92         comp 
= configs
[i
].compareLogical(o
.configs
[i
]); 
  98     if (LHN 
< RHN
) return -1; 
  99     else if (LHN 
> RHN
) return 1; 
 103 StringPool::StringPool(bool utf8
) : 
 104         mUTF8(utf8
), mValues(-1) 
 108 ssize_t 
StringPool::add(const String16
& value
, const Vector
<entry_style_span
>& spans
, 
 109         const String8
* configTypeName
, const ResTable_config
* config
) 
 111     ssize_t res 
= add(value
, false, configTypeName
, config
); 
 113         addStyleSpans(res
, spans
); 
 118 ssize_t 
StringPool::add(const String16
& value
, 
 119         bool mergeDuplicates
, const String8
* configTypeName
, const ResTable_config
* config
) 
 121     ssize_t vidx 
= mValues
.indexOfKey(value
); 
 122     ssize_t pos 
= vidx 
>= 0 ? mValues
.valueAt(vidx
) : -1; 
 123     ssize_t eidx 
= pos 
>= 0 ? mEntryArray
.itemAt(pos
) : -1; 
 125         eidx 
= mEntries
.add(entry(value
)); 
 127             fprintf(stderr
, "Failure adding string %s\n", String8(value
).string()); 
 132     if (configTypeName 
!= NULL
) { 
 133         entry
& ent 
= mEntries
.editItemAt(eidx
); 
 134         NOISY(printf("*** adding config type name %s, was %s\n", 
 135                 configTypeName
->string(), ent
.configTypeName
.string())); 
 136         if (ent
.configTypeName
.size() <= 0) { 
 137             ent
.configTypeName 
= *configTypeName
; 
 138         } else if (ent
.configTypeName 
!= *configTypeName
) { 
 139             ent
.configTypeName 
= " "; 
 143     if (config 
!= NULL
) { 
 144         // Add this to the set of configs associated with the string. 
 145         entry
& ent 
= mEntries
.editItemAt(eidx
); 
 147         for (addPos
=0; addPos
<ent
.configs
.size(); addPos
++) { 
 148             int cmp 
= ent
.configs
.itemAt(addPos
).compareLogical(*config
); 
 151                     NOISY(printf("*** inserting config: %s\n", config
->toString().string())); 
 152                     ent
.configs
.insertAt(*config
, addPos
); 
 157         if (addPos 
>= ent
.configs
.size()) { 
 158             NOISY(printf("*** adding config: %s\n", config
->toString().string())); 
 159             ent
.configs
.add(*config
); 
 163     const bool first 
= vidx 
< 0; 
 164     const bool styled 
= (pos 
>= 0 && (size_t)pos 
< mEntryStyleArray
.size()) ? 
 165         mEntryStyleArray
[pos
].spans
.size() : 0; 
 166     if (first 
|| styled 
|| !mergeDuplicates
) { 
 167         pos 
= mEntryArray
.add(eidx
); 
 169             vidx 
= mValues
.add(value
, pos
); 
 171         entry
& ent 
= mEntries
.editItemAt(eidx
); 
 172         ent
.indices
.add(pos
); 
 175     NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n", 
 176             String8(value
).string(), pos
, eidx
, vidx
)); 
 181 status_t 
StringPool::addStyleSpan(size_t idx
, const String16
& name
, 
 182                                   uint32_t start
, uint32_t end
) 
 184     entry_style_span span
; 
 186     span
.span
.firstChar 
= start
; 
 187     span
.span
.lastChar 
= end
; 
 188     return addStyleSpan(idx
, span
); 
 191 status_t 
StringPool::addStyleSpans(size_t idx
, const Vector
<entry_style_span
>& spans
) 
 193     const size_t N
=spans
.size(); 
 194     for (size_t i
=0; i
<N
; i
++) { 
 195         status_t err 
= addStyleSpan(idx
, spans
[i
]); 
 196         if (err 
!= NO_ERROR
) { 
 203 status_t 
StringPool::addStyleSpan(size_t idx
, const entry_style_span
& span
) 
 205     // Place blank entries in the span array up to this index. 
 206     while (mEntryStyleArray
.size() <= idx
) { 
 207         mEntryStyleArray
.add(); 
 210     entry_style
& style 
= mEntryStyleArray
.editItemAt(idx
); 
 211     style
.spans
.add(span
); 
 212     mEntries
.editItemAt(mEntryArray
[idx
]).hasStyles 
= true; 
 216 int StringPool::config_sort(const size_t* lhs
, const size_t* rhs
, void* state
) 
 218     StringPool
* pool 
= (StringPool
*)state
; 
 219     const entry
& lhe 
= pool
->mEntries
[pool
->mEntryArray
[*lhs
]]; 
 220     const entry
& rhe 
= pool
->mEntries
[pool
->mEntryArray
[*rhs
]]; 
 221     return lhe
.compare(rhe
); 
 224 void StringPool::sortByConfig() 
 226     LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos
.size() > 0, "Can't sort string pool after already sorted."); 
 228     const size_t N 
= mEntryArray
.size(); 
 230     // This is a vector that starts out with a 1:1 mapping to entries 
 231     // in the array, which we will sort to come up with the desired order. 
 232     // At that point it maps from the new position in the array to the 
 233     // original position the entry appeared. 
 234     Vector
<size_t> newPosToOriginalPos
; 
 235     for (size_t i
=0; i
<mEntryArray
.size(); i
++) { 
 236         newPosToOriginalPos
.add(i
); 
 240     NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n")); 
 241     newPosToOriginalPos
.sort(config_sort
, this); 
 242     NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n")); 
 244     // Create the reverse mapping from the original position in the array 
 245     // to the new position where it appears in the sorted array.  This is 
 246     // so that clients can re-map any positions they had previously stored. 
 247     mOriginalPosToNewPos 
= newPosToOriginalPos
; 
 248     for (size_t i
=0; i
<N
; i
++) { 
 249         mOriginalPosToNewPos
.editItemAt(newPosToOriginalPos
[i
]) = i
; 
 253     SortedVector
<entry
> entries
; 
 255     for (size_t i
=0; i
<N
; i
++) { 
 256         printf("#%d was %d: %s\n", i
, newPosToOriginalPos
[i
], 
 257                 mEntries
[mEntryArray
[newPosToOriginalPos
[i
]]].makeConfigsString().string()); 
 258         entries
.add(mEntries
[mEntryArray
[i
]]); 
 261     for (size_t i
=0; i
<entries
.size(); i
++) { 
 262         printf("Sorted config #%d: %s\n", i
, 
 263                 entries
[i
].makeConfigsString().string()); 
 267     // Now we rebuild the arrays. 
 268     Vector
<entry
> newEntries
; 
 269     Vector
<size_t> newEntryArray
; 
 270     Vector
<entry_style
> newEntryStyleArray
; 
 271     DefaultKeyedVector
<size_t, size_t> origOffsetToNewOffset
; 
 273     for (size_t i
=0; i
<N
; i
++) { 
 274         // We are filling in new offset 'i'; oldI is where we can find it 
 275         // in the original data structure. 
 276         size_t oldI 
= newPosToOriginalPos
[i
]; 
 277         // This is the actual entry associated with the old offset. 
 278         const entry
& oldEnt 
= mEntries
[mEntryArray
[oldI
]]; 
 279         // This is the same entry the last time we added it to the 
 280         // new entry array, if any. 
 281         ssize_t newIndexOfOffset 
= origOffsetToNewOffset
.indexOfKey(oldI
); 
 283         if (newIndexOfOffset 
< 0) { 
 284             // This is the first time we have seen the entry, so add 
 286             newOffset 
= newEntries
.add(oldEnt
); 
 287             newEntries
.editItemAt(newOffset
).indices
.clear(); 
 289             // We have seen this entry before, use the existing one 
 290             // instead of adding it again. 
 291             newOffset 
= origOffsetToNewOffset
.valueAt(newIndexOfOffset
); 
 293         // Update the indices to include this new position. 
 294         newEntries
.editItemAt(newOffset
).indices
.add(i
); 
 295         // And add the offset of the entry to the new entry array. 
 296         newEntryArray
.add(newOffset
); 
 297         // Add any old style to the new style array. 
 298         if (mEntryStyleArray
.size() > 0) { 
 299             if (oldI 
< mEntryStyleArray
.size()) { 
 300                 newEntryStyleArray
.add(mEntryStyleArray
[oldI
]); 
 302                 newEntryStyleArray
.add(entry_style()); 
 307     // Now trim any entries at the end of the new style array that are 
 309     for (ssize_t i
=newEntryStyleArray
.size()-1; i
>=0; i
--) { 
 310         const entry_style
& style 
= newEntryStyleArray
[i
]; 
 311         if (style
.spans
.size() > 0) { 
 315         // This one is not needed; remove. 
 316         newEntryStyleArray
.removeAt(i
); 
 319     // All done, install the new data structures and upate mValues with 
 320     // the new positions. 
 321     mEntries 
= newEntries
; 
 322     mEntryArray 
= newEntryArray
; 
 323     mEntryStyleArray 
= newEntryStyleArray
; 
 325     for (size_t i
=0; i
<mEntries
.size(); i
++) { 
 326         const entry
& ent 
= mEntries
[i
]; 
 327         mValues
.add(ent
.value
, ent
.indices
[0]); 
 331     printf("FINAL SORTED STRING CONFIGS:\n"); 
 332     for (size_t i
=0; i
<mEntries
.size(); i
++) { 
 333         const entry
& ent 
= mEntries
[i
]; 
 334         printf("#" ZD 
" %s: %s\n", (ZD_TYPE
)i
, ent
.makeConfigsString().string(), 
 335                 String8(ent
.value
).string()); 
 340 sp
<AaptFile
> StringPool::createStringBlock() 
 342     sp
<AaptFile
> pool 
= new AaptFile(String8(), AaptGroupEntry(), 
 344     status_t err 
= writeStringBlock(pool
); 
 345     return err 
== NO_ERROR 
? pool 
: NULL
; 
 348 #define ENCODE_LENGTH(str, chrsz, strSize) \ 
 350     size_t maxMask = 1 << ((chrsz*8)-1); \ 
 351     size_t maxSize = maxMask-1; \ 
 352     if (strSize > maxSize) { \ 
 353         *str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \ 
 358 status_t 
StringPool::writeStringBlock(const sp
<AaptFile
>& pool
) 
 360     // Allow appending.  Sorry this is a little wacky. 
 361     if (pool
->getSize() > 0) { 
 362         sp
<AaptFile
> block 
= createStringBlock(); 
 364             return UNKNOWN_ERROR
; 
 366         ssize_t res 
= pool
->writeData(block
->getData(), block
->getSize()); 
 367         return (res 
>= 0) ? (status_t
)NO_ERROR 
: res
; 
 370     // First we need to add all style span names to the string pool. 
 371     // We do this now (instead of when the span is added) so that these 
 372     // will appear at the end of the pool, not disrupting the order 
 373     // our client placed their own strings in it. 
 375     const size_t STYLES 
= mEntryStyleArray
.size(); 
 378     for (i
=0; i
<STYLES
; i
++) { 
 379         entry_style
& style 
= mEntryStyleArray
.editItemAt(i
); 
 380         const size_t N 
= style
.spans
.size(); 
 381         for (size_t i
=0; i
<N
; i
++) { 
 382             entry_style_span
& span 
= style
.spans
.editItemAt(i
); 
 383             ssize_t idx 
= add(span
.name
, true); 
 385                 fprintf(stderr
, "Error adding span for style tag '%s'\n", 
 386                         String8(span
.name
).string()); 
 389             span
.span
.name
.index 
= (uint32_t)idx
; 
 393     const size_t ENTRIES 
= mEntryArray
.size(); 
 395     // Now build the pool of unique strings. 
 397     const size_t STRINGS 
= mEntries
.size(); 
 398     const size_t preSize 
= sizeof(ResStringPool_header
) 
 399                          + (sizeof(uint32_t)*ENTRIES
) 
 400                          + (sizeof(uint32_t)*STYLES
); 
 401     if (pool
->editData(preSize
) == NULL
) { 
 402         fprintf(stderr
, "ERROR: Out of memory for string pool\n"); 
 406     const size_t charSize 
= mUTF8 
? sizeof(uint8_t) : sizeof(char16_t); 
 409     for (i
=0; i
<STRINGS
; i
++) { 
 410         entry
& ent 
= mEntries
.editItemAt(i
); 
 411         const size_t strSize 
= (ent
.value
.size()); 
 412         const size_t lenSize 
= strSize 
> (size_t)(1<<((charSize
*8)-1))-1 ? 
 413             charSize
*2 : charSize
; 
 417             encStr 
= String8(ent
.value
); 
 420         const size_t encSize 
= mUTF8 
? encStr
.size() : 0; 
 421         const size_t encLenSize 
= mUTF8 
? 
 422             (encSize 
> (size_t)(1<<((charSize
*8)-1))-1 ? 
 423                 charSize
*2 : charSize
) : 0; 
 427         const size_t totalSize 
= lenSize 
+ encLenSize 
+ 
 428             ((mUTF8 
? encSize 
: strSize
)+1)*charSize
; 
 430         void* dat 
= (void*)pool
->editData(preSize 
+ strPos 
+ totalSize
); 
 432             fprintf(stderr
, "ERROR: Out of memory for string pool\n"); 
 435         dat 
= (uint8_t*)dat 
+ preSize 
+ strPos
; 
 437             uint8_t* strings 
= (uint8_t*)dat
; 
 439             ENCODE_LENGTH(strings
, sizeof(uint8_t), strSize
) 
 441             ENCODE_LENGTH(strings
, sizeof(uint8_t), encSize
) 
 443             strncpy((char*)strings
, encStr
, encSize
+1); 
 445             uint16_t* strings 
= (uint16_t*)dat
; 
 447             ENCODE_LENGTH(strings
, sizeof(uint16_t), strSize
) 
 449             strcpy16_htod(strings
, ent
.value
); 
 455     // Pad ending string position up to a uint32_t boundary. 
 458         size_t padPos 
= ((strPos
+3)&~0x3); 
 459         uint8_t* dat 
= (uint8_t*)pool
->editData(preSize 
+ padPos
); 
 461             fprintf(stderr
, "ERROR: Out of memory padding string pool\n"); 
 464         memset(dat
+preSize
+strPos
, 0, padPos
-strPos
); 
 468     // Build the pool of style spans. 
 470     size_t styPos 
= strPos
; 
 471     for (i
=0; i
<STYLES
; i
++) { 
 472         entry_style
& ent 
= mEntryStyleArray
.editItemAt(i
); 
 473         const size_t N 
= ent
.spans
.size(); 
 474         const size_t totalSize 
= (N
*sizeof(ResStringPool_span
)) 
 475                                + sizeof(ResStringPool_ref
); 
 477         ent
.offset 
= styPos
-strPos
; 
 478         uint8_t* dat 
= (uint8_t*)pool
->editData(preSize 
+ styPos 
+ totalSize
); 
 480             fprintf(stderr
, "ERROR: Out of memory for string styles\n"); 
 483         ResStringPool_span
* span 
= (ResStringPool_span
*)(dat
+preSize
+styPos
); 
 484         for (size_t i
=0; i
<N
; i
++) { 
 485             span
->name
.index 
= htodl(ent
.spans
[i
].span
.name
.index
); 
 486             span
->firstChar 
= htodl(ent
.spans
[i
].span
.firstChar
); 
 487             span
->lastChar 
= htodl(ent
.spans
[i
].span
.lastChar
); 
 490         span
->name
.index 
= htodl(ResStringPool_span::END
); 
 496         // Add full terminator at the end (when reading we validate that 
 497         // the end of the pool is fully terminated to simplify error 
 499         size_t extra 
= sizeof(ResStringPool_span
)-sizeof(ResStringPool_ref
); 
 500         uint8_t* dat 
= (uint8_t*)pool
->editData(preSize 
+ styPos 
+ extra
); 
 502             fprintf(stderr
, "ERROR: Out of memory for string styles\n"); 
 505         uint32_t* p 
= (uint32_t*)(dat
+preSize
+styPos
); 
 507             *p
++ = htodl(ResStringPool_span::END
); 
 508             extra 
-= sizeof(uint32_t); 
 515     ResStringPool_header
* header 
= 
 516         (ResStringPool_header
*)pool
->padData(sizeof(uint32_t)); 
 517     if (header 
== NULL
) { 
 518         fprintf(stderr
, "ERROR: Out of memory for string pool\n"); 
 521     memset(header
, 0, sizeof(*header
)); 
 522     header
->header
.type 
= htods(RES_STRING_POOL_TYPE
); 
 523     header
->header
.headerSize 
= htods(sizeof(*header
)); 
 524     header
->header
.size 
= htodl(pool
->getSize()); 
 525     header
->stringCount 
= htodl(ENTRIES
); 
 526     header
->styleCount 
= htodl(STYLES
); 
 528         header
->flags 
|= htodl(ResStringPool_header::UTF8_FLAG
); 
 530     header
->stringsStart 
= htodl(preSize
); 
 531     header
->stylesStart 
= htodl(STYLES 
> 0 ? (preSize
+strPos
) : 0); 
 533     // Write string index array. 
 535     uint32_t* index 
= (uint32_t*)(header
+1); 
 536     for (i
=0; i
<ENTRIES
; i
++) { 
 537         entry
& ent 
= mEntries
.editItemAt(mEntryArray
[i
]); 
 538         *index
++ = htodl(ent
.offset
); 
 539         NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i
, 
 540                 String8(ent
.value
).string(), 
 541                 mEntryArray
[i
], ent
.offset
)); 
 544     // Write style index array. 
 546     for (i
=0; i
<STYLES
; i
++) { 
 547         *index
++ = htodl(mEntryStyleArray
[i
].offset
); 
 553 ssize_t 
StringPool::offsetForString(const String16
& val
) const 
 555     const Vector
<size_t>* indices 
= offsetsForString(val
); 
 556     ssize_t res 
= indices 
!= NULL 
&& indices
->size() > 0 ? indices
->itemAt(0) : -1; 
 557     NOISY(printf("Offset for string %s: %d (%s)\n", String8(val
).string(), res
, 
 558             res 
>= 0 ? String8(mEntries
[mEntryArray
[res
]].value
).string() : String8())); 
 562 const Vector
<size_t>* StringPool::offsetsForString(const String16
& val
) const 
 564     ssize_t pos 
= mValues
.valueFor(val
); 
 568     return &mEntries
[mEntryArray
[pos
]].indices
;