2 *******************************************************************************
4 * Copyright (C) 2000-2011, 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"
32 * Align binary data at a 16-byte offset from the start of the resource bundle,
33 * to be safe for any data type it may contain.
35 #define BIN_ALIGNMENT 16
37 static UBool gIncludeCopyright
= FALSE
;
38 static UBool gUsePoolBundle
= FALSE
;
39 static int32_t gFormatVersion
= 2;
41 static UChar gEmptyString
= 0;
43 /* How do we store string values? */
45 STRINGS_UTF16_V1
, /* formatVersion 1: int length + UChars + NUL + padding to 4 bytes */
46 STRINGS_UTF16_V2
/* formatVersion 2: optional length in 1..3 UChars + UChars + NUL */
50 MAX_IMPLICIT_STRING_LENGTH
= 40 /* do not store the length explicitly for such strings */
54 * res_none() returns the address of kNoResource,
55 * for use in non-error cases when no resource is to be added to the bundle.
56 * (NULL is used in error cases.)
58 static const struct SResource kNoResource
= { URES_NONE
};
60 static UDataInfo dataInfo
= {
69 {0x52, 0x65, 0x73, 0x42}, /* dataFormat="ResB" */
70 {1, 3, 0, 0}, /* formatVersion */
71 {1, 4, 0, 0} /* dataVersion take a look at version inside parsed resb*/
74 static const UVersionInfo gFormatVersions
[3] = { /* indexed by a major-formatVersion integer */
80 static uint8_t calcPadding(uint32_t size
) {
81 /* returns space we need to pad */
82 return (uint8_t) ((size
% sizeof(uint32_t)) ? (sizeof(uint32_t) - (size
% sizeof(uint32_t))) : 0);
86 void setIncludeCopyright(UBool val
){
87 gIncludeCopyright
=val
;
90 UBool
getIncludeCopyright(void){
91 return gIncludeCopyright
;
94 void setFormatVersion(int32_t formatVersion
) {
95 gFormatVersion
= formatVersion
;
98 void setUsePoolBundle(UBool use
) {
103 bundle_compactStrings(struct SRBRoot
*bundle
, UErrorCode
*status
);
105 /* Writing Functions */
108 * type_write16() functions write resource values into f16BitUnits
109 * and determine the resource item word, if possible.
112 res_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
116 * type_preWrite() functions calculate ("preflight") and advance the *byteOffset
117 * by the size of their data in the binary file and
118 * determine the resource item word.
119 * Most type_preWrite() functions may add any number of bytes, but res_preWrite()
120 * will always pad it to a multiple of 4.
121 * The resource item type may be a related subtype of the fType.
123 * The type_preWrite() and type_write() functions start and end at the same
125 * Prewriting allows bundle_write() to determine the root resource item word,
126 * before actually writing the bundle contents to the file,
127 * which is necessary because the root item is stored at the beginning.
130 res_preWrite(uint32_t *byteOffset
,
131 struct SRBRoot
*bundle
, struct SResource
*res
,
135 * type_write() functions write their data to mem and update the byteOffset
137 * (A kingdom for C++ and polymorphism...)
140 res_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
141 struct SRBRoot
*bundle
, struct SResource
*res
,
145 reserve16BitUnits(struct SRBRoot
*bundle
, int32_t length
, UErrorCode
*status
) {
146 if (U_FAILURE(*status
)) {
149 if ((bundle
->f16BitUnitsLength
+ length
) > bundle
->f16BitUnitsCapacity
) {
151 int32_t capacity
= 2 * bundle
->f16BitUnitsCapacity
+ length
+ 1024;
152 capacity
&= ~1; /* ensures padding fits if f16BitUnitsLength needs it */
153 newUnits
= (uint16_t *)uprv_malloc(capacity
* 2);
154 if (newUnits
== NULL
) {
155 *status
= U_MEMORY_ALLOCATION_ERROR
;
158 if (bundle
->f16BitUnitsLength
> 0) {
159 uprv_memcpy(newUnits
, bundle
->f16BitUnits
, bundle
->f16BitUnitsLength
* 2);
162 bundle
->f16BitUnitsLength
= 1;
164 uprv_free(bundle
->f16BitUnits
);
165 bundle
->f16BitUnits
= newUnits
;
166 bundle
->f16BitUnitsCapacity
= capacity
;
168 return bundle
->f16BitUnits
+ bundle
->f16BitUnitsLength
;
172 makeRes16(uint32_t resWord
) {
173 uint32_t type
, offset
;
175 return 0; /* empty string */
177 type
= RES_GET_TYPE(resWord
);
178 offset
= RES_GET_OFFSET(resWord
);
179 if (type
== URES_STRING_V2
&& offset
<= 0xffff) {
180 return (int32_t)offset
;
186 mapKey(struct SRBRoot
*bundle
, int32_t oldpos
) {
187 const KeyMapEntry
*map
= bundle
->fKeyMap
;
188 int32_t i
, start
, limit
;
190 /* do a binary search for the old, pre-bundle_compactKeys() key offset */
191 start
= bundle
->fPoolBundleKeysCount
;
192 limit
= start
+ bundle
->fKeysCount
;
193 while (start
< limit
- 1) {
194 i
= (start
+ limit
) / 2;
195 if (oldpos
< map
[i
].oldpos
) {
201 assert(oldpos
== map
[start
].oldpos
);
202 return map
[start
].newpos
;
206 makeKey16(struct SRBRoot
*bundle
, int32_t key
) {
208 return (uint16_t)key
;
210 return (uint16_t)(key
+ bundle
->fLocalKeyLimit
); /* offset in the pool bundle */
215 * Only called for UTF-16 v1 strings and duplicate UTF-16 v2 strings.
216 * For unique UTF-16 v2 strings, res_write16() sees fRes != RES_BOGUS
220 string_write16(struct SRBRoot
*bundle
, struct SResource
*res
, UErrorCode
*status
) {
221 struct SResource
*same
;
222 if ((same
= res
->u
.fString
.fSame
) != NULL
) {
223 /* This is a duplicate. */
224 if (same
->fRes
== RES_BOGUS
) {
225 /* The original has not been visited yet. */
226 string_write16(bundle
, same
, status
);
228 res
->fRes
= same
->fRes
;
229 res
->fWritten
= same
->fWritten
;
234 array_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
235 UErrorCode
*status
) {
236 struct SResource
*current
;
239 if (U_FAILURE(*status
)) {
242 if (res
->u
.fArray
.fCount
== 0 && gFormatVersion
> 1) {
243 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_ARRAY
);
244 res
->fWritten
= TRUE
;
247 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
248 res_write16(bundle
, current
, status
);
249 res16
|= makeRes16(current
->fRes
);
251 if (U_SUCCESS(*status
) && res
->u
.fArray
.fCount
<= 0xffff && res16
>= 0 && gFormatVersion
> 1) {
252 uint16_t *p16
= reserve16BitUnits(bundle
, 1 + res
->u
.fArray
.fCount
, status
);
253 if (U_SUCCESS(*status
)) {
254 res
->fRes
= URES_MAKE_RESOURCE(URES_ARRAY16
, bundle
->f16BitUnitsLength
);
255 *p16
++ = (uint16_t)res
->u
.fArray
.fCount
;
256 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
257 *p16
++ = (uint16_t)makeRes16(current
->fRes
);
259 bundle
->f16BitUnitsLength
+= 1 + res
->u
.fArray
.fCount
;
260 res
->fWritten
= TRUE
;
266 table_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
267 UErrorCode
*status
) {
268 struct SResource
*current
;
269 int32_t maxKey
= 0, maxPoolKey
= 0x80000000;
271 UBool hasLocalKeys
= FALSE
, hasPoolKeys
= FALSE
;
273 if (U_FAILURE(*status
)) {
276 if (res
->u
.fTable
.fCount
== 0 && gFormatVersion
> 1) {
277 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_TABLE
);
278 res
->fWritten
= TRUE
;
281 /* Find the smallest table type that fits the data. */
282 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
284 res_write16(bundle
, current
, status
);
285 if (bundle
->fKeyMap
== NULL
) {
288 key
= current
->fKey
= mapKey(bundle
, current
->fKey
);
297 if (key
> maxPoolKey
) {
301 res16
|= makeRes16(current
->fRes
);
303 if (U_FAILURE(*status
)) {
306 if(res
->u
.fTable
.fCount
> (uint32_t)bundle
->fMaxTableLength
) {
307 bundle
->fMaxTableLength
= res
->u
.fTable
.fCount
;
309 maxPoolKey
&= 0x7fffffff;
310 if (res
->u
.fTable
.fCount
<= 0xffff &&
311 (!hasLocalKeys
|| maxKey
< bundle
->fLocalKeyLimit
) &&
312 (!hasPoolKeys
|| maxPoolKey
< (0x10000 - bundle
->fLocalKeyLimit
))
314 if (res16
>= 0 && gFormatVersion
> 1) {
315 uint16_t *p16
= reserve16BitUnits(bundle
, 1 + res
->u
.fTable
.fCount
* 2, status
);
316 if (U_SUCCESS(*status
)) {
317 /* 16-bit count, key offsets and values */
318 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE16
, bundle
->f16BitUnitsLength
);
319 *p16
++ = (uint16_t)res
->u
.fTable
.fCount
;
320 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
321 *p16
++ = makeKey16(bundle
, current
->fKey
);
323 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
324 *p16
++ = (uint16_t)makeRes16(current
->fRes
);
326 bundle
->f16BitUnitsLength
+= 1 + res
->u
.fTable
.fCount
* 2;
327 res
->fWritten
= TRUE
;
330 /* 16-bit count, 16-bit key offsets, 32-bit values */
331 res
->u
.fTable
.fType
= URES_TABLE
;
334 /* 32-bit count, key offsets and values */
335 res
->u
.fTable
.fType
= URES_TABLE32
;
340 res_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
341 UErrorCode
*status
) {
342 if (U_FAILURE(*status
) || res
== NULL
) {
345 if (res
->fRes
!= RES_BOGUS
) {
347 * The resource item word was already precomputed, which means
348 * no further data needs to be written.
349 * This might be an integer, or an empty or UTF-16 v2 string,
350 * an empty binary, etc.
354 switch (res
->fType
) {
356 string_write16(bundle
, res
, status
);
359 array_write16(bundle
, res
, status
);
362 table_write16(bundle
, res
, status
);
365 /* Only a few resource types write 16-bit units. */
371 * Only called for UTF-16 v1 strings.
372 * For UTF-16 v2 strings, res_preWrite() sees fRes != RES_BOGUS
376 string_preWrite(uint32_t *byteOffset
,
377 struct SRBRoot
*bundle
, struct SResource
*res
,
378 UErrorCode
*status
) {
379 /* Write the UTF-16 v1 string. */
380 res
->fRes
= URES_MAKE_RESOURCE(URES_STRING
, *byteOffset
>> 2);
381 *byteOffset
+= 4 + (res
->u
.fString
.fLength
+ 1) * U_SIZEOF_UCHAR
;
385 bin_preWrite(uint32_t *byteOffset
,
386 struct SRBRoot
*bundle
, struct SResource
*res
,
387 UErrorCode
*status
) {
389 uint32_t dataStart
= *byteOffset
+ sizeof(res
->u
.fBinaryValue
.fLength
);
391 if (dataStart
% BIN_ALIGNMENT
) {
392 pad
= (BIN_ALIGNMENT
- dataStart
% BIN_ALIGNMENT
);
393 *byteOffset
+= pad
; /* pad == 4 or 8 or 12 */
395 res
->fRes
= URES_MAKE_RESOURCE(URES_BINARY
, *byteOffset
>> 2);
396 *byteOffset
+= 4 + res
->u
.fBinaryValue
.fLength
;
400 array_preWrite(uint32_t *byteOffset
,
401 struct SRBRoot
*bundle
, struct SResource
*res
,
402 UErrorCode
*status
) {
403 struct SResource
*current
;
405 if (U_FAILURE(*status
)) {
408 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
409 res_preWrite(byteOffset
, bundle
, current
, status
);
411 res
->fRes
= URES_MAKE_RESOURCE(URES_ARRAY
, *byteOffset
>> 2);
412 *byteOffset
+= (1 + res
->u
.fArray
.fCount
) * 4;
416 table_preWrite(uint32_t *byteOffset
,
417 struct SRBRoot
*bundle
, struct SResource
*res
,
418 UErrorCode
*status
) {
419 struct SResource
*current
;
421 if (U_FAILURE(*status
)) {
424 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
425 res_preWrite(byteOffset
, bundle
, current
, status
);
427 if (res
->u
.fTable
.fType
== URES_TABLE
) {
428 /* 16-bit count, 16-bit key offsets, 32-bit values */
429 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE
, *byteOffset
>> 2);
430 *byteOffset
+= 2 + res
->u
.fTable
.fCount
* 6;
432 /* 32-bit count, key offsets and values */
433 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE32
, *byteOffset
>> 2);
434 *byteOffset
+= 4 + res
->u
.fTable
.fCount
* 8;
439 res_preWrite(uint32_t *byteOffset
,
440 struct SRBRoot
*bundle
, struct SResource
*res
,
441 UErrorCode
*status
) {
442 if (U_FAILURE(*status
) || res
== NULL
) {
445 if (res
->fRes
!= RES_BOGUS
) {
447 * The resource item word was already precomputed, which means
448 * no further data needs to be written.
449 * This might be an integer, or an empty or UTF-16 v2 string,
450 * an empty binary, etc.
454 switch (res
->fType
) {
456 string_preWrite(byteOffset
, bundle
, res
, status
);
459 res
->fRes
= URES_MAKE_RESOURCE(URES_ALIAS
, *byteOffset
>> 2);
460 *byteOffset
+= 4 + (res
->u
.fString
.fLength
+ 1) * U_SIZEOF_UCHAR
;
462 case URES_INT_VECTOR
:
463 if (res
->u
.fIntVector
.fCount
== 0 && gFormatVersion
> 1) {
464 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_INT_VECTOR
);
465 res
->fWritten
= TRUE
;
467 res
->fRes
= URES_MAKE_RESOURCE(URES_INT_VECTOR
, *byteOffset
>> 2);
468 *byteOffset
+= (1 + res
->u
.fIntVector
.fCount
) * 4;
472 bin_preWrite(byteOffset
, bundle
, res
, status
);
477 array_preWrite(byteOffset
, bundle
, res
, status
);
480 table_preWrite(byteOffset
, bundle
, res
, status
);
483 *status
= U_INTERNAL_PROGRAM_ERROR
;
486 *byteOffset
+= calcPadding(*byteOffset
);
490 * Only called for UTF-16 v1 strings. For UTF-16 v2 strings,
491 * res_write() sees fWritten and exits early.
493 static void string_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
494 struct SRBRoot
*bundle
, struct SResource
*res
,
495 UErrorCode
*status
) {
496 /* Write the UTF-16 v1 string. */
497 int32_t length
= res
->u
.fString
.fLength
;
498 udata_write32(mem
, length
);
499 udata_writeUString(mem
, res
->u
.fString
.fChars
, length
+ 1);
500 *byteOffset
+= 4 + (length
+ 1) * U_SIZEOF_UCHAR
;
501 res
->fWritten
= TRUE
;
504 static void alias_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
505 struct SRBRoot
*bundle
, struct SResource
*res
,
506 UErrorCode
*status
) {
507 int32_t length
= res
->u
.fString
.fLength
;
508 udata_write32(mem
, length
);
509 udata_writeUString(mem
, res
->u
.fString
.fChars
, length
+ 1);
510 *byteOffset
+= 4 + (length
+ 1) * U_SIZEOF_UCHAR
;
513 static void array_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
514 struct SRBRoot
*bundle
, struct SResource
*res
,
515 UErrorCode
*status
) {
518 struct SResource
*current
= NULL
;
520 if (U_FAILURE(*status
)) {
523 for (i
= 0, current
= res
->u
.fArray
.fFirst
; current
!= NULL
; ++i
, current
= current
->fNext
) {
524 res_write(mem
, byteOffset
, bundle
, current
, status
);
526 assert(i
== res
->u
.fArray
.fCount
);
528 udata_write32(mem
, res
->u
.fArray
.fCount
);
529 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
530 udata_write32(mem
, current
->fRes
);
532 *byteOffset
+= (1 + res
->u
.fArray
.fCount
) * 4;
535 static void intvector_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
536 struct SRBRoot
*bundle
, struct SResource
*res
,
537 UErrorCode
*status
) {
539 udata_write32(mem
, res
->u
.fIntVector
.fCount
);
540 for(i
= 0; i
<res
->u
.fIntVector
.fCount
; i
++) {
541 udata_write32(mem
, res
->u
.fIntVector
.fArray
[i
]);
543 *byteOffset
+= (1 + res
->u
.fIntVector
.fCount
) * 4;
546 static void bin_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
547 struct SRBRoot
*bundle
, struct SResource
*res
,
548 UErrorCode
*status
) {
550 uint32_t dataStart
= *byteOffset
+ sizeof(res
->u
.fBinaryValue
.fLength
);
552 if (dataStart
% BIN_ALIGNMENT
) {
553 pad
= (BIN_ALIGNMENT
- dataStart
% BIN_ALIGNMENT
);
554 udata_writePadding(mem
, pad
); /* pad == 4 or 8 or 12 */
558 udata_write32(mem
, res
->u
.fBinaryValue
.fLength
);
559 if (res
->u
.fBinaryValue
.fLength
> 0) {
560 udata_writeBlock(mem
, res
->u
.fBinaryValue
.fData
, res
->u
.fBinaryValue
.fLength
);
562 *byteOffset
+= 4 + res
->u
.fBinaryValue
.fLength
;
565 static void table_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
566 struct SRBRoot
*bundle
, struct SResource
*res
,
567 UErrorCode
*status
) {
568 struct SResource
*current
;
571 if (U_FAILURE(*status
)) {
574 for (i
= 0, current
= res
->u
.fTable
.fFirst
; current
!= NULL
; ++i
, current
= current
->fNext
) {
575 assert(i
< res
->u
.fTable
.fCount
);
576 res_write(mem
, byteOffset
, bundle
, current
, status
);
578 assert(i
== res
->u
.fTable
.fCount
);
580 if(res
->u
.fTable
.fType
== URES_TABLE
) {
581 udata_write16(mem
, (uint16_t)res
->u
.fTable
.fCount
);
582 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
583 udata_write16(mem
, makeKey16(bundle
, current
->fKey
));
585 *byteOffset
+= (1 + res
->u
.fTable
.fCount
)* 2;
586 if ((res
->u
.fTable
.fCount
& 1) == 0) {
587 /* 16-bit count and even number of 16-bit key offsets need padding before 32-bit resource items */
588 udata_writePadding(mem
, 2);
591 } else /* URES_TABLE32 */ {
592 udata_write32(mem
, res
->u
.fTable
.fCount
);
593 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
594 udata_write32(mem
, (uint32_t)current
->fKey
);
596 *byteOffset
+= (1 + res
->u
.fTable
.fCount
)* 4;
598 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
599 udata_write32(mem
, current
->fRes
);
601 *byteOffset
+= res
->u
.fTable
.fCount
* 4;
604 void res_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
605 struct SRBRoot
*bundle
, struct SResource
*res
,
606 UErrorCode
*status
) {
609 if (U_FAILURE(*status
) || res
== NULL
) {
613 assert(res
->fRes
!= RES_BOGUS
);
616 switch (res
->fType
) {
618 string_write (mem
, byteOffset
, bundle
, res
, status
);
621 alias_write (mem
, byteOffset
, bundle
, res
, status
);
623 case URES_INT_VECTOR
:
624 intvector_write (mem
, byteOffset
, bundle
, res
, status
);
627 bin_write (mem
, byteOffset
, bundle
, res
, status
);
630 break; /* fRes was set by int_open() */
632 array_write (mem
, byteOffset
, bundle
, res
, status
);
635 table_write (mem
, byteOffset
, bundle
, res
, status
);
638 *status
= U_INTERNAL_PROGRAM_ERROR
;
641 paddingSize
= calcPadding(*byteOffset
);
642 if (paddingSize
> 0) {
643 udata_writePadding(mem
, paddingSize
);
644 *byteOffset
+= paddingSize
;
646 res
->fWritten
= TRUE
;
649 void bundle_write(struct SRBRoot
*bundle
,
650 const char *outputDir
, const char *outputPkg
,
651 char *writtenFilename
, int writtenFilenameLen
,
652 UErrorCode
*status
) {
653 UNewDataMemory
*mem
= NULL
;
654 uint32_t byteOffset
= 0;
657 int32_t indexes
[URES_INDEX_TOP
];
659 bundle_compactKeys(bundle
, status
);
661 * Add padding bytes to fKeys so that fKeysTop is 4-aligned.
662 * Safe because the capacity is a multiple of 4.
664 while (bundle
->fKeysTop
& 3) {
665 bundle
->fKeys
[bundle
->fKeysTop
++] = (char)0xaa;
668 * In URES_TABLE, use all local key offsets that fit into 16 bits,
669 * and use the remaining 16-bit offsets for pool key offsets
671 * If there are no local keys, then use the whole 16-bit space
672 * for pool key offsets.
673 * Note: This cannot be changed without changing the major formatVersion.
675 if (bundle
->fKeysBottom
< bundle
->fKeysTop
) {
676 if (bundle
->fKeysTop
<= 0x10000) {
677 bundle
->fLocalKeyLimit
= bundle
->fKeysTop
;
679 bundle
->fLocalKeyLimit
= 0x10000;
682 bundle
->fLocalKeyLimit
= 0;
685 bundle_compactStrings(bundle
, status
);
686 res_write16(bundle
, bundle
->fRoot
, status
);
687 if (bundle
->f16BitUnitsLength
& 1) {
688 bundle
->f16BitUnits
[bundle
->f16BitUnitsLength
++] = 0xaaaa; /* pad to multiple of 4 bytes */
690 /* all keys have been mapped */
691 uprv_free(bundle
->fKeyMap
);
692 bundle
->fKeyMap
= NULL
;
694 byteOffset
= bundle
->fKeysTop
+ bundle
->f16BitUnitsLength
* 2;
695 res_preWrite(&byteOffset
, bundle
, bundle
->fRoot
, status
);
697 /* total size including the root item */
700 if (U_FAILURE(*status
)) {
704 if (writtenFilename
&& writtenFilenameLen
) {
705 *writtenFilename
= 0;
708 if (writtenFilename
) {
709 int32_t off
= 0, len
= 0;
711 len
= (int32_t)uprv_strlen(outputDir
);
712 if (len
> writtenFilenameLen
) {
713 len
= writtenFilenameLen
;
715 uprv_strncpy(writtenFilename
, outputDir
, len
);
717 if (writtenFilenameLen
-= len
) {
719 writtenFilename
[off
] = U_FILE_SEP_CHAR
;
720 if (--writtenFilenameLen
) {
722 if(outputPkg
!= NULL
)
724 uprv_strcpy(writtenFilename
+off
, outputPkg
);
725 off
+= (int32_t)uprv_strlen(outputPkg
);
726 writtenFilename
[off
] = '_';
730 len
= (int32_t)uprv_strlen(bundle
->fLocale
);
731 if (len
> writtenFilenameLen
) {
732 len
= writtenFilenameLen
;
734 uprv_strncpy(writtenFilename
+ off
, bundle
->fLocale
, len
);
735 if (writtenFilenameLen
-= len
) {
738 if (len
> writtenFilenameLen
) {
739 len
= writtenFilenameLen
;
741 uprv_strncpy(writtenFilename
+ off
, ".res", len
);
749 uprv_strcpy(dataName
, outputPkg
);
750 uprv_strcat(dataName
, "_");
751 uprv_strcat(dataName
, bundle
->fLocale
);
755 uprv_strcpy(dataName
, bundle
->fLocale
);
758 uprv_memcpy(dataInfo
.formatVersion
, gFormatVersions
+ gFormatVersion
, sizeof(UVersionInfo
));
760 mem
= udata_create(outputDir
, "res", dataName
, &dataInfo
, (gIncludeCopyright
==TRUE
)? U_COPYRIGHT_STRING
:NULL
, status
);
761 if(U_FAILURE(*status
)){
765 /* write the root item */
766 udata_write32(mem
, bundle
->fRoot
->fRes
);
769 * formatVersion 1.1 (ICU 2.8):
770 * write int32_t indexes[] after root and before the strings
771 * to make it easier to parse resource bundles in icuswap or from Java etc.
773 uprv_memset(indexes
, 0, sizeof(indexes
));
774 indexes
[URES_INDEX_LENGTH
]= bundle
->fIndexLength
;
775 indexes
[URES_INDEX_KEYS_TOP
]= bundle
->fKeysTop
>>2;
776 indexes
[URES_INDEX_RESOURCES_TOP
]= (int32_t)(top
>>2);
777 indexes
[URES_INDEX_BUNDLE_TOP
]= indexes
[URES_INDEX_RESOURCES_TOP
];
778 indexes
[URES_INDEX_MAX_TABLE_LENGTH
]= bundle
->fMaxTableLength
;
781 * formatVersion 1.2 (ICU 3.6):
782 * write indexes[URES_INDEX_ATTRIBUTES] with URES_ATT_NO_FALLBACK set or not set
783 * the memset() above initialized all indexes[] to 0
785 if (bundle
->noFallback
) {
786 indexes
[URES_INDEX_ATTRIBUTES
]=URES_ATT_NO_FALLBACK
;
789 * formatVersion 2.0 (ICU 4.4):
790 * more compact string value storage, optional pool bundle
792 if (URES_INDEX_16BIT_TOP
< bundle
->fIndexLength
) {
793 indexes
[URES_INDEX_16BIT_TOP
] = (bundle
->fKeysTop
>>2) + (bundle
->f16BitUnitsLength
>>1);
795 if (URES_INDEX_POOL_CHECKSUM
< bundle
->fIndexLength
) {
796 if (bundle
->fIsPoolBundle
) {
797 indexes
[URES_INDEX_ATTRIBUTES
] |= URES_ATT_IS_POOL_BUNDLE
| URES_ATT_NO_FALLBACK
;
798 indexes
[URES_INDEX_POOL_CHECKSUM
] =
799 (int32_t)computeCRC((char *)(bundle
->fKeys
+ bundle
->fKeysBottom
),
800 (uint32_t)(bundle
->fKeysTop
- bundle
->fKeysBottom
),
802 } else if (gUsePoolBundle
) {
803 indexes
[URES_INDEX_ATTRIBUTES
] |= URES_ATT_USES_POOL_BUNDLE
;
804 indexes
[URES_INDEX_POOL_CHECKSUM
] = bundle
->fPoolChecksum
;
808 /* write the indexes[] */
809 udata_writeBlock(mem
, indexes
, bundle
->fIndexLength
*4);
811 /* write the table key strings */
812 udata_writeBlock(mem
, bundle
->fKeys
+bundle
->fKeysBottom
,
813 bundle
->fKeysTop
-bundle
->fKeysBottom
);
815 /* write the v2 UTF-16 strings, URES_TABLE16 and URES_ARRAY16 */
816 udata_writeBlock(mem
, bundle
->f16BitUnits
, bundle
->f16BitUnitsLength
*2);
818 /* write all of the bundle contents: the root item and its children */
819 byteOffset
= bundle
->fKeysTop
+ bundle
->f16BitUnitsLength
* 2;
820 res_write(mem
, &byteOffset
, bundle
, bundle
->fRoot
, status
);
821 assert(byteOffset
== top
);
823 size
= udata_finish(mem
, status
);
825 fprintf(stderr
, "genrb error: wrote %u bytes but counted %u\n",
826 (int)size
, (int)top
);
827 *status
= U_INTERNAL_PROGRAM_ERROR
;
831 /* Opening Functions */
833 /* gcc 4.2 complained "no previous prototype for res_open" without this prototype... */
834 struct SResource
* res_open(struct SRBRoot
*bundle
, const char *tag
,
835 const struct UString
* comment
, UErrorCode
* status
);
837 struct SResource
* res_open(struct SRBRoot
*bundle
, const char *tag
,
838 const struct UString
* comment
, UErrorCode
* status
){
839 struct SResource
*res
;
840 int32_t key
= bundle_addtag(bundle
, tag
, status
);
841 if (U_FAILURE(*status
)) {
845 res
= (struct SResource
*) uprv_malloc(sizeof(struct SResource
));
847 *status
= U_MEMORY_ALLOCATION_ERROR
;
850 uprv_memset(res
, 0, sizeof(struct SResource
));
852 res
->fRes
= RES_BOGUS
;
854 ustr_init(&res
->fComment
);
856 ustr_cpy(&res
->fComment
, comment
, status
);
857 if (U_FAILURE(*status
)) {
865 struct SResource
* res_none() {
866 return (struct SResource
*)&kNoResource
;
869 struct SResource
* table_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
870 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
871 if (U_FAILURE(*status
)) {
874 res
->fType
= URES_TABLE
;
875 res
->u
.fTable
.fRoot
= bundle
;
879 struct SResource
* array_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
880 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
881 if (U_FAILURE(*status
)) {
884 res
->fType
= URES_ARRAY
;
888 static int32_t U_CALLCONV
889 string_hash(const UElement key
) {
890 const struct SResource
*res
= (struct SResource
*)key
.pointer
;
891 return ustr_hashUCharsN(res
->u
.fString
.fChars
, res
->u
.fString
.fLength
);
894 static UBool U_CALLCONV
895 string_comp(const UElement key1
, const UElement key2
) {
896 const struct SResource
*res1
= (struct SResource
*)key1
.pointer
;
897 const struct SResource
*res2
= (struct SResource
*)key2
.pointer
;
898 return 0 == u_strCompare(res1
->u
.fString
.fChars
, res1
->u
.fString
.fLength
,
899 res2
->u
.fString
.fChars
, res2
->u
.fString
.fLength
,
903 struct SResource
*string_open(struct SRBRoot
*bundle
, const char *tag
, const UChar
*value
, int32_t len
, const struct UString
* comment
, UErrorCode
*status
) {
904 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
905 if (U_FAILURE(*status
)) {
908 res
->fType
= URES_STRING
;
910 if (len
== 0 && gFormatVersion
> 1) {
911 res
->u
.fString
.fChars
= &gEmptyString
;
913 res
->fWritten
= TRUE
;
917 res
->u
.fString
.fLength
= len
;
919 if (gFormatVersion
> 1) {
920 /* check for duplicates */
921 res
->u
.fString
.fChars
= (UChar
*)value
;
922 if (bundle
->fStringSet
== NULL
) {
923 UErrorCode localStatus
= U_ZERO_ERROR
; /* if failure: just don't detect dups */
924 bundle
->fStringSet
= uhash_open(string_hash
, string_comp
, string_comp
, &localStatus
);
926 res
->u
.fString
.fSame
= uhash_get(bundle
->fStringSet
, res
);
929 if (res
->u
.fString
.fSame
== NULL
) {
930 /* this is a new string */
931 res
->u
.fString
.fChars
= (UChar
*) uprv_malloc(sizeof(UChar
) * (len
+ 1));
933 if (res
->u
.fString
.fChars
== NULL
) {
934 *status
= U_MEMORY_ALLOCATION_ERROR
;
939 uprv_memcpy(res
->u
.fString
.fChars
, value
, sizeof(UChar
) * len
);
940 res
->u
.fString
.fChars
[len
] = 0;
941 if (bundle
->fStringSet
!= NULL
) {
942 /* put it into the set for finding duplicates */
943 uhash_put(bundle
->fStringSet
, res
, res
, status
);
946 if (bundle
->fStringsForm
!= STRINGS_UTF16_V1
) {
947 if (len
<= MAX_IMPLICIT_STRING_LENGTH
&& !U16_IS_TRAIL(value
[0]) && len
== u_strlen(value
)) {
949 * This string will be stored without an explicit length.
950 * Runtime will detect !U16_IS_TRAIL(value[0]) and call u_strlen().
952 res
->u
.fString
.fNumCharsForLength
= 0;
953 } else if (len
<= 0x3ee) {
954 res
->u
.fString
.fNumCharsForLength
= 1;
955 } else if (len
<= 0xfffff) {
956 res
->u
.fString
.fNumCharsForLength
= 2;
958 res
->u
.fString
.fNumCharsForLength
= 3;
960 bundle
->f16BitUnitsLength
+= res
->u
.fString
.fNumCharsForLength
+ len
+ 1; /* +1 for the NUL */
963 /* this is a duplicate of fSame */
964 struct SResource
*same
= res
->u
.fString
.fSame
;
965 res
->u
.fString
.fChars
= same
->u
.fString
.fChars
;
970 /* TODO: make alias_open and string_open use the same code */
971 struct SResource
*alias_open(struct SRBRoot
*bundle
, const char *tag
, UChar
*value
, int32_t len
, const struct UString
* comment
, UErrorCode
*status
) {
972 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
973 if (U_FAILURE(*status
)) {
976 res
->fType
= URES_ALIAS
;
977 if (len
== 0 && gFormatVersion
> 1) {
978 res
->u
.fString
.fChars
= &gEmptyString
;
979 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_ALIAS
);
980 res
->fWritten
= TRUE
;
984 res
->u
.fString
.fLength
= len
;
985 res
->u
.fString
.fChars
= (UChar
*) uprv_malloc(sizeof(UChar
) * (len
+ 1));
986 if (res
->u
.fString
.fChars
== NULL
) {
987 *status
= U_MEMORY_ALLOCATION_ERROR
;
991 uprv_memcpy(res
->u
.fString
.fChars
, value
, sizeof(UChar
) * (len
+ 1));
996 struct SResource
* intvector_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
997 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
998 if (U_FAILURE(*status
)) {
1001 res
->fType
= URES_INT_VECTOR
;
1003 res
->u
.fIntVector
.fCount
= 0;
1004 res
->u
.fIntVector
.fArray
= (uint32_t *) uprv_malloc(sizeof(uint32_t) * RESLIST_MAX_INT_VECTOR
);
1005 if (res
->u
.fIntVector
.fArray
== NULL
) {
1006 *status
= U_MEMORY_ALLOCATION_ERROR
;
1013 struct SResource
*int_open(struct SRBRoot
*bundle
, const char *tag
, int32_t value
, const struct UString
* comment
, UErrorCode
*status
) {
1014 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1015 if (U_FAILURE(*status
)) {
1018 res
->fType
= URES_INT
;
1019 res
->u
.fIntValue
.fValue
= value
;
1020 res
->fRes
= URES_MAKE_RESOURCE(URES_INT
, value
& 0x0FFFFFFF);
1021 res
->fWritten
= TRUE
;
1025 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
) {
1026 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1027 if (U_FAILURE(*status
)) {
1030 res
->fType
= URES_BINARY
;
1032 res
->u
.fBinaryValue
.fLength
= length
;
1033 res
->u
.fBinaryValue
.fFileName
= NULL
;
1034 if(fileName
!=NULL
&& uprv_strcmp(fileName
, "") !=0){
1035 res
->u
.fBinaryValue
.fFileName
= (char*) uprv_malloc(sizeof(char) * (uprv_strlen(fileName
)+1));
1036 uprv_strcpy(res
->u
.fBinaryValue
.fFileName
,fileName
);
1039 res
->u
.fBinaryValue
.fData
= (uint8_t *) uprv_malloc(sizeof(uint8_t) * length
);
1041 if (res
->u
.fBinaryValue
.fData
== NULL
) {
1042 *status
= U_MEMORY_ALLOCATION_ERROR
;
1047 uprv_memcpy(res
->u
.fBinaryValue
.fData
, data
, length
);
1050 res
->u
.fBinaryValue
.fData
= NULL
;
1051 if (gFormatVersion
> 1) {
1052 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_BINARY
);
1053 res
->fWritten
= TRUE
;
1060 struct SRBRoot
*bundle_open(const struct UString
* comment
, UBool isPoolBundle
, UErrorCode
*status
) {
1061 struct SRBRoot
*bundle
;
1063 if (U_FAILURE(*status
)) {
1067 bundle
= (struct SRBRoot
*) uprv_malloc(sizeof(struct SRBRoot
));
1068 if (bundle
== NULL
) {
1069 *status
= U_MEMORY_ALLOCATION_ERROR
;
1072 uprv_memset(bundle
, 0, sizeof(struct SRBRoot
));
1074 bundle
->fKeys
= (char *) uprv_malloc(sizeof(char) * KEY_SPACE_SIZE
);
1075 bundle
->fRoot
= table_open(bundle
, NULL
, comment
, status
);
1076 if (bundle
->fKeys
== NULL
|| bundle
->fRoot
== NULL
|| U_FAILURE(*status
)) {
1077 if (U_SUCCESS(*status
)) {
1078 *status
= U_MEMORY_ALLOCATION_ERROR
;
1080 bundle_close(bundle
, status
);
1084 bundle
->fLocale
= NULL
;
1085 bundle
->fKeysCapacity
= KEY_SPACE_SIZE
;
1086 /* formatVersion 1.1: start fKeysTop after the root item and indexes[] */
1087 bundle
->fIsPoolBundle
= isPoolBundle
;
1088 if (gUsePoolBundle
|| isPoolBundle
) {
1089 bundle
->fIndexLength
= URES_INDEX_POOL_CHECKSUM
+ 1;
1090 } else if (gFormatVersion
>= 2) {
1091 bundle
->fIndexLength
= URES_INDEX_16BIT_TOP
+ 1;
1092 } else /* formatVersion 1 */ {
1093 bundle
->fIndexLength
= URES_INDEX_ATTRIBUTES
+ 1;
1095 bundle
->fKeysBottom
= (1 /* root */ + bundle
->fIndexLength
) * 4;
1096 uprv_memset(bundle
->fKeys
, 0, bundle
->fKeysBottom
);
1097 bundle
->fKeysTop
= bundle
->fKeysBottom
;
1099 if (gFormatVersion
== 1) {
1100 bundle
->fStringsForm
= STRINGS_UTF16_V1
;
1102 bundle
->fStringsForm
= STRINGS_UTF16_V2
;
1108 /* Closing Functions */
1109 static void table_close(struct SResource
*table
) {
1110 struct SResource
*current
= NULL
;
1111 struct SResource
*prev
= NULL
;
1113 current
= table
->u
.fTable
.fFirst
;
1115 while (current
!= NULL
) {
1117 current
= current
->fNext
;
1122 table
->u
.fTable
.fFirst
= NULL
;
1125 static void array_close(struct SResource
*array
) {
1126 struct SResource
*current
= NULL
;
1127 struct SResource
*prev
= NULL
;
1132 current
= array
->u
.fArray
.fFirst
;
1134 while (current
!= NULL
) {
1136 current
= current
->fNext
;
1140 array
->u
.fArray
.fFirst
= NULL
;
1143 static void string_close(struct SResource
*string
) {
1144 if (string
->u
.fString
.fChars
!= NULL
&&
1145 string
->u
.fString
.fChars
!= &gEmptyString
&&
1146 string
->u
.fString
.fSame
== NULL
1148 uprv_free(string
->u
.fString
.fChars
);
1149 string
->u
.fString
.fChars
=NULL
;
1153 static void alias_close(struct SResource
*alias
) {
1154 if (alias
->u
.fString
.fChars
!= NULL
) {
1155 uprv_free(alias
->u
.fString
.fChars
);
1156 alias
->u
.fString
.fChars
=NULL
;
1160 static void intvector_close(struct SResource
*intvector
) {
1161 if (intvector
->u
.fIntVector
.fArray
!= NULL
) {
1162 uprv_free(intvector
->u
.fIntVector
.fArray
);
1163 intvector
->u
.fIntVector
.fArray
=NULL
;
1167 static void int_close(struct SResource
*intres
) {
1168 /* Intentionally left blank */
1171 static void bin_close(struct SResource
*binres
) {
1172 if (binres
->u
.fBinaryValue
.fData
!= NULL
) {
1173 uprv_free(binres
->u
.fBinaryValue
.fData
);
1174 binres
->u
.fBinaryValue
.fData
= NULL
;
1178 void res_close(struct SResource
*res
) {
1180 switch(res
->fType
) {
1187 case URES_INT_VECTOR
:
1188 intvector_close(res
);
1203 /* Shouldn't happen */
1207 ustr_deinit(&res
->fComment
);
1212 void bundle_close(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1213 res_close(bundle
->fRoot
);
1214 uprv_free(bundle
->fLocale
);
1215 uprv_free(bundle
->fKeys
);
1216 uprv_free(bundle
->fKeyMap
);
1217 uhash_close(bundle
->fStringSet
);
1218 uprv_free(bundle
->f16BitUnits
);
1222 void bundle_closeString(struct SRBRoot
*bundle
, struct SResource
*string
) {
1223 if (bundle
->fStringSet
!= NULL
) {
1224 uhash_remove(bundle
->fStringSet
, string
);
1226 string_close(string
);
1229 /* Adding Functions */
1230 void table_add(struct SResource
*table
, struct SResource
*res
, int linenumber
, UErrorCode
*status
) {
1231 struct SResource
*current
= NULL
;
1232 struct SResource
*prev
= NULL
;
1233 struct SResTable
*list
;
1234 const char *resKeyString
;
1236 if (U_FAILURE(*status
)) {
1239 if (res
== &kNoResource
) {
1243 /* remember this linenumber to report to the user if there is a duplicate key */
1244 res
->line
= linenumber
;
1246 /* here we need to traverse the list */
1247 list
= &(table
->u
.fTable
);
1250 /* is list still empty? */
1251 if (list
->fFirst
== NULL
) {
1257 resKeyString
= list
->fRoot
->fKeys
+ res
->fKey
;
1259 current
= list
->fFirst
;
1261 while (current
!= NULL
) {
1262 const char *currentKeyString
= list
->fRoot
->fKeys
+ current
->fKey
;
1265 * formatVersion 1: compare key strings in native-charset order
1266 * formatVersion 2 and up: compare key strings in ASCII order
1268 if (gFormatVersion
== 1 || U_CHARSET_FAMILY
== U_ASCII_FAMILY
) {
1269 diff
= uprv_strcmp(currentKeyString
, resKeyString
);
1271 diff
= uprv_compareInvCharsAsAscii(currentKeyString
, resKeyString
);
1275 current
= current
->fNext
;
1276 } else if (diff
> 0) {
1277 /* we're either in front of list, or in middle */
1279 /* front of the list */
1282 /* middle of the list */
1286 res
->fNext
= current
;
1289 /* Key already exists! ERROR! */
1290 error(linenumber
, "duplicate key '%s' in table, first appeared at line %d", currentKeyString
, current
->line
);
1291 *status
= U_UNSUPPORTED_ERROR
;
1301 void array_add(struct SResource
*array
, struct SResource
*res
, UErrorCode
*status
) {
1302 if (U_FAILURE(*status
)) {
1306 if (array
->u
.fArray
.fFirst
== NULL
) {
1307 array
->u
.fArray
.fFirst
= res
;
1308 array
->u
.fArray
.fLast
= res
;
1310 array
->u
.fArray
.fLast
->fNext
= res
;
1311 array
->u
.fArray
.fLast
= res
;
1314 (array
->u
.fArray
.fCount
)++;
1317 void intvector_add(struct SResource
*intvector
, int32_t value
, UErrorCode
*status
) {
1318 if (U_FAILURE(*status
)) {
1322 *(intvector
->u
.fIntVector
.fArray
+ intvector
->u
.fIntVector
.fCount
) = value
;
1323 intvector
->u
.fIntVector
.fCount
++;
1326 /* Misc Functions */
1328 void bundle_setlocale(struct SRBRoot
*bundle
, UChar
*locale
, UErrorCode
*status
) {
1330 if(U_FAILURE(*status
)) {
1334 if (bundle
->fLocale
!=NULL
) {
1335 uprv_free(bundle
->fLocale
);
1338 bundle
->fLocale
= (char*) uprv_malloc(sizeof(char) * (u_strlen(locale
)+1));
1340 if(bundle
->fLocale
== NULL
) {
1341 *status
= U_MEMORY_ALLOCATION_ERROR
;
1345 /*u_strcpy(bundle->fLocale, locale);*/
1346 u_UCharsToChars(locale
, bundle
->fLocale
, u_strlen(locale
)+1);
1351 getKeyString(const struct SRBRoot
*bundle
, int32_t key
) {
1353 return bundle
->fPoolBundleKeys
+ (key
& 0x7fffffff);
1355 return bundle
->fKeys
+ key
;
1360 res_getKeyString(const struct SRBRoot
*bundle
, const struct SResource
*res
, char temp
[8]) {
1361 if (res
->fKey
== -1) {
1364 return getKeyString(bundle
, res
->fKey
);
1368 bundle_getKeyBytes(struct SRBRoot
*bundle
, int32_t *pLength
) {
1369 *pLength
= bundle
->fKeysTop
- bundle
->fKeysBottom
;
1370 return bundle
->fKeys
+ bundle
->fKeysBottom
;
1374 bundle_addKeyBytes(struct SRBRoot
*bundle
, const char *keyBytes
, int32_t length
, UErrorCode
*status
) {
1377 if (U_FAILURE(*status
)) {
1380 if (length
< 0 || (keyBytes
== NULL
&& length
!= 0)) {
1381 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1385 return bundle
->fKeysTop
;
1388 keypos
= bundle
->fKeysTop
;
1389 bundle
->fKeysTop
+= length
;
1390 if (bundle
->fKeysTop
>= bundle
->fKeysCapacity
) {
1391 /* overflow - resize the keys buffer */
1392 bundle
->fKeysCapacity
+= KEY_SPACE_SIZE
;
1393 bundle
->fKeys
= uprv_realloc(bundle
->fKeys
, bundle
->fKeysCapacity
);
1394 if(bundle
->fKeys
== NULL
) {
1395 *status
= U_MEMORY_ALLOCATION_ERROR
;
1400 uprv_memcpy(bundle
->fKeys
+ keypos
, keyBytes
, length
);
1406 bundle_addtag(struct SRBRoot
*bundle
, const char *tag
, UErrorCode
*status
) {
1409 if (U_FAILURE(*status
)) {
1414 /* no error: the root table and array items have no keys */
1418 keypos
= bundle_addKeyBytes(bundle
, tag
, (int32_t)(uprv_strlen(tag
) + 1), status
);
1419 if (U_SUCCESS(*status
)) {
1420 ++bundle
->fKeysCount
;
1426 compareInt32(int32_t lPos
, int32_t rPos
) {
1428 * Compare possibly-negative key offsets. Don't just return lPos - rPos
1429 * because that is prone to negative-integer underflows.
1433 } else if (lPos
> rPos
) {
1440 static int32_t U_CALLCONV
1441 compareKeySuffixes(const void *context
, const void *l
, const void *r
) {
1442 const struct SRBRoot
*bundle
=(const struct SRBRoot
*)context
;
1443 int32_t lPos
= ((const KeyMapEntry
*)l
)->oldpos
;
1444 int32_t rPos
= ((const KeyMapEntry
*)r
)->oldpos
;
1445 const char *lStart
= getKeyString(bundle
, lPos
);
1446 const char *lLimit
= lStart
;
1447 const char *rStart
= getKeyString(bundle
, rPos
);
1448 const char *rLimit
= rStart
;
1450 while (*lLimit
!= 0) { ++lLimit
; }
1451 while (*rLimit
!= 0) { ++rLimit
; }
1452 /* compare keys in reverse character order */
1453 while (lStart
< lLimit
&& rStart
< rLimit
) {
1454 diff
= (int32_t)(uint8_t)*--lLimit
- (int32_t)(uint8_t)*--rLimit
;
1459 /* sort equal suffixes by descending key length */
1460 diff
= (int32_t)(rLimit
- rStart
) - (int32_t)(lLimit
- lStart
);
1464 /* Sort pool bundle keys first (negative oldpos), and otherwise keys in parsing order. */
1465 return compareInt32(lPos
, rPos
);
1468 static int32_t U_CALLCONV
1469 compareKeyNewpos(const void *context
, const void *l
, const void *r
) {
1470 return compareInt32(((const KeyMapEntry
*)l
)->newpos
, ((const KeyMapEntry
*)r
)->newpos
);
1473 static int32_t U_CALLCONV
1474 compareKeyOldpos(const void *context
, const void *l
, const void *r
) {
1475 return compareInt32(((const KeyMapEntry
*)l
)->oldpos
, ((const KeyMapEntry
*)r
)->oldpos
);
1479 bundle_compactKeys(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1483 int32_t keysCount
= bundle
->fPoolBundleKeysCount
+ bundle
->fKeysCount
;
1484 if (U_FAILURE(*status
) || bundle
->fKeysCount
== 0 || bundle
->fKeyMap
!= NULL
) {
1487 map
= (KeyMapEntry
*)uprv_malloc(keysCount
* sizeof(KeyMapEntry
));
1489 *status
= U_MEMORY_ALLOCATION_ERROR
;
1492 keys
= (char *)bundle
->fPoolBundleKeys
;
1493 for (i
= 0; i
< bundle
->fPoolBundleKeysCount
; ++i
) {
1495 (int32_t)(keys
- bundle
->fPoolBundleKeys
) | 0x80000000; /* negative oldpos */
1497 while (*keys
!= 0) { ++keys
; } /* skip the key */
1498 ++keys
; /* skip the NUL */
1500 keys
= bundle
->fKeys
+ bundle
->fKeysBottom
;
1501 for (; i
< keysCount
; ++i
) {
1502 map
[i
].oldpos
= (int32_t)(keys
- bundle
->fKeys
);
1504 while (*keys
!= 0) { ++keys
; } /* skip the key */
1505 ++keys
; /* skip the NUL */
1507 /* Sort the keys so that each one is immediately followed by all of its suffixes. */
1508 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1509 compareKeySuffixes
, bundle
, FALSE
, status
);
1511 * Make suffixes point into earlier, longer strings that contain them
1512 * and mark the old, now unused suffix bytes as deleted.
1514 if (U_SUCCESS(*status
)) {
1515 keys
= bundle
->fKeys
;
1516 for (i
= 0; i
< keysCount
;) {
1518 * This key is not a suffix of the previous one;
1519 * keep this one and delete the following ones that are
1520 * suffixes of this one.
1523 const char *keyLimit
;
1525 map
[i
].newpos
= map
[i
].oldpos
;
1526 if (j
< keysCount
&& map
[j
].oldpos
< 0) {
1527 /* Key string from the pool bundle, do not delete. */
1531 key
= getKeyString(bundle
, map
[i
].oldpos
);
1532 for (keyLimit
= key
; *keyLimit
!= 0; ++keyLimit
) {}
1533 for (; j
< keysCount
&& map
[j
].oldpos
>= 0; ++j
) {
1536 const char *suffixLimit
;
1538 suffix
= keys
+ map
[j
].oldpos
;
1539 for (suffixLimit
= suffix
; *suffixLimit
!= 0; ++suffixLimit
) {}
1540 offset
= (int32_t)(keyLimit
- key
) - (suffixLimit
- suffix
);
1542 break; /* suffix cannot be longer than the original */
1544 /* Is it a suffix of the earlier, longer key? */
1545 for (k
= keyLimit
; suffix
< suffixLimit
&& *--k
== *--suffixLimit
;) {}
1546 if (suffix
== suffixLimit
&& *k
== *suffixLimit
) {
1547 map
[j
].newpos
= map
[i
].oldpos
+ offset
; /* yes, point to the earlier key */
1548 /* mark the suffix as deleted */
1549 while (*suffix
!= 0) { *suffix
++ = 1; }
1552 break; /* not a suffix, restart from here */
1558 * Re-sort by newpos, then modify the key characters array in-place
1559 * to squeeze out unused bytes, and readjust the newpos offsets.
1561 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1562 compareKeyNewpos
, NULL
, FALSE
, status
);
1563 if (U_SUCCESS(*status
)) {
1564 int32_t oldpos
, newpos
, limit
;
1565 oldpos
= newpos
= bundle
->fKeysBottom
;
1566 limit
= bundle
->fKeysTop
;
1567 /* skip key offsets that point into the pool bundle rather than this new bundle */
1568 for (i
= 0; i
< keysCount
&& map
[i
].newpos
< 0; ++i
) {}
1569 if (i
< keysCount
) {
1570 while (oldpos
< limit
) {
1571 if (keys
[oldpos
] == 1) {
1572 ++oldpos
; /* skip unused bytes */
1574 /* adjust the new offsets for keys starting here */
1575 while (i
< keysCount
&& map
[i
].newpos
== oldpos
) {
1576 map
[i
++].newpos
= newpos
;
1578 /* move the key characters to their new position */
1579 keys
[newpos
++] = keys
[oldpos
++];
1582 assert(i
== keysCount
);
1584 bundle
->fKeysTop
= newpos
;
1585 /* Re-sort once more, by old offsets for binary searching. */
1586 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1587 compareKeyOldpos
, NULL
, FALSE
, status
);
1588 if (U_SUCCESS(*status
)) {
1589 /* key size reduction by limit - newpos */
1590 bundle
->fKeyMap
= map
;
1598 static int32_t U_CALLCONV
1599 compareStringSuffixes(const void *context
, const void *l
, const void *r
) {
1600 struct SResource
*left
= *((struct SResource
**)l
);
1601 struct SResource
*right
= *((struct SResource
**)r
);
1602 const UChar
*lStart
= left
->u
.fString
.fChars
;
1603 const UChar
*lLimit
= lStart
+ left
->u
.fString
.fLength
;
1604 const UChar
*rStart
= right
->u
.fString
.fChars
;
1605 const UChar
*rLimit
= rStart
+ right
->u
.fString
.fLength
;
1607 /* compare keys in reverse character order */
1608 while (lStart
< lLimit
&& rStart
< rLimit
) {
1609 diff
= (int32_t)*--lLimit
- (int32_t)*--rLimit
;
1614 /* sort equal suffixes by descending string length */
1615 return right
->u
.fString
.fLength
- left
->u
.fString
.fLength
;
1618 static int32_t U_CALLCONV
1619 compareStringLengths(const void *context
, const void *l
, const void *r
) {
1620 struct SResource
*left
= *((struct SResource
**)l
);
1621 struct SResource
*right
= *((struct SResource
**)r
);
1623 /* Make "is suffix of another string" compare greater than a non-suffix. */
1624 diff
= (int)(left
->u
.fString
.fSame
!= NULL
) - (int)(right
->u
.fString
.fSame
!= NULL
);
1628 /* sort by ascending string length */
1629 return left
->u
.fString
.fLength
- right
->u
.fString
.fLength
;
1633 string_writeUTF16v2(struct SRBRoot
*bundle
, struct SResource
*res
, int32_t utf16Length
) {
1634 int32_t length
= res
->u
.fString
.fLength
;
1635 res
->fRes
= URES_MAKE_RESOURCE(URES_STRING_V2
, utf16Length
);
1636 res
->fWritten
= TRUE
;
1637 switch(res
->u
.fString
.fNumCharsForLength
) {
1641 bundle
->f16BitUnits
[utf16Length
++] = (uint16_t)(0xdc00 + length
);
1644 bundle
->f16BitUnits
[utf16Length
] = (uint16_t)(0xdfef + (length
>> 16));
1645 bundle
->f16BitUnits
[utf16Length
+ 1] = (uint16_t)length
;
1649 bundle
->f16BitUnits
[utf16Length
] = 0xdfff;
1650 bundle
->f16BitUnits
[utf16Length
+ 1] = (uint16_t)(length
>> 16);
1651 bundle
->f16BitUnits
[utf16Length
+ 2] = (uint16_t)length
;
1655 break; /* will not occur */
1657 u_memcpy(bundle
->f16BitUnits
+ utf16Length
, res
->u
.fString
.fChars
, length
+ 1);
1658 return utf16Length
+ length
+ 1;
1662 bundle_compactStrings(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1663 if (U_FAILURE(*status
)) {
1666 switch(bundle
->fStringsForm
) {
1667 case STRINGS_UTF16_V2
:
1668 if (bundle
->f16BitUnitsLength
> 0) {
1669 struct SResource
**array
;
1670 int32_t count
= uhash_count(bundle
->fStringSet
);
1673 * Allocate enough space for the initial NUL and the UTF-16 v2 strings,
1674 * and some extra for URES_TABLE16 and URES_ARRAY16 values.
1675 * Round down to an even number.
1677 int32_t utf16Length
= (bundle
->f16BitUnitsLength
+ 20000) & ~1;
1678 bundle
->f16BitUnits
= (UChar
*)uprv_malloc(utf16Length
* U_SIZEOF_UCHAR
);
1679 array
= (struct SResource
**)uprv_malloc(count
* sizeof(struct SResource
**));
1680 if (bundle
->f16BitUnits
== NULL
|| array
== NULL
) {
1681 uprv_free(bundle
->f16BitUnits
);
1682 bundle
->f16BitUnits
= NULL
;
1684 *status
= U_MEMORY_ALLOCATION_ERROR
;
1687 bundle
->f16BitUnitsCapacity
= utf16Length
;
1688 /* insert the initial NUL */
1689 bundle
->f16BitUnits
[0] = 0;
1691 ++bundle
->f16BitUnitsLength
;
1692 for (pos
= -1, i
= 0; i
< count
; ++i
) {
1693 array
[i
] = (struct SResource
*)uhash_nextElement(bundle
->fStringSet
, &pos
)->key
.pointer
;
1695 /* Sort the strings so that each one is immediately followed by all of its suffixes. */
1696 uprv_sortArray(array
, count
, (int32_t)sizeof(struct SResource
**),
1697 compareStringSuffixes
, NULL
, FALSE
, status
);
1699 * Make suffixes point into earlier, longer strings that contain them.
1700 * Temporarily use fSame and fSuffixOffset for suffix strings to
1701 * refer to the remaining ones.
1703 if (U_SUCCESS(*status
)) {
1704 for (i
= 0; i
< count
;) {
1706 * This string is not a suffix of the previous one;
1707 * write this one and subsume the following ones that are
1708 * suffixes of this one.
1710 struct SResource
*res
= array
[i
];
1711 const UChar
*strLimit
= res
->u
.fString
.fChars
+ res
->u
.fString
.fLength
;
1713 for (j
= i
+ 1; j
< count
; ++j
) {
1714 struct SResource
*suffixRes
= array
[j
];
1716 const UChar
*suffix
= suffixRes
->u
.fString
.fChars
;
1717 const UChar
*suffixLimit
= suffix
+ suffixRes
->u
.fString
.fLength
;
1718 int32_t offset
= res
->u
.fString
.fLength
- suffixRes
->u
.fString
.fLength
;
1720 break; /* suffix cannot be longer than the original */
1722 /* Is it a suffix of the earlier, longer key? */
1723 for (s
= strLimit
; suffix
< suffixLimit
&& *--s
== *--suffixLimit
;) {}
1724 if (suffix
== suffixLimit
&& *s
== *suffixLimit
) {
1725 if (suffixRes
->u
.fString
.fNumCharsForLength
== 0) {
1726 /* yes, point to the earlier string */
1727 suffixRes
->u
.fString
.fSame
= res
;
1728 suffixRes
->u
.fString
.fSuffixOffset
= offset
;
1730 /* write the suffix by itself if we need explicit length */
1733 break; /* not a suffix, restart from here */
1740 * Re-sort the strings by ascending length (except suffixes last)
1741 * to optimize for URES_TABLE16 and URES_ARRAY16:
1742 * Keep as many as possible within reach of 16-bit offsets.
1744 uprv_sortArray(array
, count
, (int32_t)sizeof(struct SResource
**),
1745 compareStringLengths
, NULL
, FALSE
, status
);
1746 if (U_SUCCESS(*status
)) {
1747 /* Write the non-suffix strings. */
1748 for (i
= 0; i
< count
&& array
[i
]->u
.fString
.fSame
== NULL
; ++i
) {
1749 utf16Length
= string_writeUTF16v2(bundle
, array
[i
], utf16Length
);
1751 /* Write the suffix strings. Make each point to the real string. */
1752 for (; i
< count
; ++i
) {
1753 struct SResource
*res
= array
[i
];
1754 struct SResource
*same
= res
->u
.fString
.fSame
;
1755 res
->fRes
= same
->fRes
+ same
->u
.fString
.fNumCharsForLength
+ res
->u
.fString
.fSuffixOffset
;
1756 res
->u
.fString
.fSame
= NULL
;
1757 res
->fWritten
= TRUE
;
1760 assert(utf16Length
<= bundle
->f16BitUnitsLength
);
1761 bundle
->f16BitUnitsLength
= utf16Length
;