]> git.saurik.com Git - apple/icu.git/blame - icuSources/tools/genrb/reslist.c
ICU-511.35.tar.gz
[apple/icu.git] / icuSources / tools / genrb / reslist.c
CommitLineData
b75a7d8f
A
1/*
2*******************************************************************************
3*
51004dcb 4* Copyright (C) 2000-2012, International Business Machines
b75a7d8f
A
5* Corporation and others. All Rights Reserved.
6*
7*******************************************************************************
8*
9* File reslist.c
10*
11* Modification History:
12*
13* Date Name Description
14* 02/21/00 weiv Creation.
15*******************************************************************************
16*/
17
18#include <assert.h>
374ca955 19#include <stdio.h>
b75a7d8f
A
20#include "reslist.h"
21#include "unewdata.h"
22#include "unicode/ures.h"
374ca955 23#include "unicode/putil.h"
b75a7d8f
A
24#include "errmsg.h"
25
729e4ab9 26#include "uarrsort.h"
4388f060 27#include "uelement.h"
729e4ab9 28#include "uinvchar.h"
4388f060 29#include "ustr_imp.h"
51004dcb 30#include "unicode/utf16.h"
729e4ab9
A
31/*
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.
34 */
b75a7d8f
A
35#define BIN_ALIGNMENT 16
36
37static UBool gIncludeCopyright = FALSE;
729e4ab9
A
38static UBool gUsePoolBundle = FALSE;
39static int32_t gFormatVersion = 2;
40
41static UChar gEmptyString = 0;
42
43/* How do we store string values? */
44enum {
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 */
47};
48
49enum {
50 MAX_IMPLICIT_STRING_LENGTH = 40 /* do not store the length explicitly for such strings */
51};
b75a7d8f 52
46f4442e
A
53/*
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.)
57 */
729e4ab9 58static const struct SResource kNoResource = { URES_NONE };
b75a7d8f 59
729e4ab9 60static UDataInfo dataInfo= {
b75a7d8f
A
61 sizeof(UDataInfo),
62 0,
63
64 U_IS_BIG_ENDIAN,
65 U_CHARSET_FAMILY,
66 sizeof(UChar),
67 0,
68
729e4ab9
A
69 {0x52, 0x65, 0x73, 0x42}, /* dataFormat="ResB" */
70 {1, 3, 0, 0}, /* formatVersion */
b75a7d8f
A
71 {1, 4, 0, 0} /* dataVersion take a look at version inside parsed resb*/
72};
73
729e4ab9
A
74static const UVersionInfo gFormatVersions[3] = { /* indexed by a major-formatVersion integer */
75 { 0, 0, 0, 0 },
76 { 1, 3, 0, 0 },
77 { 2, 0, 0, 0 }
78};
79
b75a7d8f
A
80static 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);
83
84}
85
86void setIncludeCopyright(UBool val){
87 gIncludeCopyright=val;
88}
89
90UBool getIncludeCopyright(void){
91 return gIncludeCopyright;
92}
93
729e4ab9
A
94void setFormatVersion(int32_t formatVersion) {
95 gFormatVersion = formatVersion;
96}
b75a7d8f 97
729e4ab9
A
98void setUsePoolBundle(UBool use) {
99 gUsePoolBundle = use;
b75a7d8f
A
100}
101
729e4ab9
A
102static void
103bundle_compactStrings(struct SRBRoot *bundle, UErrorCode *status);
104
b75a7d8f 105/* Writing Functions */
b75a7d8f 106
729e4ab9
A
107/*
108 * type_write16() functions write resource values into f16BitUnits
109 * and determine the resource item word, if possible.
110 */
111static void
112res_write16(struct SRBRoot *bundle, struct SResource *res,
113 UErrorCode *status);
b75a7d8f 114
729e4ab9
A
115/*
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.
122 *
123 * The type_preWrite() and type_write() functions start and end at the same
124 * byteOffset values.
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.
128 */
129static void
130res_preWrite(uint32_t *byteOffset,
131 struct SRBRoot *bundle, struct SResource *res,
132 UErrorCode *status);
b75a7d8f 133
729e4ab9
A
134/*
135 * type_write() functions write their data to mem and update the byteOffset
136 * in parallel.
137 * (A kingdom for C++ and polymorphism...)
138 */
139static void
140res_write(UNewDataMemory *mem, uint32_t *byteOffset,
141 struct SRBRoot *bundle, struct SResource *res,
142 UErrorCode *status);
b75a7d8f 143
729e4ab9
A
144static uint16_t *
145reserve16BitUnits(struct SRBRoot *bundle, int32_t length, UErrorCode *status) {
b75a7d8f 146 if (U_FAILURE(*status)) {
729e4ab9
A
147 return NULL;
148 }
149 if ((bundle->f16BitUnitsLength + length) > bundle->f16BitUnitsCapacity) {
150 uint16_t *newUnits;
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;
156 return NULL;
157 }
158 if (bundle->f16BitUnitsLength > 0) {
159 uprv_memcpy(newUnits, bundle->f16BitUnits, bundle->f16BitUnitsLength * 2);
160 } else {
161 newUnits[0] = 0;
162 bundle->f16BitUnitsLength = 1;
163 }
164 uprv_free(bundle->f16BitUnits);
165 bundle->f16BitUnits = newUnits;
166 bundle->f16BitUnitsCapacity = capacity;
b75a7d8f 167 }
729e4ab9
A
168 return bundle->f16BitUnits + bundle->f16BitUnitsLength;
169}
b75a7d8f 170
729e4ab9
A
171static int32_t
172makeRes16(uint32_t resWord) {
173 uint32_t type, offset;
174 if (resWord == 0) {
175 return 0; /* empty string */
176 }
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;
181 }
182 return -1;
183}
b75a7d8f 184
729e4ab9
A
185static int32_t
186mapKey(struct SRBRoot *bundle, int32_t oldpos) {
187 const KeyMapEntry *map = bundle->fKeyMap;
188 int32_t i, start, limit;
189
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) {
196 limit = i;
197 } else {
198 start = i;
b75a7d8f 199 }
729e4ab9
A
200 }
201 assert(oldpos == map[start].oldpos);
202 return map[start].newpos;
203}
b75a7d8f 204
729e4ab9
A
205static uint16_t
206makeKey16(struct SRBRoot *bundle, int32_t key) {
207 if (key >= 0) {
208 return (uint16_t)key;
209 } else {
210 return (uint16_t)(key + bundle->fLocalKeyLimit); /* offset in the pool bundle */
211 }
212}
b75a7d8f 213
729e4ab9
A
214/*
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
217 * and exits early.
218 */
219static void
220string_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);
227 }
228 res->fRes = same->fRes;
229 res->fWritten = same->fWritten;
230 }
231}
b75a7d8f 232
729e4ab9
A
233static void
234array_write16(struct SRBRoot *bundle, struct SResource *res,
235 UErrorCode *status) {
236 struct SResource *current;
237 int32_t res16 = 0;
b75a7d8f 238
729e4ab9
A
239 if (U_FAILURE(*status)) {
240 return;
241 }
242 if (res->u.fArray.fCount == 0 && gFormatVersion > 1) {
243 res->fRes = URES_MAKE_EMPTY_RESOURCE(URES_ARRAY);
244 res->fWritten = TRUE;
245 return;
246 }
247 for (current = res->u.fArray.fFirst; current != NULL; current = current->fNext) {
248 res_write16(bundle, current, status);
249 res16 |= makeRes16(current->fRes);
250 }
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);
258 }
259 bundle->f16BitUnitsLength += 1 + res->u.fArray.fCount;
260 res->fWritten = TRUE;
b75a7d8f 261 }
729e4ab9
A
262 }
263}
b75a7d8f 264
729e4ab9
A
265static void
266table_write16(struct SRBRoot *bundle, struct SResource *res,
267 UErrorCode *status) {
268 struct SResource *current;
269 int32_t maxKey = 0, maxPoolKey = 0x80000000;
270 int32_t res16 = 0;
271 UBool hasLocalKeys = FALSE, hasPoolKeys = FALSE;
b75a7d8f 272
729e4ab9
A
273 if (U_FAILURE(*status)) {
274 return;
275 }
276 if (res->u.fTable.fCount == 0 && gFormatVersion > 1) {
277 res->fRes = URES_MAKE_EMPTY_RESOURCE(URES_TABLE);
278 res->fWritten = TRUE;
279 return;
280 }
281 /* Find the smallest table type that fits the data. */
282 for (current = res->u.fTable.fFirst; current != NULL; current = current->fNext) {
283 int32_t key;
284 res_write16(bundle, current, status);
285 if (bundle->fKeyMap == NULL) {
286 key = current->fKey;
287 } else {
288 key = current->fKey = mapKey(bundle, current->fKey);
289 }
290 if (key >= 0) {
291 hasLocalKeys = TRUE;
292 if (key > maxKey) {
293 maxKey = key;
294 }
295 } else {
296 hasPoolKeys = TRUE;
297 if (key > maxPoolKey) {
298 maxPoolKey = key;
299 }
300 }
301 res16 |= makeRes16(current->fRes);
302 }
303 if (U_FAILURE(*status)) {
304 return;
305 }
306 if(res->u.fTable.fCount > (uint32_t)bundle->fMaxTableLength) {
307 bundle->fMaxTableLength = res->u.fTable.fCount;
308 }
309 maxPoolKey &= 0x7fffffff;
310 if (res->u.fTable.fCount <= 0xffff &&
311 (!hasLocalKeys || maxKey < bundle->fLocalKeyLimit) &&
312 (!hasPoolKeys || maxPoolKey < (0x10000 - bundle->fLocalKeyLimit))
313 ) {
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);
322 }
323 for (current = res->u.fTable.fFirst; current != NULL; current = current->fNext) {
324 *p16++ = (uint16_t)makeRes16(current->fRes);
325 }
326 bundle->f16BitUnitsLength += 1 + res->u.fTable.fCount * 2;
327 res->fWritten = TRUE;
328 }
329 } else {
330 /* 16-bit count, 16-bit key offsets, 32-bit values */
331 res->u.fTable.fType = URES_TABLE;
332 }
b75a7d8f 333 } else {
729e4ab9
A
334 /* 32-bit count, key offsets and values */
335 res->u.fTable.fType = URES_TABLE32;
b75a7d8f 336 }
b75a7d8f
A
337}
338
729e4ab9
A
339static void
340res_write16(struct SRBRoot *bundle, struct SResource *res,
341 UErrorCode *status) {
342 if (U_FAILURE(*status) || res == NULL) {
343 return;
344 }
345 if (res->fRes != RES_BOGUS) {
346 /*
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.
351 */
352 return;
b75a7d8f 353 }
729e4ab9
A
354 switch (res->fType) {
355 case URES_STRING:
356 string_write16(bundle, res, status);
357 break;
358 case URES_ARRAY:
359 array_write16(bundle, res, status);
360 break;
361 case URES_TABLE:
362 table_write16(bundle, res, status);
363 break;
364 default:
365 /* Only a few resource types write 16-bit units. */
366 break;
367 }
368}
b75a7d8f 369
729e4ab9
A
370/*
371 * Only called for UTF-16 v1 strings.
372 * For UTF-16 v2 strings, res_preWrite() sees fRes != RES_BOGUS
373 * and exits early.
374 */
375static void
376string_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;
b75a7d8f
A
382}
383
729e4ab9
A
384static void
385bin_preWrite(uint32_t *byteOffset,
386 struct SRBRoot *bundle, struct SResource *res,
387 UErrorCode *status) {
b75a7d8f 388 uint32_t pad = 0;
729e4ab9 389 uint32_t dataStart = *byteOffset + sizeof(res->u.fBinaryValue.fLength);
b75a7d8f
A
390
391 if (dataStart % BIN_ALIGNMENT) {
392 pad = (BIN_ALIGNMENT - dataStart % BIN_ALIGNMENT);
729e4ab9 393 *byteOffset += pad; /* pad == 4 or 8 or 12 */
b75a7d8f 394 }
729e4ab9
A
395 res->fRes = URES_MAKE_RESOURCE(URES_BINARY, *byteOffset >> 2);
396 *byteOffset += 4 + res->u.fBinaryValue.fLength;
b75a7d8f
A
397}
398
729e4ab9
A
399static void
400array_preWrite(uint32_t *byteOffset,
401 struct SRBRoot *bundle, struct SResource *res,
402 UErrorCode *status) {
403 struct SResource *current;
b75a7d8f 404
729e4ab9
A
405 if (U_FAILURE(*status)) {
406 return;
407 }
408 for (current = res->u.fArray.fFirst; current != NULL; current = current->fNext) {
409 res_preWrite(byteOffset, bundle, current, status);
410 }
411 res->fRes = URES_MAKE_RESOURCE(URES_ARRAY, *byteOffset >> 2);
412 *byteOffset += (1 + res->u.fArray.fCount) * 4;
413}
b75a7d8f 414
729e4ab9
A
415static void
416table_preWrite(uint32_t *byteOffset,
417 struct SRBRoot *bundle, struct SResource *res,
418 UErrorCode *status) {
419 struct SResource *current;
b75a7d8f
A
420
421 if (U_FAILURE(*status)) {
729e4ab9 422 return;
b75a7d8f 423 }
729e4ab9
A
424 for (current = res->u.fTable.fFirst; current != NULL; current = current->fNext) {
425 res_preWrite(byteOffset, bundle, current, status);
426 }
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;
431 } else {
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;
435 }
436}
b75a7d8f 437
729e4ab9
A
438static void
439res_preWrite(uint32_t *byteOffset,
440 struct SRBRoot *bundle, struct SResource *res,
441 UErrorCode *status) {
442 if (U_FAILURE(*status) || res == NULL) {
443 return;
444 }
445 if (res->fRes != RES_BOGUS) {
446 /*
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.
451 */
452 return;
453 }
454 switch (res->fType) {
455 case URES_STRING:
456 string_preWrite(byteOffset, bundle, res, status);
457 break;
458 case URES_ALIAS:
459 res->fRes = URES_MAKE_RESOURCE(URES_ALIAS, *byteOffset >> 2);
460 *byteOffset += 4 + (res->u.fString.fLength + 1) * U_SIZEOF_UCHAR;
461 break;
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;
374ca955 466 } else {
729e4ab9
A
467 res->fRes = URES_MAKE_RESOURCE(URES_INT_VECTOR, *byteOffset >> 2);
468 *byteOffset += (1 + res->u.fIntVector.fCount) * 4;
b75a7d8f 469 }
729e4ab9
A
470 break;
471 case URES_BINARY:
472 bin_preWrite(byteOffset, bundle, res, status);
473 break;
474 case URES_INT:
475 break;
476 case URES_ARRAY:
477 array_preWrite(byteOffset, bundle, res, status);
478 break;
479 case URES_TABLE:
480 table_preWrite(byteOffset, bundle, res, status);
481 break;
482 default:
483 *status = U_INTERNAL_PROGRAM_ERROR;
484 break;
485 }
486 *byteOffset += calcPadding(*byteOffset);
487}
b75a7d8f 488
729e4ab9
A
489/*
490 * Only called for UTF-16 v1 strings. For UTF-16 v2 strings,
491 * res_write() sees fWritten and exits early.
492 */
493static 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;
502}
b75a7d8f 503
729e4ab9
A
504static 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;
511}
b75a7d8f 512
729e4ab9
A
513static void array_write(UNewDataMemory *mem, uint32_t *byteOffset,
514 struct SRBRoot *bundle, struct SResource *res,
515 UErrorCode *status) {
516 uint32_t i;
b75a7d8f 517
729e4ab9 518 struct SResource *current = NULL;
b75a7d8f 519
729e4ab9
A
520 if (U_FAILURE(*status)) {
521 return;
522 }
523 for (i = 0, current = res->u.fArray.fFirst; current != NULL; ++i, current = current->fNext) {
524 res_write(mem, byteOffset, bundle, current, status);
525 }
526 assert(i == res->u.fArray.fCount);
374ca955 527
729e4ab9
A
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);
531 }
532 *byteOffset += (1 + res->u.fArray.fCount) * 4;
533}
374ca955 534
729e4ab9
A
535static void intvector_write(UNewDataMemory *mem, uint32_t *byteOffset,
536 struct SRBRoot *bundle, struct SResource *res,
537 UErrorCode *status) {
538 uint32_t i = 0;
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]);
542 }
543 *byteOffset += (1 + res->u.fIntVector.fCount) * 4;
544}
b75a7d8f 545
729e4ab9
A
546static void bin_write(UNewDataMemory *mem, uint32_t *byteOffset,
547 struct SRBRoot *bundle, struct SResource *res,
548 UErrorCode *status) {
549 uint32_t pad = 0;
550 uint32_t dataStart = *byteOffset + sizeof(res->u.fBinaryValue.fLength);
b75a7d8f 551
729e4ab9
A
552 if (dataStart % BIN_ALIGNMENT) {
553 pad = (BIN_ALIGNMENT - dataStart % BIN_ALIGNMENT);
554 udata_writePadding(mem, pad); /* pad == 4 or 8 or 12 */
555 *byteOffset += pad;
b75a7d8f
A
556 }
557
729e4ab9
A
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);
561 }
562 *byteOffset += 4 + res->u.fBinaryValue.fLength;
b75a7d8f
A
563}
564
729e4ab9
A
565static void table_write(UNewDataMemory *mem, uint32_t *byteOffset,
566 struct SRBRoot *bundle, struct SResource *res,
567 UErrorCode *status) {
568 struct SResource *current;
569 uint32_t i;
570
b75a7d8f 571 if (U_FAILURE(*status)) {
729e4ab9 572 return;
b75a7d8f 573 }
729e4ab9
A
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);
577 }
578 assert(i == res->u.fTable.fCount);
b75a7d8f 579
729e4ab9
A
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));
584 }
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);
589 *byteOffset += 2;
b75a7d8f 590 }
729e4ab9
A
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);
595 }
596 *byteOffset += (1 + res->u.fTable.fCount)* 4;
597 }
598 for (current = res->u.fTable.fFirst; current != NULL; current = current->fNext) {
599 udata_write32(mem, current->fRes);
b75a7d8f 600 }
729e4ab9
A
601 *byteOffset += res->u.fTable.fCount * 4;
602}
b75a7d8f 603
729e4ab9
A
604void res_write(UNewDataMemory *mem, uint32_t *byteOffset,
605 struct SRBRoot *bundle, struct SResource *res,
606 UErrorCode *status) {
607 uint8_t paddingSize;
608
609 if (U_FAILURE(*status) || res == NULL) {
610 return;
611 }
612 if (res->fWritten) {
613 assert(res->fRes != RES_BOGUS);
614 return;
615 }
616 switch (res->fType) {
617 case URES_STRING:
618 string_write (mem, byteOffset, bundle, res, status);
619 break;
620 case URES_ALIAS:
621 alias_write (mem, byteOffset, bundle, res, status);
622 break;
623 case URES_INT_VECTOR:
624 intvector_write (mem, byteOffset, bundle, res, status);
625 break;
626 case URES_BINARY:
627 bin_write (mem, byteOffset, bundle, res, status);
628 break;
629 case URES_INT:
630 break; /* fRes was set by int_open() */
631 case URES_ARRAY:
632 array_write (mem, byteOffset, bundle, res, status);
633 break;
634 case URES_TABLE:
635 table_write (mem, byteOffset, bundle, res, status);
636 break;
637 default:
638 *status = U_INTERNAL_PROGRAM_ERROR;
639 break;
640 }
641 paddingSize = calcPadding(*byteOffset);
642 if (paddingSize > 0) {
643 udata_writePadding(mem, paddingSize);
644 *byteOffset += paddingSize;
645 }
646 res->fWritten = TRUE;
b75a7d8f
A
647}
648
729e4ab9
A
649void bundle_write(struct SRBRoot *bundle,
650 const char *outputDir, const char *outputPkg,
651 char *writtenFilename, int writtenFilenameLen,
652 UErrorCode *status) {
b75a7d8f 653 UNewDataMemory *mem = NULL;
729e4ab9 654 uint32_t byteOffset = 0;
374ca955 655 uint32_t top, size;
b75a7d8f 656 char dataName[1024];
374ca955 657 int32_t indexes[URES_INDEX_TOP];
b75a7d8f 658
729e4ab9
A
659 bundle_compactKeys(bundle, status);
660 /*
661 * Add padding bytes to fKeys so that fKeysTop is 4-aligned.
662 * Safe because the capacity is a multiple of 4.
663 */
664 while (bundle->fKeysTop & 3) {
665 bundle->fKeys[bundle->fKeysTop++] = (char)0xaa;
666 }
667 /*
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
670 * if there are any.
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.
674 */
675 if (bundle->fKeysBottom < bundle->fKeysTop) {
676 if (bundle->fKeysTop <= 0x10000) {
677 bundle->fLocalKeyLimit = bundle->fKeysTop;
678 } else {
679 bundle->fLocalKeyLimit = 0x10000;
680 }
681 } else {
682 bundle->fLocalKeyLimit = 0;
b75a7d8f
A
683 }
684
729e4ab9
A
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 */
689 }
690 /* all keys have been mapped */
691 uprv_free(bundle->fKeyMap);
692 bundle->fKeyMap = NULL;
693
694 byteOffset = bundle->fKeysTop + bundle->f16BitUnitsLength * 2;
695 res_preWrite(&byteOffset, bundle, bundle->fRoot, status);
696
697 /* total size including the root item */
698 top = byteOffset;
699
b75a7d8f
A
700 if (U_FAILURE(*status)) {
701 return;
702 }
703
729e4ab9
A
704 if (writtenFilename && writtenFilenameLen) {
705 *writtenFilename = 0;
706 }
707
b75a7d8f
A
708 if (writtenFilename) {
709 int32_t off = 0, len = 0;
710 if (outputDir) {
711 len = (int32_t)uprv_strlen(outputDir);
712 if (len > writtenFilenameLen) {
713 len = writtenFilenameLen;
714 }
715 uprv_strncpy(writtenFilename, outputDir, len);
716 }
717 if (writtenFilenameLen -= len) {
718 off += len;
719 writtenFilename[off] = U_FILE_SEP_CHAR;
720 if (--writtenFilenameLen) {
721 ++off;
722 if(outputPkg != NULL)
723 {
724 uprv_strcpy(writtenFilename+off, outputPkg);
374ca955 725 off += (int32_t)uprv_strlen(outputPkg);
b75a7d8f
A
726 writtenFilename[off] = '_';
727 ++off;
728 }
729
730 len = (int32_t)uprv_strlen(bundle->fLocale);
731 if (len > writtenFilenameLen) {
732 len = writtenFilenameLen;
733 }
734 uprv_strncpy(writtenFilename + off, bundle->fLocale, len);
735 if (writtenFilenameLen -= len) {
736 off += len;
737 len = 5;
738 if (len > writtenFilenameLen) {
739 len = writtenFilenameLen;
740 }
741 uprv_strncpy(writtenFilename + off, ".res", len);
742 }
743 }
744 }
745 }
746
747 if(outputPkg)
748 {
749 uprv_strcpy(dataName, outputPkg);
750 uprv_strcat(dataName, "_");
751 uprv_strcat(dataName, bundle->fLocale);
752 }
753 else
754 {
755 uprv_strcpy(dataName, bundle->fLocale);
756 }
757
729e4ab9
A
758 uprv_memcpy(dataInfo.formatVersion, gFormatVersions + gFormatVersion, sizeof(UVersionInfo));
759
b75a7d8f
A
760 mem = udata_create(outputDir, "res", dataName, &dataInfo, (gIncludeCopyright==TRUE)? U_COPYRIGHT_STRING:NULL, status);
761 if(U_FAILURE(*status)){
762 return;
763 }
b75a7d8f 764
374ca955 765 /* write the root item */
729e4ab9 766 udata_write32(mem, bundle->fRoot->fRes);
374ca955
A
767
768 /*
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.
772 */
73c04bcf 773 uprv_memset(indexes, 0, sizeof(indexes));
729e4ab9
A
774 indexes[URES_INDEX_LENGTH]= bundle->fIndexLength;
775 indexes[URES_INDEX_KEYS_TOP]= bundle->fKeysTop>>2;
374ca955
A
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;
779
73c04bcf
A
780 /*
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
784 */
729e4ab9 785 if (bundle->noFallback) {
73c04bcf
A
786 indexes[URES_INDEX_ATTRIBUTES]=URES_ATT_NO_FALLBACK;
787 }
729e4ab9
A
788 /*
789 * formatVersion 2.0 (ICU 4.4):
790 * more compact string value storage, optional pool bundle
791 */
792 if (URES_INDEX_16BIT_TOP < bundle->fIndexLength) {
793 indexes[URES_INDEX_16BIT_TOP] = (bundle->fKeysTop>>2) + (bundle->f16BitUnitsLength>>1);
794 }
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),
801 0);
802 } else if (gUsePoolBundle) {
803 indexes[URES_INDEX_ATTRIBUTES] |= URES_ATT_USES_POOL_BUNDLE;
804 indexes[URES_INDEX_POOL_CHECKSUM] = bundle->fPoolChecksum;
805 }
806 }
73c04bcf 807
374ca955 808 /* write the indexes[] */
729e4ab9 809 udata_writeBlock(mem, indexes, bundle->fIndexLength*4);
374ca955
A
810
811 /* write the table key strings */
729e4ab9
A
812 udata_writeBlock(mem, bundle->fKeys+bundle->fKeysBottom,
813 bundle->fKeysTop-bundle->fKeysBottom);
374ca955 814
729e4ab9
A
815 /* write the v2 UTF-16 strings, URES_TABLE16 and URES_ARRAY16 */
816 udata_writeBlock(mem, bundle->f16BitUnits, bundle->f16BitUnitsLength*2);
b75a7d8f 817
374ca955 818 /* write all of the bundle contents: the root item and its children */
729e4ab9
A
819 byteOffset = bundle->fKeysTop + bundle->f16BitUnitsLength * 2;
820 res_write(mem, &byteOffset, bundle, bundle->fRoot, status);
821 assert(byteOffset == top);
b75a7d8f 822
374ca955
A
823 size = udata_finish(mem, status);
824 if(top != size) {
825 fprintf(stderr, "genrb error: wrote %u bytes but counted %u\n",
826 (int)size, (int)top);
827 *status = U_INTERNAL_PROGRAM_ERROR;
828 }
b75a7d8f
A
829}
830
831/* Opening Functions */
b75a7d8f 832
729e4ab9
A
833/* gcc 4.2 complained "no previous prototype for res_open" without this prototype... */
834struct SResource* res_open(struct SRBRoot *bundle, const char *tag,
835 const struct UString* comment, UErrorCode* status);
836
837struct 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);
b75a7d8f
A
841 if (U_FAILURE(*status)) {
842 return NULL;
843 }
844
845 res = (struct SResource *) uprv_malloc(sizeof(struct SResource));
b75a7d8f
A
846 if (res == NULL) {
847 *status = U_MEMORY_ALLOCATION_ERROR;
848 return NULL;
849 }
374ca955 850 uprv_memset(res, 0, sizeof(struct SResource));
729e4ab9
A
851 res->fKey = key;
852 res->fRes = RES_BOGUS;
374ca955 853
46f4442e 854 ustr_init(&res->fComment);
374ca955 855 if(comment != NULL){
46f4442e 856 ustr_cpy(&res->fComment, comment, status);
729e4ab9
A
857 if (U_FAILURE(*status)) {
858 res_close(res);
859 return NULL;
860 }
374ca955
A
861 }
862 return res;
374ca955 863}
46f4442e
A
864
865struct SResource* res_none() {
729e4ab9 866 return (struct SResource*)&kNoResource;
46f4442e
A
867}
868
729e4ab9
A
869struct 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);
b75a7d8f 871 if (U_FAILURE(*status)) {
b75a7d8f
A
872 return NULL;
873 }
374ca955 874 res->fType = URES_TABLE;
729e4ab9 875 res->u.fTable.fRoot = bundle;
b75a7d8f
A
876 return res;
877}
878
73c04bcf 879struct SResource* array_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
729e4ab9 880 struct SResource *res = res_open(bundle, tag, comment, status);
374ca955 881 if (U_FAILURE(*status)) {
b75a7d8f
A
882 return NULL;
883 }
b75a7d8f 884 res->fType = URES_ARRAY;
729e4ab9
A
885 return res;
886}
b75a7d8f 887
729e4ab9 888static int32_t U_CALLCONV
4388f060 889string_hash(const UElement key) {
729e4ab9 890 const struct SResource *res = (struct SResource *)key.pointer;
4388f060 891 return ustr_hashUCharsN(res->u.fString.fChars, res->u.fString.fLength);
729e4ab9 892}
b75a7d8f 893
729e4ab9 894static UBool U_CALLCONV
4388f060 895string_comp(const UElement key1, const UElement key2) {
729e4ab9
A
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,
900 FALSE);
b75a7d8f
A
901}
902
4388f060 903struct SResource *string_open(struct SRBRoot *bundle, const char *tag, const UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
729e4ab9 904 struct SResource *res = res_open(bundle, tag, comment, status);
b75a7d8f
A
905 if (U_FAILURE(*status)) {
906 return NULL;
907 }
b75a7d8f 908 res->fType = URES_STRING;
b75a7d8f 909
729e4ab9
A
910 if (len == 0 && gFormatVersion > 1) {
911 res->u.fString.fChars = &gEmptyString;
912 res->fRes = 0;
913 res->fWritten = TRUE;
914 return res;
b75a7d8f
A
915 }
916
b75a7d8f 917 res->u.fString.fLength = len;
b75a7d8f 918
729e4ab9
A
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);
925 } else {
926 res->u.fString.fSame = uhash_get(bundle->fStringSet, res);
927 }
b75a7d8f 928 }
729e4ab9
A
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));
b75a7d8f 932
729e4ab9
A
933 if (res->u.fString.fChars == NULL) {
934 *status = U_MEMORY_ALLOCATION_ERROR;
935 uprv_free(res);
936 return NULL;
937 }
b75a7d8f 938
729e4ab9
A
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);
944 }
945
946 if (bundle->fStringsForm != STRINGS_UTF16_V1) {
947 if (len <= MAX_IMPLICIT_STRING_LENGTH && !U16_IS_TRAIL(value[0]) && len == u_strlen(value)) {
948 /*
949 * This string will be stored without an explicit length.
950 * Runtime will detect !U16_IS_TRAIL(value[0]) and call u_strlen().
951 */
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;
957 } else {
958 res->u.fString.fNumCharsForLength = 3;
959 }
960 bundle->f16BitUnitsLength += res->u.fString.fNumCharsForLength + len + 1; /* +1 for the NUL */
961 }
962 } else {
963 /* this is a duplicate of fSame */
964 struct SResource *same = res->u.fString.fSame;
965 res->u.fString.fChars = same->u.fString.fChars;
966 }
b75a7d8f
A
967 return res;
968}
969
970/* TODO: make alias_open and string_open use the same code */
4388f060 971struct SResource *alias_open(struct SRBRoot *bundle, const char *tag, UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
729e4ab9 972 struct SResource *res = res_open(bundle, tag, comment, status);
b75a7d8f
A
973 if (U_FAILURE(*status)) {
974 return NULL;
975 }
b75a7d8f 976 res->fType = URES_ALIAS;
729e4ab9
A
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;
981 return res;
b75a7d8f
A
982 }
983
b75a7d8f
A
984 res->u.fString.fLength = len;
985 res->u.fString.fChars = (UChar *) uprv_malloc(sizeof(UChar) * (len + 1));
b75a7d8f
A
986 if (res->u.fString.fChars == NULL) {
987 *status = U_MEMORY_ALLOCATION_ERROR;
988 uprv_free(res);
989 return NULL;
990 }
b75a7d8f 991 uprv_memcpy(res->u.fString.fChars, value, sizeof(UChar) * (len + 1));
b75a7d8f
A
992 return res;
993}
994
995
4388f060 996struct SResource* intvector_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
729e4ab9 997 struct SResource *res = res_open(bundle, tag, comment, status);
b75a7d8f
A
998 if (U_FAILURE(*status)) {
999 return NULL;
1000 }
b75a7d8f 1001 res->fType = URES_INT_VECTOR;
b75a7d8f
A
1002
1003 res->u.fIntVector.fCount = 0;
1004 res->u.fIntVector.fArray = (uint32_t *) uprv_malloc(sizeof(uint32_t) * RESLIST_MAX_INT_VECTOR);
b75a7d8f
A
1005 if (res->u.fIntVector.fArray == NULL) {
1006 *status = U_MEMORY_ALLOCATION_ERROR;
1007 uprv_free(res);
1008 return NULL;
1009 }
b75a7d8f
A
1010 return res;
1011}
1012
4388f060 1013struct SResource *int_open(struct SRBRoot *bundle, const char *tag, int32_t value, const struct UString* comment, UErrorCode *status) {
729e4ab9 1014 struct SResource *res = res_open(bundle, tag, comment, status);
b75a7d8f
A
1015 if (U_FAILURE(*status)) {
1016 return NULL;
1017 }
b75a7d8f 1018 res->fType = URES_INT;
b75a7d8f 1019 res->u.fIntValue.fValue = value;
729e4ab9
A
1020 res->fRes = URES_MAKE_RESOURCE(URES_INT, value & 0x0FFFFFFF);
1021 res->fWritten = TRUE;
b75a7d8f
A
1022 return res;
1023}
1024
374ca955 1025struct SResource *bin_open(struct SRBRoot *bundle, const char *tag, uint32_t length, uint8_t *data, const char* fileName, const struct UString* comment, UErrorCode *status) {
729e4ab9 1026 struct SResource *res = res_open(bundle, tag, comment, status);
b75a7d8f
A
1027 if (U_FAILURE(*status)) {
1028 return NULL;
1029 }
b75a7d8f 1030 res->fType = URES_BINARY;
b75a7d8f
A
1031
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);
1037 }
1038 if (length > 0) {
1039 res->u.fBinaryValue.fData = (uint8_t *) uprv_malloc(sizeof(uint8_t) * length);
1040
1041 if (res->u.fBinaryValue.fData == NULL) {
1042 *status = U_MEMORY_ALLOCATION_ERROR;
1043 uprv_free(res);
1044 return NULL;
1045 }
1046
1047 uprv_memcpy(res->u.fBinaryValue.fData, data, length);
1048 }
1049 else {
1050 res->u.fBinaryValue.fData = NULL;
729e4ab9
A
1051 if (gFormatVersion > 1) {
1052 res->fRes = URES_MAKE_EMPTY_RESOURCE(URES_BINARY);
1053 res->fWritten = TRUE;
1054 }
b75a7d8f
A
1055 }
1056
b75a7d8f
A
1057 return res;
1058}
1059
729e4ab9
A
1060struct SRBRoot *bundle_open(const struct UString* comment, UBool isPoolBundle, UErrorCode *status) {
1061 struct SRBRoot *bundle;
b75a7d8f
A
1062
1063 if (U_FAILURE(*status)) {
1064 return NULL;
1065 }
1066
1067 bundle = (struct SRBRoot *) uprv_malloc(sizeof(struct SRBRoot));
b75a7d8f
A
1068 if (bundle == NULL) {
1069 *status = U_MEMORY_ALLOCATION_ERROR;
1070 return 0;
1071 }
374ca955 1072 uprv_memset(bundle, 0, sizeof(struct SRBRoot));
b75a7d8f 1073
729e4ab9
A
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)) {
374ca955
A
1077 if (U_SUCCESS(*status)) {
1078 *status = U_MEMORY_ALLOCATION_ERROR;
1079 }
729e4ab9 1080 bundle_close(bundle, status);
b75a7d8f
A
1081 return NULL;
1082 }
1083
729e4ab9
A
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;
1094 }
1095 bundle->fKeysBottom = (1 /* root */ + bundle->fIndexLength) * 4;
1096 uprv_memset(bundle->fKeys, 0, bundle->fKeysBottom);
1097 bundle->fKeysTop = bundle->fKeysBottom;
1098
1099 if (gFormatVersion == 1) {
1100 bundle->fStringsForm = STRINGS_UTF16_V1;
1101 } else {
1102 bundle->fStringsForm = STRINGS_UTF16_V2;
1103 }
1104
b75a7d8f
A
1105 return bundle;
1106}
1107
1108/* Closing Functions */
46f4442e 1109static void table_close(struct SResource *table) {
b75a7d8f
A
1110 struct SResource *current = NULL;
1111 struct SResource *prev = NULL;
1112
1113 current = table->u.fTable.fFirst;
1114
1115 while (current != NULL) {
1116 prev = current;
1117 current = current->fNext;
1118
46f4442e 1119 res_close(prev);
b75a7d8f
A
1120 }
1121
1122 table->u.fTable.fFirst = NULL;
1123}
1124
46f4442e 1125static void array_close(struct SResource *array) {
b75a7d8f
A
1126 struct SResource *current = NULL;
1127 struct SResource *prev = NULL;
73c04bcf
A
1128
1129 if(array==NULL){
1130 return;
1131 }
b75a7d8f 1132 current = array->u.fArray.fFirst;
73c04bcf 1133
b75a7d8f
A
1134 while (current != NULL) {
1135 prev = current;
1136 current = current->fNext;
1137
46f4442e 1138 res_close(prev);
b75a7d8f
A
1139 }
1140 array->u.fArray.fFirst = NULL;
1141}
1142
46f4442e 1143static void string_close(struct SResource *string) {
729e4ab9
A
1144 if (string->u.fString.fChars != NULL &&
1145 string->u.fString.fChars != &gEmptyString &&
1146 string->u.fString.fSame == NULL
1147 ) {
b75a7d8f
A
1148 uprv_free(string->u.fString.fChars);
1149 string->u.fString.fChars =NULL;
1150 }
1151}
1152
46f4442e 1153static void alias_close(struct SResource *alias) {
b75a7d8f
A
1154 if (alias->u.fString.fChars != NULL) {
1155 uprv_free(alias->u.fString.fChars);
1156 alias->u.fString.fChars =NULL;
1157 }
1158}
1159
46f4442e 1160static void intvector_close(struct SResource *intvector) {
b75a7d8f
A
1161 if (intvector->u.fIntVector.fArray != NULL) {
1162 uprv_free(intvector->u.fIntVector.fArray);
1163 intvector->u.fIntVector.fArray =NULL;
1164 }
1165}
1166
46f4442e 1167static void int_close(struct SResource *intres) {
b75a7d8f
A
1168 /* Intentionally left blank */
1169}
1170
46f4442e 1171static void bin_close(struct SResource *binres) {
b75a7d8f
A
1172 if (binres->u.fBinaryValue.fData != NULL) {
1173 uprv_free(binres->u.fBinaryValue.fData);
1174 binres->u.fBinaryValue.fData = NULL;
1175 }
51004dcb
A
1176 if (binres->u.fBinaryValue.fFileName != NULL) {
1177 uprv_free(binres->u.fBinaryValue.fFileName);
1178 binres->u.fBinaryValue.fFileName = NULL;
1179 }
b75a7d8f
A
1180}
1181
46f4442e 1182void res_close(struct SResource *res) {
b75a7d8f
A
1183 if (res != NULL) {
1184 switch(res->fType) {
1185 case URES_STRING:
46f4442e 1186 string_close(res);
b75a7d8f
A
1187 break;
1188 case URES_ALIAS:
46f4442e 1189 alias_close(res);
b75a7d8f
A
1190 break;
1191 case URES_INT_VECTOR:
46f4442e 1192 intvector_close(res);
b75a7d8f
A
1193 break;
1194 case URES_BINARY:
46f4442e 1195 bin_close(res);
b75a7d8f
A
1196 break;
1197 case URES_INT:
46f4442e 1198 int_close(res);
b75a7d8f
A
1199 break;
1200 case URES_ARRAY:
46f4442e 1201 array_close(res);
b75a7d8f 1202 break;
374ca955 1203 case URES_TABLE:
46f4442e 1204 table_close(res);
b75a7d8f
A
1205 break;
1206 default:
1207 /* Shouldn't happen */
1208 break;
1209 }
1210
46f4442e 1211 ustr_deinit(&res->fComment);
b75a7d8f
A
1212 uprv_free(res);
1213 }
1214}
1215
1216void bundle_close(struct SRBRoot *bundle, UErrorCode *status) {
729e4ab9
A
1217 res_close(bundle->fRoot);
1218 uprv_free(bundle->fLocale);
1219 uprv_free(bundle->fKeys);
1220 uprv_free(bundle->fKeyMap);
1221 uhash_close(bundle->fStringSet);
1222 uprv_free(bundle->f16BitUnits);
1223 uprv_free(bundle);
1224}
b75a7d8f 1225
729e4ab9
A
1226void bundle_closeString(struct SRBRoot *bundle, struct SResource *string) {
1227 if (bundle->fStringSet != NULL) {
1228 uhash_remove(bundle->fStringSet, string);
b75a7d8f 1229 }
729e4ab9 1230 string_close(string);
b75a7d8f
A
1231}
1232
1233/* Adding Functions */
1234void table_add(struct SResource *table, struct SResource *res, int linenumber, UErrorCode *status) {
1235 struct SResource *current = NULL;
1236 struct SResource *prev = NULL;
1237 struct SResTable *list;
729e4ab9 1238 const char *resKeyString;
b75a7d8f
A
1239
1240 if (U_FAILURE(*status)) {
1241 return;
1242 }
46f4442e
A
1243 if (res == &kNoResource) {
1244 return;
1245 }
b75a7d8f
A
1246
1247 /* remember this linenumber to report to the user if there is a duplicate key */
1248 res->line = linenumber;
1249
1250 /* here we need to traverse the list */
1251 list = &(table->u.fTable);
b75a7d8f 1252 ++(list->fCount);
b75a7d8f
A
1253
1254 /* is list still empty? */
1255 if (list->fFirst == NULL) {
1256 list->fFirst = res;
1257 res->fNext = NULL;
1258 return;
1259 }
1260
729e4ab9
A
1261 resKeyString = list->fRoot->fKeys + res->fKey;
1262
b75a7d8f
A
1263 current = list->fFirst;
1264
1265 while (current != NULL) {
729e4ab9
A
1266 const char *currentKeyString = list->fRoot->fKeys + current->fKey;
1267 int diff;
1268 /*
1269 * formatVersion 1: compare key strings in native-charset order
1270 * formatVersion 2 and up: compare key strings in ASCII order
1271 */
1272 if (gFormatVersion == 1 || U_CHARSET_FAMILY == U_ASCII_FAMILY) {
1273 diff = uprv_strcmp(currentKeyString, resKeyString);
1274 } else {
1275 diff = uprv_compareInvCharsAsAscii(currentKeyString, resKeyString);
1276 }
1277 if (diff < 0) {
b75a7d8f
A
1278 prev = current;
1279 current = current->fNext;
729e4ab9 1280 } else if (diff > 0) {
b75a7d8f
A
1281 /* we're either in front of list, or in middle */
1282 if (prev == NULL) {
1283 /* front of the list */
1284 list->fFirst = res;
1285 } else {
1286 /* middle of the list */
1287 prev->fNext = res;
1288 }
1289
1290 res->fNext = current;
1291 return;
1292 } else {
1293 /* Key already exists! ERROR! */
729e4ab9 1294 error(linenumber, "duplicate key '%s' in table, first appeared at line %d", currentKeyString, current->line);
b75a7d8f
A
1295 *status = U_UNSUPPORTED_ERROR;
1296 return;
1297 }
1298 }
1299
1300 /* end of list */
1301 prev->fNext = res;
1302 res->fNext = NULL;
1303}
1304
1305void array_add(struct SResource *array, struct SResource *res, UErrorCode *status) {
1306 if (U_FAILURE(*status)) {
1307 return;
1308 }
1309
1310 if (array->u.fArray.fFirst == NULL) {
1311 array->u.fArray.fFirst = res;
1312 array->u.fArray.fLast = res;
1313 } else {
1314 array->u.fArray.fLast->fNext = res;
1315 array->u.fArray.fLast = res;
1316 }
1317
1318 (array->u.fArray.fCount)++;
b75a7d8f
A
1319}
1320
1321void intvector_add(struct SResource *intvector, int32_t value, UErrorCode *status) {
1322 if (U_FAILURE(*status)) {
1323 return;
1324 }
1325
1326 *(intvector->u.fIntVector.fArray + intvector->u.fIntVector.fCount) = value;
1327 intvector->u.fIntVector.fCount++;
b75a7d8f
A
1328}
1329
1330/* Misc Functions */
1331
1332void bundle_setlocale(struct SRBRoot *bundle, UChar *locale, UErrorCode *status) {
1333
1334 if(U_FAILURE(*status)) {
1335 return;
1336 }
1337
1338 if (bundle->fLocale!=NULL) {
1339 uprv_free(bundle->fLocale);
1340 }
1341
1342 bundle->fLocale= (char*) uprv_malloc(sizeof(char) * (u_strlen(locale)+1));
1343
1344 if(bundle->fLocale == NULL) {
1345 *status = U_MEMORY_ALLOCATION_ERROR;
1346 return;
1347 }
1348
1349 /*u_strcpy(bundle->fLocale, locale);*/
1350 u_UCharsToChars(locale, bundle->fLocale, u_strlen(locale)+1);
1351
1352}
1353
729e4ab9
A
1354static const char *
1355getKeyString(const struct SRBRoot *bundle, int32_t key) {
1356 if (key < 0) {
1357 return bundle->fPoolBundleKeys + (key & 0x7fffffff);
1358 } else {
1359 return bundle->fKeys + key;
1360 }
1361}
1362
1363const char *
1364res_getKeyString(const struct SRBRoot *bundle, const struct SResource *res, char temp[8]) {
1365 if (res->fKey == -1) {
1366 return NULL;
1367 }
1368 return getKeyString(bundle, res->fKey);
1369}
1370
1371const char *
1372bundle_getKeyBytes(struct SRBRoot *bundle, int32_t *pLength) {
1373 *pLength = bundle->fKeysTop - bundle->fKeysBottom;
1374 return bundle->fKeys + bundle->fKeysBottom;
1375}
374ca955
A
1376
1377int32_t
729e4ab9
A
1378bundle_addKeyBytes(struct SRBRoot *bundle, const char *keyBytes, int32_t length, UErrorCode *status) {
1379 int32_t keypos;
b75a7d8f
A
1380
1381 if (U_FAILURE(*status)) {
374ca955 1382 return -1;
b75a7d8f 1383 }
729e4ab9
A
1384 if (length < 0 || (keyBytes == NULL && length != 0)) {
1385 *status = U_ILLEGAL_ARGUMENT_ERROR;
374ca955 1386 return -1;
b75a7d8f 1387 }
729e4ab9
A
1388 if (length == 0) {
1389 return bundle->fKeysTop;
1390 }
b75a7d8f 1391
729e4ab9
A
1392 keypos = bundle->fKeysTop;
1393 bundle->fKeysTop += length;
1394 if (bundle->fKeysTop >= bundle->fKeysCapacity) {
374ca955
A
1395 /* overflow - resize the keys buffer */
1396 bundle->fKeysCapacity += KEY_SPACE_SIZE;
1397 bundle->fKeys = uprv_realloc(bundle->fKeys, bundle->fKeysCapacity);
1398 if(bundle->fKeys == NULL) {
1399 *status = U_MEMORY_ALLOCATION_ERROR;
1400 return -1;
1401 }
b75a7d8f
A
1402 }
1403
729e4ab9
A
1404 uprv_memcpy(bundle->fKeys + keypos, keyBytes, length);
1405
1406 return keypos;
1407}
1408
1409int32_t
1410bundle_addtag(struct SRBRoot *bundle, const char *tag, UErrorCode *status) {
1411 int32_t keypos;
1412
1413 if (U_FAILURE(*status)) {
1414 return -1;
1415 }
1416
1417 if (tag == NULL) {
1418 /* no error: the root table and array items have no keys */
1419 return -1;
1420 }
b75a7d8f 1421
729e4ab9
A
1422 keypos = bundle_addKeyBytes(bundle, tag, (int32_t)(uprv_strlen(tag) + 1), status);
1423 if (U_SUCCESS(*status)) {
1424 ++bundle->fKeysCount;
1425 }
b75a7d8f
A
1426 return keypos;
1427}
729e4ab9
A
1428
1429static int32_t
1430compareInt32(int32_t lPos, int32_t rPos) {
1431 /*
1432 * Compare possibly-negative key offsets. Don't just return lPos - rPos
1433 * because that is prone to negative-integer underflows.
1434 */
1435 if (lPos < rPos) {
1436 return -1;
1437 } else if (lPos > rPos) {
1438 return 1;
1439 } else {
1440 return 0;
1441 }
1442}
1443
1444static int32_t U_CALLCONV
1445compareKeySuffixes(const void *context, const void *l, const void *r) {
1446 const struct SRBRoot *bundle=(const struct SRBRoot *)context;
1447 int32_t lPos = ((const KeyMapEntry *)l)->oldpos;
1448 int32_t rPos = ((const KeyMapEntry *)r)->oldpos;
1449 const char *lStart = getKeyString(bundle, lPos);
1450 const char *lLimit = lStart;
1451 const char *rStart = getKeyString(bundle, rPos);
1452 const char *rLimit = rStart;
1453 int32_t diff;
1454 while (*lLimit != 0) { ++lLimit; }
1455 while (*rLimit != 0) { ++rLimit; }
1456 /* compare keys in reverse character order */
1457 while (lStart < lLimit && rStart < rLimit) {
1458 diff = (int32_t)(uint8_t)*--lLimit - (int32_t)(uint8_t)*--rLimit;
1459 if (diff != 0) {
1460 return diff;
1461 }
1462 }
1463 /* sort equal suffixes by descending key length */
1464 diff = (int32_t)(rLimit - rStart) - (int32_t)(lLimit - lStart);
1465 if (diff != 0) {
1466 return diff;
1467 }
1468 /* Sort pool bundle keys first (negative oldpos), and otherwise keys in parsing order. */
1469 return compareInt32(lPos, rPos);
1470}
1471
1472static int32_t U_CALLCONV
1473compareKeyNewpos(const void *context, const void *l, const void *r) {
1474 return compareInt32(((const KeyMapEntry *)l)->newpos, ((const KeyMapEntry *)r)->newpos);
1475}
1476
1477static int32_t U_CALLCONV
1478compareKeyOldpos(const void *context, const void *l, const void *r) {
1479 return compareInt32(((const KeyMapEntry *)l)->oldpos, ((const KeyMapEntry *)r)->oldpos);
1480}
1481
1482void
1483bundle_compactKeys(struct SRBRoot *bundle, UErrorCode *status) {
1484 KeyMapEntry *map;
1485 char *keys;
1486 int32_t i;
1487 int32_t keysCount = bundle->fPoolBundleKeysCount + bundle->fKeysCount;
1488 if (U_FAILURE(*status) || bundle->fKeysCount == 0 || bundle->fKeyMap != NULL) {
1489 return;
1490 }
1491 map = (KeyMapEntry *)uprv_malloc(keysCount * sizeof(KeyMapEntry));
1492 if (map == NULL) {
1493 *status = U_MEMORY_ALLOCATION_ERROR;
1494 return;
1495 }
1496 keys = (char *)bundle->fPoolBundleKeys;
1497 for (i = 0; i < bundle->fPoolBundleKeysCount; ++i) {
1498 map[i].oldpos =
1499 (int32_t)(keys - bundle->fPoolBundleKeys) | 0x80000000; /* negative oldpos */
1500 map[i].newpos = 0;
1501 while (*keys != 0) { ++keys; } /* skip the key */
1502 ++keys; /* skip the NUL */
1503 }
1504 keys = bundle->fKeys + bundle->fKeysBottom;
1505 for (; i < keysCount; ++i) {
1506 map[i].oldpos = (int32_t)(keys - bundle->fKeys);
1507 map[i].newpos = 0;
1508 while (*keys != 0) { ++keys; } /* skip the key */
1509 ++keys; /* skip the NUL */
1510 }
1511 /* Sort the keys so that each one is immediately followed by all of its suffixes. */
1512 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1513 compareKeySuffixes, bundle, FALSE, status);
1514 /*
1515 * Make suffixes point into earlier, longer strings that contain them
1516 * and mark the old, now unused suffix bytes as deleted.
1517 */
1518 if (U_SUCCESS(*status)) {
1519 keys = bundle->fKeys;
1520 for (i = 0; i < keysCount;) {
1521 /*
1522 * This key is not a suffix of the previous one;
1523 * keep this one and delete the following ones that are
1524 * suffixes of this one.
1525 */
1526 const char *key;
1527 const char *keyLimit;
1528 int32_t j = i + 1;
1529 map[i].newpos = map[i].oldpos;
1530 if (j < keysCount && map[j].oldpos < 0) {
1531 /* Key string from the pool bundle, do not delete. */
1532 i = j;
1533 continue;
1534 }
1535 key = getKeyString(bundle, map[i].oldpos);
1536 for (keyLimit = key; *keyLimit != 0; ++keyLimit) {}
1537 for (; j < keysCount && map[j].oldpos >= 0; ++j) {
1538 const char *k;
1539 char *suffix;
1540 const char *suffixLimit;
1541 int32_t offset;
1542 suffix = keys + map[j].oldpos;
1543 for (suffixLimit = suffix; *suffixLimit != 0; ++suffixLimit) {}
1544 offset = (int32_t)(keyLimit - key) - (suffixLimit - suffix);
1545 if (offset < 0) {
1546 break; /* suffix cannot be longer than the original */
1547 }
1548 /* Is it a suffix of the earlier, longer key? */
1549 for (k = keyLimit; suffix < suffixLimit && *--k == *--suffixLimit;) {}
1550 if (suffix == suffixLimit && *k == *suffixLimit) {
1551 map[j].newpos = map[i].oldpos + offset; /* yes, point to the earlier key */
1552 /* mark the suffix as deleted */
1553 while (*suffix != 0) { *suffix++ = 1; }
1554 *suffix = 1;
1555 } else {
1556 break; /* not a suffix, restart from here */
1557 }
1558 }
1559 i = j;
1560 }
1561 /*
1562 * Re-sort by newpos, then modify the key characters array in-place
1563 * to squeeze out unused bytes, and readjust the newpos offsets.
1564 */
1565 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1566 compareKeyNewpos, NULL, FALSE, status);
1567 if (U_SUCCESS(*status)) {
1568 int32_t oldpos, newpos, limit;
1569 oldpos = newpos = bundle->fKeysBottom;
1570 limit = bundle->fKeysTop;
1571 /* skip key offsets that point into the pool bundle rather than this new bundle */
1572 for (i = 0; i < keysCount && map[i].newpos < 0; ++i) {}
1573 if (i < keysCount) {
1574 while (oldpos < limit) {
1575 if (keys[oldpos] == 1) {
1576 ++oldpos; /* skip unused bytes */
1577 } else {
1578 /* adjust the new offsets for keys starting here */
1579 while (i < keysCount && map[i].newpos == oldpos) {
1580 map[i++].newpos = newpos;
1581 }
1582 /* move the key characters to their new position */
1583 keys[newpos++] = keys[oldpos++];
1584 }
1585 }
1586 assert(i == keysCount);
1587 }
1588 bundle->fKeysTop = newpos;
1589 /* Re-sort once more, by old offsets for binary searching. */
1590 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1591 compareKeyOldpos, NULL, FALSE, status);
1592 if (U_SUCCESS(*status)) {
1593 /* key size reduction by limit - newpos */
1594 bundle->fKeyMap = map;
1595 map = NULL;
1596 }
1597 }
1598 }
1599 uprv_free(map);
1600}
1601
1602static int32_t U_CALLCONV
1603compareStringSuffixes(const void *context, const void *l, const void *r) {
1604 struct SResource *left = *((struct SResource **)l);
1605 struct SResource *right = *((struct SResource **)r);
1606 const UChar *lStart = left->u.fString.fChars;
1607 const UChar *lLimit = lStart + left->u.fString.fLength;
1608 const UChar *rStart = right->u.fString.fChars;
1609 const UChar *rLimit = rStart + right->u.fString.fLength;
1610 int32_t diff;
1611 /* compare keys in reverse character order */
1612 while (lStart < lLimit && rStart < rLimit) {
1613 diff = (int32_t)*--lLimit - (int32_t)*--rLimit;
1614 if (diff != 0) {
1615 return diff;
1616 }
1617 }
1618 /* sort equal suffixes by descending string length */
1619 return right->u.fString.fLength - left->u.fString.fLength;
1620}
1621
1622static int32_t U_CALLCONV
1623compareStringLengths(const void *context, const void *l, const void *r) {
1624 struct SResource *left = *((struct SResource **)l);
1625 struct SResource *right = *((struct SResource **)r);
1626 int32_t diff;
1627 /* Make "is suffix of another string" compare greater than a non-suffix. */
1628 diff = (int)(left->u.fString.fSame != NULL) - (int)(right->u.fString.fSame != NULL);
1629 if (diff != 0) {
1630 return diff;
1631 }
1632 /* sort by ascending string length */
1633 return left->u.fString.fLength - right->u.fString.fLength;
1634}
1635
1636static int32_t
1637string_writeUTF16v2(struct SRBRoot *bundle, struct SResource *res, int32_t utf16Length) {
1638 int32_t length = res->u.fString.fLength;
1639 res->fRes = URES_MAKE_RESOURCE(URES_STRING_V2, utf16Length);
1640 res->fWritten = TRUE;
1641 switch(res->u.fString.fNumCharsForLength) {
1642 case 0:
1643 break;
1644 case 1:
1645 bundle->f16BitUnits[utf16Length++] = (uint16_t)(0xdc00 + length);
1646 break;
1647 case 2:
1648 bundle->f16BitUnits[utf16Length] = (uint16_t)(0xdfef + (length >> 16));
1649 bundle->f16BitUnits[utf16Length + 1] = (uint16_t)length;
1650 utf16Length += 2;
1651 break;
1652 case 3:
1653 bundle->f16BitUnits[utf16Length] = 0xdfff;
1654 bundle->f16BitUnits[utf16Length + 1] = (uint16_t)(length >> 16);
1655 bundle->f16BitUnits[utf16Length + 2] = (uint16_t)length;
1656 utf16Length += 3;
1657 break;
1658 default:
1659 break; /* will not occur */
1660 }
1661 u_memcpy(bundle->f16BitUnits + utf16Length, res->u.fString.fChars, length + 1);
1662 return utf16Length + length + 1;
1663}
1664
1665static void
1666bundle_compactStrings(struct SRBRoot *bundle, UErrorCode *status) {
1667 if (U_FAILURE(*status)) {
1668 return;
1669 }
1670 switch(bundle->fStringsForm) {
1671 case STRINGS_UTF16_V2:
1672 if (bundle->f16BitUnitsLength > 0) {
1673 struct SResource **array;
1674 int32_t count = uhash_count(bundle->fStringSet);
1675 int32_t i, pos;
1676 /*
1677 * Allocate enough space for the initial NUL and the UTF-16 v2 strings,
1678 * and some extra for URES_TABLE16 and URES_ARRAY16 values.
1679 * Round down to an even number.
1680 */
1681 int32_t utf16Length = (bundle->f16BitUnitsLength + 20000) & ~1;
1682 bundle->f16BitUnits = (UChar *)uprv_malloc(utf16Length * U_SIZEOF_UCHAR);
1683 array = (struct SResource **)uprv_malloc(count * sizeof(struct SResource **));
1684 if (bundle->f16BitUnits == NULL || array == NULL) {
1685 uprv_free(bundle->f16BitUnits);
1686 bundle->f16BitUnits = NULL;
1687 uprv_free(array);
1688 *status = U_MEMORY_ALLOCATION_ERROR;
1689 return;
1690 }
1691 bundle->f16BitUnitsCapacity = utf16Length;
1692 /* insert the initial NUL */
1693 bundle->f16BitUnits[0] = 0;
1694 utf16Length = 1;
1695 ++bundle->f16BitUnitsLength;
1696 for (pos = -1, i = 0; i < count; ++i) {
1697 array[i] = (struct SResource *)uhash_nextElement(bundle->fStringSet, &pos)->key.pointer;
1698 }
1699 /* Sort the strings so that each one is immediately followed by all of its suffixes. */
1700 uprv_sortArray(array, count, (int32_t)sizeof(struct SResource **),
1701 compareStringSuffixes, NULL, FALSE, status);
1702 /*
1703 * Make suffixes point into earlier, longer strings that contain them.
1704 * Temporarily use fSame and fSuffixOffset for suffix strings to
1705 * refer to the remaining ones.
1706 */
1707 if (U_SUCCESS(*status)) {
1708 for (i = 0; i < count;) {
1709 /*
1710 * This string is not a suffix of the previous one;
1711 * write this one and subsume the following ones that are
1712 * suffixes of this one.
1713 */
1714 struct SResource *res = array[i];
1715 const UChar *strLimit = res->u.fString.fChars + res->u.fString.fLength;
1716 int32_t j;
1717 for (j = i + 1; j < count; ++j) {
1718 struct SResource *suffixRes = array[j];
1719 const UChar *s;
1720 const UChar *suffix = suffixRes->u.fString.fChars;
1721 const UChar *suffixLimit = suffix + suffixRes->u.fString.fLength;
1722 int32_t offset = res->u.fString.fLength - suffixRes->u.fString.fLength;
1723 if (offset < 0) {
1724 break; /* suffix cannot be longer than the original */
1725 }
1726 /* Is it a suffix of the earlier, longer key? */
1727 for (s = strLimit; suffix < suffixLimit && *--s == *--suffixLimit;) {}
1728 if (suffix == suffixLimit && *s == *suffixLimit) {
1729 if (suffixRes->u.fString.fNumCharsForLength == 0) {
1730 /* yes, point to the earlier string */
1731 suffixRes->u.fString.fSame = res;
1732 suffixRes->u.fString.fSuffixOffset = offset;
1733 } else {
1734 /* write the suffix by itself if we need explicit length */
1735 }
1736 } else {
1737 break; /* not a suffix, restart from here */
1738 }
1739 }
1740 i = j;
1741 }
1742 }
1743 /*
1744 * Re-sort the strings by ascending length (except suffixes last)
1745 * to optimize for URES_TABLE16 and URES_ARRAY16:
1746 * Keep as many as possible within reach of 16-bit offsets.
1747 */
1748 uprv_sortArray(array, count, (int32_t)sizeof(struct SResource **),
1749 compareStringLengths, NULL, FALSE, status);
1750 if (U_SUCCESS(*status)) {
1751 /* Write the non-suffix strings. */
1752 for (i = 0; i < count && array[i]->u.fString.fSame == NULL; ++i) {
1753 utf16Length = string_writeUTF16v2(bundle, array[i], utf16Length);
1754 }
1755 /* Write the suffix strings. Make each point to the real string. */
1756 for (; i < count; ++i) {
1757 struct SResource *res = array[i];
1758 struct SResource *same = res->u.fString.fSame;
1759 res->fRes = same->fRes + same->u.fString.fNumCharsForLength + res->u.fString.fSuffixOffset;
1760 res->u.fString.fSame = NULL;
1761 res->fWritten = TRUE;
1762 }
1763 }
1764 assert(utf16Length <= bundle->f16BitUnitsLength);
1765 bundle->f16BitUnitsLength = utf16Length;
1766 uprv_free(array);
1767 }
1768 break;
1769 default:
1770 break;
1771 }
1772}