2 *******************************************************************************
4 * Copyright (C) 2000-2010, 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"
30 * Align binary data at a 16-byte offset from the start of the resource bundle,
31 * to be safe for any data type it may contain.
33 #define BIN_ALIGNMENT 16
35 static UBool gIncludeCopyright
= FALSE
;
36 static UBool gUsePoolBundle
= FALSE
;
37 static int32_t gFormatVersion
= 2;
39 static UChar gEmptyString
= 0;
41 /* How do we store string values? */
43 STRINGS_UTF16_V1
, /* formatVersion 1: int length + UChars + NUL + padding to 4 bytes */
44 STRINGS_UTF16_V2
/* formatVersion 2: optional length in 1..3 UChars + UChars + NUL */
48 MAX_IMPLICIT_STRING_LENGTH
= 40 /* do not store the length explicitly for such strings */
52 * res_none() returns the address of kNoResource,
53 * for use in non-error cases when no resource is to be added to the bundle.
54 * (NULL is used in error cases.)
56 static const struct SResource kNoResource
= { URES_NONE
};
58 static UDataInfo dataInfo
= {
67 {0x52, 0x65, 0x73, 0x42}, /* dataFormat="ResB" */
68 {1, 3, 0, 0}, /* formatVersion */
69 {1, 4, 0, 0} /* dataVersion take a look at version inside parsed resb*/
72 static const UVersionInfo gFormatVersions
[3] = { /* indexed by a major-formatVersion integer */
78 static uint8_t calcPadding(uint32_t size
) {
79 /* returns space we need to pad */
80 return (uint8_t) ((size
% sizeof(uint32_t)) ? (sizeof(uint32_t) - (size
% sizeof(uint32_t))) : 0);
84 void setIncludeCopyright(UBool val
){
85 gIncludeCopyright
=val
;
88 UBool
getIncludeCopyright(void){
89 return gIncludeCopyright
;
92 void setFormatVersion(int32_t formatVersion
) {
93 gFormatVersion
= formatVersion
;
96 void setUsePoolBundle(UBool use
) {
101 bundle_compactStrings(struct SRBRoot
*bundle
, UErrorCode
*status
);
103 /* Writing Functions */
106 * type_write16() functions write resource values into f16BitUnits
107 * and determine the resource item word, if possible.
110 res_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
114 * type_preWrite() functions calculate ("preflight") and advance the *byteOffset
115 * by the size of their data in the binary file and
116 * determine the resource item word.
117 * Most type_preWrite() functions may add any number of bytes, but res_preWrite()
118 * will always pad it to a multiple of 4.
119 * The resource item type may be a related subtype of the fType.
121 * The type_preWrite() and type_write() functions start and end at the same
123 * Prewriting allows bundle_write() to determine the root resource item word,
124 * before actually writing the bundle contents to the file,
125 * which is necessary because the root item is stored at the beginning.
128 res_preWrite(uint32_t *byteOffset
,
129 struct SRBRoot
*bundle
, struct SResource
*res
,
133 * type_write() functions write their data to mem and update the byteOffset
135 * (A kingdom for C++ and polymorphism...)
138 res_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
139 struct SRBRoot
*bundle
, struct SResource
*res
,
143 reserve16BitUnits(struct SRBRoot
*bundle
, int32_t length
, UErrorCode
*status
) {
144 if (U_FAILURE(*status
)) {
147 if ((bundle
->f16BitUnitsLength
+ length
) > bundle
->f16BitUnitsCapacity
) {
149 int32_t capacity
= 2 * bundle
->f16BitUnitsCapacity
+ length
+ 1024;
150 capacity
&= ~1; /* ensures padding fits if f16BitUnitsLength needs it */
151 newUnits
= (uint16_t *)uprv_malloc(capacity
* 2);
152 if (newUnits
== NULL
) {
153 *status
= U_MEMORY_ALLOCATION_ERROR
;
156 if (bundle
->f16BitUnitsLength
> 0) {
157 uprv_memcpy(newUnits
, bundle
->f16BitUnits
, bundle
->f16BitUnitsLength
* 2);
160 bundle
->f16BitUnitsLength
= 1;
162 uprv_free(bundle
->f16BitUnits
);
163 bundle
->f16BitUnits
= newUnits
;
164 bundle
->f16BitUnitsCapacity
= capacity
;
166 return bundle
->f16BitUnits
+ bundle
->f16BitUnitsLength
;
170 makeRes16(uint32_t resWord
) {
171 uint32_t type
, offset
;
173 return 0; /* empty string */
175 type
= RES_GET_TYPE(resWord
);
176 offset
= RES_GET_OFFSET(resWord
);
177 if (type
== URES_STRING_V2
&& offset
<= 0xffff) {
178 return (int32_t)offset
;
184 mapKey(struct SRBRoot
*bundle
, int32_t oldpos
) {
185 const KeyMapEntry
*map
= bundle
->fKeyMap
;
186 int32_t i
, start
, limit
;
188 /* do a binary search for the old, pre-bundle_compactKeys() key offset */
189 start
= bundle
->fPoolBundleKeysCount
;
190 limit
= start
+ bundle
->fKeysCount
;
191 while (start
< limit
- 1) {
192 i
= (start
+ limit
) / 2;
193 if (oldpos
< map
[i
].oldpos
) {
199 assert(oldpos
== map
[start
].oldpos
);
200 return map
[start
].newpos
;
204 makeKey16(struct SRBRoot
*bundle
, int32_t key
) {
206 return (uint16_t)key
;
208 return (uint16_t)(key
+ bundle
->fLocalKeyLimit
); /* offset in the pool bundle */
213 * Only called for UTF-16 v1 strings and duplicate UTF-16 v2 strings.
214 * For unique UTF-16 v2 strings, res_write16() sees fRes != RES_BOGUS
218 string_write16(struct SRBRoot
*bundle
, struct SResource
*res
, UErrorCode
*status
) {
219 struct SResource
*same
;
220 if ((same
= res
->u
.fString
.fSame
) != NULL
) {
221 /* This is a duplicate. */
222 if (same
->fRes
== RES_BOGUS
) {
223 /* The original has not been visited yet. */
224 string_write16(bundle
, same
, status
);
226 res
->fRes
= same
->fRes
;
227 res
->fWritten
= same
->fWritten
;
232 array_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
233 UErrorCode
*status
) {
234 struct SResource
*current
;
237 if (U_FAILURE(*status
)) {
240 if (res
->u
.fArray
.fCount
== 0 && gFormatVersion
> 1) {
241 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_ARRAY
);
242 res
->fWritten
= TRUE
;
245 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
246 res_write16(bundle
, current
, status
);
247 res16
|= makeRes16(current
->fRes
);
249 if (U_SUCCESS(*status
) && res
->u
.fArray
.fCount
<= 0xffff && res16
>= 0 && gFormatVersion
> 1) {
250 uint16_t *p16
= reserve16BitUnits(bundle
, 1 + res
->u
.fArray
.fCount
, status
);
251 if (U_SUCCESS(*status
)) {
252 res
->fRes
= URES_MAKE_RESOURCE(URES_ARRAY16
, bundle
->f16BitUnitsLength
);
253 *p16
++ = (uint16_t)res
->u
.fArray
.fCount
;
254 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
255 *p16
++ = (uint16_t)makeRes16(current
->fRes
);
257 bundle
->f16BitUnitsLength
+= 1 + res
->u
.fArray
.fCount
;
258 res
->fWritten
= TRUE
;
264 table_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
265 UErrorCode
*status
) {
266 struct SResource
*current
;
267 int32_t maxKey
= 0, maxPoolKey
= 0x80000000;
269 UBool hasLocalKeys
= FALSE
, hasPoolKeys
= FALSE
;
271 if (U_FAILURE(*status
)) {
274 if (res
->u
.fTable
.fCount
== 0 && gFormatVersion
> 1) {
275 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_TABLE
);
276 res
->fWritten
= TRUE
;
279 /* Find the smallest table type that fits the data. */
280 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
282 res_write16(bundle
, current
, status
);
283 if (bundle
->fKeyMap
== NULL
) {
286 key
= current
->fKey
= mapKey(bundle
, current
->fKey
);
295 if (key
> maxPoolKey
) {
299 res16
|= makeRes16(current
->fRes
);
301 if (U_FAILURE(*status
)) {
304 if(res
->u
.fTable
.fCount
> (uint32_t)bundle
->fMaxTableLength
) {
305 bundle
->fMaxTableLength
= res
->u
.fTable
.fCount
;
307 maxPoolKey
&= 0x7fffffff;
308 if (res
->u
.fTable
.fCount
<= 0xffff &&
309 (!hasLocalKeys
|| maxKey
< bundle
->fLocalKeyLimit
) &&
310 (!hasPoolKeys
|| maxPoolKey
< (0x10000 - bundle
->fLocalKeyLimit
))
312 if (res16
>= 0 && gFormatVersion
> 1) {
313 uint16_t *p16
= reserve16BitUnits(bundle
, 1 + res
->u
.fTable
.fCount
* 2, status
);
314 if (U_SUCCESS(*status
)) {
315 /* 16-bit count, key offsets and values */
316 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE16
, bundle
->f16BitUnitsLength
);
317 *p16
++ = (uint16_t)res
->u
.fTable
.fCount
;
318 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
319 *p16
++ = makeKey16(bundle
, current
->fKey
);
321 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
322 *p16
++ = (uint16_t)makeRes16(current
->fRes
);
324 bundle
->f16BitUnitsLength
+= 1 + res
->u
.fTable
.fCount
* 2;
325 res
->fWritten
= TRUE
;
328 /* 16-bit count, 16-bit key offsets, 32-bit values */
329 res
->u
.fTable
.fType
= URES_TABLE
;
332 /* 32-bit count, key offsets and values */
333 res
->u
.fTable
.fType
= URES_TABLE32
;
338 res_write16(struct SRBRoot
*bundle
, struct SResource
*res
,
339 UErrorCode
*status
) {
340 if (U_FAILURE(*status
) || res
== NULL
) {
343 if (res
->fRes
!= RES_BOGUS
) {
345 * The resource item word was already precomputed, which means
346 * no further data needs to be written.
347 * This might be an integer, or an empty or UTF-16 v2 string,
348 * an empty binary, etc.
352 switch (res
->fType
) {
354 string_write16(bundle
, res
, status
);
357 array_write16(bundle
, res
, status
);
360 table_write16(bundle
, res
, status
);
363 /* Only a few resource types write 16-bit units. */
369 * Only called for UTF-16 v1 strings.
370 * For UTF-16 v2 strings, res_preWrite() sees fRes != RES_BOGUS
374 string_preWrite(uint32_t *byteOffset
,
375 struct SRBRoot
*bundle
, struct SResource
*res
,
376 UErrorCode
*status
) {
377 /* Write the UTF-16 v1 string. */
378 res
->fRes
= URES_MAKE_RESOURCE(URES_STRING
, *byteOffset
>> 2);
379 *byteOffset
+= 4 + (res
->u
.fString
.fLength
+ 1) * U_SIZEOF_UCHAR
;
383 bin_preWrite(uint32_t *byteOffset
,
384 struct SRBRoot
*bundle
, struct SResource
*res
,
385 UErrorCode
*status
) {
387 uint32_t dataStart
= *byteOffset
+ sizeof(res
->u
.fBinaryValue
.fLength
);
389 if (dataStart
% BIN_ALIGNMENT
) {
390 pad
= (BIN_ALIGNMENT
- dataStart
% BIN_ALIGNMENT
);
391 *byteOffset
+= pad
; /* pad == 4 or 8 or 12 */
393 res
->fRes
= URES_MAKE_RESOURCE(URES_BINARY
, *byteOffset
>> 2);
394 *byteOffset
+= 4 + res
->u
.fBinaryValue
.fLength
;
398 array_preWrite(uint32_t *byteOffset
,
399 struct SRBRoot
*bundle
, struct SResource
*res
,
400 UErrorCode
*status
) {
401 struct SResource
*current
;
403 if (U_FAILURE(*status
)) {
406 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
407 res_preWrite(byteOffset
, bundle
, current
, status
);
409 res
->fRes
= URES_MAKE_RESOURCE(URES_ARRAY
, *byteOffset
>> 2);
410 *byteOffset
+= (1 + res
->u
.fArray
.fCount
) * 4;
414 table_preWrite(uint32_t *byteOffset
,
415 struct SRBRoot
*bundle
, struct SResource
*res
,
416 UErrorCode
*status
) {
417 struct SResource
*current
;
419 if (U_FAILURE(*status
)) {
422 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
423 res_preWrite(byteOffset
, bundle
, current
, status
);
425 if (res
->u
.fTable
.fType
== URES_TABLE
) {
426 /* 16-bit count, 16-bit key offsets, 32-bit values */
427 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE
, *byteOffset
>> 2);
428 *byteOffset
+= 2 + res
->u
.fTable
.fCount
* 6;
430 /* 32-bit count, key offsets and values */
431 res
->fRes
= URES_MAKE_RESOURCE(URES_TABLE32
, *byteOffset
>> 2);
432 *byteOffset
+= 4 + res
->u
.fTable
.fCount
* 8;
437 res_preWrite(uint32_t *byteOffset
,
438 struct SRBRoot
*bundle
, struct SResource
*res
,
439 UErrorCode
*status
) {
440 if (U_FAILURE(*status
) || res
== NULL
) {
443 if (res
->fRes
!= RES_BOGUS
) {
445 * The resource item word was already precomputed, which means
446 * no further data needs to be written.
447 * This might be an integer, or an empty or UTF-16 v2 string,
448 * an empty binary, etc.
452 switch (res
->fType
) {
454 string_preWrite(byteOffset
, bundle
, res
, status
);
457 res
->fRes
= URES_MAKE_RESOURCE(URES_ALIAS
, *byteOffset
>> 2);
458 *byteOffset
+= 4 + (res
->u
.fString
.fLength
+ 1) * U_SIZEOF_UCHAR
;
460 case URES_INT_VECTOR
:
461 if (res
->u
.fIntVector
.fCount
== 0 && gFormatVersion
> 1) {
462 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_INT_VECTOR
);
463 res
->fWritten
= TRUE
;
465 res
->fRes
= URES_MAKE_RESOURCE(URES_INT_VECTOR
, *byteOffset
>> 2);
466 *byteOffset
+= (1 + res
->u
.fIntVector
.fCount
) * 4;
470 bin_preWrite(byteOffset
, bundle
, res
, status
);
475 array_preWrite(byteOffset
, bundle
, res
, status
);
478 table_preWrite(byteOffset
, bundle
, res
, status
);
481 *status
= U_INTERNAL_PROGRAM_ERROR
;
484 *byteOffset
+= calcPadding(*byteOffset
);
488 * Only called for UTF-16 v1 strings. For UTF-16 v2 strings,
489 * res_write() sees fWritten and exits early.
491 static void string_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
492 struct SRBRoot
*bundle
, struct SResource
*res
,
493 UErrorCode
*status
) {
494 /* Write the UTF-16 v1 string. */
495 int32_t length
= res
->u
.fString
.fLength
;
496 udata_write32(mem
, length
);
497 udata_writeUString(mem
, res
->u
.fString
.fChars
, length
+ 1);
498 *byteOffset
+= 4 + (length
+ 1) * U_SIZEOF_UCHAR
;
499 res
->fWritten
= TRUE
;
502 static void alias_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
503 struct SRBRoot
*bundle
, struct SResource
*res
,
504 UErrorCode
*status
) {
505 int32_t length
= res
->u
.fString
.fLength
;
506 udata_write32(mem
, length
);
507 udata_writeUString(mem
, res
->u
.fString
.fChars
, length
+ 1);
508 *byteOffset
+= 4 + (length
+ 1) * U_SIZEOF_UCHAR
;
511 static void array_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
512 struct SRBRoot
*bundle
, struct SResource
*res
,
513 UErrorCode
*status
) {
516 struct SResource
*current
= NULL
;
518 if (U_FAILURE(*status
)) {
521 for (i
= 0, current
= res
->u
.fArray
.fFirst
; current
!= NULL
; ++i
, current
= current
->fNext
) {
522 res_write(mem
, byteOffset
, bundle
, current
, status
);
524 assert(i
== res
->u
.fArray
.fCount
);
526 udata_write32(mem
, res
->u
.fArray
.fCount
);
527 for (current
= res
->u
.fArray
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
528 udata_write32(mem
, current
->fRes
);
530 *byteOffset
+= (1 + res
->u
.fArray
.fCount
) * 4;
533 static void intvector_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
534 struct SRBRoot
*bundle
, struct SResource
*res
,
535 UErrorCode
*status
) {
537 udata_write32(mem
, res
->u
.fIntVector
.fCount
);
538 for(i
= 0; i
<res
->u
.fIntVector
.fCount
; i
++) {
539 udata_write32(mem
, res
->u
.fIntVector
.fArray
[i
]);
541 *byteOffset
+= (1 + res
->u
.fIntVector
.fCount
) * 4;
544 static void bin_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
545 struct SRBRoot
*bundle
, struct SResource
*res
,
546 UErrorCode
*status
) {
548 uint32_t dataStart
= *byteOffset
+ sizeof(res
->u
.fBinaryValue
.fLength
);
550 if (dataStart
% BIN_ALIGNMENT
) {
551 pad
= (BIN_ALIGNMENT
- dataStart
% BIN_ALIGNMENT
);
552 udata_writePadding(mem
, pad
); /* pad == 4 or 8 or 12 */
556 udata_write32(mem
, res
->u
.fBinaryValue
.fLength
);
557 if (res
->u
.fBinaryValue
.fLength
> 0) {
558 udata_writeBlock(mem
, res
->u
.fBinaryValue
.fData
, res
->u
.fBinaryValue
.fLength
);
560 *byteOffset
+= 4 + res
->u
.fBinaryValue
.fLength
;
563 static void table_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
564 struct SRBRoot
*bundle
, struct SResource
*res
,
565 UErrorCode
*status
) {
566 struct SResource
*current
;
569 if (U_FAILURE(*status
)) {
572 for (i
= 0, current
= res
->u
.fTable
.fFirst
; current
!= NULL
; ++i
, current
= current
->fNext
) {
573 assert(i
< res
->u
.fTable
.fCount
);
574 res_write(mem
, byteOffset
, bundle
, current
, status
);
576 assert(i
== res
->u
.fTable
.fCount
);
578 if(res
->u
.fTable
.fType
== URES_TABLE
) {
579 udata_write16(mem
, (uint16_t)res
->u
.fTable
.fCount
);
580 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
581 udata_write16(mem
, makeKey16(bundle
, current
->fKey
));
583 *byteOffset
+= (1 + res
->u
.fTable
.fCount
)* 2;
584 if ((res
->u
.fTable
.fCount
& 1) == 0) {
585 /* 16-bit count and even number of 16-bit key offsets need padding before 32-bit resource items */
586 udata_writePadding(mem
, 2);
589 } else /* URES_TABLE32 */ {
590 udata_write32(mem
, res
->u
.fTable
.fCount
);
591 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
592 udata_write32(mem
, (uint32_t)current
->fKey
);
594 *byteOffset
+= (1 + res
->u
.fTable
.fCount
)* 4;
596 for (current
= res
->u
.fTable
.fFirst
; current
!= NULL
; current
= current
->fNext
) {
597 udata_write32(mem
, current
->fRes
);
599 *byteOffset
+= res
->u
.fTable
.fCount
* 4;
602 void res_write(UNewDataMemory
*mem
, uint32_t *byteOffset
,
603 struct SRBRoot
*bundle
, struct SResource
*res
,
604 UErrorCode
*status
) {
607 if (U_FAILURE(*status
) || res
== NULL
) {
611 assert(res
->fRes
!= RES_BOGUS
);
614 switch (res
->fType
) {
616 string_write (mem
, byteOffset
, bundle
, res
, status
);
619 alias_write (mem
, byteOffset
, bundle
, res
, status
);
621 case URES_INT_VECTOR
:
622 intvector_write (mem
, byteOffset
, bundle
, res
, status
);
625 bin_write (mem
, byteOffset
, bundle
, res
, status
);
628 break; /* fRes was set by int_open() */
630 array_write (mem
, byteOffset
, bundle
, res
, status
);
633 table_write (mem
, byteOffset
, bundle
, res
, status
);
636 *status
= U_INTERNAL_PROGRAM_ERROR
;
639 paddingSize
= calcPadding(*byteOffset
);
640 if (paddingSize
> 0) {
641 udata_writePadding(mem
, paddingSize
);
642 *byteOffset
+= paddingSize
;
644 res
->fWritten
= TRUE
;
647 void bundle_write(struct SRBRoot
*bundle
,
648 const char *outputDir
, const char *outputPkg
,
649 char *writtenFilename
, int writtenFilenameLen
,
650 UErrorCode
*status
) {
651 UNewDataMemory
*mem
= NULL
;
652 uint32_t byteOffset
= 0;
655 int32_t indexes
[URES_INDEX_TOP
];
657 bundle_compactKeys(bundle
, status
);
659 * Add padding bytes to fKeys so that fKeysTop is 4-aligned.
660 * Safe because the capacity is a multiple of 4.
662 while (bundle
->fKeysTop
& 3) {
663 bundle
->fKeys
[bundle
->fKeysTop
++] = (char)0xaa;
666 * In URES_TABLE, use all local key offsets that fit into 16 bits,
667 * and use the remaining 16-bit offsets for pool key offsets
669 * If there are no local keys, then use the whole 16-bit space
670 * for pool key offsets.
671 * Note: This cannot be changed without changing the major formatVersion.
673 if (bundle
->fKeysBottom
< bundle
->fKeysTop
) {
674 if (bundle
->fKeysTop
<= 0x10000) {
675 bundle
->fLocalKeyLimit
= bundle
->fKeysTop
;
677 bundle
->fLocalKeyLimit
= 0x10000;
680 bundle
->fLocalKeyLimit
= 0;
683 bundle_compactStrings(bundle
, status
);
684 res_write16(bundle
, bundle
->fRoot
, status
);
685 if (bundle
->f16BitUnitsLength
& 1) {
686 bundle
->f16BitUnits
[bundle
->f16BitUnitsLength
++] = 0xaaaa; /* pad to multiple of 4 bytes */
688 /* all keys have been mapped */
689 uprv_free(bundle
->fKeyMap
);
690 bundle
->fKeyMap
= NULL
;
692 byteOffset
= bundle
->fKeysTop
+ bundle
->f16BitUnitsLength
* 2;
693 res_preWrite(&byteOffset
, bundle
, bundle
->fRoot
, status
);
695 /* total size including the root item */
698 if (U_FAILURE(*status
)) {
702 if (writtenFilename
&& writtenFilenameLen
) {
703 *writtenFilename
= 0;
706 if (writtenFilename
) {
707 int32_t off
= 0, len
= 0;
709 len
= (int32_t)uprv_strlen(outputDir
);
710 if (len
> writtenFilenameLen
) {
711 len
= writtenFilenameLen
;
713 uprv_strncpy(writtenFilename
, outputDir
, len
);
715 if (writtenFilenameLen
-= len
) {
717 writtenFilename
[off
] = U_FILE_SEP_CHAR
;
718 if (--writtenFilenameLen
) {
720 if(outputPkg
!= NULL
)
722 uprv_strcpy(writtenFilename
+off
, outputPkg
);
723 off
+= (int32_t)uprv_strlen(outputPkg
);
724 writtenFilename
[off
] = '_';
728 len
= (int32_t)uprv_strlen(bundle
->fLocale
);
729 if (len
> writtenFilenameLen
) {
730 len
= writtenFilenameLen
;
732 uprv_strncpy(writtenFilename
+ off
, bundle
->fLocale
, len
);
733 if (writtenFilenameLen
-= len
) {
736 if (len
> writtenFilenameLen
) {
737 len
= writtenFilenameLen
;
739 uprv_strncpy(writtenFilename
+ off
, ".res", len
);
747 uprv_strcpy(dataName
, outputPkg
);
748 uprv_strcat(dataName
, "_");
749 uprv_strcat(dataName
, bundle
->fLocale
);
753 uprv_strcpy(dataName
, bundle
->fLocale
);
756 uprv_memcpy(dataInfo
.formatVersion
, gFormatVersions
+ gFormatVersion
, sizeof(UVersionInfo
));
758 mem
= udata_create(outputDir
, "res", dataName
, &dataInfo
, (gIncludeCopyright
==TRUE
)? U_COPYRIGHT_STRING
:NULL
, status
);
759 if(U_FAILURE(*status
)){
763 /* write the root item */
764 udata_write32(mem
, bundle
->fRoot
->fRes
);
767 * formatVersion 1.1 (ICU 2.8):
768 * write int32_t indexes[] after root and before the strings
769 * to make it easier to parse resource bundles in icuswap or from Java etc.
771 uprv_memset(indexes
, 0, sizeof(indexes
));
772 indexes
[URES_INDEX_LENGTH
]= bundle
->fIndexLength
;
773 indexes
[URES_INDEX_KEYS_TOP
]= bundle
->fKeysTop
>>2;
774 indexes
[URES_INDEX_RESOURCES_TOP
]= (int32_t)(top
>>2);
775 indexes
[URES_INDEX_BUNDLE_TOP
]= indexes
[URES_INDEX_RESOURCES_TOP
];
776 indexes
[URES_INDEX_MAX_TABLE_LENGTH
]= bundle
->fMaxTableLength
;
779 * formatVersion 1.2 (ICU 3.6):
780 * write indexes[URES_INDEX_ATTRIBUTES] with URES_ATT_NO_FALLBACK set or not set
781 * the memset() above initialized all indexes[] to 0
783 if (bundle
->noFallback
) {
784 indexes
[URES_INDEX_ATTRIBUTES
]=URES_ATT_NO_FALLBACK
;
787 * formatVersion 2.0 (ICU 4.4):
788 * more compact string value storage, optional pool bundle
790 if (URES_INDEX_16BIT_TOP
< bundle
->fIndexLength
) {
791 indexes
[URES_INDEX_16BIT_TOP
] = (bundle
->fKeysTop
>>2) + (bundle
->f16BitUnitsLength
>>1);
793 if (URES_INDEX_POOL_CHECKSUM
< bundle
->fIndexLength
) {
794 if (bundle
->fIsPoolBundle
) {
795 indexes
[URES_INDEX_ATTRIBUTES
] |= URES_ATT_IS_POOL_BUNDLE
| URES_ATT_NO_FALLBACK
;
796 indexes
[URES_INDEX_POOL_CHECKSUM
] =
797 (int32_t)computeCRC((char *)(bundle
->fKeys
+ bundle
->fKeysBottom
),
798 (uint32_t)(bundle
->fKeysTop
- bundle
->fKeysBottom
),
800 } else if (gUsePoolBundle
) {
801 indexes
[URES_INDEX_ATTRIBUTES
] |= URES_ATT_USES_POOL_BUNDLE
;
802 indexes
[URES_INDEX_POOL_CHECKSUM
] = bundle
->fPoolChecksum
;
806 /* write the indexes[] */
807 udata_writeBlock(mem
, indexes
, bundle
->fIndexLength
*4);
809 /* write the table key strings */
810 udata_writeBlock(mem
, bundle
->fKeys
+bundle
->fKeysBottom
,
811 bundle
->fKeysTop
-bundle
->fKeysBottom
);
813 /* write the v2 UTF-16 strings, URES_TABLE16 and URES_ARRAY16 */
814 udata_writeBlock(mem
, bundle
->f16BitUnits
, bundle
->f16BitUnitsLength
*2);
816 /* write all of the bundle contents: the root item and its children */
817 byteOffset
= bundle
->fKeysTop
+ bundle
->f16BitUnitsLength
* 2;
818 res_write(mem
, &byteOffset
, bundle
, bundle
->fRoot
, status
);
819 assert(byteOffset
== top
);
821 size
= udata_finish(mem
, status
);
823 fprintf(stderr
, "genrb error: wrote %u bytes but counted %u\n",
824 (int)size
, (int)top
);
825 *status
= U_INTERNAL_PROGRAM_ERROR
;
829 /* Opening Functions */
831 /* gcc 4.2 complained "no previous prototype for res_open" without this prototype... */
832 struct SResource
* res_open(struct SRBRoot
*bundle
, const char *tag
,
833 const struct UString
* comment
, UErrorCode
* status
);
835 struct SResource
* res_open(struct SRBRoot
*bundle
, const char *tag
,
836 const struct UString
* comment
, UErrorCode
* status
){
837 struct SResource
*res
;
838 int32_t key
= bundle_addtag(bundle
, tag
, status
);
839 if (U_FAILURE(*status
)) {
843 res
= (struct SResource
*) uprv_malloc(sizeof(struct SResource
));
845 *status
= U_MEMORY_ALLOCATION_ERROR
;
848 uprv_memset(res
, 0, sizeof(struct SResource
));
850 res
->fRes
= RES_BOGUS
;
852 ustr_init(&res
->fComment
);
854 ustr_cpy(&res
->fComment
, comment
, status
);
855 if (U_FAILURE(*status
)) {
863 struct SResource
* res_none() {
864 return (struct SResource
*)&kNoResource
;
867 struct SResource
* table_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
868 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
869 if (U_FAILURE(*status
)) {
872 res
->fType
= URES_TABLE
;
873 res
->u
.fTable
.fRoot
= bundle
;
877 struct SResource
* array_open(struct SRBRoot
*bundle
, const char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
878 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
879 if (U_FAILURE(*status
)) {
882 res
->fType
= URES_ARRAY
;
886 static int32_t U_CALLCONV
887 string_hash(const UHashTok key
) {
888 const struct SResource
*res
= (struct SResource
*)key
.pointer
;
889 return uhash_hashUCharsN(res
->u
.fString
.fChars
, res
->u
.fString
.fLength
);
892 static UBool U_CALLCONV
893 string_comp(const UHashTok key1
, const UHashTok key2
) {
894 const struct SResource
*res1
= (struct SResource
*)key1
.pointer
;
895 const struct SResource
*res2
= (struct SResource
*)key2
.pointer
;
896 return 0 == u_strCompare(res1
->u
.fString
.fChars
, res1
->u
.fString
.fLength
,
897 res2
->u
.fString
.fChars
, res2
->u
.fString
.fLength
,
901 struct SResource
*string_open(struct SRBRoot
*bundle
, char *tag
, const UChar
*value
, int32_t len
, const struct UString
* comment
, UErrorCode
*status
) {
902 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
903 if (U_FAILURE(*status
)) {
906 res
->fType
= URES_STRING
;
908 if (len
== 0 && gFormatVersion
> 1) {
909 res
->u
.fString
.fChars
= &gEmptyString
;
911 res
->fWritten
= TRUE
;
915 res
->u
.fString
.fLength
= len
;
917 if (gFormatVersion
> 1) {
918 /* check for duplicates */
919 res
->u
.fString
.fChars
= (UChar
*)value
;
920 if (bundle
->fStringSet
== NULL
) {
921 UErrorCode localStatus
= U_ZERO_ERROR
; /* if failure: just don't detect dups */
922 bundle
->fStringSet
= uhash_open(string_hash
, string_comp
, string_comp
, &localStatus
);
924 res
->u
.fString
.fSame
= uhash_get(bundle
->fStringSet
, res
);
927 if (res
->u
.fString
.fSame
== NULL
) {
928 /* this is a new string */
929 res
->u
.fString
.fChars
= (UChar
*) uprv_malloc(sizeof(UChar
) * (len
+ 1));
931 if (res
->u
.fString
.fChars
== NULL
) {
932 *status
= U_MEMORY_ALLOCATION_ERROR
;
937 uprv_memcpy(res
->u
.fString
.fChars
, value
, sizeof(UChar
) * len
);
938 res
->u
.fString
.fChars
[len
] = 0;
939 if (bundle
->fStringSet
!= NULL
) {
940 /* put it into the set for finding duplicates */
941 uhash_put(bundle
->fStringSet
, res
, res
, status
);
944 if (bundle
->fStringsForm
!= STRINGS_UTF16_V1
) {
945 if (len
<= MAX_IMPLICIT_STRING_LENGTH
&& !U16_IS_TRAIL(value
[0]) && len
== u_strlen(value
)) {
947 * This string will be stored without an explicit length.
948 * Runtime will detect !U16_IS_TRAIL(value[0]) and call u_strlen().
950 res
->u
.fString
.fNumCharsForLength
= 0;
951 } else if (len
<= 0x3ee) {
952 res
->u
.fString
.fNumCharsForLength
= 1;
953 } else if (len
<= 0xfffff) {
954 res
->u
.fString
.fNumCharsForLength
= 2;
956 res
->u
.fString
.fNumCharsForLength
= 3;
958 bundle
->f16BitUnitsLength
+= res
->u
.fString
.fNumCharsForLength
+ len
+ 1; /* +1 for the NUL */
961 /* this is a duplicate of fSame */
962 struct SResource
*same
= res
->u
.fString
.fSame
;
963 res
->u
.fString
.fChars
= same
->u
.fString
.fChars
;
968 /* TODO: make alias_open and string_open use the same code */
969 struct SResource
*alias_open(struct SRBRoot
*bundle
, char *tag
, UChar
*value
, int32_t len
, const struct UString
* comment
, UErrorCode
*status
) {
970 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
971 if (U_FAILURE(*status
)) {
974 res
->fType
= URES_ALIAS
;
975 if (len
== 0 && gFormatVersion
> 1) {
976 res
->u
.fString
.fChars
= &gEmptyString
;
977 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_ALIAS
);
978 res
->fWritten
= TRUE
;
982 res
->u
.fString
.fLength
= len
;
983 res
->u
.fString
.fChars
= (UChar
*) uprv_malloc(sizeof(UChar
) * (len
+ 1));
984 if (res
->u
.fString
.fChars
== NULL
) {
985 *status
= U_MEMORY_ALLOCATION_ERROR
;
989 uprv_memcpy(res
->u
.fString
.fChars
, value
, sizeof(UChar
) * (len
+ 1));
994 struct SResource
* intvector_open(struct SRBRoot
*bundle
, char *tag
, const struct UString
* comment
, UErrorCode
*status
) {
995 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
996 if (U_FAILURE(*status
)) {
999 res
->fType
= URES_INT_VECTOR
;
1001 res
->u
.fIntVector
.fCount
= 0;
1002 res
->u
.fIntVector
.fArray
= (uint32_t *) uprv_malloc(sizeof(uint32_t) * RESLIST_MAX_INT_VECTOR
);
1003 if (res
->u
.fIntVector
.fArray
== NULL
) {
1004 *status
= U_MEMORY_ALLOCATION_ERROR
;
1011 struct SResource
*int_open(struct SRBRoot
*bundle
, char *tag
, int32_t value
, const struct UString
* comment
, UErrorCode
*status
) {
1012 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1013 if (U_FAILURE(*status
)) {
1016 res
->fType
= URES_INT
;
1017 res
->u
.fIntValue
.fValue
= value
;
1018 res
->fRes
= URES_MAKE_RESOURCE(URES_INT
, value
& 0x0FFFFFFF);
1019 res
->fWritten
= TRUE
;
1023 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
) {
1024 struct SResource
*res
= res_open(bundle
, tag
, comment
, status
);
1025 if (U_FAILURE(*status
)) {
1028 res
->fType
= URES_BINARY
;
1030 res
->u
.fBinaryValue
.fLength
= length
;
1031 res
->u
.fBinaryValue
.fFileName
= NULL
;
1032 if(fileName
!=NULL
&& uprv_strcmp(fileName
, "") !=0){
1033 res
->u
.fBinaryValue
.fFileName
= (char*) uprv_malloc(sizeof(char) * (uprv_strlen(fileName
)+1));
1034 uprv_strcpy(res
->u
.fBinaryValue
.fFileName
,fileName
);
1037 res
->u
.fBinaryValue
.fData
= (uint8_t *) uprv_malloc(sizeof(uint8_t) * length
);
1039 if (res
->u
.fBinaryValue
.fData
== NULL
) {
1040 *status
= U_MEMORY_ALLOCATION_ERROR
;
1045 uprv_memcpy(res
->u
.fBinaryValue
.fData
, data
, length
);
1048 res
->u
.fBinaryValue
.fData
= NULL
;
1049 if (gFormatVersion
> 1) {
1050 res
->fRes
= URES_MAKE_EMPTY_RESOURCE(URES_BINARY
);
1051 res
->fWritten
= TRUE
;
1058 struct SRBRoot
*bundle_open(const struct UString
* comment
, UBool isPoolBundle
, UErrorCode
*status
) {
1059 struct SRBRoot
*bundle
;
1061 if (U_FAILURE(*status
)) {
1065 bundle
= (struct SRBRoot
*) uprv_malloc(sizeof(struct SRBRoot
));
1066 if (bundle
== NULL
) {
1067 *status
= U_MEMORY_ALLOCATION_ERROR
;
1070 uprv_memset(bundle
, 0, sizeof(struct SRBRoot
));
1072 bundle
->fKeys
= (char *) uprv_malloc(sizeof(char) * KEY_SPACE_SIZE
);
1073 bundle
->fRoot
= table_open(bundle
, NULL
, comment
, status
);
1074 if (bundle
->fKeys
== NULL
|| bundle
->fRoot
== NULL
|| U_FAILURE(*status
)) {
1075 if (U_SUCCESS(*status
)) {
1076 *status
= U_MEMORY_ALLOCATION_ERROR
;
1078 bundle_close(bundle
, status
);
1082 bundle
->fLocale
= NULL
;
1083 bundle
->fKeysCapacity
= KEY_SPACE_SIZE
;
1084 /* formatVersion 1.1: start fKeysTop after the root item and indexes[] */
1085 bundle
->fIsPoolBundle
= isPoolBundle
;
1086 if (gUsePoolBundle
|| isPoolBundle
) {
1087 bundle
->fIndexLength
= URES_INDEX_POOL_CHECKSUM
+ 1;
1088 } else if (gFormatVersion
>= 2) {
1089 bundle
->fIndexLength
= URES_INDEX_16BIT_TOP
+ 1;
1090 } else /* formatVersion 1 */ {
1091 bundle
->fIndexLength
= URES_INDEX_ATTRIBUTES
+ 1;
1093 bundle
->fKeysBottom
= (1 /* root */ + bundle
->fIndexLength
) * 4;
1094 uprv_memset(bundle
->fKeys
, 0, bundle
->fKeysBottom
);
1095 bundle
->fKeysTop
= bundle
->fKeysBottom
;
1097 if (gFormatVersion
== 1) {
1098 bundle
->fStringsForm
= STRINGS_UTF16_V1
;
1100 bundle
->fStringsForm
= STRINGS_UTF16_V2
;
1106 /* Closing Functions */
1107 static void table_close(struct SResource
*table
) {
1108 struct SResource
*current
= NULL
;
1109 struct SResource
*prev
= NULL
;
1111 current
= table
->u
.fTable
.fFirst
;
1113 while (current
!= NULL
) {
1115 current
= current
->fNext
;
1120 table
->u
.fTable
.fFirst
= NULL
;
1123 static void array_close(struct SResource
*array
) {
1124 struct SResource
*current
= NULL
;
1125 struct SResource
*prev
= NULL
;
1130 current
= array
->u
.fArray
.fFirst
;
1132 while (current
!= NULL
) {
1134 current
= current
->fNext
;
1138 array
->u
.fArray
.fFirst
= NULL
;
1141 static void string_close(struct SResource
*string
) {
1142 if (string
->u
.fString
.fChars
!= NULL
&&
1143 string
->u
.fString
.fChars
!= &gEmptyString
&&
1144 string
->u
.fString
.fSame
== NULL
1146 uprv_free(string
->u
.fString
.fChars
);
1147 string
->u
.fString
.fChars
=NULL
;
1151 static void alias_close(struct SResource
*alias
) {
1152 if (alias
->u
.fString
.fChars
!= NULL
) {
1153 uprv_free(alias
->u
.fString
.fChars
);
1154 alias
->u
.fString
.fChars
=NULL
;
1158 static void intvector_close(struct SResource
*intvector
) {
1159 if (intvector
->u
.fIntVector
.fArray
!= NULL
) {
1160 uprv_free(intvector
->u
.fIntVector
.fArray
);
1161 intvector
->u
.fIntVector
.fArray
=NULL
;
1165 static void int_close(struct SResource
*intres
) {
1166 /* Intentionally left blank */
1169 static void bin_close(struct SResource
*binres
) {
1170 if (binres
->u
.fBinaryValue
.fData
!= NULL
) {
1171 uprv_free(binres
->u
.fBinaryValue
.fData
);
1172 binres
->u
.fBinaryValue
.fData
= NULL
;
1176 void res_close(struct SResource
*res
) {
1178 switch(res
->fType
) {
1185 case URES_INT_VECTOR
:
1186 intvector_close(res
);
1201 /* Shouldn't happen */
1205 ustr_deinit(&res
->fComment
);
1210 void bundle_close(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1211 res_close(bundle
->fRoot
);
1212 uprv_free(bundle
->fLocale
);
1213 uprv_free(bundle
->fKeys
);
1214 uprv_free(bundle
->fKeyMap
);
1215 uhash_close(bundle
->fStringSet
);
1216 uprv_free(bundle
->f16BitUnits
);
1220 void bundle_closeString(struct SRBRoot
*bundle
, struct SResource
*string
) {
1221 if (bundle
->fStringSet
!= NULL
) {
1222 uhash_remove(bundle
->fStringSet
, string
);
1224 string_close(string
);
1227 /* Adding Functions */
1228 void table_add(struct SResource
*table
, struct SResource
*res
, int linenumber
, UErrorCode
*status
) {
1229 struct SResource
*current
= NULL
;
1230 struct SResource
*prev
= NULL
;
1231 struct SResTable
*list
;
1232 const char *resKeyString
;
1234 if (U_FAILURE(*status
)) {
1237 if (res
== &kNoResource
) {
1241 /* remember this linenumber to report to the user if there is a duplicate key */
1242 res
->line
= linenumber
;
1244 /* here we need to traverse the list */
1245 list
= &(table
->u
.fTable
);
1248 /* is list still empty? */
1249 if (list
->fFirst
== NULL
) {
1255 resKeyString
= list
->fRoot
->fKeys
+ res
->fKey
;
1257 current
= list
->fFirst
;
1259 while (current
!= NULL
) {
1260 const char *currentKeyString
= list
->fRoot
->fKeys
+ current
->fKey
;
1263 * formatVersion 1: compare key strings in native-charset order
1264 * formatVersion 2 and up: compare key strings in ASCII order
1266 if (gFormatVersion
== 1 || U_CHARSET_FAMILY
== U_ASCII_FAMILY
) {
1267 diff
= uprv_strcmp(currentKeyString
, resKeyString
);
1269 diff
= uprv_compareInvCharsAsAscii(currentKeyString
, resKeyString
);
1273 current
= current
->fNext
;
1274 } else if (diff
> 0) {
1275 /* we're either in front of list, or in middle */
1277 /* front of the list */
1280 /* middle of the list */
1284 res
->fNext
= current
;
1287 /* Key already exists! ERROR! */
1288 error(linenumber
, "duplicate key '%s' in table, first appeared at line %d", currentKeyString
, current
->line
);
1289 *status
= U_UNSUPPORTED_ERROR
;
1299 void array_add(struct SResource
*array
, struct SResource
*res
, UErrorCode
*status
) {
1300 if (U_FAILURE(*status
)) {
1304 if (array
->u
.fArray
.fFirst
== NULL
) {
1305 array
->u
.fArray
.fFirst
= res
;
1306 array
->u
.fArray
.fLast
= res
;
1308 array
->u
.fArray
.fLast
->fNext
= res
;
1309 array
->u
.fArray
.fLast
= res
;
1312 (array
->u
.fArray
.fCount
)++;
1315 void intvector_add(struct SResource
*intvector
, int32_t value
, UErrorCode
*status
) {
1316 if (U_FAILURE(*status
)) {
1320 *(intvector
->u
.fIntVector
.fArray
+ intvector
->u
.fIntVector
.fCount
) = value
;
1321 intvector
->u
.fIntVector
.fCount
++;
1324 /* Misc Functions */
1326 void bundle_setlocale(struct SRBRoot
*bundle
, UChar
*locale
, UErrorCode
*status
) {
1328 if(U_FAILURE(*status
)) {
1332 if (bundle
->fLocale
!=NULL
) {
1333 uprv_free(bundle
->fLocale
);
1336 bundle
->fLocale
= (char*) uprv_malloc(sizeof(char) * (u_strlen(locale
)+1));
1338 if(bundle
->fLocale
== NULL
) {
1339 *status
= U_MEMORY_ALLOCATION_ERROR
;
1343 /*u_strcpy(bundle->fLocale, locale);*/
1344 u_UCharsToChars(locale
, bundle
->fLocale
, u_strlen(locale
)+1);
1349 getKeyString(const struct SRBRoot
*bundle
, int32_t key
) {
1351 return bundle
->fPoolBundleKeys
+ (key
& 0x7fffffff);
1353 return bundle
->fKeys
+ key
;
1358 res_getKeyString(const struct SRBRoot
*bundle
, const struct SResource
*res
, char temp
[8]) {
1359 if (res
->fKey
== -1) {
1362 return getKeyString(bundle
, res
->fKey
);
1366 bundle_getKeyBytes(struct SRBRoot
*bundle
, int32_t *pLength
) {
1367 *pLength
= bundle
->fKeysTop
- bundle
->fKeysBottom
;
1368 return bundle
->fKeys
+ bundle
->fKeysBottom
;
1372 bundle_addKeyBytes(struct SRBRoot
*bundle
, const char *keyBytes
, int32_t length
, UErrorCode
*status
) {
1375 if (U_FAILURE(*status
)) {
1378 if (length
< 0 || (keyBytes
== NULL
&& length
!= 0)) {
1379 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1383 return bundle
->fKeysTop
;
1386 keypos
= bundle
->fKeysTop
;
1387 bundle
->fKeysTop
+= length
;
1388 if (bundle
->fKeysTop
>= bundle
->fKeysCapacity
) {
1389 /* overflow - resize the keys buffer */
1390 bundle
->fKeysCapacity
+= KEY_SPACE_SIZE
;
1391 bundle
->fKeys
= uprv_realloc(bundle
->fKeys
, bundle
->fKeysCapacity
);
1392 if(bundle
->fKeys
== NULL
) {
1393 *status
= U_MEMORY_ALLOCATION_ERROR
;
1398 uprv_memcpy(bundle
->fKeys
+ keypos
, keyBytes
, length
);
1404 bundle_addtag(struct SRBRoot
*bundle
, const char *tag
, UErrorCode
*status
) {
1407 if (U_FAILURE(*status
)) {
1412 /* no error: the root table and array items have no keys */
1416 keypos
= bundle_addKeyBytes(bundle
, tag
, (int32_t)(uprv_strlen(tag
) + 1), status
);
1417 if (U_SUCCESS(*status
)) {
1418 ++bundle
->fKeysCount
;
1424 compareInt32(int32_t lPos
, int32_t rPos
) {
1426 * Compare possibly-negative key offsets. Don't just return lPos - rPos
1427 * because that is prone to negative-integer underflows.
1431 } else if (lPos
> rPos
) {
1438 static int32_t U_CALLCONV
1439 compareKeySuffixes(const void *context
, const void *l
, const void *r
) {
1440 const struct SRBRoot
*bundle
=(const struct SRBRoot
*)context
;
1441 int32_t lPos
= ((const KeyMapEntry
*)l
)->oldpos
;
1442 int32_t rPos
= ((const KeyMapEntry
*)r
)->oldpos
;
1443 const char *lStart
= getKeyString(bundle
, lPos
);
1444 const char *lLimit
= lStart
;
1445 const char *rStart
= getKeyString(bundle
, rPos
);
1446 const char *rLimit
= rStart
;
1448 while (*lLimit
!= 0) { ++lLimit
; }
1449 while (*rLimit
!= 0) { ++rLimit
; }
1450 /* compare keys in reverse character order */
1451 while (lStart
< lLimit
&& rStart
< rLimit
) {
1452 diff
= (int32_t)(uint8_t)*--lLimit
- (int32_t)(uint8_t)*--rLimit
;
1457 /* sort equal suffixes by descending key length */
1458 diff
= (int32_t)(rLimit
- rStart
) - (int32_t)(lLimit
- lStart
);
1462 /* Sort pool bundle keys first (negative oldpos), and otherwise keys in parsing order. */
1463 return compareInt32(lPos
, rPos
);
1466 static int32_t U_CALLCONV
1467 compareKeyNewpos(const void *context
, const void *l
, const void *r
) {
1468 return compareInt32(((const KeyMapEntry
*)l
)->newpos
, ((const KeyMapEntry
*)r
)->newpos
);
1471 static int32_t U_CALLCONV
1472 compareKeyOldpos(const void *context
, const void *l
, const void *r
) {
1473 return compareInt32(((const KeyMapEntry
*)l
)->oldpos
, ((const KeyMapEntry
*)r
)->oldpos
);
1477 bundle_compactKeys(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1481 int32_t keysCount
= bundle
->fPoolBundleKeysCount
+ bundle
->fKeysCount
;
1482 if (U_FAILURE(*status
) || bundle
->fKeysCount
== 0 || bundle
->fKeyMap
!= NULL
) {
1485 map
= (KeyMapEntry
*)uprv_malloc(keysCount
* sizeof(KeyMapEntry
));
1487 *status
= U_MEMORY_ALLOCATION_ERROR
;
1490 keys
= (char *)bundle
->fPoolBundleKeys
;
1491 for (i
= 0; i
< bundle
->fPoolBundleKeysCount
; ++i
) {
1493 (int32_t)(keys
- bundle
->fPoolBundleKeys
) | 0x80000000; /* negative oldpos */
1495 while (*keys
!= 0) { ++keys
; } /* skip the key */
1496 ++keys
; /* skip the NUL */
1498 keys
= bundle
->fKeys
+ bundle
->fKeysBottom
;
1499 for (; i
< keysCount
; ++i
) {
1500 map
[i
].oldpos
= (int32_t)(keys
- bundle
->fKeys
);
1502 while (*keys
!= 0) { ++keys
; } /* skip the key */
1503 ++keys
; /* skip the NUL */
1505 /* Sort the keys so that each one is immediately followed by all of its suffixes. */
1506 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1507 compareKeySuffixes
, bundle
, FALSE
, status
);
1509 * Make suffixes point into earlier, longer strings that contain them
1510 * and mark the old, now unused suffix bytes as deleted.
1512 if (U_SUCCESS(*status
)) {
1513 keys
= bundle
->fKeys
;
1514 for (i
= 0; i
< keysCount
;) {
1516 * This key is not a suffix of the previous one;
1517 * keep this one and delete the following ones that are
1518 * suffixes of this one.
1521 const char *keyLimit
;
1523 map
[i
].newpos
= map
[i
].oldpos
;
1524 if (j
< keysCount
&& map
[j
].oldpos
< 0) {
1525 /* Key string from the pool bundle, do not delete. */
1529 key
= getKeyString(bundle
, map
[i
].oldpos
);
1530 for (keyLimit
= key
; *keyLimit
!= 0; ++keyLimit
) {}
1531 for (; j
< keysCount
&& map
[j
].oldpos
>= 0; ++j
) {
1534 const char *suffixLimit
;
1536 suffix
= keys
+ map
[j
].oldpos
;
1537 for (suffixLimit
= suffix
; *suffixLimit
!= 0; ++suffixLimit
) {}
1538 offset
= (int32_t)(keyLimit
- key
) - (suffixLimit
- suffix
);
1540 break; /* suffix cannot be longer than the original */
1542 /* Is it a suffix of the earlier, longer key? */
1543 for (k
= keyLimit
; suffix
< suffixLimit
&& *--k
== *--suffixLimit
;) {}
1544 if (suffix
== suffixLimit
&& *k
== *suffixLimit
) {
1545 map
[j
].newpos
= map
[i
].oldpos
+ offset
; /* yes, point to the earlier key */
1546 /* mark the suffix as deleted */
1547 while (*suffix
!= 0) { *suffix
++ = 1; }
1550 break; /* not a suffix, restart from here */
1556 * Re-sort by newpos, then modify the key characters array in-place
1557 * to squeeze out unused bytes, and readjust the newpos offsets.
1559 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1560 compareKeyNewpos
, NULL
, FALSE
, status
);
1561 if (U_SUCCESS(*status
)) {
1562 int32_t oldpos
, newpos
, limit
;
1563 oldpos
= newpos
= bundle
->fKeysBottom
;
1564 limit
= bundle
->fKeysTop
;
1565 /* skip key offsets that point into the pool bundle rather than this new bundle */
1566 for (i
= 0; i
< keysCount
&& map
[i
].newpos
< 0; ++i
) {}
1567 if (i
< keysCount
) {
1568 while (oldpos
< limit
) {
1569 if (keys
[oldpos
] == 1) {
1570 ++oldpos
; /* skip unused bytes */
1572 /* adjust the new offsets for keys starting here */
1573 while (i
< keysCount
&& map
[i
].newpos
== oldpos
) {
1574 map
[i
++].newpos
= newpos
;
1576 /* move the key characters to their new position */
1577 keys
[newpos
++] = keys
[oldpos
++];
1580 assert(i
== keysCount
);
1582 bundle
->fKeysTop
= newpos
;
1583 /* Re-sort once more, by old offsets for binary searching. */
1584 uprv_sortArray(map
, keysCount
, (int32_t)sizeof(KeyMapEntry
),
1585 compareKeyOldpos
, NULL
, FALSE
, status
);
1586 if (U_SUCCESS(*status
)) {
1587 /* key size reduction by limit - newpos */
1588 bundle
->fKeyMap
= map
;
1596 static int32_t U_CALLCONV
1597 compareStringSuffixes(const void *context
, const void *l
, const void *r
) {
1598 struct SResource
*left
= *((struct SResource
**)l
);
1599 struct SResource
*right
= *((struct SResource
**)r
);
1600 const UChar
*lStart
= left
->u
.fString
.fChars
;
1601 const UChar
*lLimit
= lStart
+ left
->u
.fString
.fLength
;
1602 const UChar
*rStart
= right
->u
.fString
.fChars
;
1603 const UChar
*rLimit
= rStart
+ right
->u
.fString
.fLength
;
1605 /* compare keys in reverse character order */
1606 while (lStart
< lLimit
&& rStart
< rLimit
) {
1607 diff
= (int32_t)*--lLimit
- (int32_t)*--rLimit
;
1612 /* sort equal suffixes by descending string length */
1613 return right
->u
.fString
.fLength
- left
->u
.fString
.fLength
;
1616 static int32_t U_CALLCONV
1617 compareStringLengths(const void *context
, const void *l
, const void *r
) {
1618 struct SResource
*left
= *((struct SResource
**)l
);
1619 struct SResource
*right
= *((struct SResource
**)r
);
1621 /* Make "is suffix of another string" compare greater than a non-suffix. */
1622 diff
= (int)(left
->u
.fString
.fSame
!= NULL
) - (int)(right
->u
.fString
.fSame
!= NULL
);
1626 /* sort by ascending string length */
1627 return left
->u
.fString
.fLength
- right
->u
.fString
.fLength
;
1631 string_writeUTF16v2(struct SRBRoot
*bundle
, struct SResource
*res
, int32_t utf16Length
) {
1632 int32_t length
= res
->u
.fString
.fLength
;
1633 res
->fRes
= URES_MAKE_RESOURCE(URES_STRING_V2
, utf16Length
);
1634 res
->fWritten
= TRUE
;
1635 switch(res
->u
.fString
.fNumCharsForLength
) {
1639 bundle
->f16BitUnits
[utf16Length
++] = (uint16_t)(0xdc00 + length
);
1642 bundle
->f16BitUnits
[utf16Length
] = (uint16_t)(0xdfef + (length
>> 16));
1643 bundle
->f16BitUnits
[utf16Length
+ 1] = (uint16_t)length
;
1647 bundle
->f16BitUnits
[utf16Length
] = 0xdfff;
1648 bundle
->f16BitUnits
[utf16Length
+ 1] = (uint16_t)(length
>> 16);
1649 bundle
->f16BitUnits
[utf16Length
+ 2] = (uint16_t)length
;
1653 break; /* will not occur */
1655 u_memcpy(bundle
->f16BitUnits
+ utf16Length
, res
->u
.fString
.fChars
, length
+ 1);
1656 return utf16Length
+ length
+ 1;
1660 bundle_compactStrings(struct SRBRoot
*bundle
, UErrorCode
*status
) {
1661 if (U_FAILURE(*status
)) {
1664 switch(bundle
->fStringsForm
) {
1665 case STRINGS_UTF16_V2
:
1666 if (bundle
->f16BitUnitsLength
> 0) {
1667 struct SResource
**array
;
1668 int32_t count
= uhash_count(bundle
->fStringSet
);
1671 * Allocate enough space for the initial NUL and the UTF-16 v2 strings,
1672 * and some extra for URES_TABLE16 and URES_ARRAY16 values.
1673 * Round down to an even number.
1675 int32_t utf16Length
= (bundle
->f16BitUnitsLength
+ 20000) & ~1;
1676 bundle
->f16BitUnits
= (UChar
*)uprv_malloc(utf16Length
* U_SIZEOF_UCHAR
);
1677 array
= (struct SResource
**)uprv_malloc(count
* sizeof(struct SResource
**));
1678 if (bundle
->f16BitUnits
== NULL
|| array
== NULL
) {
1679 uprv_free(bundle
->f16BitUnits
);
1680 bundle
->f16BitUnits
= NULL
;
1682 *status
= U_MEMORY_ALLOCATION_ERROR
;
1685 bundle
->f16BitUnitsCapacity
= utf16Length
;
1686 /* insert the initial NUL */
1687 bundle
->f16BitUnits
[0] = 0;
1689 ++bundle
->f16BitUnitsLength
;
1690 for (pos
= -1, i
= 0; i
< count
; ++i
) {
1691 array
[i
] = (struct SResource
*)uhash_nextElement(bundle
->fStringSet
, &pos
)->key
.pointer
;
1693 /* Sort the strings so that each one is immediately followed by all of its suffixes. */
1694 uprv_sortArray(array
, count
, (int32_t)sizeof(struct SResource
**),
1695 compareStringSuffixes
, NULL
, FALSE
, status
);
1697 * Make suffixes point into earlier, longer strings that contain them.
1698 * Temporarily use fSame and fSuffixOffset for suffix strings to
1699 * refer to the remaining ones.
1701 if (U_SUCCESS(*status
)) {
1702 for (i
= 0; i
< count
;) {
1704 * This string is not a suffix of the previous one;
1705 * write this one and subsume the following ones that are
1706 * suffixes of this one.
1708 struct SResource
*res
= array
[i
];
1709 const UChar
*strLimit
= res
->u
.fString
.fChars
+ res
->u
.fString
.fLength
;
1711 for (j
= i
+ 1; j
< count
; ++j
) {
1712 struct SResource
*suffixRes
= array
[j
];
1714 const UChar
*suffix
= suffixRes
->u
.fString
.fChars
;
1715 const UChar
*suffixLimit
= suffix
+ suffixRes
->u
.fString
.fLength
;
1716 int32_t offset
= res
->u
.fString
.fLength
- suffixRes
->u
.fString
.fLength
;
1718 break; /* suffix cannot be longer than the original */
1720 /* Is it a suffix of the earlier, longer key? */
1721 for (s
= strLimit
; suffix
< suffixLimit
&& *--s
== *--suffixLimit
;) {}
1722 if (suffix
== suffixLimit
&& *s
== *suffixLimit
) {
1723 if (suffixRes
->u
.fString
.fNumCharsForLength
== 0) {
1724 /* yes, point to the earlier string */
1725 suffixRes
->u
.fString
.fSame
= res
;
1726 suffixRes
->u
.fString
.fSuffixOffset
= offset
;
1728 /* write the suffix by itself if we need explicit length */
1731 break; /* not a suffix, restart from here */
1738 * Re-sort the strings by ascending length (except suffixes last)
1739 * to optimize for URES_TABLE16 and URES_ARRAY16:
1740 * Keep as many as possible within reach of 16-bit offsets.
1742 uprv_sortArray(array
, count
, (int32_t)sizeof(struct SResource
**),
1743 compareStringLengths
, NULL
, FALSE
, status
);
1744 if (U_SUCCESS(*status
)) {
1745 /* Write the non-suffix strings. */
1746 for (i
= 0; i
< count
&& array
[i
]->u
.fString
.fSame
== NULL
; ++i
) {
1747 utf16Length
= string_writeUTF16v2(bundle
, array
[i
], utf16Length
);
1749 /* Write the suffix strings. Make each point to the real string. */
1750 for (; i
< count
; ++i
) {
1751 struct SResource
*res
= array
[i
];
1752 struct SResource
*same
= res
->u
.fString
.fSame
;
1753 res
->fRes
= same
->fRes
+ same
->u
.fString
.fNumCharsForLength
+ res
->u
.fString
.fSuffixOffset
;
1754 res
->u
.fString
.fSame
= NULL
;
1755 res
->fWritten
= TRUE
;
1758 assert(utf16Length
<= bundle
->f16BitUnitsLength
);
1759 bundle
->f16BitUnitsLength
= utf16Length
;