2 *******************************************************************************
4 * Copyright (C) 2000-2014, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
11 * Modification History:
13 * Date Name Description
14 * 02/21/00 weiv Creation.
15 *******************************************************************************
22 #include "unicode/ures.h"
23 #include "unicode/putil.h"
31 #include "unicode/utf16.h"
33 * Align binary data at a 16-byte offset from the start of the resource bundle,
34 * to be safe for any data type it may contain.
36 #define BIN_ALIGNMENT 16
38 static UBool gIncludeCopyright
= FALSE
;
39 static UBool gUsePoolBundle
= FALSE
;
40 static int32_t gFormatVersion
= 2;
42 static UChar gEmptyString
= 0;
44 /* How do we store string values? */
46 STRINGS_UTF16_V1
, /* formatVersion 1: int length + UChars + NUL + padding to 4 bytes */
47 STRINGS_UTF16_V2
/* formatVersion 2: optional length in 1..3 UChars + UChars + NUL */
51 MAX_IMPLICIT_STRING_LENGTH
= 40 /* do not store the length explicitly for such strings */
55 * res_none() returns the address of kNoResource,
56 * for use in non-error cases when no resource is to be added to the bundle.
57 * (NULL is used in error cases.)
59 static const struct SResource kNoResource
= { URES_NONE
};
61 static UDataInfo dataInfo
= {
70 {0x52, 0x65, 0x73, 0x42}, /* dataFormat="ResB" */
71 {1, 3, 0, 0}, /* formatVersion */
72 {1, 4, 0, 0} /* dataVersion take a look at version inside parsed resb*/
75 static const UVersionInfo gFormatVersions
[3] = { /* indexed by a major-formatVersion integer */
81 static uint8_t calcPadding(uint32_t size
) {
82 /* returns space we need to pad */
83 return (uint8_t) ((size
% sizeof(uint32_t)) ? (sizeof(uint32_t) - (size
% sizeof(uint32_t))) : 0);
87 void setIncludeCopyright(UBool val
){
88 gIncludeCopyright
=val
;
91 UBool
getIncludeCopyright(void){
92 return gIncludeCopyright
;
95 void setFormatVersion(int32_t formatVersion
) {
96 gFormatVersion
= formatVersion
;
99 void setUsePoolBundle(UBool use
) {
100 gUsePoolBundle
= use
;
104 bundle_compactStrings(struct SRBRoot
*bundle
, UErrorCode
*status
);
106 /* Writing Functions */
110 * Find duplicates and count the total number of string code units
111 * so that they can be written first to the 16-bit array,
112 * for minimal string and container storage.
114 * We walk the final parse tree, rather than collecting this information while building it,
115 * so that we need not deal with changes to the parse tree (especially removing resources).
118 res_preflightStrings(struct SRBRoot
*bundle
, struct SResource
*res
, UHashtable
*stringSet
,
122 * type_write16() functions write resource values into f16BitUnits
123 * and determine the resource item word, if possible.
126 res_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
130 * type_preWrite() functions calculate ("preflight") and advance the *byteOffset
131 * by the size of their data in the binary file and
132 * determine the resource item word.
133 * Most type_preWrite() functions may add any number of bytes, but res_preWrite()
134 * will always pad it to a multiple of 4.
135 * The resource item type may be a related subtype of the fType.
137 * The type_preWrite() and type_write() functions start and end at the same
139 * Prewriting allows bundle_write() to determine the root resource item word,
140 * before actually writing the bundle contents to the file,
141 * which is necessary because the root item is stored at the beginning.
144 res_preWrite(uint32_t *byteOffset
,
145 struct SRBRoot
*bundle
, struct SResource
*res
,
149 * type_write() functions write their data to mem and update the byteOffset
151 * (A kingdom for C++ and polymorphism...)
154 res_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
155 struct SRBRoot
*bundle
, struct SResource
*res
,
159 string_preflightStrings(struct SRBRoot
*bundle
, struct SResource
*res
, UHashtable
*stringSet
,
160 UErrorCode
*status
) {
161 res
->u
.fString
.fSame
= uhash_get(stringSet
, res
);
162 if (res
->u
.fString
.fSame
!= NULL
) {
163 return; /* This is a duplicate of an earlier-visited string. */
165 /* Put this string into the set for finding duplicates. */
166 uhash_put(stringSet
, res
, res
, status
);
168 if (bundle
->fStringsForm
!= STRINGS_UTF16_V1
) {
169 const UChar
*s
= res
->u
.fString
.fChars
;
170 int32_t len
= res
->u
.fString
.fLength
;
171 if (len
<= MAX_IMPLICIT_STRING_LENGTH
&& !U16_IS_TRAIL(s
[0]) && len
== u_strlen(s
)) {
173 * This string will be stored without an explicit length.
174 * Runtime will detect !U16_IS_TRAIL(s[0]) and call u_strlen().
176 res
->u
.fString
.fNumCharsForLength
= 0;
177 } else if (len
<= 0x3ee) {
178 res
->u
.fString
.fNumCharsForLength
= 1;
179 } else if (len
<= 0xfffff) {
180 res
->u
.fString
.fNumCharsForLength
= 2;
182 res
->u
.fString
.fNumCharsForLength
= 3;
184 bundle
->f16BitUnitsLength
+= res
->u
.fString
.fNumCharsForLength
+ len
+ 1; /* +1 for the NUL */
189 array_preflightStrings(struct SRBRoot
*bundle
, struct SResource
*res
, UHashtable
*stringSet
,
190 UErrorCode
*status
) {
191 struct SResource
*current
;
193 if (U_FAILURE(*status
)) {
196 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
197 res_preflightStrings(bundle
, current
, stringSet
, status
);
202 table_preflightStrings(struct SRBRoot
*bundle
, struct SResource
*res
, UHashtable
*stringSet
,
203 UErrorCode
*status
) {
204 struct SResource
*current
;
206 if (U_FAILURE(*status
)) {
209 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
210 res_preflightStrings(bundle
, current
, stringSet
, status
);
215 res_preflightStrings(struct SRBRoot
*bundle
, struct SResource
*res
, UHashtable
*stringSet
,
216 UErrorCode
*status
) {
217 if (U_FAILURE(*status
) || res
== NULL
) {
220 if (res
->fRes
!= RES_BOGUS
) {
222 * The resource item word was already precomputed, which means
223 * no further data needs to be written.
224 * This might be an integer, or an empty string/binary/etc.
228 switch (res
->fType
) {
230 string_preflightStrings(bundle
, res
, stringSet
, status
);
233 array_preflightStrings(bundle
, res
, stringSet
, status
);
236 table_preflightStrings(bundle
, res
, stringSet
, status
);
239 /* Neither a string nor a container. */
245 reserve16BitUnits(struct SRBRoot
*bundle
, int32_t length
, UErrorCode
*status
) {
246 if (U_FAILURE(*status
)) {
249 if ((bundle
->f16BitUnitsLength
+ length
) > bundle
->f16BitUnitsCapacity
) {
251 int32_t capacity
= 2 * bundle
->f16BitUnitsCapacity
+ length
+ 1024;
252 capacity
&= ~1; /* ensures padding fits if f16BitUnitsLength needs it */
253 newUnits
= (uint16_t *)uprv_malloc(capacity
* 2);
254 if (newUnits
== NULL
) {
255 *status
= U_MEMORY_ALLOCATION_ERROR
;
258 if (bundle
->f16BitUnitsLength
> 0) {
259 uprv_memcpy(newUnits
, bundle
->f16BitUnits
, bundle
->f16BitUnitsLength
* 2);
262 bundle
->f16BitUnitsLength
= 1;
264 uprv_free(bundle
->f16BitUnits
);
265 bundle
->f16BitUnits
= newUnits
;
266 bundle
->f16BitUnitsCapacity
= capacity
;
268 return bundle
->f16BitUnits
+ bundle
->f16BitUnitsLength
;
272 makeRes16(uint32_t resWord
) {
273 uint32_t type
, offset
;
275 return 0; /* empty string */
277 type
= RES_GET_TYPE(resWord
);
278 offset
= RES_GET_OFFSET(resWord
);
279 if (type
== URES_STRING_V2
&& offset
<= 0xffff) {
280 return (int32_t)offset
;
286 mapKey(struct SRBRoot
*bundle
, int32_t oldpos
) {
287 const KeyMapEntry
*map
= bundle
->fKeyMap
;
288 int32_t i
, start
, limit
;
290 /* do a binary search for the old, pre-bundle_compactKeys() key offset */
291 start
= bundle
->fPoolBundleKeysCount
;
292 limit
= start
+ bundle
->fKeysCount
;
293 while (start
< limit
- 1) {
294 i
= (start
+ limit
) / 2;
295 if (oldpos
< map
[i
].oldpos
) {
301 assert(oldpos
== map
[start
].oldpos
);
302 return map
[start
].newpos
;
306 makeKey16(struct SRBRoot
*bundle
, int32_t key
) {
308 return (uint16_t)key
;
310 return (uint16_t)(key
+ bundle
->fLocalKeyLimit
); /* offset in the pool bundle */
315 * Only called for UTF-16 v1 strings and duplicate UTF-16 v2 strings.
316 * For unique UTF-16 v2 strings, res_write16() sees fRes != RES_BOGUS
320 string_write16(struct SRBRoot
*bundle
, struct SResource
*res
, UErrorCode
*status
) {
321 struct SResource
*same
;
322 if ((same
= res
->u
.fString
.fSame
) != NULL
) {
323 /* This is a duplicate. */
324 assert(same
->fRes
!= RES_BOGUS
&& same
->fWritten
);
325 res
->fRes
= same
->fRes
;
326 res
->fWritten
= same
->fWritten
;
331 array_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
332 UErrorCode
*status
) {
333 struct SResource
*current
;
336 if (U_FAILURE(*status
)) {
339 if (res
->u
.fArray
.fCount
== 0 && gFormatVersion
> 1) {
340 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_ARRAY
);
341 res
->fWritten
= TRUE
;
344 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
345 res_write16(bundle
, current
, status
);
346 res16
|= makeRes16(current
->fRes
);
348 if (U_SUCCESS(*status
) && res
->u
.fArray
.fCount
<= 0xffff && res16
>= 0 && gFormatVersion
> 1) {
349 uint16_t *p16
= reserve16BitUnits(bundle
, 1 + res
->u
.fArray
.fCount
, status
);
350 if (U_SUCCESS(*status
)) {
351 res
->fRes
= URES_MAKE_RESOURCE(URES_ARRAY16
, bundle
->f16BitUnitsLength
);
352 *p16
++ = (uint16_t)res
->u
.fArray
.fCount
;
353 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
354 *p16
++ = (uint16_t)makeRes16(current
->fRes
);
356 bundle
->f16BitUnitsLength
+= 1 + res
->u
.fArray
.fCount
;
357 res
->fWritten
= TRUE
;
363 table_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
364 UErrorCode
*status
) {
365 struct SResource
*current
;
366 int32_t maxKey
= 0, maxPoolKey
= 0x80000000;
368 UBool hasLocalKeys
= FALSE
, hasPoolKeys
= FALSE
;
370 if (U_FAILURE(*status
)) {
373 if (res
->u
.fTable
.fCount
== 0 && gFormatVersion
> 1) {
374 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_TABLE
);
375 res
->fWritten
= TRUE
;
378 /* Find the smallest table type that fits the data. */
379 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
381 res_write16(bundle
, current
, status
);
382 if (bundle
->fKeyMap
== NULL
) {
385 key
= current
->fKey
= mapKey(bundle
, current
->fKey
);
394 if (key
> maxPoolKey
) {
398 res16
|= makeRes16(current
->fRes
);
400 if (U_FAILURE(*status
)) {
403 if(res
->u
.fTable
.fCount
> (uint32_t)bundle
->fMaxTableLength
) {
404 bundle
->fMaxTableLength
= res
->u
.fTable
.fCount
;
406 maxPoolKey
&= 0x7fffffff;
407 if (res
->u
.fTable
.fCount
<= 0xffff &&
408 (!hasLocalKeys
|| maxKey
< bundle
->fLocalKeyLimit
) &&
409 (!hasPoolKeys
|| maxPoolKey
< (0x10000 - bundle
->fLocalKeyLimit
))
411 if (res16
>= 0 && gFormatVersion
> 1) {
412 uint16_t *p16
= reserve16BitUnits(bundle
, 1 + res
->u
.fTable
.fCount
* 2, status
);
413 if (U_SUCCESS(*status
)) {
414 /* 16-bit count, key offsets and values */
415 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE16
, bundle
->f16BitUnitsLength
);
416 *p16
++ = (uint16_t)res
->u
.fTable
.fCount
;
417 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
418 *p16
++ = makeKey16(bundle
, current
->fKey
);
420 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
421 *p16
++ = (uint16_t)makeRes16(current
->fRes
);
423 bundle
->f16BitUnitsLength
+= 1 + res
->u
.fTable
.fCount
* 2;
424 res
->fWritten
= TRUE
;
427 /* 16-bit count, 16-bit key offsets, 32-bit values */
428 res
->u
.fTable
.fType
= URES_TABLE
;
431 /* 32-bit count, key offsets and values */
432 res
->u
.fTable
.fType
= URES_TABLE32
;
437 res_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
438 UErrorCode
*status
) {
439 if (U_FAILURE(*status
) || res
== NULL
) {
442 if (res
->fRes
!= RES_BOGUS
) {
444 * The resource item word was already precomputed, which means
445 * no further data needs to be written.
446 * This might be an integer, or an empty or UTF-16 v2 string,
447 * an empty binary, etc.
451 switch (res
->fType
) {
453 string_write16(bundle
, res
, status
);
456 array_write16(bundle
, res
, status
);
459 table_write16(bundle
, res
, status
);
462 /* Only a few resource types write 16-bit units. */
468 * Only called for UTF-16 v1 strings.
469 * For UTF-16 v2 strings, res_preWrite() sees fRes != RES_BOGUS
473 string_preWrite(uint32_t *byteOffset
,
474 struct SRBRoot
*bundle
, struct SResource
*res
,
475 UErrorCode
*status
) {
476 /* Write the UTF-16 v1 string. */
477 res
->fRes
= URES_MAKE_RESOURCE(URES_STRING
, *byteOffset
>> 2);
478 *byteOffset
+= 4 + (res
->u
.fString
.fLength
+ 1) * U_SIZEOF_UCHAR
;
482 bin_preWrite(uint32_t *byteOffset
,
483 struct SRBRoot
*bundle
, struct SResource
*res
,
484 UErrorCode
*status
) {
486 uint32_t dataStart
= *byteOffset
+ sizeof(res
->u
.fBinaryValue
.fLength
);
488 if (dataStart
% BIN_ALIGNMENT
) {
489 pad
= (BIN_ALIGNMENT
- dataStart
% BIN_ALIGNMENT
);
490 *byteOffset
+= pad
; /* pad == 4 or 8 or 12 */
492 res
->fRes
= URES_MAKE_RESOURCE(URES_BINARY
, *byteOffset
>> 2);
493 *byteOffset
+= 4 + res
->u
.fBinaryValue
.fLength
;
497 array_preWrite(uint32_t *byteOffset
,
498 struct SRBRoot
*bundle
, struct SResource
*res
,
499 UErrorCode
*status
) {
500 struct SResource
*current
;
502 if (U_FAILURE(*status
)) {
505 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
506 res_preWrite(byteOffset
, bundle
, current
, status
);
508 res
->fRes
= URES_MAKE_RESOURCE(URES_ARRAY
, *byteOffset
>> 2);
509 *byteOffset
+= (1 + res
->u
.fArray
.fCount
) * 4;
513 table_preWrite(uint32_t *byteOffset
,
514 struct SRBRoot
*bundle
, struct SResource
*res
,
515 UErrorCode
*status
) {
516 struct SResource
*current
;
518 if (U_FAILURE(*status
)) {
521 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
522 res_preWrite(byteOffset
, bundle
, current
, status
);
524 if (res
->u
.fTable
.fType
== URES_TABLE
) {
525 /* 16-bit count, 16-bit key offsets, 32-bit values */
526 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE
, *byteOffset
>> 2);
527 *byteOffset
+= 2 + res
->u
.fTable
.fCount
* 6;
529 /* 32-bit count, key offsets and values */
530 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE32
, *byteOffset
>> 2);
531 *byteOffset
+= 4 + res
->u
.fTable
.fCount
* 8;
536 res_preWrite(uint32_t *byteOffset
,
537 struct SRBRoot
*bundle
, struct SResource
*res
,
538 UErrorCode
*status
) {
539 if (U_FAILURE(*status
) || res
== NULL
) {
542 if (res
->fRes
!= RES_BOGUS
) {
544 * The resource item word was already precomputed, which means
545 * no further data needs to be written.
546 * This might be an integer, or an empty or UTF-16 v2 string,
547 * an empty binary, etc.
551 switch (res
->fType
) {
553 string_preWrite(byteOffset
, bundle
, res
, status
);
556 res
->fRes
= URES_MAKE_RESOURCE(URES_ALIAS
, *byteOffset
>> 2);
557 *byteOffset
+= 4 + (res
->u
.fString
.fLength
+ 1) * U_SIZEOF_UCHAR
;
559 case URES_INT_VECTOR
:
560 if (res
->u
.fIntVector
.fCount
== 0 && gFormatVersion
> 1) {
561 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_INT_VECTOR
);
562 res
->fWritten
= TRUE
;
564 res
->fRes
= URES_MAKE_RESOURCE(URES_INT_VECTOR
, *byteOffset
>> 2);
565 *byteOffset
+= (1 + res
->u
.fIntVector
.fCount
) * 4;
569 bin_preWrite(byteOffset
, bundle
, res
, status
);
574 array_preWrite(byteOffset
, bundle
, res
, status
);
577 table_preWrite(byteOffset
, bundle
, res
, status
);
580 *status
= U_INTERNAL_PROGRAM_ERROR
;
583 *byteOffset
+= calcPadding(*byteOffset
);
587 * Only called for UTF-16 v1 strings. For UTF-16 v2 strings,
588 * res_write() sees fWritten and exits early.
590 static void string_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
591 struct SRBRoot
*bundle
, struct SResource
*res
,
592 UErrorCode
*status
) {
593 /* Write the UTF-16 v1 string. */
594 int32_t length
= res
->u
.fString
.fLength
;
595 udata_write32(mem
, length
);
596 udata_writeUString(mem
, res
->u
.fString
.fChars
, length
+ 1);
597 *byteOffset
+= 4 + (length
+ 1) * U_SIZEOF_UCHAR
;
598 res
->fWritten
= TRUE
;
601 static void alias_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
602 struct SRBRoot
*bundle
, struct SResource
*res
,
603 UErrorCode
*status
) {
604 int32_t length
= res
->u
.fString
.fLength
;
605 udata_write32(mem
, length
);
606 udata_writeUString(mem
, res
->u
.fString
.fChars
, length
+ 1);
607 *byteOffset
+= 4 + (length
+ 1) * U_SIZEOF_UCHAR
;
610 static void array_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
611 struct SRBRoot
*bundle
, struct SResource
*res
,
612 UErrorCode
*status
) {
615 struct SResource
*current
= NULL
;
617 if (U_FAILURE(*status
)) {
620 for (i
= 0, current
= res
->u
.fArray
.fFirst
; current
!= NULL
; ++i
, current
= current
->fNext
) {
621 res_write(mem
, byteOffset
, bundle
, current
, status
);
623 assert(i
== res
->u
.fArray
.fCount
);
625 udata_write32(mem
, res
->u
.fArray
.fCount
);
626 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
627 udata_write32(mem
, current
->fRes
);
629 *byteOffset
+= (1 + res
->u
.fArray
.fCount
) * 4;
632 static void intvector_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
633 struct SRBRoot
*bundle
, struct SResource
*res
,
634 UErrorCode
*status
) {
636 udata_write32(mem
, res
->u
.fIntVector
.fCount
);
637 for(i
= 0; i
<res
->u
.fIntVector
.fCount
; i
++) {
638 udata_write32(mem
, res
->u
.fIntVector
.fArray
[i
]);
640 *byteOffset
+= (1 + res
->u
.fIntVector
.fCount
) * 4;
643 static void bin_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
644 struct SRBRoot
*bundle
, struct SResource
*res
,
645 UErrorCode
*status
) {
647 uint32_t dataStart
= *byteOffset
+ sizeof(res
->u
.fBinaryValue
.fLength
);
649 if (dataStart
% BIN_ALIGNMENT
) {
650 pad
= (BIN_ALIGNMENT
- dataStart
% BIN_ALIGNMENT
);
651 udata_writePadding(mem
, pad
); /* pad == 4 or 8 or 12 */
655 udata_write32(mem
, res
->u
.fBinaryValue
.fLength
);
656 if (res
->u
.fBinaryValue
.fLength
> 0) {
657 udata_writeBlock(mem
, res
->u
.fBinaryValue
.fData
, res
->u
.fBinaryValue
.fLength
);
659 *byteOffset
+= 4 + res
->u
.fBinaryValue
.fLength
;
662 static void table_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
663 struct SRBRoot
*bundle
, struct SResource
*res
,
664 UErrorCode
*status
) {
665 struct SResource
*current
;
668 if (U_FAILURE(*status
)) {
671 for (i
= 0, current
= res
->u
.fTable
.fFirst
; current
!= NULL
; ++i
, current
= current
->fNext
) {
672 assert(i
< res
->u
.fTable
.fCount
);
673 res_write(mem
, byteOffset
, bundle
, current
, status
);
675 assert(i
== res
->u
.fTable
.fCount
);
677 if(res
->u
.fTable
.fType
== URES_TABLE
) {
678 udata_write16(mem
, (uint16_t)res
->u
.fTable
.fCount
);
679 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
680 udata_write16(mem
, makeKey16(bundle
, current
->fKey
));
682 *byteOffset
+= (1 + res
->u
.fTable
.fCount
)* 2;
683 if ((res
->u
.fTable
.fCount
& 1) == 0) {
684 /* 16-bit count and even number of 16-bit key offsets need padding before 32-bit resource items */
685 udata_writePadding(mem
, 2);
688 } else /* URES_TABLE32 */ {
689 udata_write32(mem
, res
->u
.fTable
.fCount
);
690 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
691 udata_write32(mem
, (uint32_t)current
->fKey
);
693 *byteOffset
+= (1 + res
->u
.fTable
.fCount
)* 4;
695 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
696 udata_write32(mem
, current
->fRes
);
698 *byteOffset
+= res
->u
.fTable
.fCount
* 4;
701 void res_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
702 struct SRBRoot
*bundle
, struct SResource
*res
,
703 UErrorCode
*status
) {
706 if (U_FAILURE(*status
) || res
== NULL
) {
710 assert(res
->fRes
!= RES_BOGUS
);
713 switch (res
->fType
) {
715 string_write (mem
, byteOffset
, bundle
, res
, status
);
718 alias_write (mem
, byteOffset
, bundle
, res
, status
);
720 case URES_INT_VECTOR
:
721 intvector_write (mem
, byteOffset
, bundle
, res
, status
);
724 bin_write (mem
, byteOffset
, bundle
, res
, status
);
727 break; /* fRes was set by int_open() */
729 array_write (mem
, byteOffset
, bundle
, res
, status
);
732 table_write (mem
, byteOffset
, bundle
, res
, status
);
735 *status
= U_INTERNAL_PROGRAM_ERROR
;
738 paddingSize
= calcPadding(*byteOffset
);
739 if (paddingSize
> 0) {
740 udata_writePadding(mem
, paddingSize
);
741 *byteOffset
+= paddingSize
;
743 res
->fWritten
= TRUE
;
746 void bundle_write(struct SRBRoot
*bundle
,
747 const char *outputDir
, const char *outputPkg
,
748 char *writtenFilename
, int writtenFilenameLen
,
749 UErrorCode
*status
) {
750 UNewDataMemory
*mem
= NULL
;
751 uint32_t byteOffset
= 0;
754 int32_t indexes
[URES_INDEX_TOP
];
756 bundle_compactKeys(bundle
, status
);
758 * Add padding bytes to fKeys so that fKeysTop is 4-aligned.
759 * Safe because the capacity is a multiple of 4.
761 while (bundle
->fKeysTop
& 3) {
762 bundle
->fKeys
[bundle
->fKeysTop
++] = (char)0xaa;
765 * In URES_TABLE, use all local key offsets that fit into 16 bits,
766 * and use the remaining 16-bit offsets for pool key offsets
768 * If there are no local keys, then use the whole 16-bit space
769 * for pool key offsets.
770 * Note: This cannot be changed without changing the major formatVersion.
772 if (bundle
->fKeysBottom
< bundle
->fKeysTop
) {
773 if (bundle
->fKeysTop
<= 0x10000) {
774 bundle
->fLocalKeyLimit
= bundle
->fKeysTop
;
776 bundle
->fLocalKeyLimit
= 0x10000;
779 bundle
->fLocalKeyLimit
= 0;
782 bundle_compactStrings(bundle
, status
);
783 res_write16(bundle
, bundle
->fRoot
, status
);
784 if (bundle
->f16BitUnitsLength
& 1) {
785 bundle
->f16BitUnits
[bundle
->f16BitUnitsLength
++] = 0xaaaa; /* pad to multiple of 4 bytes */
787 /* all keys have been mapped */
788 uprv_free(bundle
->fKeyMap
);
789 bundle
->fKeyMap
= NULL
;
791 byteOffset
= bundle
->fKeysTop
+ bundle
->f16BitUnitsLength
* 2;
792 res_preWrite(&byteOffset
, bundle
, bundle
->fRoot
, status
);
794 /* total size including the root item */
797 if (U_FAILURE(*status
)) {
801 if (writtenFilename
&& writtenFilenameLen
) {
802 *writtenFilename
= 0;
805 if (writtenFilename
) {
806 int32_t off
= 0, len
= 0;
808 len
= (int32_t)uprv_strlen(outputDir
);
809 if (len
> writtenFilenameLen
) {
810 len
= writtenFilenameLen
;
812 uprv_strncpy(writtenFilename
, outputDir
, len
);
814 if (writtenFilenameLen
-= len
) {
816 writtenFilename
[off
] = U_FILE_SEP_CHAR
;
817 if (--writtenFilenameLen
) {
819 if(outputPkg
!= NULL
)
821 uprv_strcpy(writtenFilename
+off
, outputPkg
);
822 off
+= (int32_t)uprv_strlen(outputPkg
);
823 writtenFilename
[off
] = '_';
827 len
= (int32_t)uprv_strlen(bundle
->fLocale
);
828 if (len
> writtenFilenameLen
) {
829 len
= writtenFilenameLen
;
831 uprv_strncpy(writtenFilename
+ off
, bundle
->fLocale
, len
);
832 if (writtenFilenameLen
-= len
) {
835 if (len
> writtenFilenameLen
) {
836 len
= writtenFilenameLen
;
838 uprv_strncpy(writtenFilename
+ off
, ".res", len
);
846 uprv_strcpy(dataName
, outputPkg
);
847 uprv_strcat(dataName
, "_");
848 uprv_strcat(dataName
, bundle
->fLocale
);
852 uprv_strcpy(dataName
, bundle
->fLocale
);
855 uprv_memcpy(dataInfo
.formatVersion
, gFormatVersions
+ gFormatVersion
, sizeof(UVersionInfo
));
857 mem
= udata_create(outputDir
, "res", dataName
, &dataInfo
, (gIncludeCopyright
==TRUE
)? U_COPYRIGHT_STRING
:NULL
, status
);
858 if(U_FAILURE(*status
)){
862 /* write the root item */
863 udata_write32(mem
, bundle
->fRoot
->fRes
);
866 * formatVersion 1.1 (ICU 2.8):
867 * write int32_t indexes[] after root and before the strings
868 * to make it easier to parse resource bundles in icuswap or from Java etc.
870 uprv_memset(indexes
, 0, sizeof(indexes
));
871 indexes
[URES_INDEX_LENGTH
]= bundle
->fIndexLength
;
872 indexes
[URES_INDEX_KEYS_TOP
]= bundle
->fKeysTop
>>2;
873 indexes
[URES_INDEX_RESOURCES_TOP
]= (int32_t)(top
>>2);
874 indexes
[URES_INDEX_BUNDLE_TOP
]= indexes
[URES_INDEX_RESOURCES_TOP
];
875 indexes
[URES_INDEX_MAX_TABLE_LENGTH
]= bundle
->fMaxTableLength
;
878 * formatVersion 1.2 (ICU 3.6):
879 * write indexes[URES_INDEX_ATTRIBUTES] with URES_ATT_NO_FALLBACK set or not set
880 * the memset() above initialized all indexes[] to 0
882 if (bundle
->noFallback
) {
883 indexes
[URES_INDEX_ATTRIBUTES
]=URES_ATT_NO_FALLBACK
;
886 * formatVersion 2.0 (ICU 4.4):
887 * more compact string value storage, optional pool bundle
889 if (URES_INDEX_16BIT_TOP
< bundle
->fIndexLength
) {
890 indexes
[URES_INDEX_16BIT_TOP
] = (bundle
->fKeysTop
>>2) + (bundle
->f16BitUnitsLength
>>1);
892 if (URES_INDEX_POOL_CHECKSUM
< bundle
->fIndexLength
) {
893 if (bundle
->fIsPoolBundle
) {
894 indexes
[URES_INDEX_ATTRIBUTES
] |= URES_ATT_IS_POOL_BUNDLE
| URES_ATT_NO_FALLBACK
;
895 indexes
[URES_INDEX_POOL_CHECKSUM
] =
896 (int32_t)computeCRC((char *)(bundle
->fKeys
+ bundle
->fKeysBottom
),
897 (uint32_t)(bundle
->fKeysTop
- bundle
->fKeysBottom
),
899 } else if (gUsePoolBundle
) {
900 indexes
[URES_INDEX_ATTRIBUTES
] |= URES_ATT_USES_POOL_BUNDLE
;
901 indexes
[URES_INDEX_POOL_CHECKSUM
] = bundle
->fPoolChecksum
;
905 /* write the indexes[] */
906 udata_writeBlock(mem
, indexes
, bundle
->fIndexLength
*4);
908 /* write the table key strings */
909 udata_writeBlock(mem
, bundle
->fKeys
+bundle
->fKeysBottom
,
910 bundle
->fKeysTop
-bundle
->fKeysBottom
);
912 /* write the v2 UTF-16 strings, URES_TABLE16 and URES_ARRAY16 */
913 udata_writeBlock(mem
, bundle
->f16BitUnits
, bundle
->f16BitUnitsLength
*2);
915 /* write all of the bundle contents: the root item and its children */
916 byteOffset
= bundle
->fKeysTop
+ bundle
->f16BitUnitsLength
* 2;
917 res_write(mem
, &byteOffset
, bundle
, bundle
->fRoot
, status
);
918 assert(byteOffset
== top
);
920 size
= udata_finish(mem
, status
);
922 fprintf(stderr
, "genrb error: wrote %u bytes but counted %u\n",
923 (int)size
, (int)top
);
924 *status
= U_INTERNAL_PROGRAM_ERROR
;
928 /* Opening Functions */
930 /* gcc 4.2 complained "no previous prototype for res_open" without this prototype... */
931 struct SResource
* res_open(struct SRBRoot
*bundle
, const char *tag
,
932 const struct UString
* comment
, UErrorCode
* status
);
934 struct SResource
* res_open(struct SRBRoot
*bundle
, const char *tag
,
935 const struct UString
* comment
, UErrorCode
* status
){
936 struct SResource
*res
;
937 int32_t key
= bundle_addtag(bundle
, tag
, status
);
938 if (U_FAILURE(*status
)) {
942 res
= (struct SResource
*) uprv_malloc(sizeof(struct SResource
));
944 *status
= U_MEMORY_ALLOCATION_ERROR
;
947 uprv_memset(res
, 0, sizeof(struct SResource
));
949 res
->fRes
= RES_BOGUS
;
951 ustr_init(&res
->fComment
);
953 ustr_cpy(&res
->fComment
, comment
, status
);
954 if (U_FAILURE(*status
)) {
962 struct SResource
* res_none() {
963 return (struct SResource
*)&kNoResource
;
966 struct SResource
* table_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
967 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
968 if (U_FAILURE(*status
)) {
971 res
->fType
= URES_TABLE
;
972 res
->u
.fTable
.fRoot
= bundle
;
976 struct SResource
* array_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
977 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
978 if (U_FAILURE(*status
)) {
981 res
->fType
= URES_ARRAY
;
985 static int32_t U_CALLCONV
986 string_hash(const UElement key
) {
987 const struct SResource
*res
= (struct SResource
*)key
.pointer
;
988 return ustr_hashUCharsN(res
->u
.fString
.fChars
, res
->u
.fString
.fLength
);
991 static UBool U_CALLCONV
992 string_comp(const UElement key1
, const UElement key2
) {
993 const struct SResource
*res1
= (struct SResource
*)key1
.pointer
;
994 const struct SResource
*res2
= (struct SResource
*)key2
.pointer
;
995 return 0 == u_strCompare(res1
->u
.fString
.fChars
, res1
->u
.fString
.fLength
,
996 res2
->u
.fString
.fChars
, res2
->u
.fString
.fLength
,
1000 static struct SResource
*
1001 stringbase_open(struct SRBRoot
*bundle
, const char *tag
, int8_t type
,
1002 const UChar
*value
, int32_t len
, const struct UString
* comment
,
1003 UErrorCode
*status
) {
1004 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1005 if (U_FAILURE(*status
)) {
1010 if (len
== 0 && gFormatVersion
> 1) {
1011 res
->u
.fString
.fChars
= &gEmptyString
;
1012 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(type
);
1013 res
->fWritten
= TRUE
;
1017 res
->u
.fString
.fLength
= len
;
1018 res
->u
.fString
.fChars
= (UChar
*) uprv_malloc(sizeof(UChar
) * (len
+ 1));
1019 if (res
->u
.fString
.fChars
== NULL
) {
1020 *status
= U_MEMORY_ALLOCATION_ERROR
;
1024 uprv_memcpy(res
->u
.fString
.fChars
, value
, sizeof(UChar
) * len
);
1025 res
->u
.fString
.fChars
[len
] = 0;
1029 struct SResource
*string_open(struct SRBRoot
*bundle
, const char *tag
, const UChar
*value
, int32_t len
, const struct UString
* comment
, UErrorCode
*status
) {
1030 return stringbase_open(bundle
, tag
, URES_STRING
, value
, len
, comment
, status
);
1033 struct SResource
*alias_open(struct SRBRoot
*bundle
, const char *tag
, UChar
*value
, int32_t len
, const struct UString
* comment
, UErrorCode
*status
) {
1034 return stringbase_open(bundle
, tag
, URES_ALIAS
, value
, len
, comment
, status
);
1038 struct SResource
* intvector_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
1039 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1040 if (U_FAILURE(*status
)) {
1043 res
->fType
= URES_INT_VECTOR
;
1045 res
->u
.fIntVector
.fCount
= 0;
1046 res
->u
.fIntVector
.fArray
= (uint32_t *) uprv_malloc(sizeof(uint32_t) * RESLIST_MAX_INT_VECTOR
);
1047 if (res
->u
.fIntVector
.fArray
== NULL
) {
1048 *status
= U_MEMORY_ALLOCATION_ERROR
;
1055 struct SResource
*int_open(struct SRBRoot
*bundle
, const char *tag
, int32_t value
, const struct UString
* comment
, UErrorCode
*status
) {
1056 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1057 if (U_FAILURE(*status
)) {
1060 res
->fType
= URES_INT
;
1061 res
->u
.fIntValue
.fValue
= value
;
1062 res
->fRes
= URES_MAKE_RESOURCE(URES_INT
, value
& 0x0FFFFFFF);
1063 res
->fWritten
= TRUE
;
1067 struct SResource
*bin_open(struct SRBRoot
*bundle
, const char *tag
, uint32_t length
, uint8_t *data
, const char* fileName
, const struct UString
* comment
, UErrorCode
*status
) {
1068 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1069 if (U_FAILURE(*status
)) {
1072 res
->fType
= URES_BINARY
;
1074 res
->u
.fBinaryValue
.fLength
= length
;
1075 res
->u
.fBinaryValue
.fFileName
= NULL
;
1076 if(fileName
!=NULL
&& uprv_strcmp(fileName
, "") !=0){
1077 res
->u
.fBinaryValue
.fFileName
= (char*) uprv_malloc(sizeof(char) * (uprv_strlen(fileName
)+1));
1078 uprv_strcpy(res
->u
.fBinaryValue
.fFileName
,fileName
);
1081 res
->u
.fBinaryValue
.fData
= (uint8_t *) uprv_malloc(sizeof(uint8_t) * length
);
1083 if (res
->u
.fBinaryValue
.fData
== NULL
) {
1084 *status
= U_MEMORY_ALLOCATION_ERROR
;
1089 uprv_memcpy(res
->u
.fBinaryValue
.fData
, data
, length
);
1092 res
->u
.fBinaryValue
.fData
= NULL
;
1093 if (gFormatVersion
> 1) {
1094 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_BINARY
);
1095 res
->fWritten
= TRUE
;
1102 struct SRBRoot
*bundle_open(const struct UString
* comment
, UBool isPoolBundle
, UErrorCode
*status
) {
1103 struct SRBRoot
*bundle
;
1105 if (U_FAILURE(*status
)) {
1109 bundle
= (struct SRBRoot
*) uprv_malloc(sizeof(struct SRBRoot
));
1110 if (bundle
== NULL
) {
1111 *status
= U_MEMORY_ALLOCATION_ERROR
;
1114 uprv_memset(bundle
, 0, sizeof(struct SRBRoot
));
1116 bundle
->fKeys
= (char *) uprv_malloc(sizeof(char) * KEY_SPACE_SIZE
);
1117 bundle
->fRoot
= table_open(bundle
, NULL
, comment
, status
);
1118 if (bundle
->fKeys
== NULL
|| bundle
->fRoot
== NULL
|| U_FAILURE(*status
)) {
1119 if (U_SUCCESS(*status
)) {
1120 *status
= U_MEMORY_ALLOCATION_ERROR
;
1122 bundle_close(bundle
, status
);
1126 bundle
->fLocale
= NULL
;
1127 bundle
->fKeysCapacity
= KEY_SPACE_SIZE
;
1128 /* formatVersion 1.1: start fKeysTop after the root item and indexes[] */
1129 bundle
->fIsPoolBundle
= isPoolBundle
;
1130 if (gUsePoolBundle
|| isPoolBundle
) {
1131 bundle
->fIndexLength
= URES_INDEX_POOL_CHECKSUM
+ 1;
1132 } else if (gFormatVersion
>= 2) {
1133 bundle
->fIndexLength
= URES_INDEX_16BIT_TOP
+ 1;
1134 } else /* formatVersion 1 */ {
1135 bundle
->fIndexLength
= URES_INDEX_ATTRIBUTES
+ 1;
1137 bundle
->fKeysBottom
= (1 /* root */ + bundle
->fIndexLength
) * 4;
1138 uprv_memset(bundle
->fKeys
, 0, bundle
->fKeysBottom
);
1139 bundle
->fKeysTop
= bundle
->fKeysBottom
;
1141 if (gFormatVersion
== 1) {
1142 bundle
->fStringsForm
= STRINGS_UTF16_V1
;
1144 bundle
->fStringsForm
= STRINGS_UTF16_V2
;
1150 /* Closing Functions */
1151 static void table_close(struct SResource
*table
) {
1152 struct SResource
*current
= NULL
;
1153 struct SResource
*prev
= NULL
;
1155 current
= table
->u
.fTable
.fFirst
;
1157 while (current
!= NULL
) {
1159 current
= current
->fNext
;
1164 table
->u
.fTable
.fFirst
= NULL
;
1167 static void array_close(struct SResource
*array
) {
1168 struct SResource
*current
= NULL
;
1169 struct SResource
*prev
= NULL
;
1174 current
= array
->u
.fArray
.fFirst
;
1176 while (current
!= NULL
) {
1178 current
= current
->fNext
;
1182 array
->u
.fArray
.fFirst
= NULL
;
1185 static void string_close(struct SResource
*string
) {
1186 if (string
->u
.fString
.fChars
!= NULL
&&
1187 string
->u
.fString
.fChars
!= &gEmptyString
) {
1188 uprv_free(string
->u
.fString
.fChars
);
1189 string
->u
.fString
.fChars
=NULL
;
1193 static void alias_close(struct SResource
*alias
) {
1194 if (alias
->u
.fString
.fChars
!= NULL
) {
1195 uprv_free(alias
->u
.fString
.fChars
);
1196 alias
->u
.fString
.fChars
=NULL
;
1200 static void intvector_close(struct SResource
*intvector
) {
1201 if (intvector
->u
.fIntVector
.fArray
!= NULL
) {
1202 uprv_free(intvector
->u
.fIntVector
.fArray
);
1203 intvector
->u
.fIntVector
.fArray
=NULL
;
1207 static void int_close(struct SResource
*intres
) {
1208 /* Intentionally left blank */
1211 static void bin_close(struct SResource
*binres
) {
1212 if (binres
->u
.fBinaryValue
.fData
!= NULL
) {
1213 uprv_free(binres
->u
.fBinaryValue
.fData
);
1214 binres
->u
.fBinaryValue
.fData
= NULL
;
1216 if (binres
->u
.fBinaryValue
.fFileName
!= NULL
) {
1217 uprv_free(binres
->u
.fBinaryValue
.fFileName
);
1218 binres
->u
.fBinaryValue
.fFileName
= NULL
;
1222 void res_close(struct SResource
*res
) {
1224 switch(res
->fType
) {
1231 case URES_INT_VECTOR
:
1232 intvector_close(res
);
1247 /* Shouldn't happen */
1251 ustr_deinit(&res
->fComment
);
1256 void bundle_close(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1257 res_close(bundle
->fRoot
);
1258 uprv_free(bundle
->fLocale
);
1259 uprv_free(bundle
->fKeys
);
1260 uprv_free(bundle
->fKeyMap
);
1261 uprv_free(bundle
->f16BitUnits
);
1265 /* Adding Functions */
1266 void table_add(struct SResource
*table
, struct SResource
*res
, int linenumber
, UErrorCode
*status
) {
1267 struct SResource
*current
= NULL
;
1268 struct SResource
*prev
= NULL
;
1269 struct SResTable
*list
;
1270 const char *resKeyString
;
1272 if (U_FAILURE(*status
)) {
1275 if (res
== &kNoResource
) {
1279 /* remember this linenumber to report to the user if there is a duplicate key */
1280 res
->line
= linenumber
;
1282 /* here we need to traverse the list */
1283 list
= &(table
->u
.fTable
);
1286 /* is list still empty? */
1287 if (list
->fFirst
== NULL
) {
1293 resKeyString
= list
->fRoot
->fKeys
+ res
->fKey
;
1295 current
= list
->fFirst
;
1297 while (current
!= NULL
) {
1298 const char *currentKeyString
= list
->fRoot
->fKeys
+ current
->fKey
;
1301 * formatVersion 1: compare key strings in native-charset order
1302 * formatVersion 2 and up: compare key strings in ASCII order
1304 if (gFormatVersion
== 1 || U_CHARSET_FAMILY
== U_ASCII_FAMILY
) {
1305 diff
= uprv_strcmp(currentKeyString
, resKeyString
);
1307 diff
= uprv_compareInvCharsAsAscii(currentKeyString
, resKeyString
);
1311 current
= current
->fNext
;
1312 } else if (diff
> 0) {
1313 /* we're either in front of list, or in middle */
1315 /* front of the list */
1318 /* middle of the list */
1322 res
->fNext
= current
;
1325 /* Key already exists! ERROR! */
1326 error(linenumber
, "duplicate key '%s' in table, first appeared at line %d", currentKeyString
, current
->line
);
1327 *status
= U_UNSUPPORTED_ERROR
;
1337 void array_add(struct SResource
*array
, struct SResource
*res
, UErrorCode
*status
) {
1338 if (U_FAILURE(*status
)) {
1342 if (array
->u
.fArray
.fFirst
== NULL
) {
1343 array
->u
.fArray
.fFirst
= res
;
1344 array
->u
.fArray
.fLast
= res
;
1346 array
->u
.fArray
.fLast
->fNext
= res
;
1347 array
->u
.fArray
.fLast
= res
;
1350 (array
->u
.fArray
.fCount
)++;
1353 void intvector_add(struct SResource
*intvector
, int32_t value
, UErrorCode
*status
) {
1354 if (U_FAILURE(*status
)) {
1358 *(intvector
->u
.fIntVector
.fArray
+ intvector
->u
.fIntVector
.fCount
) = value
;
1359 intvector
->u
.fIntVector
.fCount
++;
1362 /* Misc Functions */
1364 void bundle_setlocale(struct SRBRoot
*bundle
, UChar
*locale
, UErrorCode
*status
) {
1366 if(U_FAILURE(*status
)) {
1370 if (bundle
->fLocale
!=NULL
) {
1371 uprv_free(bundle
->fLocale
);
1374 bundle
->fLocale
= (char*) uprv_malloc(sizeof(char) * (u_strlen(locale
)+1));
1376 if(bundle
->fLocale
== NULL
) {
1377 *status
= U_MEMORY_ALLOCATION_ERROR
;
1381 /*u_strcpy(bundle->fLocale, locale);*/
1382 u_UCharsToChars(locale
, bundle
->fLocale
, u_strlen(locale
)+1);
1387 getKeyString(const struct SRBRoot
*bundle
, int32_t key
) {
1389 return bundle
->fPoolBundleKeys
+ (key
& 0x7fffffff);
1391 return bundle
->fKeys
+ key
;
1396 res_getKeyString(const struct SRBRoot
*bundle
, const struct SResource
*res
, char temp
[8]) {
1397 if (res
->fKey
== -1) {
1400 return getKeyString(bundle
, res
->fKey
);
1404 bundle_getKeyBytes(struct SRBRoot
*bundle
, int32_t *pLength
) {
1405 *pLength
= bundle
->fKeysTop
- bundle
->fKeysBottom
;
1406 return bundle
->fKeys
+ bundle
->fKeysBottom
;
1410 bundle_addKeyBytes(struct SRBRoot
*bundle
, const char *keyBytes
, int32_t length
, UErrorCode
*status
) {
1413 if (U_FAILURE(*status
)) {
1416 if (length
< 0 || (keyBytes
== NULL
&& length
!= 0)) {
1417 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1421 return bundle
->fKeysTop
;
1424 keypos
= bundle
->fKeysTop
;
1425 bundle
->fKeysTop
+= length
;
1426 if (bundle
->fKeysTop
>= bundle
->fKeysCapacity
) {
1427 /* overflow - resize the keys buffer */
1428 bundle
->fKeysCapacity
+= KEY_SPACE_SIZE
;
1429 bundle
->fKeys
= uprv_realloc(bundle
->fKeys
, bundle
->fKeysCapacity
);
1430 if(bundle
->fKeys
== NULL
) {
1431 *status
= U_MEMORY_ALLOCATION_ERROR
;
1436 uprv_memcpy(bundle
->fKeys
+ keypos
, keyBytes
, length
);
1442 bundle_addtag(struct SRBRoot
*bundle
, const char *tag
, UErrorCode
*status
) {
1445 if (U_FAILURE(*status
)) {
1450 /* no error: the root table and array items have no keys */
1454 keypos
= bundle_addKeyBytes(bundle
, tag
, (int32_t)(uprv_strlen(tag
) + 1), status
);
1455 if (U_SUCCESS(*status
)) {
1456 ++bundle
->fKeysCount
;
1462 compareInt32(int32_t lPos
, int32_t rPos
) {
1464 * Compare possibly-negative key offsets. Don't just return lPos - rPos
1465 * because that is prone to negative-integer underflows.
1469 } else if (lPos
> rPos
) {
1476 static int32_t U_CALLCONV
1477 compareKeySuffixes(const void *context
, const void *l
, const void *r
) {
1478 const struct SRBRoot
*bundle
=(const struct SRBRoot
*)context
;
1479 int32_t lPos
= ((const KeyMapEntry
*)l
)->oldpos
;
1480 int32_t rPos
= ((const KeyMapEntry
*)r
)->oldpos
;
1481 const char *lStart
= getKeyString(bundle
, lPos
);
1482 const char *lLimit
= lStart
;
1483 const char *rStart
= getKeyString(bundle
, rPos
);
1484 const char *rLimit
= rStart
;
1486 while (*lLimit
!= 0) { ++lLimit
; }
1487 while (*rLimit
!= 0) { ++rLimit
; }
1488 /* compare keys in reverse character order */
1489 while (lStart
< lLimit
&& rStart
< rLimit
) {
1490 diff
= (int32_t)(uint8_t)*--lLimit
- (int32_t)(uint8_t)*--rLimit
;
1495 /* sort equal suffixes by descending key length */
1496 diff
= (int32_t)(rLimit
- rStart
) - (int32_t)(lLimit
- lStart
);
1500 /* Sort pool bundle keys first (negative oldpos), and otherwise keys in parsing order. */
1501 return compareInt32(lPos
, rPos
);
1504 static int32_t U_CALLCONV
1505 compareKeyNewpos(const void *context
, const void *l
, const void *r
) {
1506 return compareInt32(((const KeyMapEntry
*)l
)->newpos
, ((const KeyMapEntry
*)r
)->newpos
);
1509 static int32_t U_CALLCONV
1510 compareKeyOldpos(const void *context
, const void *l
, const void *r
) {
1511 return compareInt32(((const KeyMapEntry
*)l
)->oldpos
, ((const KeyMapEntry
*)r
)->oldpos
);
1515 bundle_compactKeys(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1519 int32_t keysCount
= bundle
->fPoolBundleKeysCount
+ bundle
->fKeysCount
;
1520 if (U_FAILURE(*status
) || bundle
->fKeysCount
== 0 || bundle
->fKeyMap
!= NULL
) {
1523 map
= (KeyMapEntry
*)uprv_malloc(keysCount
* sizeof(KeyMapEntry
));
1525 *status
= U_MEMORY_ALLOCATION_ERROR
;
1528 keys
= (char *)bundle
->fPoolBundleKeys
;
1529 for (i
= 0; i
< bundle
->fPoolBundleKeysCount
; ++i
) {
1531 (int32_t)(keys
- bundle
->fPoolBundleKeys
) | 0x80000000; /* negative oldpos */
1533 while (*keys
!= 0) { ++keys
; } /* skip the key */
1534 ++keys
; /* skip the NUL */
1536 keys
= bundle
->fKeys
+ bundle
->fKeysBottom
;
1537 for (; i
< keysCount
; ++i
) {
1538 map
[i
].oldpos
= (int32_t)(keys
- bundle
->fKeys
);
1540 while (*keys
!= 0) { ++keys
; } /* skip the key */
1541 ++keys
; /* skip the NUL */
1543 /* Sort the keys so that each one is immediately followed by all of its suffixes. */
1544 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1545 compareKeySuffixes
, bundle
, FALSE
, status
);
1547 * Make suffixes point into earlier, longer strings that contain them
1548 * and mark the old, now unused suffix bytes as deleted.
1550 if (U_SUCCESS(*status
)) {
1551 keys
= bundle
->fKeys
;
1552 for (i
= 0; i
< keysCount
;) {
1554 * This key is not a suffix of the previous one;
1555 * keep this one and delete the following ones that are
1556 * suffixes of this one.
1559 const char *keyLimit
;
1561 map
[i
].newpos
= map
[i
].oldpos
;
1562 if (j
< keysCount
&& map
[j
].oldpos
< 0) {
1563 /* Key string from the pool bundle, do not delete. */
1567 key
= getKeyString(bundle
, map
[i
].oldpos
);
1568 for (keyLimit
= key
; *keyLimit
!= 0; ++keyLimit
) {}
1569 for (; j
< keysCount
&& map
[j
].oldpos
>= 0; ++j
) {
1572 const char *suffixLimit
;
1574 suffix
= keys
+ map
[j
].oldpos
;
1575 for (suffixLimit
= suffix
; *suffixLimit
!= 0; ++suffixLimit
) {}
1576 offset
= (int32_t)(keyLimit
- key
) - (suffixLimit
- suffix
);
1578 break; /* suffix cannot be longer than the original */
1580 /* Is it a suffix of the earlier, longer key? */
1581 for (k
= keyLimit
; suffix
< suffixLimit
&& *--k
== *--suffixLimit
;) {}
1582 if (suffix
== suffixLimit
&& *k
== *suffixLimit
) {
1583 map
[j
].newpos
= map
[i
].oldpos
+ offset
; /* yes, point to the earlier key */
1584 /* mark the suffix as deleted */
1585 while (*suffix
!= 0) { *suffix
++ = 1; }
1588 break; /* not a suffix, restart from here */
1594 * Re-sort by newpos, then modify the key characters array in-place
1595 * to squeeze out unused bytes, and readjust the newpos offsets.
1597 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1598 compareKeyNewpos
, NULL
, FALSE
, status
);
1599 if (U_SUCCESS(*status
)) {
1600 int32_t oldpos
, newpos
, limit
;
1601 oldpos
= newpos
= bundle
->fKeysBottom
;
1602 limit
= bundle
->fKeysTop
;
1603 /* skip key offsets that point into the pool bundle rather than this new bundle */
1604 for (i
= 0; i
< keysCount
&& map
[i
].newpos
< 0; ++i
) {}
1605 if (i
< keysCount
) {
1606 while (oldpos
< limit
) {
1607 if (keys
[oldpos
] == 1) {
1608 ++oldpos
; /* skip unused bytes */
1610 /* adjust the new offsets for keys starting here */
1611 while (i
< keysCount
&& map
[i
].newpos
== oldpos
) {
1612 map
[i
++].newpos
= newpos
;
1614 /* move the key characters to their new position */
1615 keys
[newpos
++] = keys
[oldpos
++];
1618 assert(i
== keysCount
);
1620 bundle
->fKeysTop
= newpos
;
1621 /* Re-sort once more, by old offsets for binary searching. */
1622 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1623 compareKeyOldpos
, NULL
, FALSE
, status
);
1624 if (U_SUCCESS(*status
)) {
1625 /* key size reduction by limit - newpos */
1626 bundle
->fKeyMap
= map
;
1634 static int32_t U_CALLCONV
1635 compareStringSuffixes(const void *context
, const void *l
, const void *r
) {
1636 struct SResource
*left
= *((struct SResource
**)l
);
1637 struct SResource
*right
= *((struct SResource
**)r
);
1638 const UChar
*lStart
= left
->u
.fString
.fChars
;
1639 const UChar
*lLimit
= lStart
+ left
->u
.fString
.fLength
;
1640 const UChar
*rStart
= right
->u
.fString
.fChars
;
1641 const UChar
*rLimit
= rStart
+ right
->u
.fString
.fLength
;
1643 /* compare keys in reverse character order */
1644 while (lStart
< lLimit
&& rStart
< rLimit
) {
1645 diff
= (int32_t)*--lLimit
- (int32_t)*--rLimit
;
1650 /* sort equal suffixes by descending string length */
1651 return right
->u
.fString
.fLength
- left
->u
.fString
.fLength
;
1654 static int32_t U_CALLCONV
1655 compareStringLengths(const void *context
, const void *l
, const void *r
) {
1656 struct SResource
*left
= *((struct SResource
**)l
);
1657 struct SResource
*right
= *((struct SResource
**)r
);
1659 /* Make "is suffix of another string" compare greater than a non-suffix. */
1660 diff
= (int)(left
->u
.fString
.fSame
!= NULL
) - (int)(right
->u
.fString
.fSame
!= NULL
);
1664 /* sort by ascending string length */
1665 return left
->u
.fString
.fLength
- right
->u
.fString
.fLength
;
1669 string_writeUTF16v2(struct SRBRoot
*bundle
, struct SResource
*res
, int32_t utf16Length
) {
1670 int32_t length
= res
->u
.fString
.fLength
;
1671 res
->fRes
= URES_MAKE_RESOURCE(URES_STRING_V2
, utf16Length
);
1672 res
->fWritten
= TRUE
;
1673 switch(res
->u
.fString
.fNumCharsForLength
) {
1677 bundle
->f16BitUnits
[utf16Length
++] = (uint16_t)(0xdc00 + length
);
1680 bundle
->f16BitUnits
[utf16Length
] = (uint16_t)(0xdfef + (length
>> 16));
1681 bundle
->f16BitUnits
[utf16Length
+ 1] = (uint16_t)length
;
1685 bundle
->f16BitUnits
[utf16Length
] = 0xdfff;
1686 bundle
->f16BitUnits
[utf16Length
+ 1] = (uint16_t)(length
>> 16);
1687 bundle
->f16BitUnits
[utf16Length
+ 2] = (uint16_t)length
;
1691 break; /* will not occur */
1693 u_memcpy(bundle
->f16BitUnits
+ utf16Length
, res
->u
.fString
.fChars
, length
+ 1);
1694 return utf16Length
+ length
+ 1;
1698 bundle_compactStrings(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1699 UHashtable
*stringSet
;
1700 if (gFormatVersion
> 1) {
1701 stringSet
= uhash_open(string_hash
, string_comp
, string_comp
, status
);
1702 res_preflightStrings(bundle
, bundle
->fRoot
, stringSet
, status
);
1706 if (U_FAILURE(*status
)) {
1707 uhash_close(stringSet
);
1710 switch(bundle
->fStringsForm
) {
1711 case STRINGS_UTF16_V2
:
1712 if (bundle
->f16BitUnitsLength
> 0) {
1713 struct SResource
**array
;
1714 int32_t count
= uhash_count(stringSet
);
1717 * Allocate enough space for the initial NUL and the UTF-16 v2 strings,
1718 * and some extra for URES_TABLE16 and URES_ARRAY16 values.
1719 * Round down to an even number.
1721 int32_t utf16Length
= (bundle
->f16BitUnitsLength
+ 20000) & ~1;
1722 bundle
->f16BitUnits
= (UChar
*)uprv_malloc(utf16Length
* U_SIZEOF_UCHAR
);
1723 array
= (struct SResource
**)uprv_malloc(count
* sizeof(struct SResource
**));
1724 if (bundle
->f16BitUnits
== NULL
|| array
== NULL
) {
1725 uprv_free(bundle
->f16BitUnits
);
1726 bundle
->f16BitUnits
= NULL
;
1728 uhash_close(stringSet
);
1729 *status
= U_MEMORY_ALLOCATION_ERROR
;
1732 bundle
->f16BitUnitsCapacity
= utf16Length
;
1733 /* insert the initial NUL */
1734 bundle
->f16BitUnits
[0] = 0;
1736 ++bundle
->f16BitUnitsLength
;
1737 for (pos
= UHASH_FIRST
, i
= 0; i
< count
; ++i
) {
1738 array
[i
] = (struct SResource
*)uhash_nextElement(stringSet
, &pos
)->key
.pointer
;
1740 /* Sort the strings so that each one is immediately followed by all of its suffixes. */
1741 uprv_sortArray(array
, count
, (int32_t)sizeof(struct SResource
**),
1742 compareStringSuffixes
, NULL
, FALSE
, status
);
1744 * Make suffixes point into earlier, longer strings that contain them.
1745 * Temporarily use fSame and fSuffixOffset for suffix strings to
1746 * refer to the remaining ones.
1748 if (U_SUCCESS(*status
)) {
1749 for (i
= 0; i
< count
;) {
1751 * This string is not a suffix of the previous one;
1752 * write this one and subsume the following ones that are
1753 * suffixes of this one.
1755 struct SResource
*res
= array
[i
];
1756 const UChar
*strLimit
= res
->u
.fString
.fChars
+ res
->u
.fString
.fLength
;
1758 for (j
= i
+ 1; j
< count
; ++j
) {
1759 struct SResource
*suffixRes
= array
[j
];
1761 const UChar
*suffix
= suffixRes
->u
.fString
.fChars
;
1762 const UChar
*suffixLimit
= suffix
+ suffixRes
->u
.fString
.fLength
;
1763 int32_t offset
= res
->u
.fString
.fLength
- suffixRes
->u
.fString
.fLength
;
1765 break; /* suffix cannot be longer than the original */
1767 /* Is it a suffix of the earlier, longer key? */
1768 for (s
= strLimit
; suffix
< suffixLimit
&& *--s
== *--suffixLimit
;) {}
1769 if (suffix
== suffixLimit
&& *s
== *suffixLimit
) {
1770 if (suffixRes
->u
.fString
.fNumCharsForLength
== 0) {
1771 /* yes, point to the earlier string */
1772 suffixRes
->u
.fString
.fSame
= res
;
1773 suffixRes
->u
.fString
.fSuffixOffset
= offset
;
1775 /* write the suffix by itself if we need explicit length */
1778 break; /* not a suffix, restart from here */
1785 * Re-sort the strings by ascending length (except suffixes last)
1786 * to optimize for URES_TABLE16 and URES_ARRAY16:
1787 * Keep as many as possible within reach of 16-bit offsets.
1789 uprv_sortArray(array
, count
, (int32_t)sizeof(struct SResource
**),
1790 compareStringLengths
, NULL
, FALSE
, status
);
1791 if (U_SUCCESS(*status
)) {
1792 /* Write the non-suffix strings. */
1793 for (i
= 0; i
< count
&& array
[i
]->u
.fString
.fSame
== NULL
; ++i
) {
1794 utf16Length
= string_writeUTF16v2(bundle
, array
[i
], utf16Length
);
1796 /* Write the suffix strings. Make each point to the real string. */
1797 for (; i
< count
; ++i
) {
1798 struct SResource
*res
= array
[i
];
1799 struct SResource
*same
= res
->u
.fString
.fSame
;
1800 res
->fRes
= same
->fRes
+ same
->u
.fString
.fNumCharsForLength
+ res
->u
.fString
.fSuffixOffset
;
1801 res
->u
.fString
.fSame
= NULL
;
1802 res
->fWritten
= TRUE
;
1805 assert(utf16Length
<= bundle
->f16BitUnitsLength
);
1806 bundle
->f16BitUnitsLength
= utf16Length
;
1813 uhash_close(stringSet
);