1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 * Copyright (C) 2000-2015, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 *******************************************************************************
10 *
11 * File reslist.cpp
12 *
13 * Modification History:
14 *
15 * Date Name Description
16 * 02/21/00 weiv Creation.
17 *******************************************************************************
18 */
19
20 // Safer use of UnicodeString.
21 #ifndef UNISTR_FROM_CHAR_EXPLICIT
22 # define UNISTR_FROM_CHAR_EXPLICIT explicit
23 #endif
24
25 // Less important, but still a good idea.
26 #ifndef UNISTR_FROM_STRING_EXPLICIT
27 # define UNISTR_FROM_STRING_EXPLICIT explicit
28 #endif
29
30 #include <assert.h>
31 #include <iostream>
32 #include <set>
33 #include <stdio.h>
34
35 #include "unicode/localpointer.h"
36 #include "reslist.h"
37 #include "unewdata.h"
38 #include "unicode/ures.h"
39 #include "unicode/putil.h"
40 #include "errmsg.h"
41 #include "filterrb.h"
42 #include "toolutil.h"
43
44 #include "uarrsort.h"
45 #include "uelement.h"
46 #include "uhash.h"
47 #include "uinvchar.h"
48 #include "ustr_imp.h"
49 #include "unicode/utf16.h"
50 #include "uassert.h"
51
52 /*
53 * Align binary data at a 16-byte offset from the start of the resource bundle,
54 * to be safe for any data type it may contain.
55 */
56 #define BIN_ALIGNMENT 16
57
58 // This numeric constant must be at least 1.
59 // If StringResource.fNumUnitsSaved == 0 then the string occurs only once,
60 // and it makes no sense to move it to the pool bundle.
61 // The larger the threshold for fNumUnitsSaved
62 // the smaller the savings, and the smaller the pool bundle.
63 // We trade some total size reduction to reduce the pool bundle a bit,
64 // so that one can reasonably save data size by
65 // removing bundle files without rebuilding the pool bundle.
66 // This can also help to keep the pool and total (pool+local) string indexes
67 // within 16 bits, that is, within range of Table16 and Array16 containers.
68 #ifndef GENRB_MIN_16BIT_UNITS_SAVED_FOR_POOL_STRING
69 # define GENRB_MIN_16BIT_UNITS_SAVED_FOR_POOL_STRING 10
70 #endif
71
72 U_NAMESPACE_USE
73
74 static UBool gIncludeCopyright = FALSE;
75 static UBool gUsePoolBundle = FALSE;
76 static UBool gIsDefaultFormatVersion = TRUE;
77 static int32_t gFormatVersion = 3;
78
79 /* How do we store string values? */
80 enum {
81 STRINGS_UTF16_V1, /* formatVersion 1: int length + UChars + NUL + padding to 4 bytes */
82 STRINGS_UTF16_V2 /* formatVersion 2 & up: optional length in 1..3 UChars + UChars + NUL */
83 };
84
85 static const int32_t MAX_IMPLICIT_STRING_LENGTH = 40; /* do not store the length explicitly for such strings */
86
87 static const ResFile kNoPoolBundle;
88
89 /*
90 * res_none() returns the address of kNoResource,
91 * for use in non-error cases when no resource is to be added to the bundle.
92 * (NULL is used in error cases.)
93 */
94 static SResource kNoResource; // TODO: const
95
96 static UDataInfo dataInfo= {
97 sizeof(UDataInfo),
98 0,
99
100 U_IS_BIG_ENDIAN,
101 U_CHARSET_FAMILY,
102 sizeof(UChar),
103 0,
104
105 {0x52, 0x65, 0x73, 0x42}, /* dataFormat="ResB" */
106 {1, 3, 0, 0}, /* formatVersion */
107 {1, 4, 0, 0} /* dataVersion take a look at version inside parsed resb*/
108 };
109
110 static const UVersionInfo gFormatVersions[4] = { /* indexed by a major-formatVersion integer */
111 { 0, 0, 0, 0 },
112 { 1, 3, 0, 0 },
113 { 2, 0, 0, 0 },
114 { 3, 0, 0, 0 }
115 };
116 // Remember to update genrb.h GENRB_VERSION when changing the data format.
117 // (Or maybe we should remove GENRB_VERSION and report the ICU version number?)
118
calcPadding(uint32_t size)119 static uint8_t calcPadding(uint32_t size) {
120 /* returns space we need to pad */
121 return (uint8_t) ((size % sizeof(uint32_t)) ? (sizeof(uint32_t) - (size % sizeof(uint32_t))) : 0);
122
123 }
124
setIncludeCopyright(UBool val)125 void setIncludeCopyright(UBool val){
126 gIncludeCopyright=val;
127 }
128
getIncludeCopyright(void)129 UBool getIncludeCopyright(void){
130 return gIncludeCopyright;
131 }
132
setFormatVersion(int32_t formatVersion)133 void setFormatVersion(int32_t formatVersion) {
134 gIsDefaultFormatVersion = FALSE;
135 gFormatVersion = formatVersion;
136 }
137
getFormatVersion()138 int32_t getFormatVersion() {
139 return gFormatVersion;
140 }
141
setUsePoolBundle(UBool use)142 void setUsePoolBundle(UBool use) {
143 gUsePoolBundle = use;
144 }
145
146 // TODO: return const pointer, or find another way to express "none"
res_none()147 struct SResource* res_none() {
148 return &kNoResource;
149 }
150
SResource()151 SResource::SResource()
152 : fType(URES_NONE), fWritten(FALSE), fRes(RES_BOGUS), fRes16(-1), fKey(-1), fKey16(-1),
153 line(0), fNext(NULL) {
154 ustr_init(&fComment);
155 }
156
SResource(SRBRoot * bundle,const char * tag,int8_t type,const UString * comment,UErrorCode & errorCode)157 SResource::SResource(SRBRoot *bundle, const char *tag, int8_t type, const UString* comment,
158 UErrorCode &errorCode)
159 : fType(type), fWritten(FALSE), fRes(RES_BOGUS), fRes16(-1),
160 fKey(bundle != NULL ? bundle->addTag(tag, errorCode) : -1), fKey16(-1),
161 line(0), fNext(NULL) {
162 ustr_init(&fComment);
163 if(comment != NULL) {
164 ustr_cpy(&fComment, comment, &errorCode);
165 }
166 }
167
~SResource()168 SResource::~SResource() {
169 ustr_deinit(&fComment);
170 }
171
~ContainerResource()172 ContainerResource::~ContainerResource() {
173 SResource *current = fFirst;
174 while (current != NULL) {
175 SResource *next = current->fNext;
176 delete current;
177 current = next;
178 }
179 }
180
~TableResource()181 TableResource::~TableResource() {}
182
183 // TODO: clarify that containers adopt new items, even in error cases; use LocalPointer
add(SResource * res,int linenumber,UErrorCode & errorCode)184 void TableResource::add(SResource *res, int linenumber, UErrorCode &errorCode) {
185 if (U_FAILURE(errorCode) || res == NULL || res == &kNoResource) {
186 return;
187 }
188
189 /* remember this linenumber to report to the user if there is a duplicate key */
190 res->line = linenumber;
191
192 /* here we need to traverse the list */
193 ++fCount;
194
195 /* is the list still empty? */
196 if (fFirst == NULL) {
197 fFirst = res;
198 res->fNext = NULL;
199 return;
200 }
201
202 const char *resKeyString = fRoot->fKeys + res->fKey;
203
204 SResource *current = fFirst;
205
206 SResource *prev = NULL;
207 while (current != NULL) {
208 const char *currentKeyString = fRoot->fKeys + current->fKey;
209 int diff;
210 /*
211 * formatVersion 1: compare key strings in native-charset order
212 * formatVersion 2 and up: compare key strings in ASCII order
213 */
214 if (gFormatVersion == 1 || U_CHARSET_FAMILY == U_ASCII_FAMILY) {
215 diff = uprv_strcmp(currentKeyString, resKeyString);
216 } else {
217 diff = uprv_compareInvCharsAsAscii(currentKeyString, resKeyString);
218 }
219 if (diff < 0) {
220 prev = current;
221 current = current->fNext;
222 } else if (diff > 0) {
223 /* we're either in front of the list, or in the middle */
224 if (prev == NULL) {
225 /* front of the list */
226 fFirst = res;
227 } else {
228 /* middle of the list */
229 prev->fNext = res;
230 }
231
232 res->fNext = current;
233 return;
234 } else {
235 /* Key already exists! ERROR! */
236 error(linenumber, "duplicate key '%s' in table, first appeared at line %d", currentKeyString, current->line);
237 errorCode = U_UNSUPPORTED_ERROR;
238 return;
239 }
240 }
241
242 /* end of list */
243 prev->fNext = res;
244 res->fNext = NULL;
245 }
246
~ArrayResource()247 ArrayResource::~ArrayResource() {}
248
add(SResource * res)249 void ArrayResource::add(SResource *res) {
250 if (res != NULL && res != &kNoResource) {
251 if (fFirst == NULL) {
252 fFirst = res;
253 } else {
254 fLast->fNext = res;
255 }
256 fLast = res;
257 ++fCount;
258 }
259 }
260
~PseudoListResource()261 PseudoListResource::~PseudoListResource() {}
262
add(SResource * res)263 void PseudoListResource::add(SResource *res) {
264 if (res != NULL && res != &kNoResource) {
265 res->fNext = fFirst;
266 fFirst = res;
267 ++fCount;
268 }
269 }
270
StringBaseResource(SRBRoot * bundle,const char * tag,int8_t type,const UChar * value,int32_t len,const UString * comment,UErrorCode & errorCode)271 StringBaseResource::StringBaseResource(SRBRoot *bundle, const char *tag, int8_t type,
272 const UChar *value, int32_t len,
273 const UString* comment, UErrorCode &errorCode)
274 : SResource(bundle, tag, type, comment, errorCode) {
275 if (len == 0 && gFormatVersion > 1) {
276 fRes = URES_MAKE_EMPTY_RESOURCE(type);
277 fWritten = TRUE;
278 return;
279 }
280
281 fString.setTo(ConstChar16Ptr(value), len);
282 fString.getTerminatedBuffer(); // Some code relies on NUL-termination.
283 if (U_SUCCESS(errorCode) && fString.isBogus()) {
284 errorCode = U_MEMORY_ALLOCATION_ERROR;
285 }
286 }
287
StringBaseResource(SRBRoot * bundle,int8_t type,const icu::UnicodeString & value,UErrorCode & errorCode)288 StringBaseResource::StringBaseResource(SRBRoot *bundle, int8_t type,
289 const icu::UnicodeString &value, UErrorCode &errorCode)
290 : SResource(bundle, NULL, type, NULL, errorCode), fString(value) {
291 if (value.isEmpty() && gFormatVersion > 1) {
292 fRes = URES_MAKE_EMPTY_RESOURCE(type);
293 fWritten = TRUE;
294 return;
295 }
296
297 fString.getTerminatedBuffer(); // Some code relies on NUL-termination.
298 if (U_SUCCESS(errorCode) && fString.isBogus()) {
299 errorCode = U_MEMORY_ALLOCATION_ERROR;
300 }
301 }
302
303 // Pool bundle string, alias the buffer. Guaranteed NUL-terminated and not empty.
StringBaseResource(int8_t type,const UChar * value,int32_t len,UErrorCode & errorCode)304 StringBaseResource::StringBaseResource(int8_t type, const UChar *value, int32_t len,
305 UErrorCode &errorCode)
306 : SResource(NULL, NULL, type, NULL, errorCode), fString(TRUE, value, len) {
307 assert(len > 0);
308 assert(!fString.isBogus());
309 }
310
~StringBaseResource()311 StringBaseResource::~StringBaseResource() {}
312
313 static int32_t U_CALLCONV
string_hash(const UElement key)314 string_hash(const UElement key) {
315 const StringResource *res = static_cast<const StringResource *>(key.pointer);
316 return res->fString.hashCode();
317 }
318
319 static UBool U_CALLCONV
string_comp(const UElement key1,const UElement key2)320 string_comp(const UElement key1, const UElement key2) {
321 const StringResource *res1 = static_cast<const StringResource *>(key1.pointer);
322 const StringResource *res2 = static_cast<const StringResource *>(key2.pointer);
323 return res1->fString == res2->fString;
324 }
325
~StringResource()326 StringResource::~StringResource() {}
327
~AliasResource()328 AliasResource::~AliasResource() {}
329
IntResource(SRBRoot * bundle,const char * tag,int32_t value,const UString * comment,UErrorCode & errorCode)330 IntResource::IntResource(SRBRoot *bundle, const char *tag, int32_t value,
331 const UString* comment, UErrorCode &errorCode)
332 : SResource(bundle, tag, URES_INT, comment, errorCode) {
333 fValue = value;
334 fRes = URES_MAKE_RESOURCE(URES_INT, value & RES_MAX_OFFSET);
335 fWritten = TRUE;
336 }
337
~IntResource()338 IntResource::~IntResource() {}
339
IntVectorResource(SRBRoot * bundle,const char * tag,const UString * comment,UErrorCode & errorCode)340 IntVectorResource::IntVectorResource(SRBRoot *bundle, const char *tag,
341 const UString* comment, UErrorCode &errorCode)
342 : SResource(bundle, tag, URES_INT_VECTOR, comment, errorCode),
343 fCount(0), fArray(new uint32_t[RESLIST_MAX_INT_VECTOR]) {
344 if (fArray == NULL) {
345 errorCode = U_MEMORY_ALLOCATION_ERROR;
346 return;
347 }
348 }
349
~IntVectorResource()350 IntVectorResource::~IntVectorResource() {
351 delete[] fArray;
352 }
353
add(int32_t value,UErrorCode & errorCode)354 void IntVectorResource::add(int32_t value, UErrorCode &errorCode) {
355 if (U_SUCCESS(errorCode)) {
356 fArray[fCount++] = value;
357 }
358 }
359
BinaryResource(SRBRoot * bundle,const char * tag,uint32_t length,uint8_t * data,const char * fileName,const UString * comment,UErrorCode & errorCode)360 BinaryResource::BinaryResource(SRBRoot *bundle, const char *tag,
361 uint32_t length, uint8_t *data, const char* fileName,
362 const UString* comment, UErrorCode &errorCode)
363 : SResource(bundle, tag, URES_BINARY, comment, errorCode),
364 fLength(length), fData(NULL), fFileName(NULL) {
365 if (U_FAILURE(errorCode)) {
366 return;
367 }
368 if (fileName != NULL && *fileName != 0){
369 fFileName = new char[uprv_strlen(fileName)+1];
370 if (fFileName == NULL) {
371 errorCode = U_MEMORY_ALLOCATION_ERROR;
372 return;
373 }
374 uprv_strcpy(fFileName, fileName);
375 }
376 if (length > 0) {
377 fData = new uint8_t[length];
378 if (fData == NULL) {
379 errorCode = U_MEMORY_ALLOCATION_ERROR;
380 return;
381 }
382 uprv_memcpy(fData, data, length);
383 } else {
384 if (gFormatVersion > 1) {
385 fRes = URES_MAKE_EMPTY_RESOURCE(URES_BINARY);
386 fWritten = TRUE;
387 }
388 }
389 }
390
~BinaryResource()391 BinaryResource::~BinaryResource() {
392 delete[] fData;
393 delete[] fFileName;
394 }
395
396 /* Writing Functions */
397
398 void
handlePreflightStrings(SRBRoot * bundle,UHashtable * stringSet,UErrorCode & errorCode)399 StringResource::handlePreflightStrings(SRBRoot *bundle, UHashtable *stringSet,
400 UErrorCode &errorCode) {
401 assert(fSame == NULL);
402 fSame = static_cast<StringResource *>(uhash_get(stringSet, this));
403 if (fSame != NULL) {
404 // This is a duplicate of a pool bundle string or of an earlier-visited string.
405 if (++fSame->fNumCopies == 1) {
406 assert(fSame->fWritten);
407 int32_t poolStringIndex = (int32_t)RES_GET_OFFSET(fSame->fRes);
408 if (poolStringIndex >= bundle->fPoolStringIndexLimit) {
409 bundle->fPoolStringIndexLimit = poolStringIndex + 1;
410 }
411 }
412 return;
413 }
414 /* Put this string into the set for finding duplicates. */
415 fNumCopies = 1;
416 uhash_put(stringSet, this, this, &errorCode);
417
418 if (bundle->fStringsForm != STRINGS_UTF16_V1) {
419 int32_t len = length();
420 if (len <= MAX_IMPLICIT_STRING_LENGTH &&
421 !U16_IS_TRAIL(fString[0]) && fString.indexOf((UChar)0) < 0) {
422 /*
423 * This string will be stored without an explicit length.
424 * Runtime will detect !U16_IS_TRAIL(s[0]) and call u_strlen().
425 */
426 fNumCharsForLength = 0;
427 } else if (len <= 0x3ee) {
428 fNumCharsForLength = 1;
429 } else if (len <= 0xfffff) {
430 fNumCharsForLength = 2;
431 } else {
432 fNumCharsForLength = 3;
433 }
434 bundle->f16BitStringsLength += fNumCharsForLength + len + 1; /* +1 for the NUL */
435 }
436 }
437
438 void
handlePreflightStrings(SRBRoot * bundle,UHashtable * stringSet,UErrorCode & errorCode)439 ContainerResource::handlePreflightStrings(SRBRoot *bundle, UHashtable *stringSet,
440 UErrorCode &errorCode) {
441 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
442 current->preflightStrings(bundle, stringSet, errorCode);
443 }
444 }
445
446 void
preflightStrings(SRBRoot * bundle,UHashtable * stringSet,UErrorCode & errorCode)447 SResource::preflightStrings(SRBRoot *bundle, UHashtable *stringSet, UErrorCode &errorCode) {
448 if (U_FAILURE(errorCode)) {
449 return;
450 }
451 if (fRes != RES_BOGUS) {
452 /*
453 * The resource item word was already precomputed, which means
454 * no further data needs to be written.
455 * This might be an integer, or an empty string/binary/etc.
456 */
457 return;
458 }
459 handlePreflightStrings(bundle, stringSet, errorCode);
460 }
461
462 void
handlePreflightStrings(SRBRoot *,UHashtable *,UErrorCode &)463 SResource::handlePreflightStrings(SRBRoot * /*bundle*/, UHashtable * /*stringSet*/,
464 UErrorCode & /*errorCode*/) {
465 /* Neither a string nor a container. */
466 }
467
468 int32_t
makeRes16(uint32_t resWord) const469 SRBRoot::makeRes16(uint32_t resWord) const {
470 if (resWord == 0) {
471 return 0; /* empty string */
472 }
473 uint32_t type = RES_GET_TYPE(resWord);
474 int32_t offset = (int32_t)RES_GET_OFFSET(resWord);
475 if (type == URES_STRING_V2) {
476 assert(offset > 0);
477 if (offset < fPoolStringIndexLimit) {
478 if (offset < fPoolStringIndex16Limit) {
479 return offset;
480 }
481 } else {
482 offset = offset - fPoolStringIndexLimit + fPoolStringIndex16Limit;
483 if (offset <= 0xffff) {
484 return offset;
485 }
486 }
487 }
488 return -1;
489 }
490
491 int32_t
mapKey(int32_t oldpos) const492 SRBRoot::mapKey(int32_t oldpos) const {
493 const KeyMapEntry *map = fKeyMap;
494 if (map == NULL) {
495 return oldpos;
496 }
497 int32_t i, start, limit;
498
499 /* do a binary search for the old, pre-compactKeys() key offset */
500 start = fUsePoolBundle->fKeysCount;
501 limit = start + fKeysCount;
502 while (start < limit - 1) {
503 i = (start + limit) / 2;
504 if (oldpos < map[i].oldpos) {
505 limit = i;
506 } else {
507 start = i;
508 }
509 }
510 assert(oldpos == map[start].oldpos);
511 return map[start].newpos;
512 }
513
514 /*
515 * Only called for UTF-16 v1 strings and duplicate UTF-16 v2 strings.
516 * For unique UTF-16 v2 strings, write16() sees fRes != RES_BOGUS
517 * and exits early.
518 */
519 void
handleWrite16(SRBRoot *)520 StringResource::handleWrite16(SRBRoot * /*bundle*/) {
521 SResource *same;
522 if ((same = fSame) != NULL) {
523 /* This is a duplicate. */
524 assert(same->fRes != RES_BOGUS && same->fWritten);
525 fRes = same->fRes;
526 fWritten = same->fWritten;
527 }
528 }
529
530 void
writeAllRes16(SRBRoot * bundle)531 ContainerResource::writeAllRes16(SRBRoot *bundle) {
532 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
533 bundle->f16BitUnits.append((UChar)current->fRes16);
534 }
535 fWritten = TRUE;
536 }
537
538 void
handleWrite16(SRBRoot * bundle)539 ArrayResource::handleWrite16(SRBRoot *bundle) {
540 if (fCount == 0 && gFormatVersion > 1) {
541 fRes = URES_MAKE_EMPTY_RESOURCE(URES_ARRAY);
542 fWritten = TRUE;
543 return;
544 }
545
546 int32_t res16 = 0;
547 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
548 current->write16(bundle);
549 res16 |= current->fRes16;
550 }
551 if (fCount <= 0xffff && res16 >= 0 && gFormatVersion > 1) {
552 fRes = URES_MAKE_RESOURCE(URES_ARRAY16, bundle->f16BitUnits.length());
553 bundle->f16BitUnits.append((UChar)fCount);
554 writeAllRes16(bundle);
555 }
556 }
557
558 void
handleWrite16(SRBRoot * bundle)559 TableResource::handleWrite16(SRBRoot *bundle) {
560 if (fCount == 0 && gFormatVersion > 1) {
561 fRes = URES_MAKE_EMPTY_RESOURCE(URES_TABLE);
562 fWritten = TRUE;
563 return;
564 }
565 /* Find the smallest table type that fits the data. */
566 int32_t key16 = 0;
567 int32_t res16 = 0;
568 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
569 current->write16(bundle);
570 key16 |= current->fKey16;
571 res16 |= current->fRes16;
572 }
573 if(fCount > (uint32_t)bundle->fMaxTableLength) {
574 bundle->fMaxTableLength = fCount;
575 }
576 if (fCount <= 0xffff && key16 >= 0) {
577 if (res16 >= 0 && gFormatVersion > 1) {
578 /* 16-bit count, key offsets and values */
579 fRes = URES_MAKE_RESOURCE(URES_TABLE16, bundle->f16BitUnits.length());
580 bundle->f16BitUnits.append((UChar)fCount);
581 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
582 bundle->f16BitUnits.append((UChar)current->fKey16);
583 }
584 writeAllRes16(bundle);
585 } else {
586 /* 16-bit count, 16-bit key offsets, 32-bit values */
587 fTableType = URES_TABLE;
588 }
589 } else {
590 /* 32-bit count, key offsets and values */
591 fTableType = URES_TABLE32;
592 }
593 }
594
595 void
handleWrite16(SRBRoot *)596 PseudoListResource::handleWrite16(SRBRoot * /*bundle*/) {
597 fRes = URES_MAKE_EMPTY_RESOURCE(URES_TABLE);
598 fWritten = TRUE;
599 }
600
601 void
write16(SRBRoot * bundle)602 SResource::write16(SRBRoot *bundle) {
603 if (fKey >= 0) {
604 // A tagged resource has a non-negative key index into the parsed key strings.
605 // compactKeys() built a map from parsed key index to the final key index.
606 // After the mapping, negative key indexes are used for shared pool bundle keys.
607 fKey = bundle->mapKey(fKey);
608 // If the key index fits into a Key16 for a Table or Table16,
609 // then set the fKey16 field accordingly.
610 // Otherwise keep it at -1.
611 if (fKey >= 0) {
612 if (fKey < bundle->fLocalKeyLimit) {
613 fKey16 = fKey;
614 }
615 } else {
616 int32_t poolKeyIndex = fKey & 0x7fffffff;
617 if (poolKeyIndex <= 0xffff) {
618 poolKeyIndex += bundle->fLocalKeyLimit;
619 if (poolKeyIndex <= 0xffff) {
620 fKey16 = poolKeyIndex;
621 }
622 }
623 }
624 }
625 /*
626 * fRes != RES_BOGUS:
627 * The resource item word was already precomputed, which means
628 * no further data needs to be written.
629 * This might be an integer, or an empty or UTF-16 v2 string,
630 * an empty binary, etc.
631 */
632 if (fRes == RES_BOGUS) {
633 handleWrite16(bundle);
634 }
635 // Compute fRes16 for precomputed as well as just-computed fRes.
636 fRes16 = bundle->makeRes16(fRes);
637 }
638
639 void
handleWrite16(SRBRoot *)640 SResource::handleWrite16(SRBRoot * /*bundle*/) {
641 /* Only a few resource types write 16-bit units. */
642 }
643
644 /*
645 * Only called for UTF-16 v1 strings, and for aliases.
646 * For UTF-16 v2 strings, preWrite() sees fRes != RES_BOGUS
647 * and exits early.
648 */
649 void
handlePreWrite(uint32_t * byteOffset)650 StringBaseResource::handlePreWrite(uint32_t *byteOffset) {
651 /* Write the UTF-16 v1 string. */
652 fRes = URES_MAKE_RESOURCE(fType, *byteOffset >> 2);
653 *byteOffset += 4 + (length() + 1) * U_SIZEOF_UCHAR;
654 }
655
656 void
handlePreWrite(uint32_t * byteOffset)657 IntVectorResource::handlePreWrite(uint32_t *byteOffset) {
658 if (fCount == 0 && gFormatVersion > 1) {
659 fRes = URES_MAKE_EMPTY_RESOURCE(URES_INT_VECTOR);
660 fWritten = TRUE;
661 } else {
662 fRes = URES_MAKE_RESOURCE(URES_INT_VECTOR, *byteOffset >> 2);
663 *byteOffset += (1 + fCount) * 4;
664 }
665 }
666
667 void
handlePreWrite(uint32_t * byteOffset)668 BinaryResource::handlePreWrite(uint32_t *byteOffset) {
669 uint32_t pad = 0;
670 uint32_t dataStart = *byteOffset + sizeof(fLength);
671
672 if (dataStart % BIN_ALIGNMENT) {
673 pad = (BIN_ALIGNMENT - dataStart % BIN_ALIGNMENT);
674 *byteOffset += pad; /* pad == 4 or 8 or 12 */
675 }
676 fRes = URES_MAKE_RESOURCE(URES_BINARY, *byteOffset >> 2);
677 *byteOffset += 4 + fLength;
678 }
679
680 void
preWriteAllRes(uint32_t * byteOffset)681 ContainerResource::preWriteAllRes(uint32_t *byteOffset) {
682 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
683 current->preWrite(byteOffset);
684 }
685 }
686
687 void
handlePreWrite(uint32_t * byteOffset)688 ArrayResource::handlePreWrite(uint32_t *byteOffset) {
689 preWriteAllRes(byteOffset);
690 fRes = URES_MAKE_RESOURCE(URES_ARRAY, *byteOffset >> 2);
691 *byteOffset += (1 + fCount) * 4;
692 }
693
694 void
handlePreWrite(uint32_t * byteOffset)695 TableResource::handlePreWrite(uint32_t *byteOffset) {
696 preWriteAllRes(byteOffset);
697 if (fTableType == URES_TABLE) {
698 /* 16-bit count, 16-bit key offsets, 32-bit values */
699 fRes = URES_MAKE_RESOURCE(URES_TABLE, *byteOffset >> 2);
700 *byteOffset += 2 + fCount * 6;
701 } else {
702 /* 32-bit count, key offsets and values */
703 fRes = URES_MAKE_RESOURCE(URES_TABLE32, *byteOffset >> 2);
704 *byteOffset += 4 + fCount * 8;
705 }
706 }
707
708 void
preWrite(uint32_t * byteOffset)709 SResource::preWrite(uint32_t *byteOffset) {
710 if (fRes != RES_BOGUS) {
711 /*
712 * The resource item word was already precomputed, which means
713 * no further data needs to be written.
714 * This might be an integer, or an empty or UTF-16 v2 string,
715 * an empty binary, etc.
716 */
717 return;
718 }
719 handlePreWrite(byteOffset);
720 *byteOffset += calcPadding(*byteOffset);
721 }
722
723 void
handlePreWrite(uint32_t *)724 SResource::handlePreWrite(uint32_t * /*byteOffset*/) {
725 assert(FALSE);
726 }
727
728 /*
729 * Only called for UTF-16 v1 strings, and for aliases. For UTF-16 v2 strings,
730 * write() sees fWritten and exits early.
731 */
732 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)733 StringBaseResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
734 /* Write the UTF-16 v1 string. */
735 int32_t len = length();
736 udata_write32(mem, len);
737 udata_writeUString(mem, getBuffer(), len + 1);
738 *byteOffset += 4 + (len + 1) * U_SIZEOF_UCHAR;
739 fWritten = TRUE;
740 }
741
742 void
writeAllRes(UNewDataMemory * mem,uint32_t * byteOffset)743 ContainerResource::writeAllRes(UNewDataMemory *mem, uint32_t *byteOffset) {
744 uint32_t i = 0;
745 for (SResource *current = fFirst; current != NULL; ++i, current = current->fNext) {
746 current->write(mem, byteOffset);
747 }
748 assert(i == fCount);
749 }
750
751 void
writeAllRes32(UNewDataMemory * mem,uint32_t * byteOffset)752 ContainerResource::writeAllRes32(UNewDataMemory *mem, uint32_t *byteOffset) {
753 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
754 udata_write32(mem, current->fRes);
755 }
756 *byteOffset += fCount * 4;
757 }
758
759 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)760 ArrayResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
761 writeAllRes(mem, byteOffset);
762 udata_write32(mem, fCount);
763 *byteOffset += 4;
764 writeAllRes32(mem, byteOffset);
765 }
766
767 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)768 IntVectorResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
769 udata_write32(mem, fCount);
770 for(uint32_t i = 0; i < fCount; ++i) {
771 udata_write32(mem, fArray[i]);
772 }
773 *byteOffset += (1 + fCount) * 4;
774 }
775
776 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)777 BinaryResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
778 uint32_t pad = 0;
779 uint32_t dataStart = *byteOffset + sizeof(fLength);
780
781 if (dataStart % BIN_ALIGNMENT) {
782 pad = (BIN_ALIGNMENT - dataStart % BIN_ALIGNMENT);
783 udata_writePadding(mem, pad); /* pad == 4 or 8 or 12 */
784 *byteOffset += pad;
785 }
786
787 udata_write32(mem, fLength);
788 if (fLength > 0) {
789 udata_writeBlock(mem, fData, fLength);
790 }
791 *byteOffset += 4 + fLength;
792 }
793
794 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)795 TableResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
796 writeAllRes(mem, byteOffset);
797 if(fTableType == URES_TABLE) {
798 udata_write16(mem, (uint16_t)fCount);
799 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
800 udata_write16(mem, current->fKey16);
801 }
802 *byteOffset += (1 + fCount)* 2;
803 if ((fCount & 1) == 0) {
804 /* 16-bit count and even number of 16-bit key offsets need padding before 32-bit resource items */
805 udata_writePadding(mem, 2);
806 *byteOffset += 2;
807 }
808 } else /* URES_TABLE32 */ {
809 udata_write32(mem, fCount);
810 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
811 udata_write32(mem, (uint32_t)current->fKey);
812 }
813 *byteOffset += (1 + fCount)* 4;
814 }
815 writeAllRes32(mem, byteOffset);
816 }
817
818 void
write(UNewDataMemory * mem,uint32_t * byteOffset)819 SResource::write(UNewDataMemory *mem, uint32_t *byteOffset) {
820 if (fWritten) {
821 assert(fRes != RES_BOGUS);
822 return;
823 }
824 handleWrite(mem, byteOffset);
825 uint8_t paddingSize = calcPadding(*byteOffset);
826 if (paddingSize > 0) {
827 udata_writePadding(mem, paddingSize);
828 *byteOffset += paddingSize;
829 }
830 fWritten = TRUE;
831 }
832
833 void
handleWrite(UNewDataMemory *,uint32_t *)834 SResource::handleWrite(UNewDataMemory * /*mem*/, uint32_t * /*byteOffset*/) {
835 assert(FALSE);
836 }
837
write(const char * outputDir,const char * outputPkg,char * writtenFilename,int writtenFilenameLen,UErrorCode & errorCode)838 void SRBRoot::write(const char *outputDir, const char *outputPkg,
839 char *writtenFilename, int writtenFilenameLen,
840 UErrorCode &errorCode) {
841 UNewDataMemory *mem = NULL;
842 uint32_t byteOffset = 0;
843 uint32_t top, size;
844 char dataName[1024];
845 int32_t indexes[URES_INDEX_TOP];
846
847 compactKeys(errorCode);
848 /*
849 * Add padding bytes to fKeys so that fKeysTop is 4-aligned.
850 * Safe because the capacity is a multiple of 4.
851 */
852 while (fKeysTop & 3) {
853 fKeys[fKeysTop++] = (char)0xaa;
854 }
855 /*
856 * In URES_TABLE, use all local key offsets that fit into 16 bits,
857 * and use the remaining 16-bit offsets for pool key offsets
858 * if there are any.
859 * If there are no local keys, then use the whole 16-bit space
860 * for pool key offsets.
861 * Note: This cannot be changed without changing the major formatVersion.
862 */
863 if (fKeysBottom < fKeysTop) {
864 if (fKeysTop <= 0x10000) {
865 fLocalKeyLimit = fKeysTop;
866 } else {
867 fLocalKeyLimit = 0x10000;
868 }
869 } else {
870 fLocalKeyLimit = 0;
871 }
872
873 UHashtable *stringSet;
874 if (gFormatVersion > 1) {
875 stringSet = uhash_open(string_hash, string_comp, string_comp, &errorCode);
876 if (U_SUCCESS(errorCode) &&
877 fUsePoolBundle != NULL && fUsePoolBundle->fStrings != NULL) {
878 for (SResource *current = fUsePoolBundle->fStrings->fFirst;
879 current != NULL;
880 current = current->fNext) {
881 StringResource *sr = static_cast<StringResource *>(current);
882 sr->fNumCopies = 0;
883 sr->fNumUnitsSaved = 0;
884 uhash_put(stringSet, sr, sr, &errorCode);
885 }
886 }
887 fRoot->preflightStrings(this, stringSet, errorCode);
888 } else {
889 stringSet = NULL;
890 }
891 if (fStringsForm == STRINGS_UTF16_V2 && f16BitStringsLength > 0) {
892 compactStringsV2(stringSet, errorCode);
893 }
894 uhash_close(stringSet);
895 if (U_FAILURE(errorCode)) {
896 return;
897 }
898
899 int32_t formatVersion = gFormatVersion;
900 if (fPoolStringIndexLimit != 0) {
901 int32_t sum = fPoolStringIndexLimit + fLocalStringIndexLimit;
902 if ((sum - 1) > RES_MAX_OFFSET) {
903 errorCode = U_BUFFER_OVERFLOW_ERROR;
904 return;
905 }
906 if (fPoolStringIndexLimit < 0x10000 && sum <= 0x10000) {
907 // 16-bit indexes work for all pool + local strings.
908 fPoolStringIndex16Limit = fPoolStringIndexLimit;
909 } else {
910 // Set the pool index threshold so that 16-bit indexes work
911 // for some pool strings and some local strings.
912 fPoolStringIndex16Limit = (int32_t)(
913 ((int64_t)fPoolStringIndexLimit * 0xffff) / sum);
914 }
915 } else if (gIsDefaultFormatVersion && formatVersion == 3 && !fIsPoolBundle) {
916 // If we just default to formatVersion 3
917 // but there are no pool bundle strings to share
918 // and we do not write a pool bundle,
919 // then write formatVersion 2 which is just as good.
920 formatVersion = 2;
921 }
922
923 fRoot->write16(this);
924 if (f16BitUnits.isBogus()) {
925 errorCode = U_MEMORY_ALLOCATION_ERROR;
926 return;
927 }
928 if (f16BitUnits.length() & 1) {
929 f16BitUnits.append((UChar)0xaaaa); /* pad to multiple of 4 bytes */
930 }
931
932 byteOffset = fKeysTop + f16BitUnits.length() * 2;
933 fRoot->preWrite(&byteOffset);
934
935 /* total size including the root item */
936 top = byteOffset;
937
938 if (writtenFilename && writtenFilenameLen) {
939 *writtenFilename = 0;
940 }
941
942 if (writtenFilename) {
943 int32_t off = 0, len = 0;
944 if (outputDir) {
945 len = (int32_t)uprv_strlen(outputDir);
946 if (len > writtenFilenameLen) {
947 len = writtenFilenameLen;
948 }
949 uprv_strncpy(writtenFilename, outputDir, len);
950 }
951 if (writtenFilenameLen -= len) {
952 off += len;
953 writtenFilename[off] = U_FILE_SEP_CHAR;
954 if (--writtenFilenameLen) {
955 ++off;
956 if(outputPkg != NULL)
957 {
958 uprv_strcpy(writtenFilename+off, outputPkg);
959 off += (int32_t)uprv_strlen(outputPkg);
960 writtenFilename[off] = '_';
961 ++off;
962 }
963
964 len = (int32_t)uprv_strlen(fLocale);
965 if (len > writtenFilenameLen) {
966 len = writtenFilenameLen;
967 }
968 uprv_strncpy(writtenFilename + off, fLocale, len);
969 if (writtenFilenameLen -= len) {
970 off += len;
971 len = 5;
972 if (len > writtenFilenameLen) {
973 len = writtenFilenameLen;
974 }
975 uprv_strncpy(writtenFilename + off, ".res", len);
976 }
977 }
978 }
979 }
980
981 if(outputPkg)
982 {
983 uprv_strcpy(dataName, outputPkg);
984 uprv_strcat(dataName, "_");
985 uprv_strcat(dataName, fLocale);
986 }
987 else
988 {
989 uprv_strcpy(dataName, fLocale);
990 }
991
992 uprv_memcpy(dataInfo.formatVersion, gFormatVersions + formatVersion, sizeof(UVersionInfo));
993
994 mem = udata_create(outputDir, "res", dataName,
995 &dataInfo, (gIncludeCopyright==TRUE)? U_COPYRIGHT_STRING:NULL, &errorCode);
996 if(U_FAILURE(errorCode)){
997 return;
998 }
999
1000 /* write the root item */
1001 udata_write32(mem, fRoot->fRes);
1002
1003 /*
1004 * formatVersion 1.1 (ICU 2.8):
1005 * write int32_t indexes[] after root and before the key strings
1006 * to make it easier to parse resource bundles in icuswap or from Java etc.
1007 */
1008 uprv_memset(indexes, 0, sizeof(indexes));
1009 indexes[URES_INDEX_LENGTH]= fIndexLength;
1010 indexes[URES_INDEX_KEYS_TOP]= fKeysTop>>2;
1011 indexes[URES_INDEX_RESOURCES_TOP]= (int32_t)(top>>2);
1012 indexes[URES_INDEX_BUNDLE_TOP]= indexes[URES_INDEX_RESOURCES_TOP];
1013 indexes[URES_INDEX_MAX_TABLE_LENGTH]= fMaxTableLength;
1014
1015 /*
1016 * formatVersion 1.2 (ICU 3.6):
1017 * write indexes[URES_INDEX_ATTRIBUTES] with URES_ATT_NO_FALLBACK set or not set
1018 * the memset() above initialized all indexes[] to 0
1019 */
1020 if (fNoFallback) {
1021 indexes[URES_INDEX_ATTRIBUTES]=URES_ATT_NO_FALLBACK;
1022 }
1023 /*
1024 * formatVersion 2.0 (ICU 4.4):
1025 * more compact string value storage, optional pool bundle
1026 */
1027 if (URES_INDEX_16BIT_TOP < fIndexLength) {
1028 indexes[URES_INDEX_16BIT_TOP] = (fKeysTop>>2) + (f16BitUnits.length()>>1);
1029 }
1030 if (URES_INDEX_POOL_CHECKSUM < fIndexLength) {
1031 if (fIsPoolBundle) {
1032 indexes[URES_INDEX_ATTRIBUTES] |= URES_ATT_IS_POOL_BUNDLE | URES_ATT_NO_FALLBACK;
1033 uint32_t checksum = computeCRC((const char *)(fKeys + fKeysBottom),
1034 (uint32_t)(fKeysTop - fKeysBottom), 0);
1035 if (f16BitUnits.length() <= 1) {
1036 // no pool strings to checksum
1037 } else if (U_IS_BIG_ENDIAN) {
1038 checksum = computeCRC(reinterpret_cast<const char *>(f16BitUnits.getBuffer()),
1039 (uint32_t)f16BitUnits.length() * 2, checksum);
1040 } else {
1041 // Swap to big-endian so we get the same checksum on all platforms
1042 // (except for charset family, due to the key strings).
1043 UnicodeString s(f16BitUnits);
1044 assert(!s.isBogus());
1045 // .getBuffer(capacity) returns a mutable buffer
1046 char16_t* p = s.getBuffer(f16BitUnits.length());
1047 for (int32_t count = f16BitUnits.length(); count > 0; --count) {
1048 uint16_t x = *p;
1049 *p++ = (uint16_t)((x << 8) | (x >> 8));
1050 }
1051 s.releaseBuffer(f16BitUnits.length());
1052 checksum = computeCRC((const char *)s.getBuffer(),
1053 (uint32_t)f16BitUnits.length() * 2, checksum);
1054 }
1055 indexes[URES_INDEX_POOL_CHECKSUM] = (int32_t)checksum;
1056 } else if (gUsePoolBundle) {
1057 indexes[URES_INDEX_ATTRIBUTES] |= URES_ATT_USES_POOL_BUNDLE;
1058 indexes[URES_INDEX_POOL_CHECKSUM] = fUsePoolBundle->fChecksum;
1059 }
1060 }
1061 // formatVersion 3 (ICU 56):
1062 // share string values via pool bundle strings
1063 indexes[URES_INDEX_LENGTH] |= fPoolStringIndexLimit << 8; // bits 23..0 -> 31..8
1064 indexes[URES_INDEX_ATTRIBUTES] |= (fPoolStringIndexLimit >> 12) & 0xf000; // bits 27..24 -> 15..12
1065 indexes[URES_INDEX_ATTRIBUTES] |= fPoolStringIndex16Limit << 16;
1066
1067 /* write the indexes[] */
1068 udata_writeBlock(mem, indexes, fIndexLength*4);
1069
1070 /* write the table key strings */
1071 udata_writeBlock(mem, fKeys+fKeysBottom,
1072 fKeysTop-fKeysBottom);
1073
1074 /* write the v2 UTF-16 strings, URES_TABLE16 and URES_ARRAY16 */
1075 udata_writeBlock(mem, f16BitUnits.getBuffer(), f16BitUnits.length()*2);
1076
1077 /* write all of the bundle contents: the root item and its children */
1078 byteOffset = fKeysTop + f16BitUnits.length() * 2;
1079 fRoot->write(mem, &byteOffset);
1080 assert(byteOffset == top);
1081
1082 size = udata_finish(mem, &errorCode);
1083 if(top != size) {
1084 fprintf(stderr, "genrb error: wrote %u bytes but counted %u\n",
1085 (int)size, (int)top);
1086 errorCode = U_INTERNAL_PROGRAM_ERROR;
1087 }
1088 }
1089
1090 /* Opening Functions */
1091
table_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1092 TableResource* table_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1093 LocalPointer<TableResource> res(new TableResource(bundle, tag, comment, *status), *status);
1094 return U_SUCCESS(*status) ? res.orphan() : NULL;
1095 }
1096
array_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1097 ArrayResource* array_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1098 LocalPointer<ArrayResource> res(new ArrayResource(bundle, tag, comment, *status), *status);
1099 return U_SUCCESS(*status) ? res.orphan() : NULL;
1100 }
1101
string_open(struct SRBRoot * bundle,const char * tag,const UChar * value,int32_t len,const struct UString * comment,UErrorCode * status)1102 struct SResource *string_open(struct SRBRoot *bundle, const char *tag, const UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
1103 LocalPointer<SResource> res(
1104 new StringResource(bundle, tag, value, len, comment, *status), *status);
1105 return U_SUCCESS(*status) ? res.orphan() : NULL;
1106 }
1107
alias_open(struct SRBRoot * bundle,const char * tag,UChar * value,int32_t len,const struct UString * comment,UErrorCode * status)1108 struct SResource *alias_open(struct SRBRoot *bundle, const char *tag, UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
1109 LocalPointer<SResource> res(
1110 new AliasResource(bundle, tag, value, len, comment, *status), *status);
1111 return U_SUCCESS(*status) ? res.orphan() : NULL;
1112 }
1113
intvector_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1114 IntVectorResource *intvector_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1115 LocalPointer<IntVectorResource> res(
1116 new IntVectorResource(bundle, tag, comment, *status), *status);
1117 return U_SUCCESS(*status) ? res.orphan() : NULL;
1118 }
1119
int_open(struct SRBRoot * bundle,const char * tag,int32_t value,const struct UString * comment,UErrorCode * status)1120 struct SResource *int_open(struct SRBRoot *bundle, const char *tag, int32_t value, const struct UString* comment, UErrorCode *status) {
1121 LocalPointer<SResource> res(new IntResource(bundle, tag, value, comment, *status), *status);
1122 return U_SUCCESS(*status) ? res.orphan() : NULL;
1123 }
1124
bin_open(struct SRBRoot * bundle,const char * tag,uint32_t length,uint8_t * data,const char * fileName,const struct UString * comment,UErrorCode * status)1125 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) {
1126 LocalPointer<SResource> res(
1127 new BinaryResource(bundle, tag, length, data, fileName, comment, *status), *status);
1128 return U_SUCCESS(*status) ? res.orphan() : NULL;
1129 }
1130
SRBRoot(const UString * comment,UBool isPoolBundle,UErrorCode & errorCode)1131 SRBRoot::SRBRoot(const UString *comment, UBool isPoolBundle, UErrorCode &errorCode)
1132 : fRoot(NULL), fLocale(NULL), fIndexLength(0), fMaxTableLength(0), fNoFallback(FALSE),
1133 fStringsForm(STRINGS_UTF16_V1), fIsPoolBundle(isPoolBundle),
1134 fKeys(NULL), fKeyMap(NULL),
1135 fKeysBottom(0), fKeysTop(0), fKeysCapacity(0),
1136 fKeysCount(0), fLocalKeyLimit(0),
1137 f16BitUnits(), f16BitStringsLength(0),
1138 fUsePoolBundle(&kNoPoolBundle),
1139 fPoolStringIndexLimit(0), fPoolStringIndex16Limit(0), fLocalStringIndexLimit(0),
1140 fWritePoolBundle(NULL) {
1141 if (U_FAILURE(errorCode)) {
1142 return;
1143 }
1144
1145 if (gFormatVersion > 1) {
1146 // f16BitUnits must start with a zero for empty resources.
1147 // We might be able to omit it if there are no empty 16-bit resources.
1148 f16BitUnits.append((UChar)0);
1149 }
1150
1151 fKeys = (char *) uprv_malloc(sizeof(char) * KEY_SPACE_SIZE);
1152 if (isPoolBundle) {
1153 fRoot = new PseudoListResource(this, errorCode);
1154 } else {
1155 fRoot = new TableResource(this, NULL, comment, errorCode);
1156 }
1157 if (fKeys == NULL || fRoot == NULL || U_FAILURE(errorCode)) {
1158 if (U_SUCCESS(errorCode)) {
1159 errorCode = U_MEMORY_ALLOCATION_ERROR;
1160 }
1161 return;
1162 }
1163
1164 fKeysCapacity = KEY_SPACE_SIZE;
1165 /* formatVersion 1.1 and up: start fKeysTop after the root item and indexes[] */
1166 if (gUsePoolBundle || isPoolBundle) {
1167 fIndexLength = URES_INDEX_POOL_CHECKSUM + 1;
1168 } else if (gFormatVersion >= 2) {
1169 fIndexLength = URES_INDEX_16BIT_TOP + 1;
1170 } else /* formatVersion 1 */ {
1171 fIndexLength = URES_INDEX_ATTRIBUTES + 1;
1172 }
1173 fKeysBottom = (1 /* root */ + fIndexLength) * 4;
1174 uprv_memset(fKeys, 0, fKeysBottom);
1175 fKeysTop = fKeysBottom;
1176
1177 if (gFormatVersion == 1) {
1178 fStringsForm = STRINGS_UTF16_V1;
1179 } else {
1180 fStringsForm = STRINGS_UTF16_V2;
1181 }
1182 }
1183
1184 /* Closing Functions */
1185
res_close(struct SResource * res)1186 void res_close(struct SResource *res) {
1187 delete res;
1188 }
1189
~SRBRoot()1190 SRBRoot::~SRBRoot() {
1191 delete fRoot;
1192 uprv_free(fLocale);
1193 uprv_free(fKeys);
1194 uprv_free(fKeyMap);
1195 }
1196
1197 /* Misc Functions */
1198
setLocale(UChar * locale,UErrorCode & errorCode)1199 void SRBRoot::setLocale(UChar *locale, UErrorCode &errorCode) {
1200 if(U_FAILURE(errorCode)) {
1201 return;
1202 }
1203
1204 uprv_free(fLocale);
1205 fLocale = (char*) uprv_malloc(sizeof(char) * (u_strlen(locale)+1));
1206 if(fLocale == NULL) {
1207 errorCode = U_MEMORY_ALLOCATION_ERROR;
1208 return;
1209 }
1210
1211 u_UCharsToChars(locale, fLocale, u_strlen(locale)+1);
1212 }
1213
1214 const char *
getKeyString(int32_t key) const1215 SRBRoot::getKeyString(int32_t key) const {
1216 if (key < 0) {
1217 return fUsePoolBundle->fKeys + (key & 0x7fffffff);
1218 } else {
1219 return fKeys + key;
1220 }
1221 }
1222
1223 const char *
getKeyString(const SRBRoot * bundle) const1224 SResource::getKeyString(const SRBRoot *bundle) const {
1225 if (fKey == -1) {
1226 return NULL;
1227 }
1228 return bundle->getKeyString(fKey);
1229 }
1230
1231 const char *
getKeyBytes(int32_t * pLength) const1232 SRBRoot::getKeyBytes(int32_t *pLength) const {
1233 *pLength = fKeysTop - fKeysBottom;
1234 return fKeys + fKeysBottom;
1235 }
1236
1237 int32_t
addKeyBytes(const char * keyBytes,int32_t length,UErrorCode & errorCode)1238 SRBRoot::addKeyBytes(const char *keyBytes, int32_t length, UErrorCode &errorCode) {
1239 int32_t keypos;
1240
1241 // It is not legal to add new key bytes after compactKeys is run!
1242 U_ASSERT(fKeyMap == nullptr);
1243
1244 if (U_FAILURE(errorCode)) {
1245 return -1;
1246 }
1247 if (length < 0 || (keyBytes == NULL && length != 0)) {
1248 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
1249 return -1;
1250 }
1251 if (length == 0) {
1252 return fKeysTop;
1253 }
1254
1255 keypos = fKeysTop;
1256 fKeysTop += length;
1257 if (fKeysTop >= fKeysCapacity) {
1258 /* overflow - resize the keys buffer */
1259 fKeysCapacity += KEY_SPACE_SIZE;
1260 fKeys = static_cast<char *>(uprv_realloc(fKeys, fKeysCapacity));
1261 if(fKeys == NULL) {
1262 errorCode = U_MEMORY_ALLOCATION_ERROR;
1263 return -1;
1264 }
1265 }
1266
1267 uprv_memcpy(fKeys + keypos, keyBytes, length);
1268
1269 return keypos;
1270 }
1271
1272 int32_t
addTag(const char * tag,UErrorCode & errorCode)1273 SRBRoot::addTag(const char *tag, UErrorCode &errorCode) {
1274 int32_t keypos;
1275
1276 if (U_FAILURE(errorCode)) {
1277 return -1;
1278 }
1279
1280 if (tag == NULL) {
1281 /* no error: the root table and array items have no keys */
1282 return -1;
1283 }
1284
1285 keypos = addKeyBytes(tag, (int32_t)(uprv_strlen(tag) + 1), errorCode);
1286 if (U_SUCCESS(errorCode)) {
1287 ++fKeysCount;
1288 }
1289 return keypos;
1290 }
1291
1292 static int32_t
compareInt32(int32_t lPos,int32_t rPos)1293 compareInt32(int32_t lPos, int32_t rPos) {
1294 /*
1295 * Compare possibly-negative key offsets. Don't just return lPos - rPos
1296 * because that is prone to negative-integer underflows.
1297 */
1298 if (lPos < rPos) {
1299 return -1;
1300 } else if (lPos > rPos) {
1301 return 1;
1302 } else {
1303 return 0;
1304 }
1305 }
1306
1307 static int32_t U_CALLCONV
compareKeySuffixes(const void * context,const void * l,const void * r)1308 compareKeySuffixes(const void *context, const void *l, const void *r) {
1309 const struct SRBRoot *bundle=(const struct SRBRoot *)context;
1310 int32_t lPos = ((const KeyMapEntry *)l)->oldpos;
1311 int32_t rPos = ((const KeyMapEntry *)r)->oldpos;
1312 const char *lStart = bundle->getKeyString(lPos);
1313 const char *lLimit = lStart;
1314 const char *rStart = bundle->getKeyString(rPos);
1315 const char *rLimit = rStart;
1316 int32_t diff;
1317 while (*lLimit != 0) { ++lLimit; }
1318 while (*rLimit != 0) { ++rLimit; }
1319 /* compare keys in reverse character order */
1320 while (lStart < lLimit && rStart < rLimit) {
1321 diff = (int32_t)(uint8_t)*--lLimit - (int32_t)(uint8_t)*--rLimit;
1322 if (diff != 0) {
1323 return diff;
1324 }
1325 }
1326 /* sort equal suffixes by descending key length */
1327 diff = (int32_t)(rLimit - rStart) - (int32_t)(lLimit - lStart);
1328 if (diff != 0) {
1329 return diff;
1330 }
1331 /* Sort pool bundle keys first (negative oldpos), and otherwise keys in parsing order. */
1332 return compareInt32(lPos, rPos);
1333 }
1334
1335 static int32_t U_CALLCONV
compareKeyNewpos(const void *,const void * l,const void * r)1336 compareKeyNewpos(const void * /*context*/, const void *l, const void *r) {
1337 return compareInt32(((const KeyMapEntry *)l)->newpos, ((const KeyMapEntry *)r)->newpos);
1338 }
1339
1340 static int32_t U_CALLCONV
compareKeyOldpos(const void *,const void * l,const void * r)1341 compareKeyOldpos(const void * /*context*/, const void *l, const void *r) {
1342 return compareInt32(((const KeyMapEntry *)l)->oldpos, ((const KeyMapEntry *)r)->oldpos);
1343 }
1344
collectKeys(std::function<void (int32_t)> collector) const1345 void SResource::collectKeys(std::function<void(int32_t)> collector) const {
1346 collector(fKey);
1347 }
1348
collectKeys(std::function<void (int32_t)> collector) const1349 void ContainerResource::collectKeys(std::function<void(int32_t)> collector) const {
1350 collector(fKey);
1351 for (SResource* curr = fFirst; curr != NULL; curr = curr->fNext) {
1352 curr->collectKeys(collector);
1353 }
1354 }
1355
1356 void
compactKeys(UErrorCode & errorCode)1357 SRBRoot::compactKeys(UErrorCode &errorCode) {
1358 KeyMapEntry *map;
1359 char *keys;
1360 int32_t i;
1361
1362 // Except for pool bundles, keys might not be used.
1363 // Do not add unused keys to the final bundle.
1364 std::set<int32_t> keysInUse;
1365 if (!fIsPoolBundle) {
1366 fRoot->collectKeys([&keysInUse](int32_t key) {
1367 if (key >= 0) {
1368 keysInUse.insert(key);
1369 }
1370 });
1371 fKeysCount = static_cast<int32_t>(keysInUse.size());
1372 }
1373
1374 int32_t keysCount = fUsePoolBundle->fKeysCount + fKeysCount;
1375 if (U_FAILURE(errorCode) || fKeyMap != NULL) {
1376 return;
1377 }
1378 map = (KeyMapEntry *)uprv_malloc(keysCount * sizeof(KeyMapEntry));
1379 if (map == NULL) {
1380 errorCode = U_MEMORY_ALLOCATION_ERROR;
1381 return;
1382 }
1383 keys = (char *)fUsePoolBundle->fKeys;
1384 for (i = 0; i < fUsePoolBundle->fKeysCount; ++i) {
1385 map[i].oldpos =
1386 (int32_t)(keys - fUsePoolBundle->fKeys) | 0x80000000; /* negative oldpos */
1387 map[i].newpos = 0;
1388 while (*keys != 0) { ++keys; } /* skip the key */
1389 ++keys; /* skip the NUL */
1390 }
1391 keys = fKeys + fKeysBottom;
1392 while (i < keysCount) {
1393 int32_t keyOffset = static_cast<int32_t>(keys - fKeys);
1394 if (!fIsPoolBundle && keysInUse.count(keyOffset) == 0) {
1395 // Mark the unused key as deleted
1396 while (*keys != 0) { *keys++ = 1; }
1397 *keys++ = 1;
1398 } else {
1399 map[i].oldpos = keyOffset;
1400 map[i].newpos = 0;
1401 while (*keys != 0) { ++keys; } /* skip the key */
1402 ++keys; /* skip the NUL */
1403 i++;
1404 }
1405 }
1406 if (keys != fKeys + fKeysTop) {
1407 // Throw away any unused keys from the end
1408 fKeysTop = static_cast<int32_t>(keys - fKeys);
1409 }
1410 /* Sort the keys so that each one is immediately followed by all of its suffixes. */
1411 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1412 compareKeySuffixes, this, FALSE, &errorCode);
1413 /*
1414 * Make suffixes point into earlier, longer strings that contain them
1415 * and mark the old, now unused suffix bytes as deleted.
1416 */
1417 if (U_SUCCESS(errorCode)) {
1418 keys = fKeys;
1419 for (i = 0; i < keysCount;) {
1420 /*
1421 * This key is not a suffix of the previous one;
1422 * keep this one and delete the following ones that are
1423 * suffixes of this one.
1424 */
1425 const char *key;
1426 const char *keyLimit;
1427 int32_t j = i + 1;
1428 map[i].newpos = map[i].oldpos;
1429 if (j < keysCount && map[j].oldpos < 0) {
1430 /* Key string from the pool bundle, do not delete. */
1431 i = j;
1432 continue;
1433 }
1434 key = getKeyString(map[i].oldpos);
1435 for (keyLimit = key; *keyLimit != 0; ++keyLimit) {}
1436 for (; j < keysCount && map[j].oldpos >= 0; ++j) {
1437 const char *k;
1438 char *suffix;
1439 const char *suffixLimit;
1440 int32_t offset;
1441 suffix = keys + map[j].oldpos;
1442 for (suffixLimit = suffix; *suffixLimit != 0; ++suffixLimit) {}
1443 offset = static_cast<int32_t>((keyLimit - key) - (suffixLimit - suffix));
1444 if (offset < 0) {
1445 break; /* suffix cannot be longer than the original */
1446 }
1447 /* Is it a suffix of the earlier, longer key? */
1448 for (k = keyLimit; suffix < suffixLimit && *--k == *--suffixLimit;) {}
1449 if (suffix == suffixLimit && *k == *suffixLimit) {
1450 map[j].newpos = map[i].oldpos + offset; /* yes, point to the earlier key */
1451 // Mark the suffix as deleted
1452 while (*suffix != 0) { *suffix++ = 1; }
1453 *suffix = 1;
1454 } else {
1455 break; /* not a suffix, restart from here */
1456 }
1457 }
1458 i = j;
1459 }
1460 /*
1461 * Re-sort by newpos, then modify the key characters array in-place
1462 * to squeeze out unused bytes, and readjust the newpos offsets.
1463 */
1464 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1465 compareKeyNewpos, NULL, FALSE, &errorCode);
1466 if (U_SUCCESS(errorCode)) {
1467 int32_t oldpos, newpos, limit;
1468 oldpos = newpos = fKeysBottom;
1469 limit = fKeysTop;
1470 /* skip key offsets that point into the pool bundle rather than this new bundle */
1471 for (i = 0; i < keysCount && map[i].newpos < 0; ++i) {}
1472 if (i < keysCount) {
1473 while (oldpos < limit) {
1474 if (keys[oldpos] == 1) {
1475 ++oldpos; /* skip unused bytes */
1476 } else {
1477 /* adjust the new offsets for keys starting here */
1478 while (i < keysCount && map[i].newpos == oldpos) {
1479 map[i++].newpos = newpos;
1480 }
1481 /* move the key characters to their new position */
1482 keys[newpos++] = keys[oldpos++];
1483 }
1484 }
1485 U_ASSERT(i == keysCount);
1486 }
1487 fKeysTop = newpos;
1488 /* Re-sort once more, by old offsets for binary searching. */
1489 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1490 compareKeyOldpos, NULL, FALSE, &errorCode);
1491 if (U_SUCCESS(errorCode)) {
1492 /* key size reduction by limit - newpos */
1493 fKeyMap = map;
1494 map = NULL;
1495 }
1496 }
1497 }
1498 uprv_free(map);
1499 }
1500
1501 static int32_t U_CALLCONV
compareStringSuffixes(const void *,const void * l,const void * r)1502 compareStringSuffixes(const void * /*context*/, const void *l, const void *r) {
1503 const StringResource *left = *((const StringResource **)l);
1504 const StringResource *right = *((const StringResource **)r);
1505 const UChar *lStart = left->getBuffer();
1506 const UChar *lLimit = lStart + left->length();
1507 const UChar *rStart = right->getBuffer();
1508 const UChar *rLimit = rStart + right->length();
1509 int32_t diff;
1510 /* compare keys in reverse character order */
1511 while (lStart < lLimit && rStart < rLimit) {
1512 diff = (int32_t)*--lLimit - (int32_t)*--rLimit;
1513 if (diff != 0) {
1514 return diff;
1515 }
1516 }
1517 /* sort equal suffixes by descending string length */
1518 return right->length() - left->length();
1519 }
1520
1521 static int32_t U_CALLCONV
compareStringLengths(const void *,const void * l,const void * r)1522 compareStringLengths(const void * /*context*/, const void *l, const void *r) {
1523 const StringResource *left = *((const StringResource **)l);
1524 const StringResource *right = *((const StringResource **)r);
1525 int32_t diff;
1526 /* Make "is suffix of another string" compare greater than a non-suffix. */
1527 diff = (int)(left->fSame != NULL) - (int)(right->fSame != NULL);
1528 if (diff != 0) {
1529 return diff;
1530 }
1531 /* sort by ascending string length */
1532 diff = left->length() - right->length();
1533 if (diff != 0) {
1534 return diff;
1535 }
1536 // sort by descending size reduction
1537 diff = right->fNumUnitsSaved - left->fNumUnitsSaved;
1538 if (diff != 0) {
1539 return diff;
1540 }
1541 // sort lexically
1542 return left->fString.compare(right->fString);
1543 }
1544
1545 void
writeUTF16v2(int32_t base,UnicodeString & dest)1546 StringResource::writeUTF16v2(int32_t base, UnicodeString &dest) {
1547 int32_t len = length();
1548 fRes = URES_MAKE_RESOURCE(URES_STRING_V2, base + dest.length());
1549 fWritten = TRUE;
1550 switch(fNumCharsForLength) {
1551 case 0:
1552 break;
1553 case 1:
1554 dest.append((UChar)(0xdc00 + len));
1555 break;
1556 case 2:
1557 dest.append((UChar)(0xdfef + (len >> 16)));
1558 dest.append((UChar)len);
1559 break;
1560 case 3:
1561 dest.append((UChar)0xdfff);
1562 dest.append((UChar)(len >> 16));
1563 dest.append((UChar)len);
1564 break;
1565 default:
1566 break; /* will not occur */
1567 }
1568 dest.append(fString);
1569 dest.append((UChar)0);
1570 }
1571
1572 void
compactStringsV2(UHashtable * stringSet,UErrorCode & errorCode)1573 SRBRoot::compactStringsV2(UHashtable *stringSet, UErrorCode &errorCode) {
1574 if (U_FAILURE(errorCode)) {
1575 return;
1576 }
1577 // Store the StringResource pointers in an array for
1578 // easy sorting and processing.
1579 // We enumerate a set of strings, so there are no duplicates.
1580 int32_t count = uhash_count(stringSet);
1581 LocalArray<StringResource *> array(new StringResource *[count], errorCode);
1582 if (U_FAILURE(errorCode)) {
1583 return;
1584 }
1585 for (int32_t pos = UHASH_FIRST, i = 0; i < count; ++i) {
1586 array[i] = (StringResource *)uhash_nextElement(stringSet, &pos)->key.pointer;
1587 }
1588 /* Sort the strings so that each one is immediately followed by all of its suffixes. */
1589 uprv_sortArray(array.getAlias(), count, (int32_t)sizeof(struct SResource **),
1590 compareStringSuffixes, NULL, FALSE, &errorCode);
1591 if (U_FAILURE(errorCode)) {
1592 return;
1593 }
1594 /*
1595 * Make suffixes point into earlier, longer strings that contain them.
1596 * Temporarily use fSame and fSuffixOffset for suffix strings to
1597 * refer to the remaining ones.
1598 */
1599 for (int32_t i = 0; i < count;) {
1600 /*
1601 * This string is not a suffix of the previous one;
1602 * write this one and subsume the following ones that are
1603 * suffixes of this one.
1604 */
1605 StringResource *res = array[i];
1606 res->fNumUnitsSaved = (res->fNumCopies - 1) * res->get16BitStringsLength();
1607 // Whole duplicates of pool strings are already account for in fPoolStringIndexLimit,
1608 // see StringResource::handlePreflightStrings().
1609 int32_t j;
1610 for (j = i + 1; j < count; ++j) {
1611 StringResource *suffixRes = array[j];
1612 /* Is it a suffix of the earlier, longer string? */
1613 if (res->fString.endsWith(suffixRes->fString)) {
1614 assert(res->length() != suffixRes->length()); // Set strings are unique.
1615 if (suffixRes->fWritten) {
1616 // Pool string, skip.
1617 } else if (suffixRes->fNumCharsForLength == 0) {
1618 /* yes, point to the earlier string */
1619 suffixRes->fSame = res;
1620 suffixRes->fSuffixOffset = res->length() - suffixRes->length();
1621 if (res->fWritten) {
1622 // Suffix-share res which is a pool string.
1623 // Compute the resource word and collect the maximum.
1624 suffixRes->fRes =
1625 res->fRes + res->fNumCharsForLength + suffixRes->fSuffixOffset;
1626 int32_t poolStringIndex = (int32_t)RES_GET_OFFSET(suffixRes->fRes);
1627 if (poolStringIndex >= fPoolStringIndexLimit) {
1628 fPoolStringIndexLimit = poolStringIndex + 1;
1629 }
1630 suffixRes->fWritten = TRUE;
1631 }
1632 res->fNumUnitsSaved += suffixRes->fNumCopies * suffixRes->get16BitStringsLength();
1633 } else {
1634 /* write the suffix by itself if we need explicit length */
1635 }
1636 } else {
1637 break; /* not a suffix, restart from here */
1638 }
1639 }
1640 i = j;
1641 }
1642 /*
1643 * Re-sort the strings by ascending length (except suffixes last)
1644 * to optimize for URES_TABLE16 and URES_ARRAY16:
1645 * Keep as many as possible within reach of 16-bit offsets.
1646 */
1647 uprv_sortArray(array.getAlias(), count, (int32_t)sizeof(struct SResource **),
1648 compareStringLengths, NULL, FALSE, &errorCode);
1649 if (U_FAILURE(errorCode)) {
1650 return;
1651 }
1652 if (fIsPoolBundle) {
1653 // Write strings that are sufficiently shared.
1654 // Avoid writing other strings.
1655 int32_t numStringsWritten = 0;
1656 int32_t numUnitsSaved = 0;
1657 int32_t numUnitsNotSaved = 0;
1658 for (int32_t i = 0; i < count; ++i) {
1659 StringResource *res = array[i];
1660 // Maximum pool string index when suffix-sharing the last character.
1661 int32_t maxStringIndex =
1662 f16BitUnits.length() + res->fNumCharsForLength + res->length() - 1;
1663 if (res->fNumUnitsSaved >= GENRB_MIN_16BIT_UNITS_SAVED_FOR_POOL_STRING &&
1664 maxStringIndex < RES_MAX_OFFSET) {
1665 res->writeUTF16v2(0, f16BitUnits);
1666 ++numStringsWritten;
1667 numUnitsSaved += res->fNumUnitsSaved;
1668 } else {
1669 numUnitsNotSaved += res->fNumUnitsSaved;
1670 res->fRes = URES_MAKE_EMPTY_RESOURCE(URES_STRING);
1671 res->fWritten = TRUE;
1672 }
1673 }
1674 if (f16BitUnits.isBogus()) {
1675 errorCode = U_MEMORY_ALLOCATION_ERROR;
1676 }
1677 if (getShowWarning()) { // not quiet
1678 printf("number of shared strings: %d\n", (int)numStringsWritten);
1679 printf("16-bit units for strings: %6d = %6d bytes\n",
1680 (int)f16BitUnits.length(), (int)f16BitUnits.length() * 2);
1681 printf("16-bit units saved: %6d = %6d bytes\n",
1682 (int)numUnitsSaved, (int)numUnitsSaved * 2);
1683 printf("16-bit units not saved: %6d = %6d bytes\n",
1684 (int)numUnitsNotSaved, (int)numUnitsNotSaved * 2);
1685 }
1686 } else {
1687 assert(fPoolStringIndexLimit <= fUsePoolBundle->fStringIndexLimit);
1688 /* Write the non-suffix strings. */
1689 int32_t i;
1690 for (i = 0; i < count && array[i]->fSame == NULL; ++i) {
1691 StringResource *res = array[i];
1692 if (!res->fWritten) {
1693 int32_t localStringIndex = f16BitUnits.length();
1694 if (localStringIndex >= fLocalStringIndexLimit) {
1695 fLocalStringIndexLimit = localStringIndex + 1;
1696 }
1697 res->writeUTF16v2(fPoolStringIndexLimit, f16BitUnits);
1698 }
1699 }
1700 if (f16BitUnits.isBogus()) {
1701 errorCode = U_MEMORY_ALLOCATION_ERROR;
1702 return;
1703 }
1704 if (fWritePoolBundle != NULL && gFormatVersion >= 3) {
1705 PseudoListResource *poolStrings =
1706 static_cast<PseudoListResource *>(fWritePoolBundle->fRoot);
1707 for (i = 0; i < count && array[i]->fSame == NULL; ++i) {
1708 assert(!array[i]->fString.isEmpty());
1709 StringResource *poolString =
1710 new StringResource(fWritePoolBundle, array[i]->fString, errorCode);
1711 if (poolString == NULL) {
1712 errorCode = U_MEMORY_ALLOCATION_ERROR;
1713 break;
1714 }
1715 poolStrings->add(poolString);
1716 }
1717 }
1718 /* Write the suffix strings. Make each point to the real string. */
1719 for (; i < count; ++i) {
1720 StringResource *res = array[i];
1721 if (res->fWritten) {
1722 continue;
1723 }
1724 StringResource *same = res->fSame;
1725 assert(res->length() != same->length()); // Set strings are unique.
1726 res->fRes = same->fRes + same->fNumCharsForLength + res->fSuffixOffset;
1727 int32_t localStringIndex = (int32_t)RES_GET_OFFSET(res->fRes) - fPoolStringIndexLimit;
1728 // Suffixes of pool strings have been set already.
1729 assert(localStringIndex >= 0);
1730 if (localStringIndex >= fLocalStringIndexLimit) {
1731 fLocalStringIndexLimit = localStringIndex + 1;
1732 }
1733 res->fWritten = TRUE;
1734 }
1735 }
1736 // +1 to account for the initial zero in f16BitUnits
1737 assert(f16BitUnits.length() <= (f16BitStringsLength + 1));
1738 }
1739
applyFilter(const PathFilter &,ResKeyPath &,const SRBRoot *)1740 void SResource::applyFilter(
1741 const PathFilter& /*filter*/,
1742 ResKeyPath& /*path*/,
1743 const SRBRoot* /*bundle*/) {
1744 // Only a few resource types (tables) are capable of being filtered.
1745 }
1746
applyFilter(const PathFilter & filter,ResKeyPath & path,const SRBRoot * bundle)1747 void TableResource::applyFilter(
1748 const PathFilter& filter,
1749 ResKeyPath& path,
1750 const SRBRoot* bundle) {
1751 SResource* prev = nullptr;
1752 SResource* curr = fFirst;
1753 for (; curr != nullptr;) {
1754 path.push(curr->getKeyString(bundle));
1755 auto inclusion = filter.match(path);
1756 if (inclusion == PathFilter::EInclusion::INCLUDE) {
1757 // Include whole subtree
1758 // no-op
1759 if (isVerbose()) {
1760 std::cout << "genrb subtree: " << bundle->fLocale << ": INCLUDE: " << path << std::endl;
1761 }
1762 } else if (inclusion == PathFilter::EInclusion::EXCLUDE) {
1763 // Reject the whole subtree
1764 // Remove it from the linked list
1765 if (isVerbose()) {
1766 std::cout << "genrb subtree: " << bundle->fLocale << ": DELETE: " << path << std::endl;
1767 }
1768 if (prev == nullptr) {
1769 fFirst = curr->fNext;
1770 } else {
1771 prev->fNext = curr->fNext;
1772 }
1773 fCount--;
1774 delete curr;
1775 curr = prev;
1776 } else {
1777 U_ASSERT(inclusion == PathFilter::EInclusion::PARTIAL);
1778 // Recurse into the child
1779 curr->applyFilter(filter, path, bundle);
1780 }
1781 path.pop();
1782
1783 prev = curr;
1784 if (curr == nullptr) {
1785 curr = fFirst;
1786 } else {
1787 curr = curr->fNext;
1788 }
1789 }
1790 }
1791