]>
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 sorted
, bool utf8
)
102 : mSorted(sorted
), mUTF8(utf8
), mValues(-1), mIdents(-1)
106 ssize_t
StringPool::add(const String16
& value
, bool mergeDuplicates
,
107 const String8
* configTypeName
, const ResTable_config
* config
)
109 return add(String16(), value
, mergeDuplicates
, configTypeName
, config
);
112 ssize_t
StringPool::add(const String16
& value
, const Vector
<entry_style_span
>& spans
,
113 const String8
* configTypeName
, const ResTable_config
* config
)
115 ssize_t res
= add(String16(), value
, false, configTypeName
, config
);
117 addStyleSpans(res
, spans
);
122 ssize_t
StringPool::add(const String16
& ident
, const String16
& value
,
123 bool mergeDuplicates
, const String8
* configTypeName
, const ResTable_config
* config
)
125 if (ident
.size() > 0) {
126 ssize_t idx
= mIdents
.valueFor(ident
);
128 fprintf(stderr
, "ERROR: Duplicate string identifier %s\n",
129 String8(mEntries
[idx
].value
).string());
130 return UNKNOWN_ERROR
;
134 ssize_t vidx
= mValues
.indexOfKey(value
);
135 ssize_t pos
= vidx
>= 0 ? mValues
.valueAt(vidx
) : -1;
136 ssize_t eidx
= pos
>= 0 ? mEntryArray
.itemAt(pos
) : -1;
138 eidx
= mEntries
.add(entry(value
));
140 fprintf(stderr
, "Failure adding string %s\n", String8(value
).string());
145 if (configTypeName
!= NULL
) {
146 entry
& ent
= mEntries
.editItemAt(eidx
);
147 NOISY(printf("*** adding config type name %s, was %s\n",
148 configTypeName
->string(), ent
.configTypeName
.string()));
149 if (ent
.configTypeName
.size() <= 0) {
150 ent
.configTypeName
= *configTypeName
;
151 } else if (ent
.configTypeName
!= *configTypeName
) {
152 ent
.configTypeName
= " ";
156 if (config
!= NULL
) {
157 // Add this to the set of configs associated with the string.
158 entry
& ent
= mEntries
.editItemAt(eidx
);
160 for (addPos
=0; addPos
<ent
.configs
.size(); addPos
++) {
161 int cmp
= ent
.configs
.itemAt(addPos
).compareLogical(*config
);
164 NOISY(printf("*** inserting config: %s\n", config
->toString().string()));
165 ent
.configs
.insertAt(*config
, addPos
);
170 if (addPos
>= ent
.configs
.size()) {
171 NOISY(printf("*** adding config: %s\n", config
->toString().string()));
172 ent
.configs
.add(*config
);
176 const bool first
= vidx
< 0;
177 const bool styled
= (pos
>= 0 && (size_t)pos
< mEntryStyleArray
.size()) ?
178 mEntryStyleArray
[pos
].spans
.size() : 0;
179 if (first
|| styled
|| !mergeDuplicates
) {
180 pos
= mEntryArray
.add(eidx
);
182 vidx
= mValues
.add(value
, pos
);
185 entry
& ent
= mEntries
.editItemAt(eidx
);
186 ent
.indices
.add(pos
);
190 if (ident
.size() > 0) {
191 mIdents
.add(ident
, vidx
);
194 NOISY(printf("Adding string %s to pool: pos=%d eidx=%d vidx=%d\n",
195 String8(value
).string(), pos
, eidx
, vidx
));
200 status_t
StringPool::addStyleSpan(size_t idx
, const String16
& name
,
201 uint32_t start
, uint32_t end
)
203 entry_style_span span
;
205 span
.span
.firstChar
= start
;
206 span
.span
.lastChar
= end
;
207 return addStyleSpan(idx
, span
);
210 status_t
StringPool::addStyleSpans(size_t idx
, const Vector
<entry_style_span
>& spans
)
212 const size_t N
=spans
.size();
213 for (size_t i
=0; i
<N
; i
++) {
214 status_t err
= addStyleSpan(idx
, spans
[i
]);
215 if (err
!= NO_ERROR
) {
222 status_t
StringPool::addStyleSpan(size_t idx
, const entry_style_span
& span
)
224 LOG_ALWAYS_FATAL_IF(mSorted
, "Can't use styles with sorted string pools.");
226 // Place blank entries in the span array up to this index.
227 while (mEntryStyleArray
.size() <= idx
) {
228 mEntryStyleArray
.add();
231 entry_style
& style
= mEntryStyleArray
.editItemAt(idx
);
232 style
.spans
.add(span
);
233 mEntries
.editItemAt(mEntryArray
[idx
]).hasStyles
= true;
237 size_t StringPool::size() const
239 return mSorted
? mValues
.size() : mEntryArray
.size();
242 const StringPool::entry
& StringPool::entryAt(size_t idx
) const
245 return mEntries
[mEntryArray
[idx
]];
247 return mEntries
[mEntryArray
[mValues
.valueAt(idx
)]];
251 size_t StringPool::countIdentifiers() const
253 return mIdents
.size();
256 int StringPool::config_sort(const size_t* lhs
, const size_t* rhs
, void* state
)
258 StringPool
* pool
= (StringPool
*)state
;
259 const entry
& lhe
= pool
->mEntries
[pool
->mEntryArray
[*lhs
]];
260 const entry
& rhe
= pool
->mEntries
[pool
->mEntryArray
[*rhs
]];
261 return lhe
.compare(rhe
);
264 void StringPool::sortByConfig()
266 LOG_ALWAYS_FATAL_IF(mSorted
, "Can't sort string pool containing identifiers.");
267 LOG_ALWAYS_FATAL_IF(mIdents
.size() > 0, "Can't sort string pool containing identifiers.");
268 LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos
.size() > 0, "Can't sort string pool after already sorted.");
270 const size_t N
= mEntryArray
.size();
272 // This is a vector that starts out with a 1:1 mapping to entries
273 // in the array, which we will sort to come up with the desired order.
274 // At that point it maps from the new position in the array to the
275 // original position the entry appeared.
276 Vector
<size_t> newPosToOriginalPos
;
277 for (size_t i
=0; i
<mEntryArray
.size(); i
++) {
278 newPosToOriginalPos
.add(i
);
282 NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n"));
283 newPosToOriginalPos
.sort(config_sort
, this);
284 NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n"));
286 // Create the reverse mapping from the original position in the array
287 // to the new position where it appears in the sorted array. This is
288 // so that clients can re-map any positions they had previously stored.
289 mOriginalPosToNewPos
= newPosToOriginalPos
;
290 for (size_t i
=0; i
<N
; i
++) {
291 mOriginalPosToNewPos
.editItemAt(newPosToOriginalPos
[i
]) = i
;
295 SortedVector
<entry
> entries
;
297 for (size_t i
=0; i
<N
; i
++) {
298 printf("#%d was %d: %s\n", i
, newPosToOriginalPos
[i
],
299 mEntries
[mEntryArray
[newPosToOriginalPos
[i
]]].makeConfigsString().string());
300 entries
.add(mEntries
[mEntryArray
[i
]]);
303 for (size_t i
=0; i
<entries
.size(); i
++) {
304 printf("Sorted config #%d: %s\n", i
,
305 entries
[i
].makeConfigsString().string());
309 // Now we rebuild the arrays.
310 Vector
<entry
> newEntries
;
311 Vector
<size_t> newEntryArray
;
312 Vector
<entry_style
> newEntryStyleArray
;
313 DefaultKeyedVector
<size_t, size_t> origOffsetToNewOffset
;
315 for (size_t i
=0; i
<N
; i
++) {
316 // We are filling in new offset 'i'; oldI is where we can find it
317 // in the original data structure.
318 size_t oldI
= newPosToOriginalPos
[i
];
319 // This is the actual entry associated with the old offset.
320 const entry
& oldEnt
= mEntries
[mEntryArray
[oldI
]];
321 // This is the same entry the last time we added it to the
322 // new entry array, if any.
323 ssize_t newIndexOfOffset
= origOffsetToNewOffset
.indexOfKey(oldI
);
325 if (newIndexOfOffset
< 0) {
326 // This is the first time we have seen the entry, so add
328 newOffset
= newEntries
.add(oldEnt
);
329 newEntries
.editItemAt(newOffset
).indices
.clear();
331 // We have seen this entry before, use the existing one
332 // instead of adding it again.
333 newOffset
= origOffsetToNewOffset
.valueAt(newIndexOfOffset
);
335 // Update the indices to include this new position.
336 newEntries
.editItemAt(newOffset
).indices
.add(i
);
337 // And add the offset of the entry to the new entry array.
338 newEntryArray
.add(newOffset
);
339 // Add any old style to the new style array.
340 if (mEntryStyleArray
.size() > 0) {
341 if (oldI
< mEntryStyleArray
.size()) {
342 newEntryStyleArray
.add(mEntryStyleArray
[oldI
]);
344 newEntryStyleArray
.add(entry_style());
349 // Now trim any entries at the end of the new style array that are
351 for (ssize_t i
=newEntryStyleArray
.size()-1; i
>=0; i
--) {
352 const entry_style
& style
= newEntryStyleArray
[i
];
353 if (style
.spans
.size() > 0) {
357 // This one is not needed; remove.
358 newEntryStyleArray
.removeAt(i
);
361 // All done, install the new data structures and upate mValues with
362 // the new positions.
363 mEntries
= newEntries
;
364 mEntryArray
= newEntryArray
;
365 mEntryStyleArray
= newEntryStyleArray
;
367 for (size_t i
=0; i
<mEntries
.size(); i
++) {
368 const entry
& ent
= mEntries
[i
];
369 mValues
.add(ent
.value
, ent
.indices
[0]);
373 printf("FINAL SORTED STRING CONFIGS:\n");
374 for (size_t i
=0; i
<mEntries
.size(); i
++) {
375 const entry
& ent
= mEntries
[i
];
376 printf("#" ZD
" %s: %s\n", (ZD_TYPE
)i
, ent
.makeConfigsString().string(),
377 String8(ent
.value
).string());
382 sp
<AaptFile
> StringPool::createStringBlock()
384 sp
<AaptFile
> pool
= new AaptFile(String8(), AaptGroupEntry(),
386 status_t err
= writeStringBlock(pool
);
387 return err
== NO_ERROR
? pool
: NULL
;
390 #define ENCODE_LENGTH(str, chrsz, strSize) \
392 size_t maxMask = 1 << ((chrsz*8)-1); \
393 size_t maxSize = maxMask-1; \
394 if (strSize > maxSize) { \
395 *str++ = maxMask | ((strSize>>(chrsz*8))&maxSize); \
400 status_t
StringPool::writeStringBlock(const sp
<AaptFile
>& pool
)
402 // Allow appending. Sorry this is a little wacky.
403 if (pool
->getSize() > 0) {
404 sp
<AaptFile
> block
= createStringBlock();
406 return UNKNOWN_ERROR
;
408 ssize_t res
= pool
->writeData(block
->getData(), block
->getSize());
409 return (res
>= 0) ? (status_t
)NO_ERROR
: res
;
412 // First we need to add all style span names to the string pool.
413 // We do this now (instead of when the span is added) so that these
414 // will appear at the end of the pool, not disrupting the order
415 // our client placed their own strings in it.
417 const size_t STYLES
= mEntryStyleArray
.size();
420 for (i
=0; i
<STYLES
; i
++) {
421 entry_style
& style
= mEntryStyleArray
.editItemAt(i
);
422 const size_t N
= style
.spans
.size();
423 for (size_t i
=0; i
<N
; i
++) {
424 entry_style_span
& span
= style
.spans
.editItemAt(i
);
425 ssize_t idx
= add(span
.name
, true);
427 fprintf(stderr
, "Error adding span for style tag '%s'\n",
428 String8(span
.name
).string());
431 span
.span
.name
.index
= (uint32_t)idx
;
435 const size_t ENTRIES
= size();
437 // Now build the pool of unique strings.
439 const size_t STRINGS
= mEntries
.size();
440 const size_t preSize
= sizeof(ResStringPool_header
)
441 + (sizeof(uint32_t)*ENTRIES
)
442 + (sizeof(uint32_t)*STYLES
);
443 if (pool
->editData(preSize
) == NULL
) {
444 fprintf(stderr
, "ERROR: Out of memory for string pool\n");
448 const size_t charSize
= mUTF8
? sizeof(uint8_t) : sizeof(char16_t);
451 for (i
=0; i
<STRINGS
; i
++) {
452 entry
& ent
= mEntries
.editItemAt(i
);
453 const size_t strSize
= (ent
.value
.size());
454 const size_t lenSize
= strSize
> (size_t)(1<<((charSize
*8)-1))-1 ?
455 charSize
*2 : charSize
;
459 encStr
= String8(ent
.value
);
462 const size_t encSize
= mUTF8
? encStr
.size() : 0;
463 const size_t encLenSize
= mUTF8
?
464 (encSize
> (size_t)(1<<((charSize
*8)-1))-1 ?
465 charSize
*2 : charSize
) : 0;
469 const size_t totalSize
= lenSize
+ encLenSize
+
470 ((mUTF8
? encSize
: strSize
)+1)*charSize
;
472 void* dat
= (void*)pool
->editData(preSize
+ strPos
+ totalSize
);
474 fprintf(stderr
, "ERROR: Out of memory for string pool\n");
477 dat
= (uint8_t*)dat
+ preSize
+ strPos
;
479 uint8_t* strings
= (uint8_t*)dat
;
481 ENCODE_LENGTH(strings
, sizeof(uint8_t), strSize
)
483 ENCODE_LENGTH(strings
, sizeof(uint8_t), encSize
)
485 strncpy((char*)strings
, encStr
, encSize
+1);
487 uint16_t* strings
= (uint16_t*)dat
;
489 ENCODE_LENGTH(strings
, sizeof(uint16_t), strSize
)
491 strcpy16_htod(strings
, ent
.value
);
497 // Pad ending string position up to a uint32_t boundary.
500 size_t padPos
= ((strPos
+3)&~0x3);
501 uint8_t* dat
= (uint8_t*)pool
->editData(preSize
+ padPos
);
503 fprintf(stderr
, "ERROR: Out of memory padding string pool\n");
506 memset(dat
+preSize
+strPos
, 0, padPos
-strPos
);
510 // Build the pool of style spans.
512 size_t styPos
= strPos
;
513 for (i
=0; i
<STYLES
; i
++) {
514 entry_style
& ent
= mEntryStyleArray
.editItemAt(i
);
515 const size_t N
= ent
.spans
.size();
516 const size_t totalSize
= (N
*sizeof(ResStringPool_span
))
517 + sizeof(ResStringPool_ref
);
519 ent
.offset
= styPos
-strPos
;
520 uint8_t* dat
= (uint8_t*)pool
->editData(preSize
+ styPos
+ totalSize
);
522 fprintf(stderr
, "ERROR: Out of memory for string styles\n");
525 ResStringPool_span
* span
= (ResStringPool_span
*)(dat
+preSize
+styPos
);
526 for (size_t i
=0; i
<N
; i
++) {
527 span
->name
.index
= htodl(ent
.spans
[i
].span
.name
.index
);
528 span
->firstChar
= htodl(ent
.spans
[i
].span
.firstChar
);
529 span
->lastChar
= htodl(ent
.spans
[i
].span
.lastChar
);
532 span
->name
.index
= htodl(ResStringPool_span::END
);
538 // Add full terminator at the end (when reading we validate that
539 // the end of the pool is fully terminated to simplify error
541 size_t extra
= sizeof(ResStringPool_span
)-sizeof(ResStringPool_ref
);
542 uint8_t* dat
= (uint8_t*)pool
->editData(preSize
+ styPos
+ extra
);
544 fprintf(stderr
, "ERROR: Out of memory for string styles\n");
547 uint32_t* p
= (uint32_t*)(dat
+preSize
+styPos
);
549 *p
++ = htodl(ResStringPool_span::END
);
550 extra
-= sizeof(uint32_t);
557 ResStringPool_header
* header
=
558 (ResStringPool_header
*)pool
->padData(sizeof(uint32_t));
559 if (header
== NULL
) {
560 fprintf(stderr
, "ERROR: Out of memory for string pool\n");
563 memset(header
, 0, sizeof(*header
));
564 header
->header
.type
= htods(RES_STRING_POOL_TYPE
);
565 header
->header
.headerSize
= htods(sizeof(*header
));
566 header
->header
.size
= htodl(pool
->getSize());
567 header
->stringCount
= htodl(ENTRIES
);
568 header
->styleCount
= htodl(STYLES
);
570 header
->flags
|= htodl(ResStringPool_header::SORTED_FLAG
);
573 header
->flags
|= htodl(ResStringPool_header::UTF8_FLAG
);
575 header
->stringsStart
= htodl(preSize
);
576 header
->stylesStart
= htodl(STYLES
> 0 ? (preSize
+strPos
) : 0);
578 // Write string index array.
580 uint32_t* index
= (uint32_t*)(header
+1);
582 for (i
=0; i
<ENTRIES
; i
++) {
583 entry
& ent
= const_cast<entry
&>(entryAt(i
));
586 *index
++ = htodl(ent
.offset
);
589 for (i
=0; i
<ENTRIES
; i
++) {
590 entry
& ent
= mEntries
.editItemAt(mEntryArray
[i
]);
591 *index
++ = htodl(ent
.offset
);
592 NOISY(printf("Writing entry #%d: \"%s\" ent=%d off=%d\n", i
,
593 String8(ent
.value
).string(),
594 mEntryArray
[i
], ent
.offset
));
598 // Write style index array.
601 for (i
=0; i
<STYLES
; i
++) {
602 LOG_ALWAYS_FATAL("Shouldn't be here!");
605 for (i
=0; i
<STYLES
; i
++) {
606 *index
++ = htodl(mEntryStyleArray
[i
].offset
);
613 ssize_t
StringPool::offsetForString(const String16
& val
) const
615 const Vector
<size_t>* indices
= offsetsForString(val
);
616 ssize_t res
= indices
!= NULL
&& indices
->size() > 0 ? indices
->itemAt(0) : -1;
617 NOISY(printf("Offset for string %s: %d (%s)\n", String8(val
).string(), res
,
618 res
>= 0 ? String8(mEntries
[mEntryArray
[res
]].value
).string() : String8()));
622 const Vector
<size_t>* StringPool::offsetsForString(const String16
& val
) const
624 ssize_t pos
= mValues
.valueFor(val
);
628 return &mEntries
[mEntryArray
[pos
]].indices
;