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), fSize(RESLIST_INT_VECTOR_INIT_SIZE),
344 fArray(new uint32_t[fSize]) {
345 if (fArray == NULL) {
346 errorCode = U_MEMORY_ALLOCATION_ERROR;
347 return;
348 }
349 }
350
~IntVectorResource()351 IntVectorResource::~IntVectorResource() {
352 delete[] fArray;
353 }
354
add(int32_t value,UErrorCode & errorCode)355 void IntVectorResource::add(int32_t value, UErrorCode &errorCode) {
356 if (fCount == fSize) {
357 uint32_t* tmp = new uint32_t[2 * fSize];
358 if (tmp == nullptr) {
359 errorCode = U_MEMORY_ALLOCATION_ERROR;
360 return;
361 }
362 uprv_memcpy(tmp, fArray, fSize * sizeof(uint32_t));
363 delete[] fArray;
364 fArray = tmp;
365 fSize *= 2;
366 }
367 if (U_SUCCESS(errorCode)) {
368 fArray[fCount++] = value;
369 }
370 }
371
BinaryResource(SRBRoot * bundle,const char * tag,uint32_t length,uint8_t * data,const char * fileName,const UString * comment,UErrorCode & errorCode)372 BinaryResource::BinaryResource(SRBRoot *bundle, const char *tag,
373 uint32_t length, uint8_t *data, const char* fileName,
374 const UString* comment, UErrorCode &errorCode)
375 : SResource(bundle, tag, URES_BINARY, comment, errorCode),
376 fLength(length), fData(NULL), fFileName(NULL) {
377 if (U_FAILURE(errorCode)) {
378 return;
379 }
380 if (fileName != NULL && *fileName != 0){
381 fFileName = new char[uprv_strlen(fileName)+1];
382 if (fFileName == NULL) {
383 errorCode = U_MEMORY_ALLOCATION_ERROR;
384 return;
385 }
386 uprv_strcpy(fFileName, fileName);
387 }
388 if (length > 0) {
389 fData = new uint8_t[length];
390 if (fData == NULL) {
391 errorCode = U_MEMORY_ALLOCATION_ERROR;
392 return;
393 }
394 uprv_memcpy(fData, data, length);
395 } else {
396 if (gFormatVersion > 1) {
397 fRes = URES_MAKE_EMPTY_RESOURCE(URES_BINARY);
398 fWritten = TRUE;
399 }
400 }
401 }
402
~BinaryResource()403 BinaryResource::~BinaryResource() {
404 delete[] fData;
405 delete[] fFileName;
406 }
407
408 /* Writing Functions */
409
410 void
handlePreflightStrings(SRBRoot * bundle,UHashtable * stringSet,UErrorCode & errorCode)411 StringResource::handlePreflightStrings(SRBRoot *bundle, UHashtable *stringSet,
412 UErrorCode &errorCode) {
413 assert(fSame == NULL);
414 fSame = static_cast<StringResource *>(uhash_get(stringSet, this));
415 if (fSame != NULL) {
416 // This is a duplicate of a pool bundle string or of an earlier-visited string.
417 if (++fSame->fNumCopies == 1) {
418 assert(fSame->fWritten);
419 int32_t poolStringIndex = (int32_t)RES_GET_OFFSET(fSame->fRes);
420 if (poolStringIndex >= bundle->fPoolStringIndexLimit) {
421 bundle->fPoolStringIndexLimit = poolStringIndex + 1;
422 }
423 }
424 return;
425 }
426 /* Put this string into the set for finding duplicates. */
427 fNumCopies = 1;
428 uhash_put(stringSet, this, this, &errorCode);
429
430 if (bundle->fStringsForm != STRINGS_UTF16_V1) {
431 int32_t len = length();
432 if (len <= MAX_IMPLICIT_STRING_LENGTH &&
433 !U16_IS_TRAIL(fString[0]) && fString.indexOf((UChar)0) < 0) {
434 /*
435 * This string will be stored without an explicit length.
436 * Runtime will detect !U16_IS_TRAIL(s[0]) and call u_strlen().
437 */
438 fNumCharsForLength = 0;
439 } else if (len <= 0x3ee) {
440 fNumCharsForLength = 1;
441 } else if (len <= 0xfffff) {
442 fNumCharsForLength = 2;
443 } else {
444 fNumCharsForLength = 3;
445 }
446 bundle->f16BitStringsLength += fNumCharsForLength + len + 1; /* +1 for the NUL */
447 }
448 }
449
450 void
handlePreflightStrings(SRBRoot * bundle,UHashtable * stringSet,UErrorCode & errorCode)451 ContainerResource::handlePreflightStrings(SRBRoot *bundle, UHashtable *stringSet,
452 UErrorCode &errorCode) {
453 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
454 current->preflightStrings(bundle, stringSet, errorCode);
455 }
456 }
457
458 void
preflightStrings(SRBRoot * bundle,UHashtable * stringSet,UErrorCode & errorCode)459 SResource::preflightStrings(SRBRoot *bundle, UHashtable *stringSet, UErrorCode &errorCode) {
460 if (U_FAILURE(errorCode)) {
461 return;
462 }
463 if (fRes != RES_BOGUS) {
464 /*
465 * The resource item word was already precomputed, which means
466 * no further data needs to be written.
467 * This might be an integer, or an empty string/binary/etc.
468 */
469 return;
470 }
471 handlePreflightStrings(bundle, stringSet, errorCode);
472 }
473
474 void
handlePreflightStrings(SRBRoot *,UHashtable *,UErrorCode &)475 SResource::handlePreflightStrings(SRBRoot * /*bundle*/, UHashtable * /*stringSet*/,
476 UErrorCode & /*errorCode*/) {
477 /* Neither a string nor a container. */
478 }
479
480 int32_t
makeRes16(uint32_t resWord) const481 SRBRoot::makeRes16(uint32_t resWord) const {
482 if (resWord == 0) {
483 return 0; /* empty string */
484 }
485 uint32_t type = RES_GET_TYPE(resWord);
486 int32_t offset = (int32_t)RES_GET_OFFSET(resWord);
487 if (type == URES_STRING_V2) {
488 assert(offset > 0);
489 if (offset < fPoolStringIndexLimit) {
490 if (offset < fPoolStringIndex16Limit) {
491 return offset;
492 }
493 } else {
494 offset = offset - fPoolStringIndexLimit + fPoolStringIndex16Limit;
495 if (offset <= 0xffff) {
496 return offset;
497 }
498 }
499 }
500 return -1;
501 }
502
503 int32_t
mapKey(int32_t oldpos) const504 SRBRoot::mapKey(int32_t oldpos) const {
505 const KeyMapEntry *map = fKeyMap;
506 if (map == NULL) {
507 return oldpos;
508 }
509 int32_t i, start, limit;
510
511 /* do a binary search for the old, pre-compactKeys() key offset */
512 start = fUsePoolBundle->fKeysCount;
513 limit = start + fKeysCount;
514 while (start < limit - 1) {
515 i = (start + limit) / 2;
516 if (oldpos < map[i].oldpos) {
517 limit = i;
518 } else {
519 start = i;
520 }
521 }
522 assert(oldpos == map[start].oldpos);
523 return map[start].newpos;
524 }
525
526 /*
527 * Only called for UTF-16 v1 strings and duplicate UTF-16 v2 strings.
528 * For unique UTF-16 v2 strings, write16() sees fRes != RES_BOGUS
529 * and exits early.
530 */
531 void
handleWrite16(SRBRoot *)532 StringResource::handleWrite16(SRBRoot * /*bundle*/) {
533 SResource *same;
534 if ((same = fSame) != NULL) {
535 /* This is a duplicate. */
536 assert(same->fRes != RES_BOGUS && same->fWritten);
537 fRes = same->fRes;
538 fWritten = same->fWritten;
539 }
540 }
541
542 void
writeAllRes16(SRBRoot * bundle)543 ContainerResource::writeAllRes16(SRBRoot *bundle) {
544 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
545 bundle->f16BitUnits.append((UChar)current->fRes16);
546 }
547 fWritten = TRUE;
548 }
549
550 void
handleWrite16(SRBRoot * bundle)551 ArrayResource::handleWrite16(SRBRoot *bundle) {
552 if (fCount == 0 && gFormatVersion > 1) {
553 fRes = URES_MAKE_EMPTY_RESOURCE(URES_ARRAY);
554 fWritten = TRUE;
555 return;
556 }
557
558 int32_t res16 = 0;
559 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
560 current->write16(bundle);
561 res16 |= current->fRes16;
562 }
563 if (fCount <= 0xffff && res16 >= 0 && gFormatVersion > 1) {
564 fRes = URES_MAKE_RESOURCE(URES_ARRAY16, bundle->f16BitUnits.length());
565 bundle->f16BitUnits.append((UChar)fCount);
566 writeAllRes16(bundle);
567 }
568 }
569
570 void
handleWrite16(SRBRoot * bundle)571 TableResource::handleWrite16(SRBRoot *bundle) {
572 if (fCount == 0 && gFormatVersion > 1) {
573 fRes = URES_MAKE_EMPTY_RESOURCE(URES_TABLE);
574 fWritten = TRUE;
575 return;
576 }
577 /* Find the smallest table type that fits the data. */
578 int32_t key16 = 0;
579 int32_t res16 = 0;
580 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
581 current->write16(bundle);
582 key16 |= current->fKey16;
583 res16 |= current->fRes16;
584 }
585 if(fCount > (uint32_t)bundle->fMaxTableLength) {
586 bundle->fMaxTableLength = fCount;
587 }
588 if (fCount <= 0xffff && key16 >= 0) {
589 if (res16 >= 0 && gFormatVersion > 1) {
590 /* 16-bit count, key offsets and values */
591 fRes = URES_MAKE_RESOURCE(URES_TABLE16, bundle->f16BitUnits.length());
592 bundle->f16BitUnits.append((UChar)fCount);
593 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
594 bundle->f16BitUnits.append((UChar)current->fKey16);
595 }
596 writeAllRes16(bundle);
597 } else {
598 /* 16-bit count, 16-bit key offsets, 32-bit values */
599 fTableType = URES_TABLE;
600 }
601 } else {
602 /* 32-bit count, key offsets and values */
603 fTableType = URES_TABLE32;
604 }
605 }
606
607 void
handleWrite16(SRBRoot *)608 PseudoListResource::handleWrite16(SRBRoot * /*bundle*/) {
609 fRes = URES_MAKE_EMPTY_RESOURCE(URES_TABLE);
610 fWritten = TRUE;
611 }
612
613 void
write16(SRBRoot * bundle)614 SResource::write16(SRBRoot *bundle) {
615 if (fKey >= 0) {
616 // A tagged resource has a non-negative key index into the parsed key strings.
617 // compactKeys() built a map from parsed key index to the final key index.
618 // After the mapping, negative key indexes are used for shared pool bundle keys.
619 fKey = bundle->mapKey(fKey);
620 // If the key index fits into a Key16 for a Table or Table16,
621 // then set the fKey16 field accordingly.
622 // Otherwise keep it at -1.
623 if (fKey >= 0) {
624 if (fKey < bundle->fLocalKeyLimit) {
625 fKey16 = fKey;
626 }
627 } else {
628 int32_t poolKeyIndex = fKey & 0x7fffffff;
629 if (poolKeyIndex <= 0xffff) {
630 poolKeyIndex += bundle->fLocalKeyLimit;
631 if (poolKeyIndex <= 0xffff) {
632 fKey16 = poolKeyIndex;
633 }
634 }
635 }
636 }
637 /*
638 * fRes != RES_BOGUS:
639 * The resource item word was already precomputed, which means
640 * no further data needs to be written.
641 * This might be an integer, or an empty or UTF-16 v2 string,
642 * an empty binary, etc.
643 */
644 if (fRes == RES_BOGUS) {
645 handleWrite16(bundle);
646 }
647 // Compute fRes16 for precomputed as well as just-computed fRes.
648 fRes16 = bundle->makeRes16(fRes);
649 }
650
651 void
handleWrite16(SRBRoot *)652 SResource::handleWrite16(SRBRoot * /*bundle*/) {
653 /* Only a few resource types write 16-bit units. */
654 }
655
656 /*
657 * Only called for UTF-16 v1 strings, and for aliases.
658 * For UTF-16 v2 strings, preWrite() sees fRes != RES_BOGUS
659 * and exits early.
660 */
661 void
handlePreWrite(uint32_t * byteOffset)662 StringBaseResource::handlePreWrite(uint32_t *byteOffset) {
663 /* Write the UTF-16 v1 string. */
664 fRes = URES_MAKE_RESOURCE(fType, *byteOffset >> 2);
665 *byteOffset += 4 + (length() + 1) * U_SIZEOF_UCHAR;
666 }
667
668 void
handlePreWrite(uint32_t * byteOffset)669 IntVectorResource::handlePreWrite(uint32_t *byteOffset) {
670 if (fCount == 0 && gFormatVersion > 1) {
671 fRes = URES_MAKE_EMPTY_RESOURCE(URES_INT_VECTOR);
672 fWritten = TRUE;
673 } else {
674 fRes = URES_MAKE_RESOURCE(URES_INT_VECTOR, *byteOffset >> 2);
675 *byteOffset += (1 + fCount) * 4;
676 }
677 }
678
679 void
handlePreWrite(uint32_t * byteOffset)680 BinaryResource::handlePreWrite(uint32_t *byteOffset) {
681 uint32_t pad = 0;
682 uint32_t dataStart = *byteOffset + sizeof(fLength);
683
684 if (dataStart % BIN_ALIGNMENT) {
685 pad = (BIN_ALIGNMENT - dataStart % BIN_ALIGNMENT);
686 *byteOffset += pad; /* pad == 4 or 8 or 12 */
687 }
688 fRes = URES_MAKE_RESOURCE(URES_BINARY, *byteOffset >> 2);
689 *byteOffset += 4 + fLength;
690 }
691
692 void
preWriteAllRes(uint32_t * byteOffset)693 ContainerResource::preWriteAllRes(uint32_t *byteOffset) {
694 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
695 current->preWrite(byteOffset);
696 }
697 }
698
699 void
handlePreWrite(uint32_t * byteOffset)700 ArrayResource::handlePreWrite(uint32_t *byteOffset) {
701 preWriteAllRes(byteOffset);
702 fRes = URES_MAKE_RESOURCE(URES_ARRAY, *byteOffset >> 2);
703 *byteOffset += (1 + fCount) * 4;
704 }
705
706 void
handlePreWrite(uint32_t * byteOffset)707 TableResource::handlePreWrite(uint32_t *byteOffset) {
708 preWriteAllRes(byteOffset);
709 if (fTableType == URES_TABLE) {
710 /* 16-bit count, 16-bit key offsets, 32-bit values */
711 fRes = URES_MAKE_RESOURCE(URES_TABLE, *byteOffset >> 2);
712 *byteOffset += 2 + fCount * 6;
713 } else {
714 /* 32-bit count, key offsets and values */
715 fRes = URES_MAKE_RESOURCE(URES_TABLE32, *byteOffset >> 2);
716 *byteOffset += 4 + fCount * 8;
717 }
718 }
719
720 void
preWrite(uint32_t * byteOffset)721 SResource::preWrite(uint32_t *byteOffset) {
722 if (fRes != RES_BOGUS) {
723 /*
724 * The resource item word was already precomputed, which means
725 * no further data needs to be written.
726 * This might be an integer, or an empty or UTF-16 v2 string,
727 * an empty binary, etc.
728 */
729 return;
730 }
731 handlePreWrite(byteOffset);
732 *byteOffset += calcPadding(*byteOffset);
733 }
734
735 void
handlePreWrite(uint32_t *)736 SResource::handlePreWrite(uint32_t * /*byteOffset*/) {
737 assert(FALSE);
738 }
739
740 /*
741 * Only called for UTF-16 v1 strings, and for aliases. For UTF-16 v2 strings,
742 * write() sees fWritten and exits early.
743 */
744 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)745 StringBaseResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
746 /* Write the UTF-16 v1 string. */
747 int32_t len = length();
748 udata_write32(mem, len);
749 udata_writeUString(mem, getBuffer(), len + 1);
750 *byteOffset += 4 + (len + 1) * U_SIZEOF_UCHAR;
751 fWritten = TRUE;
752 }
753
754 void
writeAllRes(UNewDataMemory * mem,uint32_t * byteOffset)755 ContainerResource::writeAllRes(UNewDataMemory *mem, uint32_t *byteOffset) {
756 uint32_t i = 0;
757 for (SResource *current = fFirst; current != NULL; ++i, current = current->fNext) {
758 current->write(mem, byteOffset);
759 }
760 assert(i == fCount);
761 }
762
763 void
writeAllRes32(UNewDataMemory * mem,uint32_t * byteOffset)764 ContainerResource::writeAllRes32(UNewDataMemory *mem, uint32_t *byteOffset) {
765 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
766 udata_write32(mem, current->fRes);
767 }
768 *byteOffset += fCount * 4;
769 }
770
771 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)772 ArrayResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
773 writeAllRes(mem, byteOffset);
774 udata_write32(mem, fCount);
775 *byteOffset += 4;
776 writeAllRes32(mem, byteOffset);
777 }
778
779 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)780 IntVectorResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
781 udata_write32(mem, fCount);
782 for(uint32_t i = 0; i < fCount; ++i) {
783 udata_write32(mem, fArray[i]);
784 }
785 *byteOffset += (1 + fCount) * 4;
786 }
787
788 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)789 BinaryResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
790 uint32_t pad = 0;
791 uint32_t dataStart = *byteOffset + sizeof(fLength);
792
793 if (dataStart % BIN_ALIGNMENT) {
794 pad = (BIN_ALIGNMENT - dataStart % BIN_ALIGNMENT);
795 udata_writePadding(mem, pad); /* pad == 4 or 8 or 12 */
796 *byteOffset += pad;
797 }
798
799 udata_write32(mem, fLength);
800 if (fLength > 0) {
801 udata_writeBlock(mem, fData, fLength);
802 }
803 *byteOffset += 4 + fLength;
804 }
805
806 void
handleWrite(UNewDataMemory * mem,uint32_t * byteOffset)807 TableResource::handleWrite(UNewDataMemory *mem, uint32_t *byteOffset) {
808 writeAllRes(mem, byteOffset);
809 if(fTableType == URES_TABLE) {
810 udata_write16(mem, (uint16_t)fCount);
811 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
812 udata_write16(mem, current->fKey16);
813 }
814 *byteOffset += (1 + fCount)* 2;
815 if ((fCount & 1) == 0) {
816 /* 16-bit count and even number of 16-bit key offsets need padding before 32-bit resource items */
817 udata_writePadding(mem, 2);
818 *byteOffset += 2;
819 }
820 } else /* URES_TABLE32 */ {
821 udata_write32(mem, fCount);
822 for (SResource *current = fFirst; current != NULL; current = current->fNext) {
823 udata_write32(mem, (uint32_t)current->fKey);
824 }
825 *byteOffset += (1 + fCount)* 4;
826 }
827 writeAllRes32(mem, byteOffset);
828 }
829
830 void
write(UNewDataMemory * mem,uint32_t * byteOffset)831 SResource::write(UNewDataMemory *mem, uint32_t *byteOffset) {
832 if (fWritten) {
833 assert(fRes != RES_BOGUS);
834 return;
835 }
836 handleWrite(mem, byteOffset);
837 uint8_t paddingSize = calcPadding(*byteOffset);
838 if (paddingSize > 0) {
839 udata_writePadding(mem, paddingSize);
840 *byteOffset += paddingSize;
841 }
842 fWritten = TRUE;
843 }
844
845 void
handleWrite(UNewDataMemory *,uint32_t *)846 SResource::handleWrite(UNewDataMemory * /*mem*/, uint32_t * /*byteOffset*/) {
847 assert(FALSE);
848 }
849
write(const char * outputDir,const char * outputPkg,char * writtenFilename,int writtenFilenameLen,UErrorCode & errorCode)850 void SRBRoot::write(const char *outputDir, const char *outputPkg,
851 char *writtenFilename, int writtenFilenameLen,
852 UErrorCode &errorCode) {
853 UNewDataMemory *mem = NULL;
854 uint32_t byteOffset = 0;
855 uint32_t top, size;
856 char dataName[1024];
857 int32_t indexes[URES_INDEX_TOP];
858
859 compactKeys(errorCode);
860 /*
861 * Add padding bytes to fKeys so that fKeysTop is 4-aligned.
862 * Safe because the capacity is a multiple of 4.
863 */
864 while (fKeysTop & 3) {
865 fKeys[fKeysTop++] = (char)0xaa;
866 }
867 /*
868 * In URES_TABLE, use all local key offsets that fit into 16 bits,
869 * and use the remaining 16-bit offsets for pool key offsets
870 * if there are any.
871 * If there are no local keys, then use the whole 16-bit space
872 * for pool key offsets.
873 * Note: This cannot be changed without changing the major formatVersion.
874 */
875 if (fKeysBottom < fKeysTop) {
876 if (fKeysTop <= 0x10000) {
877 fLocalKeyLimit = fKeysTop;
878 } else {
879 fLocalKeyLimit = 0x10000;
880 }
881 } else {
882 fLocalKeyLimit = 0;
883 }
884
885 UHashtable *stringSet;
886 if (gFormatVersion > 1) {
887 stringSet = uhash_open(string_hash, string_comp, string_comp, &errorCode);
888 if (U_SUCCESS(errorCode) &&
889 fUsePoolBundle != NULL && fUsePoolBundle->fStrings != NULL) {
890 for (SResource *current = fUsePoolBundle->fStrings->fFirst;
891 current != NULL;
892 current = current->fNext) {
893 StringResource *sr = static_cast<StringResource *>(current);
894 sr->fNumCopies = 0;
895 sr->fNumUnitsSaved = 0;
896 uhash_put(stringSet, sr, sr, &errorCode);
897 }
898 }
899 fRoot->preflightStrings(this, stringSet, errorCode);
900 } else {
901 stringSet = NULL;
902 }
903 if (fStringsForm == STRINGS_UTF16_V2 && f16BitStringsLength > 0) {
904 compactStringsV2(stringSet, errorCode);
905 }
906 uhash_close(stringSet);
907 if (U_FAILURE(errorCode)) {
908 return;
909 }
910
911 int32_t formatVersion = gFormatVersion;
912 if (fPoolStringIndexLimit != 0) {
913 int32_t sum = fPoolStringIndexLimit + fLocalStringIndexLimit;
914 if ((sum - 1) > RES_MAX_OFFSET) {
915 errorCode = U_BUFFER_OVERFLOW_ERROR;
916 return;
917 }
918 if (fPoolStringIndexLimit < 0x10000 && sum <= 0x10000) {
919 // 16-bit indexes work for all pool + local strings.
920 fPoolStringIndex16Limit = fPoolStringIndexLimit;
921 } else {
922 // Set the pool index threshold so that 16-bit indexes work
923 // for some pool strings and some local strings.
924 fPoolStringIndex16Limit = (int32_t)(
925 ((int64_t)fPoolStringIndexLimit * 0xffff) / sum);
926 }
927 } else if (gIsDefaultFormatVersion && formatVersion == 3 && !fIsPoolBundle) {
928 // If we just default to formatVersion 3
929 // but there are no pool bundle strings to share
930 // and we do not write a pool bundle,
931 // then write formatVersion 2 which is just as good.
932 formatVersion = 2;
933 }
934
935 fRoot->write16(this);
936 if (f16BitUnits.isBogus()) {
937 errorCode = U_MEMORY_ALLOCATION_ERROR;
938 return;
939 }
940 if (f16BitUnits.length() & 1) {
941 f16BitUnits.append((UChar)0xaaaa); /* pad to multiple of 4 bytes */
942 }
943
944 byteOffset = fKeysTop + f16BitUnits.length() * 2;
945 fRoot->preWrite(&byteOffset);
946
947 /* total size including the root item */
948 top = byteOffset;
949
950 if (writtenFilename && writtenFilenameLen) {
951 *writtenFilename = 0;
952 }
953
954 if (writtenFilename) {
955 int32_t off = 0, len = 0;
956 if (outputDir) {
957 uprv_strncpy(writtenFilename, outputDir, writtenFilenameLen);
958 }
959 if (writtenFilenameLen -= len) {
960 off += len;
961 writtenFilename[off] = U_FILE_SEP_CHAR;
962 if (--writtenFilenameLen) {
963 ++off;
964 if(outputPkg != NULL)
965 {
966 uprv_strcpy(writtenFilename+off, outputPkg);
967 off += (int32_t)uprv_strlen(outputPkg);
968 writtenFilename[off] = '_';
969 ++off;
970 }
971
972 len = (int32_t)uprv_strlen(fLocale);
973 if (len > writtenFilenameLen) {
974 len = writtenFilenameLen;
975 }
976 uprv_strncpy(writtenFilename + off, fLocale, writtenFilenameLen - off);
977 if (writtenFilenameLen -= len) {
978 off += len;
979 uprv_strncpy(writtenFilename + off, ".res", writtenFilenameLen - off);
980 }
981 }
982 }
983 }
984
985 if(outputPkg)
986 {
987 uprv_strcpy(dataName, outputPkg);
988 uprv_strcat(dataName, "_");
989 uprv_strcat(dataName, fLocale);
990 }
991 else
992 {
993 uprv_strcpy(dataName, fLocale);
994 }
995
996 uprv_memcpy(dataInfo.formatVersion, gFormatVersions + formatVersion, sizeof(UVersionInfo));
997
998 mem = udata_create(outputDir, "res", dataName,
999 &dataInfo, (gIncludeCopyright==TRUE)? U_COPYRIGHT_STRING:NULL, &errorCode);
1000 if(U_FAILURE(errorCode)){
1001 return;
1002 }
1003
1004 /* write the root item */
1005 udata_write32(mem, fRoot->fRes);
1006
1007 /*
1008 * formatVersion 1.1 (ICU 2.8):
1009 * write int32_t indexes[] after root and before the key strings
1010 * to make it easier to parse resource bundles in icuswap or from Java etc.
1011 */
1012 uprv_memset(indexes, 0, sizeof(indexes));
1013 indexes[URES_INDEX_LENGTH]= fIndexLength;
1014 indexes[URES_INDEX_KEYS_TOP]= fKeysTop>>2;
1015 indexes[URES_INDEX_RESOURCES_TOP]= (int32_t)(top>>2);
1016 indexes[URES_INDEX_BUNDLE_TOP]= indexes[URES_INDEX_RESOURCES_TOP];
1017 indexes[URES_INDEX_MAX_TABLE_LENGTH]= fMaxTableLength;
1018
1019 /*
1020 * formatVersion 1.2 (ICU 3.6):
1021 * write indexes[URES_INDEX_ATTRIBUTES] with URES_ATT_NO_FALLBACK set or not set
1022 * the memset() above initialized all indexes[] to 0
1023 */
1024 if (fNoFallback) {
1025 indexes[URES_INDEX_ATTRIBUTES]=URES_ATT_NO_FALLBACK;
1026 }
1027 /*
1028 * formatVersion 2.0 (ICU 4.4):
1029 * more compact string value storage, optional pool bundle
1030 */
1031 if (URES_INDEX_16BIT_TOP < fIndexLength) {
1032 indexes[URES_INDEX_16BIT_TOP] = (fKeysTop>>2) + (f16BitUnits.length()>>1);
1033 }
1034 if (URES_INDEX_POOL_CHECKSUM < fIndexLength) {
1035 if (fIsPoolBundle) {
1036 indexes[URES_INDEX_ATTRIBUTES] |= URES_ATT_IS_POOL_BUNDLE | URES_ATT_NO_FALLBACK;
1037 uint32_t checksum = computeCRC((const char *)(fKeys + fKeysBottom),
1038 (uint32_t)(fKeysTop - fKeysBottom), 0);
1039 if (f16BitUnits.length() <= 1) {
1040 // no pool strings to checksum
1041 } else if (U_IS_BIG_ENDIAN) {
1042 checksum = computeCRC(reinterpret_cast<const char *>(f16BitUnits.getBuffer()),
1043 (uint32_t)f16BitUnits.length() * 2, checksum);
1044 } else {
1045 // Swap to big-endian so we get the same checksum on all platforms
1046 // (except for charset family, due to the key strings).
1047 UnicodeString s(f16BitUnits);
1048 assert(!s.isBogus());
1049 // .getBuffer(capacity) returns a mutable buffer
1050 char16_t* p = s.getBuffer(f16BitUnits.length());
1051 for (int32_t count = f16BitUnits.length(); count > 0; --count) {
1052 uint16_t x = *p;
1053 *p++ = (uint16_t)((x << 8) | (x >> 8));
1054 }
1055 s.releaseBuffer(f16BitUnits.length());
1056 checksum = computeCRC((const char *)s.getBuffer(),
1057 (uint32_t)f16BitUnits.length() * 2, checksum);
1058 }
1059 indexes[URES_INDEX_POOL_CHECKSUM] = (int32_t)checksum;
1060 } else if (gUsePoolBundle) {
1061 indexes[URES_INDEX_ATTRIBUTES] |= URES_ATT_USES_POOL_BUNDLE;
1062 indexes[URES_INDEX_POOL_CHECKSUM] = fUsePoolBundle->fChecksum;
1063 }
1064 }
1065 // formatVersion 3 (ICU 56):
1066 // share string values via pool bundle strings
1067 indexes[URES_INDEX_LENGTH] |= fPoolStringIndexLimit << 8; // bits 23..0 -> 31..8
1068 indexes[URES_INDEX_ATTRIBUTES] |= (fPoolStringIndexLimit >> 12) & 0xf000; // bits 27..24 -> 15..12
1069 indexes[URES_INDEX_ATTRIBUTES] |= fPoolStringIndex16Limit << 16;
1070
1071 /* write the indexes[] */
1072 udata_writeBlock(mem, indexes, fIndexLength*4);
1073
1074 /* write the table key strings */
1075 udata_writeBlock(mem, fKeys+fKeysBottom,
1076 fKeysTop-fKeysBottom);
1077
1078 /* write the v2 UTF-16 strings, URES_TABLE16 and URES_ARRAY16 */
1079 udata_writeBlock(mem, f16BitUnits.getBuffer(), f16BitUnits.length()*2);
1080
1081 /* write all of the bundle contents: the root item and its children */
1082 byteOffset = fKeysTop + f16BitUnits.length() * 2;
1083 fRoot->write(mem, &byteOffset);
1084 assert(byteOffset == top);
1085
1086 size = udata_finish(mem, &errorCode);
1087 if(top != size) {
1088 fprintf(stderr, "genrb error: wrote %u bytes but counted %u\n",
1089 (int)size, (int)top);
1090 errorCode = U_INTERNAL_PROGRAM_ERROR;
1091 }
1092 }
1093
1094 /* Opening Functions */
1095
table_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1096 TableResource* table_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1097 LocalPointer<TableResource> res(new TableResource(bundle, tag, comment, *status), *status);
1098 return U_SUCCESS(*status) ? res.orphan() : NULL;
1099 }
1100
array_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1101 ArrayResource* array_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1102 LocalPointer<ArrayResource> res(new ArrayResource(bundle, tag, comment, *status), *status);
1103 return U_SUCCESS(*status) ? res.orphan() : NULL;
1104 }
1105
string_open(struct SRBRoot * bundle,const char * tag,const UChar * value,int32_t len,const struct UString * comment,UErrorCode * status)1106 struct SResource *string_open(struct SRBRoot *bundle, const char *tag, const UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
1107 LocalPointer<SResource> res(
1108 new StringResource(bundle, tag, value, len, comment, *status), *status);
1109 return U_SUCCESS(*status) ? res.orphan() : NULL;
1110 }
1111
alias_open(struct SRBRoot * bundle,const char * tag,UChar * value,int32_t len,const struct UString * comment,UErrorCode * status)1112 struct SResource *alias_open(struct SRBRoot *bundle, const char *tag, UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
1113 LocalPointer<SResource> res(
1114 new AliasResource(bundle, tag, value, len, comment, *status), *status);
1115 return U_SUCCESS(*status) ? res.orphan() : NULL;
1116 }
1117
intvector_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1118 IntVectorResource *intvector_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1119 LocalPointer<IntVectorResource> res(
1120 new IntVectorResource(bundle, tag, comment, *status), *status);
1121 return U_SUCCESS(*status) ? res.orphan() : NULL;
1122 }
1123
int_open(struct SRBRoot * bundle,const char * tag,int32_t value,const struct UString * comment,UErrorCode * status)1124 struct SResource *int_open(struct SRBRoot *bundle, const char *tag, int32_t value, const struct UString* comment, UErrorCode *status) {
1125 LocalPointer<SResource> res(new IntResource(bundle, tag, value, comment, *status), *status);
1126 return U_SUCCESS(*status) ? res.orphan() : NULL;
1127 }
1128
bin_open(struct SRBRoot * bundle,const char * tag,uint32_t length,uint8_t * data,const char * fileName,const struct UString * comment,UErrorCode * status)1129 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) {
1130 LocalPointer<SResource> res(
1131 new BinaryResource(bundle, tag, length, data, fileName, comment, *status), *status);
1132 return U_SUCCESS(*status) ? res.orphan() : NULL;
1133 }
1134
SRBRoot(const UString * comment,UBool isPoolBundle,UErrorCode & errorCode)1135 SRBRoot::SRBRoot(const UString *comment, UBool isPoolBundle, UErrorCode &errorCode)
1136 : fRoot(NULL), fLocale(NULL), fIndexLength(0), fMaxTableLength(0), fNoFallback(FALSE),
1137 fStringsForm(STRINGS_UTF16_V1), fIsPoolBundle(isPoolBundle),
1138 fKeys(NULL), fKeyMap(NULL),
1139 fKeysBottom(0), fKeysTop(0), fKeysCapacity(0),
1140 fKeysCount(0), fLocalKeyLimit(0),
1141 f16BitUnits(), f16BitStringsLength(0),
1142 fUsePoolBundle(&kNoPoolBundle),
1143 fPoolStringIndexLimit(0), fPoolStringIndex16Limit(0), fLocalStringIndexLimit(0),
1144 fWritePoolBundle(NULL) {
1145 if (U_FAILURE(errorCode)) {
1146 return;
1147 }
1148
1149 if (gFormatVersion > 1) {
1150 // f16BitUnits must start with a zero for empty resources.
1151 // We might be able to omit it if there are no empty 16-bit resources.
1152 f16BitUnits.append((UChar)0);
1153 }
1154
1155 fKeys = (char *) uprv_malloc(sizeof(char) * KEY_SPACE_SIZE);
1156 if (isPoolBundle) {
1157 fRoot = new PseudoListResource(this, errorCode);
1158 } else {
1159 fRoot = new TableResource(this, NULL, comment, errorCode);
1160 }
1161 if (fKeys == NULL || fRoot == NULL || U_FAILURE(errorCode)) {
1162 if (U_SUCCESS(errorCode)) {
1163 errorCode = U_MEMORY_ALLOCATION_ERROR;
1164 }
1165 return;
1166 }
1167
1168 fKeysCapacity = KEY_SPACE_SIZE;
1169 /* formatVersion 1.1 and up: start fKeysTop after the root item and indexes[] */
1170 if (gUsePoolBundle || isPoolBundle) {
1171 fIndexLength = URES_INDEX_POOL_CHECKSUM + 1;
1172 } else if (gFormatVersion >= 2) {
1173 fIndexLength = URES_INDEX_16BIT_TOP + 1;
1174 } else /* formatVersion 1 */ {
1175 fIndexLength = URES_INDEX_ATTRIBUTES + 1;
1176 }
1177 fKeysBottom = (1 /* root */ + fIndexLength) * 4;
1178 uprv_memset(fKeys, 0, fKeysBottom);
1179 fKeysTop = fKeysBottom;
1180
1181 if (gFormatVersion == 1) {
1182 fStringsForm = STRINGS_UTF16_V1;
1183 } else {
1184 fStringsForm = STRINGS_UTF16_V2;
1185 }
1186 }
1187
1188 /* Closing Functions */
1189
res_close(struct SResource * res)1190 void res_close(struct SResource *res) {
1191 delete res;
1192 }
1193
~SRBRoot()1194 SRBRoot::~SRBRoot() {
1195 delete fRoot;
1196 uprv_free(fLocale);
1197 uprv_free(fKeys);
1198 uprv_free(fKeyMap);
1199 }
1200
1201 /* Misc Functions */
1202
setLocale(UChar * locale,UErrorCode & errorCode)1203 void SRBRoot::setLocale(UChar *locale, UErrorCode &errorCode) {
1204 if(U_FAILURE(errorCode)) {
1205 return;
1206 }
1207
1208 uprv_free(fLocale);
1209 fLocale = (char*) uprv_malloc(sizeof(char) * (u_strlen(locale)+1));
1210 if(fLocale == NULL) {
1211 errorCode = U_MEMORY_ALLOCATION_ERROR;
1212 return;
1213 }
1214
1215 u_UCharsToChars(locale, fLocale, u_strlen(locale)+1);
1216 }
1217
1218 const char *
getKeyString(int32_t key) const1219 SRBRoot::getKeyString(int32_t key) const {
1220 if (key < 0) {
1221 return fUsePoolBundle->fKeys + (key & 0x7fffffff);
1222 } else {
1223 return fKeys + key;
1224 }
1225 }
1226
1227 const char *
getKeyString(const SRBRoot * bundle) const1228 SResource::getKeyString(const SRBRoot *bundle) const {
1229 if (fKey == -1) {
1230 return NULL;
1231 }
1232 return bundle->getKeyString(fKey);
1233 }
1234
1235 const char *
getKeyBytes(int32_t * pLength) const1236 SRBRoot::getKeyBytes(int32_t *pLength) const {
1237 *pLength = fKeysTop - fKeysBottom;
1238 return fKeys + fKeysBottom;
1239 }
1240
1241 int32_t
addKeyBytes(const char * keyBytes,int32_t length,UErrorCode & errorCode)1242 SRBRoot::addKeyBytes(const char *keyBytes, int32_t length, UErrorCode &errorCode) {
1243 int32_t keypos;
1244
1245 // It is not legal to add new key bytes after compactKeys is run!
1246 U_ASSERT(fKeyMap == nullptr);
1247
1248 if (U_FAILURE(errorCode)) {
1249 return -1;
1250 }
1251 if (length < 0 || (keyBytes == NULL && length != 0)) {
1252 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
1253 return -1;
1254 }
1255 if (length == 0) {
1256 return fKeysTop;
1257 }
1258
1259 keypos = fKeysTop;
1260 fKeysTop += length;
1261 if (fKeysTop >= fKeysCapacity) {
1262 /* overflow - resize the keys buffer */
1263 fKeysCapacity += KEY_SPACE_SIZE;
1264 fKeys = static_cast<char *>(uprv_realloc(fKeys, fKeysCapacity));
1265 if(fKeys == NULL) {
1266 errorCode = U_MEMORY_ALLOCATION_ERROR;
1267 return -1;
1268 }
1269 }
1270
1271 uprv_memcpy(fKeys + keypos, keyBytes, length);
1272
1273 return keypos;
1274 }
1275
1276 int32_t
addTag(const char * tag,UErrorCode & errorCode)1277 SRBRoot::addTag(const char *tag, UErrorCode &errorCode) {
1278 int32_t keypos;
1279
1280 if (U_FAILURE(errorCode)) {
1281 return -1;
1282 }
1283
1284 if (tag == NULL) {
1285 /* no error: the root table and array items have no keys */
1286 return -1;
1287 }
1288
1289 keypos = addKeyBytes(tag, (int32_t)(uprv_strlen(tag) + 1), errorCode);
1290 if (U_SUCCESS(errorCode)) {
1291 ++fKeysCount;
1292 }
1293 return keypos;
1294 }
1295
1296 static int32_t
compareInt32(int32_t lPos,int32_t rPos)1297 compareInt32(int32_t lPos, int32_t rPos) {
1298 /*
1299 * Compare possibly-negative key offsets. Don't just return lPos - rPos
1300 * because that is prone to negative-integer underflows.
1301 */
1302 if (lPos < rPos) {
1303 return -1;
1304 } else if (lPos > rPos) {
1305 return 1;
1306 } else {
1307 return 0;
1308 }
1309 }
1310
1311 static int32_t U_CALLCONV
compareKeySuffixes(const void * context,const void * l,const void * r)1312 compareKeySuffixes(const void *context, const void *l, const void *r) {
1313 const struct SRBRoot *bundle=(const struct SRBRoot *)context;
1314 int32_t lPos = ((const KeyMapEntry *)l)->oldpos;
1315 int32_t rPos = ((const KeyMapEntry *)r)->oldpos;
1316 const char *lStart = bundle->getKeyString(lPos);
1317 const char *lLimit = lStart;
1318 const char *rStart = bundle->getKeyString(rPos);
1319 const char *rLimit = rStart;
1320 int32_t diff;
1321 while (*lLimit != 0) { ++lLimit; }
1322 while (*rLimit != 0) { ++rLimit; }
1323 /* compare keys in reverse character order */
1324 while (lStart < lLimit && rStart < rLimit) {
1325 diff = (int32_t)(uint8_t)*--lLimit - (int32_t)(uint8_t)*--rLimit;
1326 if (diff != 0) {
1327 return diff;
1328 }
1329 }
1330 /* sort equal suffixes by descending key length */
1331 diff = (int32_t)(rLimit - rStart) - (int32_t)(lLimit - lStart);
1332 if (diff != 0) {
1333 return diff;
1334 }
1335 /* Sort pool bundle keys first (negative oldpos), and otherwise keys in parsing order. */
1336 return compareInt32(lPos, rPos);
1337 }
1338
1339 static int32_t U_CALLCONV
compareKeyNewpos(const void *,const void * l,const void * r)1340 compareKeyNewpos(const void * /*context*/, const void *l, const void *r) {
1341 return compareInt32(((const KeyMapEntry *)l)->newpos, ((const KeyMapEntry *)r)->newpos);
1342 }
1343
1344 static int32_t U_CALLCONV
compareKeyOldpos(const void *,const void * l,const void * r)1345 compareKeyOldpos(const void * /*context*/, const void *l, const void *r) {
1346 return compareInt32(((const KeyMapEntry *)l)->oldpos, ((const KeyMapEntry *)r)->oldpos);
1347 }
1348
collectKeys(std::function<void (int32_t)> collector) const1349 void SResource::collectKeys(std::function<void(int32_t)> collector) const {
1350 collector(fKey);
1351 }
1352
collectKeys(std::function<void (int32_t)> collector) const1353 void ContainerResource::collectKeys(std::function<void(int32_t)> collector) const {
1354 collector(fKey);
1355 for (SResource* curr = fFirst; curr != NULL; curr = curr->fNext) {
1356 curr->collectKeys(collector);
1357 }
1358 }
1359
1360 void
compactKeys(UErrorCode & errorCode)1361 SRBRoot::compactKeys(UErrorCode &errorCode) {
1362 KeyMapEntry *map;
1363 char *keys;
1364 int32_t i;
1365
1366 // Except for pool bundles, keys might not be used.
1367 // Do not add unused keys to the final bundle.
1368 std::set<int32_t> keysInUse;
1369 if (!fIsPoolBundle) {
1370 fRoot->collectKeys([&keysInUse](int32_t key) {
1371 if (key >= 0) {
1372 keysInUse.insert(key);
1373 }
1374 });
1375 fKeysCount = static_cast<int32_t>(keysInUse.size());
1376 }
1377
1378 int32_t keysCount = fUsePoolBundle->fKeysCount + fKeysCount;
1379 if (U_FAILURE(errorCode) || fKeyMap != NULL) {
1380 return;
1381 }
1382 map = (KeyMapEntry *)uprv_malloc(keysCount * sizeof(KeyMapEntry));
1383 if (map == NULL) {
1384 errorCode = U_MEMORY_ALLOCATION_ERROR;
1385 return;
1386 }
1387 keys = (char *)fUsePoolBundle->fKeys;
1388 for (i = 0; i < fUsePoolBundle->fKeysCount; ++i) {
1389 map[i].oldpos =
1390 (int32_t)(keys - fUsePoolBundle->fKeys) | 0x80000000; /* negative oldpos */
1391 map[i].newpos = 0;
1392 while (*keys != 0) { ++keys; } /* skip the key */
1393 ++keys; /* skip the NUL */
1394 }
1395 keys = fKeys + fKeysBottom;
1396 while (i < keysCount) {
1397 int32_t keyOffset = static_cast<int32_t>(keys - fKeys);
1398 if (!fIsPoolBundle && keysInUse.count(keyOffset) == 0) {
1399 // Mark the unused key as deleted
1400 while (*keys != 0) { *keys++ = 1; }
1401 *keys++ = 1;
1402 } else {
1403 map[i].oldpos = keyOffset;
1404 map[i].newpos = 0;
1405 while (*keys != 0) { ++keys; } /* skip the key */
1406 ++keys; /* skip the NUL */
1407 i++;
1408 }
1409 }
1410 if (keys != fKeys + fKeysTop) {
1411 // Throw away any unused keys from the end
1412 fKeysTop = static_cast<int32_t>(keys - fKeys);
1413 }
1414 /* Sort the keys so that each one is immediately followed by all of its suffixes. */
1415 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1416 compareKeySuffixes, this, FALSE, &errorCode);
1417 /*
1418 * Make suffixes point into earlier, longer strings that contain them
1419 * and mark the old, now unused suffix bytes as deleted.
1420 */
1421 if (U_SUCCESS(errorCode)) {
1422 keys = fKeys;
1423 for (i = 0; i < keysCount;) {
1424 /*
1425 * This key is not a suffix of the previous one;
1426 * keep this one and delete the following ones that are
1427 * suffixes of this one.
1428 */
1429 const char *key;
1430 const char *keyLimit;
1431 int32_t j = i + 1;
1432 map[i].newpos = map[i].oldpos;
1433 if (j < keysCount && map[j].oldpos < 0) {
1434 /* Key string from the pool bundle, do not delete. */
1435 i = j;
1436 continue;
1437 }
1438 key = getKeyString(map[i].oldpos);
1439 for (keyLimit = key; *keyLimit != 0; ++keyLimit) {}
1440 for (; j < keysCount && map[j].oldpos >= 0; ++j) {
1441 const char *k;
1442 char *suffix;
1443 const char *suffixLimit;
1444 int32_t offset;
1445 suffix = keys + map[j].oldpos;
1446 for (suffixLimit = suffix; *suffixLimit != 0; ++suffixLimit) {}
1447 offset = static_cast<int32_t>((keyLimit - key) - (suffixLimit - suffix));
1448 if (offset < 0) {
1449 break; /* suffix cannot be longer than the original */
1450 }
1451 /* Is it a suffix of the earlier, longer key? */
1452 for (k = keyLimit; suffix < suffixLimit && *--k == *--suffixLimit;) {}
1453 if (suffix == suffixLimit && *k == *suffixLimit) {
1454 map[j].newpos = map[i].oldpos + offset; /* yes, point to the earlier key */
1455 // Mark the suffix as deleted
1456 while (*suffix != 0) { *suffix++ = 1; }
1457 *suffix = 1;
1458 } else {
1459 break; /* not a suffix, restart from here */
1460 }
1461 }
1462 i = j;
1463 }
1464 /*
1465 * Re-sort by newpos, then modify the key characters array in-place
1466 * to squeeze out unused bytes, and readjust the newpos offsets.
1467 */
1468 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1469 compareKeyNewpos, NULL, FALSE, &errorCode);
1470 if (U_SUCCESS(errorCode)) {
1471 int32_t oldpos, newpos, limit;
1472 oldpos = newpos = fKeysBottom;
1473 limit = fKeysTop;
1474 /* skip key offsets that point into the pool bundle rather than this new bundle */
1475 for (i = 0; i < keysCount && map[i].newpos < 0; ++i) {}
1476 if (i < keysCount) {
1477 while (oldpos < limit) {
1478 if (keys[oldpos] == 1) {
1479 ++oldpos; /* skip unused bytes */
1480 } else {
1481 /* adjust the new offsets for keys starting here */
1482 while (i < keysCount && map[i].newpos == oldpos) {
1483 map[i++].newpos = newpos;
1484 }
1485 /* move the key characters to their new position */
1486 keys[newpos++] = keys[oldpos++];
1487 }
1488 }
1489 U_ASSERT(i == keysCount);
1490 }
1491 fKeysTop = newpos;
1492 /* Re-sort once more, by old offsets for binary searching. */
1493 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1494 compareKeyOldpos, NULL, FALSE, &errorCode);
1495 if (U_SUCCESS(errorCode)) {
1496 /* key size reduction by limit - newpos */
1497 fKeyMap = map;
1498 map = NULL;
1499 }
1500 }
1501 }
1502 uprv_free(map);
1503 }
1504
1505 static int32_t U_CALLCONV
compareStringSuffixes(const void *,const void * l,const void * r)1506 compareStringSuffixes(const void * /*context*/, const void *l, const void *r) {
1507 const StringResource *left = *((const StringResource **)l);
1508 const StringResource *right = *((const StringResource **)r);
1509 const UChar *lStart = left->getBuffer();
1510 const UChar *lLimit = lStart + left->length();
1511 const UChar *rStart = right->getBuffer();
1512 const UChar *rLimit = rStart + right->length();
1513 int32_t diff;
1514 /* compare keys in reverse character order */
1515 while (lStart < lLimit && rStart < rLimit) {
1516 diff = (int32_t)*--lLimit - (int32_t)*--rLimit;
1517 if (diff != 0) {
1518 return diff;
1519 }
1520 }
1521 /* sort equal suffixes by descending string length */
1522 return right->length() - left->length();
1523 }
1524
1525 static int32_t U_CALLCONV
compareStringLengths(const void *,const void * l,const void * r)1526 compareStringLengths(const void * /*context*/, const void *l, const void *r) {
1527 const StringResource *left = *((const StringResource **)l);
1528 const StringResource *right = *((const StringResource **)r);
1529 int32_t diff;
1530 /* Make "is suffix of another string" compare greater than a non-suffix. */
1531 diff = (int)(left->fSame != NULL) - (int)(right->fSame != NULL);
1532 if (diff != 0) {
1533 return diff;
1534 }
1535 /* sort by ascending string length */
1536 diff = left->length() - right->length();
1537 if (diff != 0) {
1538 return diff;
1539 }
1540 // sort by descending size reduction
1541 diff = right->fNumUnitsSaved - left->fNumUnitsSaved;
1542 if (diff != 0) {
1543 return diff;
1544 }
1545 // sort lexically
1546 return left->fString.compare(right->fString);
1547 }
1548
1549 void
writeUTF16v2(int32_t base,UnicodeString & dest)1550 StringResource::writeUTF16v2(int32_t base, UnicodeString &dest) {
1551 int32_t len = length();
1552 fRes = URES_MAKE_RESOURCE(URES_STRING_V2, base + dest.length());
1553 fWritten = TRUE;
1554 switch(fNumCharsForLength) {
1555 case 0:
1556 break;
1557 case 1:
1558 dest.append((UChar)(0xdc00 + len));
1559 break;
1560 case 2:
1561 dest.append((UChar)(0xdfef + (len >> 16)));
1562 dest.append((UChar)len);
1563 break;
1564 case 3:
1565 dest.append((UChar)0xdfff);
1566 dest.append((UChar)(len >> 16));
1567 dest.append((UChar)len);
1568 break;
1569 default:
1570 break; /* will not occur */
1571 }
1572 dest.append(fString);
1573 dest.append((UChar)0);
1574 }
1575
1576 void
compactStringsV2(UHashtable * stringSet,UErrorCode & errorCode)1577 SRBRoot::compactStringsV2(UHashtable *stringSet, UErrorCode &errorCode) {
1578 if (U_FAILURE(errorCode)) {
1579 return;
1580 }
1581 // Store the StringResource pointers in an array for
1582 // easy sorting and processing.
1583 // We enumerate a set of strings, so there are no duplicates.
1584 int32_t count = uhash_count(stringSet);
1585 LocalArray<StringResource *> array(new StringResource *[count], errorCode);
1586 if (U_FAILURE(errorCode)) {
1587 return;
1588 }
1589 for (int32_t pos = UHASH_FIRST, i = 0; i < count; ++i) {
1590 array[i] = (StringResource *)uhash_nextElement(stringSet, &pos)->key.pointer;
1591 }
1592 /* Sort the strings so that each one is immediately followed by all of its suffixes. */
1593 uprv_sortArray(array.getAlias(), count, (int32_t)sizeof(struct SResource **),
1594 compareStringSuffixes, NULL, FALSE, &errorCode);
1595 if (U_FAILURE(errorCode)) {
1596 return;
1597 }
1598 /*
1599 * Make suffixes point into earlier, longer strings that contain them.
1600 * Temporarily use fSame and fSuffixOffset for suffix strings to
1601 * refer to the remaining ones.
1602 */
1603 for (int32_t i = 0; i < count;) {
1604 /*
1605 * This string is not a suffix of the previous one;
1606 * write this one and subsume the following ones that are
1607 * suffixes of this one.
1608 */
1609 StringResource *res = array[i];
1610 res->fNumUnitsSaved = (res->fNumCopies - 1) * res->get16BitStringsLength();
1611 // Whole duplicates of pool strings are already account for in fPoolStringIndexLimit,
1612 // see StringResource::handlePreflightStrings().
1613 int32_t j;
1614 for (j = i + 1; j < count; ++j) {
1615 StringResource *suffixRes = array[j];
1616 /* Is it a suffix of the earlier, longer string? */
1617 if (res->fString.endsWith(suffixRes->fString)) {
1618 assert(res->length() != suffixRes->length()); // Set strings are unique.
1619 if (suffixRes->fWritten) {
1620 // Pool string, skip.
1621 } else if (suffixRes->fNumCharsForLength == 0) {
1622 /* yes, point to the earlier string */
1623 suffixRes->fSame = res;
1624 suffixRes->fSuffixOffset = res->length() - suffixRes->length();
1625 if (res->fWritten) {
1626 // Suffix-share res which is a pool string.
1627 // Compute the resource word and collect the maximum.
1628 suffixRes->fRes =
1629 res->fRes + res->fNumCharsForLength + suffixRes->fSuffixOffset;
1630 int32_t poolStringIndex = (int32_t)RES_GET_OFFSET(suffixRes->fRes);
1631 if (poolStringIndex >= fPoolStringIndexLimit) {
1632 fPoolStringIndexLimit = poolStringIndex + 1;
1633 }
1634 suffixRes->fWritten = TRUE;
1635 }
1636 res->fNumUnitsSaved += suffixRes->fNumCopies * suffixRes->get16BitStringsLength();
1637 } else {
1638 /* write the suffix by itself if we need explicit length */
1639 }
1640 } else {
1641 break; /* not a suffix, restart from here */
1642 }
1643 }
1644 i = j;
1645 }
1646 /*
1647 * Re-sort the strings by ascending length (except suffixes last)
1648 * to optimize for URES_TABLE16 and URES_ARRAY16:
1649 * Keep as many as possible within reach of 16-bit offsets.
1650 */
1651 uprv_sortArray(array.getAlias(), count, (int32_t)sizeof(struct SResource **),
1652 compareStringLengths, NULL, FALSE, &errorCode);
1653 if (U_FAILURE(errorCode)) {
1654 return;
1655 }
1656 if (fIsPoolBundle) {
1657 // Write strings that are sufficiently shared.
1658 // Avoid writing other strings.
1659 int32_t numStringsWritten = 0;
1660 int32_t numUnitsSaved = 0;
1661 int32_t numUnitsNotSaved = 0;
1662 for (int32_t i = 0; i < count; ++i) {
1663 StringResource *res = array[i];
1664 // Maximum pool string index when suffix-sharing the last character.
1665 int32_t maxStringIndex =
1666 f16BitUnits.length() + res->fNumCharsForLength + res->length() - 1;
1667 if (res->fNumUnitsSaved >= GENRB_MIN_16BIT_UNITS_SAVED_FOR_POOL_STRING &&
1668 maxStringIndex < RES_MAX_OFFSET) {
1669 res->writeUTF16v2(0, f16BitUnits);
1670 ++numStringsWritten;
1671 numUnitsSaved += res->fNumUnitsSaved;
1672 } else {
1673 numUnitsNotSaved += res->fNumUnitsSaved;
1674 res->fRes = URES_MAKE_EMPTY_RESOURCE(URES_STRING);
1675 res->fWritten = TRUE;
1676 }
1677 }
1678 if (f16BitUnits.isBogus()) {
1679 errorCode = U_MEMORY_ALLOCATION_ERROR;
1680 }
1681 if (getShowWarning()) { // not quiet
1682 printf("number of shared strings: %d\n", (int)numStringsWritten);
1683 printf("16-bit units for strings: %6d = %6d bytes\n",
1684 (int)f16BitUnits.length(), (int)f16BitUnits.length() * 2);
1685 printf("16-bit units saved: %6d = %6d bytes\n",
1686 (int)numUnitsSaved, (int)numUnitsSaved * 2);
1687 printf("16-bit units not saved: %6d = %6d bytes\n",
1688 (int)numUnitsNotSaved, (int)numUnitsNotSaved * 2);
1689 }
1690 } else {
1691 assert(fPoolStringIndexLimit <= fUsePoolBundle->fStringIndexLimit);
1692 /* Write the non-suffix strings. */
1693 int32_t i;
1694 for (i = 0; i < count && array[i]->fSame == NULL; ++i) {
1695 StringResource *res = array[i];
1696 if (!res->fWritten) {
1697 int32_t localStringIndex = f16BitUnits.length();
1698 if (localStringIndex >= fLocalStringIndexLimit) {
1699 fLocalStringIndexLimit = localStringIndex + 1;
1700 }
1701 res->writeUTF16v2(fPoolStringIndexLimit, f16BitUnits);
1702 }
1703 }
1704 if (f16BitUnits.isBogus()) {
1705 errorCode = U_MEMORY_ALLOCATION_ERROR;
1706 return;
1707 }
1708 if (fWritePoolBundle != NULL && gFormatVersion >= 3) {
1709 PseudoListResource *poolStrings =
1710 static_cast<PseudoListResource *>(fWritePoolBundle->fRoot);
1711 for (i = 0; i < count && array[i]->fSame == NULL; ++i) {
1712 assert(!array[i]->fString.isEmpty());
1713 StringResource *poolString =
1714 new StringResource(fWritePoolBundle, array[i]->fString, errorCode);
1715 if (poolString == NULL) {
1716 errorCode = U_MEMORY_ALLOCATION_ERROR;
1717 break;
1718 }
1719 poolStrings->add(poolString);
1720 }
1721 }
1722 /* Write the suffix strings. Make each point to the real string. */
1723 for (; i < count; ++i) {
1724 StringResource *res = array[i];
1725 if (res->fWritten) {
1726 continue;
1727 }
1728 StringResource *same = res->fSame;
1729 assert(res->length() != same->length()); // Set strings are unique.
1730 res->fRes = same->fRes + same->fNumCharsForLength + res->fSuffixOffset;
1731 int32_t localStringIndex = (int32_t)RES_GET_OFFSET(res->fRes) - fPoolStringIndexLimit;
1732 // Suffixes of pool strings have been set already.
1733 assert(localStringIndex >= 0);
1734 if (localStringIndex >= fLocalStringIndexLimit) {
1735 fLocalStringIndexLimit = localStringIndex + 1;
1736 }
1737 res->fWritten = TRUE;
1738 }
1739 }
1740 // +1 to account for the initial zero in f16BitUnits
1741 assert(f16BitUnits.length() <= (f16BitStringsLength + 1));
1742 }
1743
applyFilter(const PathFilter &,ResKeyPath &,const SRBRoot *)1744 void SResource::applyFilter(
1745 const PathFilter& /*filter*/,
1746 ResKeyPath& /*path*/,
1747 const SRBRoot* /*bundle*/) {
1748 // Only a few resource types (tables) are capable of being filtered.
1749 }
1750
applyFilter(const PathFilter & filter,ResKeyPath & path,const SRBRoot * bundle)1751 void TableResource::applyFilter(
1752 const PathFilter& filter,
1753 ResKeyPath& path,
1754 const SRBRoot* bundle) {
1755 SResource* prev = nullptr;
1756 SResource* curr = fFirst;
1757 for (; curr != nullptr;) {
1758 path.push(curr->getKeyString(bundle));
1759 auto inclusion = filter.match(path);
1760 if (inclusion == PathFilter::EInclusion::INCLUDE) {
1761 // Include whole subtree
1762 // no-op
1763 if (isVerbose()) {
1764 std::cout << "genrb subtree: " << bundle->fLocale << ": INCLUDE: " << path << std::endl;
1765 }
1766 } else if (inclusion == PathFilter::EInclusion::EXCLUDE) {
1767 // Reject the whole subtree
1768 // Remove it from the linked list
1769 if (isVerbose()) {
1770 std::cout << "genrb subtree: " << bundle->fLocale << ": DELETE: " << path << std::endl;
1771 }
1772 if (prev == nullptr) {
1773 fFirst = curr->fNext;
1774 } else {
1775 prev->fNext = curr->fNext;
1776 }
1777 fCount--;
1778 delete curr;
1779 curr = prev;
1780 } else {
1781 U_ASSERT(inclusion == PathFilter::EInclusion::PARTIAL);
1782 // Recurse into the child
1783 curr->applyFilter(filter, path, bundle);
1784 }
1785 path.pop();
1786
1787 prev = curr;
1788 if (curr == nullptr) {
1789 curr = fFirst;
1790 } else {
1791 curr = curr->fNext;
1792 }
1793 }
1794 }
1795