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 len = (int32_t)uprv_strlen(outputDir);
958 if (len > writtenFilenameLen) {
959 len = writtenFilenameLen;
960 }
961 uprv_strncpy(writtenFilename, outputDir, len);
962 }
963 if (writtenFilenameLen -= len) {
964 off += len;
965 writtenFilename[off] = U_FILE_SEP_CHAR;
966 if (--writtenFilenameLen) {
967 ++off;
968 if(outputPkg != NULL)
969 {
970 uprv_strcpy(writtenFilename+off, outputPkg);
971 off += (int32_t)uprv_strlen(outputPkg);
972 writtenFilename[off] = '_';
973 ++off;
974 }
975
976 len = (int32_t)uprv_strlen(fLocale);
977 if (len > writtenFilenameLen) {
978 len = writtenFilenameLen;
979 }
980 uprv_strncpy(writtenFilename + off, fLocale, len);
981 if (writtenFilenameLen -= len) {
982 off += len;
983 len = 5;
984 if (len > writtenFilenameLen) {
985 len = writtenFilenameLen;
986 }
987 uprv_strncpy(writtenFilename + off, ".res", len);
988 }
989 }
990 }
991 }
992
993 if(outputPkg)
994 {
995 uprv_strcpy(dataName, outputPkg);
996 uprv_strcat(dataName, "_");
997 uprv_strcat(dataName, fLocale);
998 }
999 else
1000 {
1001 uprv_strcpy(dataName, fLocale);
1002 }
1003
1004 uprv_memcpy(dataInfo.formatVersion, gFormatVersions + formatVersion, sizeof(UVersionInfo));
1005
1006 mem = udata_create(outputDir, "res", dataName,
1007 &dataInfo, (gIncludeCopyright==TRUE)? U_COPYRIGHT_STRING:NULL, &errorCode);
1008 if(U_FAILURE(errorCode)){
1009 return;
1010 }
1011
1012 /* write the root item */
1013 udata_write32(mem, fRoot->fRes);
1014
1015 /*
1016 * formatVersion 1.1 (ICU 2.8):
1017 * write int32_t indexes[] after root and before the key strings
1018 * to make it easier to parse resource bundles in icuswap or from Java etc.
1019 */
1020 uprv_memset(indexes, 0, sizeof(indexes));
1021 indexes[URES_INDEX_LENGTH]= fIndexLength;
1022 indexes[URES_INDEX_KEYS_TOP]= fKeysTop>>2;
1023 indexes[URES_INDEX_RESOURCES_TOP]= (int32_t)(top>>2);
1024 indexes[URES_INDEX_BUNDLE_TOP]= indexes[URES_INDEX_RESOURCES_TOP];
1025 indexes[URES_INDEX_MAX_TABLE_LENGTH]= fMaxTableLength;
1026
1027 /*
1028 * formatVersion 1.2 (ICU 3.6):
1029 * write indexes[URES_INDEX_ATTRIBUTES] with URES_ATT_NO_FALLBACK set or not set
1030 * the memset() above initialized all indexes[] to 0
1031 */
1032 if (fNoFallback) {
1033 indexes[URES_INDEX_ATTRIBUTES]=URES_ATT_NO_FALLBACK;
1034 }
1035 /*
1036 * formatVersion 2.0 (ICU 4.4):
1037 * more compact string value storage, optional pool bundle
1038 */
1039 if (URES_INDEX_16BIT_TOP < fIndexLength) {
1040 indexes[URES_INDEX_16BIT_TOP] = (fKeysTop>>2) + (f16BitUnits.length()>>1);
1041 }
1042 if (URES_INDEX_POOL_CHECKSUM < fIndexLength) {
1043 if (fIsPoolBundle) {
1044 indexes[URES_INDEX_ATTRIBUTES] |= URES_ATT_IS_POOL_BUNDLE | URES_ATT_NO_FALLBACK;
1045 uint32_t checksum = computeCRC((const char *)(fKeys + fKeysBottom),
1046 (uint32_t)(fKeysTop - fKeysBottom), 0);
1047 if (f16BitUnits.length() <= 1) {
1048 // no pool strings to checksum
1049 } else if (U_IS_BIG_ENDIAN) {
1050 checksum = computeCRC(reinterpret_cast<const char *>(f16BitUnits.getBuffer()),
1051 (uint32_t)f16BitUnits.length() * 2, checksum);
1052 } else {
1053 // Swap to big-endian so we get the same checksum on all platforms
1054 // (except for charset family, due to the key strings).
1055 UnicodeString s(f16BitUnits);
1056 assert(!s.isBogus());
1057 // .getBuffer(capacity) returns a mutable buffer
1058 char16_t* p = s.getBuffer(f16BitUnits.length());
1059 for (int32_t count = f16BitUnits.length(); count > 0; --count) {
1060 uint16_t x = *p;
1061 *p++ = (uint16_t)((x << 8) | (x >> 8));
1062 }
1063 s.releaseBuffer(f16BitUnits.length());
1064 checksum = computeCRC((const char *)s.getBuffer(),
1065 (uint32_t)f16BitUnits.length() * 2, checksum);
1066 }
1067 indexes[URES_INDEX_POOL_CHECKSUM] = (int32_t)checksum;
1068 } else if (gUsePoolBundle) {
1069 indexes[URES_INDEX_ATTRIBUTES] |= URES_ATT_USES_POOL_BUNDLE;
1070 indexes[URES_INDEX_POOL_CHECKSUM] = fUsePoolBundle->fChecksum;
1071 }
1072 }
1073 // formatVersion 3 (ICU 56):
1074 // share string values via pool bundle strings
1075 indexes[URES_INDEX_LENGTH] |= fPoolStringIndexLimit << 8; // bits 23..0 -> 31..8
1076 indexes[URES_INDEX_ATTRIBUTES] |= (fPoolStringIndexLimit >> 12) & 0xf000; // bits 27..24 -> 15..12
1077 indexes[URES_INDEX_ATTRIBUTES] |= fPoolStringIndex16Limit << 16;
1078
1079 /* write the indexes[] */
1080 udata_writeBlock(mem, indexes, fIndexLength*4);
1081
1082 /* write the table key strings */
1083 udata_writeBlock(mem, fKeys+fKeysBottom,
1084 fKeysTop-fKeysBottom);
1085
1086 /* write the v2 UTF-16 strings, URES_TABLE16 and URES_ARRAY16 */
1087 udata_writeBlock(mem, f16BitUnits.getBuffer(), f16BitUnits.length()*2);
1088
1089 /* write all of the bundle contents: the root item and its children */
1090 byteOffset = fKeysTop + f16BitUnits.length() * 2;
1091 fRoot->write(mem, &byteOffset);
1092 assert(byteOffset == top);
1093
1094 size = udata_finish(mem, &errorCode);
1095 if(top != size) {
1096 fprintf(stderr, "genrb error: wrote %u bytes but counted %u\n",
1097 (int)size, (int)top);
1098 errorCode = U_INTERNAL_PROGRAM_ERROR;
1099 }
1100 }
1101
1102 /* Opening Functions */
1103
table_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1104 TableResource* table_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1105 LocalPointer<TableResource> res(new TableResource(bundle, tag, comment, *status), *status);
1106 return U_SUCCESS(*status) ? res.orphan() : NULL;
1107 }
1108
array_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1109 ArrayResource* array_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1110 LocalPointer<ArrayResource> res(new ArrayResource(bundle, tag, comment, *status), *status);
1111 return U_SUCCESS(*status) ? res.orphan() : NULL;
1112 }
1113
string_open(struct SRBRoot * bundle,const char * tag,const UChar * value,int32_t len,const struct UString * comment,UErrorCode * status)1114 struct SResource *string_open(struct SRBRoot *bundle, const char *tag, const UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
1115 LocalPointer<SResource> res(
1116 new StringResource(bundle, tag, value, len, comment, *status), *status);
1117 return U_SUCCESS(*status) ? res.orphan() : NULL;
1118 }
1119
alias_open(struct SRBRoot * bundle,const char * tag,UChar * value,int32_t len,const struct UString * comment,UErrorCode * status)1120 struct SResource *alias_open(struct SRBRoot *bundle, const char *tag, UChar *value, int32_t len, const struct UString* comment, UErrorCode *status) {
1121 LocalPointer<SResource> res(
1122 new AliasResource(bundle, tag, value, len, comment, *status), *status);
1123 return U_SUCCESS(*status) ? res.orphan() : NULL;
1124 }
1125
intvector_open(struct SRBRoot * bundle,const char * tag,const struct UString * comment,UErrorCode * status)1126 IntVectorResource *intvector_open(struct SRBRoot *bundle, const char *tag, const struct UString* comment, UErrorCode *status) {
1127 LocalPointer<IntVectorResource> res(
1128 new IntVectorResource(bundle, tag, comment, *status), *status);
1129 return U_SUCCESS(*status) ? res.orphan() : NULL;
1130 }
1131
int_open(struct SRBRoot * bundle,const char * tag,int32_t value,const struct UString * comment,UErrorCode * status)1132 struct SResource *int_open(struct SRBRoot *bundle, const char *tag, int32_t value, const struct UString* comment, UErrorCode *status) {
1133 LocalPointer<SResource> res(new IntResource(bundle, tag, value, comment, *status), *status);
1134 return U_SUCCESS(*status) ? res.orphan() : NULL;
1135 }
1136
bin_open(struct SRBRoot * bundle,const char * tag,uint32_t length,uint8_t * data,const char * fileName,const struct UString * comment,UErrorCode * status)1137 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) {
1138 LocalPointer<SResource> res(
1139 new BinaryResource(bundle, tag, length, data, fileName, comment, *status), *status);
1140 return U_SUCCESS(*status) ? res.orphan() : NULL;
1141 }
1142
SRBRoot(const UString * comment,UBool isPoolBundle,UErrorCode & errorCode)1143 SRBRoot::SRBRoot(const UString *comment, UBool isPoolBundle, UErrorCode &errorCode)
1144 : fRoot(NULL), fLocale(NULL), fIndexLength(0), fMaxTableLength(0), fNoFallback(FALSE),
1145 fStringsForm(STRINGS_UTF16_V1), fIsPoolBundle(isPoolBundle),
1146 fKeys(NULL), fKeyMap(NULL),
1147 fKeysBottom(0), fKeysTop(0), fKeysCapacity(0),
1148 fKeysCount(0), fLocalKeyLimit(0),
1149 f16BitUnits(), f16BitStringsLength(0),
1150 fUsePoolBundle(&kNoPoolBundle),
1151 fPoolStringIndexLimit(0), fPoolStringIndex16Limit(0), fLocalStringIndexLimit(0),
1152 fWritePoolBundle(NULL) {
1153 if (U_FAILURE(errorCode)) {
1154 return;
1155 }
1156
1157 if (gFormatVersion > 1) {
1158 // f16BitUnits must start with a zero for empty resources.
1159 // We might be able to omit it if there are no empty 16-bit resources.
1160 f16BitUnits.append((UChar)0);
1161 }
1162
1163 fKeys = (char *) uprv_malloc(sizeof(char) * KEY_SPACE_SIZE);
1164 if (isPoolBundle) {
1165 fRoot = new PseudoListResource(this, errorCode);
1166 } else {
1167 fRoot = new TableResource(this, NULL, comment, errorCode);
1168 }
1169 if (fKeys == NULL || fRoot == NULL || U_FAILURE(errorCode)) {
1170 if (U_SUCCESS(errorCode)) {
1171 errorCode = U_MEMORY_ALLOCATION_ERROR;
1172 }
1173 return;
1174 }
1175
1176 fKeysCapacity = KEY_SPACE_SIZE;
1177 /* formatVersion 1.1 and up: start fKeysTop after the root item and indexes[] */
1178 if (gUsePoolBundle || isPoolBundle) {
1179 fIndexLength = URES_INDEX_POOL_CHECKSUM + 1;
1180 } else if (gFormatVersion >= 2) {
1181 fIndexLength = URES_INDEX_16BIT_TOP + 1;
1182 } else /* formatVersion 1 */ {
1183 fIndexLength = URES_INDEX_ATTRIBUTES + 1;
1184 }
1185 fKeysBottom = (1 /* root */ + fIndexLength) * 4;
1186 uprv_memset(fKeys, 0, fKeysBottom);
1187 fKeysTop = fKeysBottom;
1188
1189 if (gFormatVersion == 1) {
1190 fStringsForm = STRINGS_UTF16_V1;
1191 } else {
1192 fStringsForm = STRINGS_UTF16_V2;
1193 }
1194 }
1195
1196 /* Closing Functions */
1197
res_close(struct SResource * res)1198 void res_close(struct SResource *res) {
1199 delete res;
1200 }
1201
~SRBRoot()1202 SRBRoot::~SRBRoot() {
1203 delete fRoot;
1204 uprv_free(fLocale);
1205 uprv_free(fKeys);
1206 uprv_free(fKeyMap);
1207 }
1208
1209 /* Misc Functions */
1210
setLocale(UChar * locale,UErrorCode & errorCode)1211 void SRBRoot::setLocale(UChar *locale, UErrorCode &errorCode) {
1212 if(U_FAILURE(errorCode)) {
1213 return;
1214 }
1215
1216 uprv_free(fLocale);
1217 fLocale = (char*) uprv_malloc(sizeof(char) * (u_strlen(locale)+1));
1218 if(fLocale == NULL) {
1219 errorCode = U_MEMORY_ALLOCATION_ERROR;
1220 return;
1221 }
1222
1223 u_UCharsToChars(locale, fLocale, u_strlen(locale)+1);
1224 }
1225
1226 const char *
getKeyString(int32_t key) const1227 SRBRoot::getKeyString(int32_t key) const {
1228 if (key < 0) {
1229 return fUsePoolBundle->fKeys + (key & 0x7fffffff);
1230 } else {
1231 return fKeys + key;
1232 }
1233 }
1234
1235 const char *
getKeyString(const SRBRoot * bundle) const1236 SResource::getKeyString(const SRBRoot *bundle) const {
1237 if (fKey == -1) {
1238 return NULL;
1239 }
1240 return bundle->getKeyString(fKey);
1241 }
1242
1243 const char *
getKeyBytes(int32_t * pLength) const1244 SRBRoot::getKeyBytes(int32_t *pLength) const {
1245 *pLength = fKeysTop - fKeysBottom;
1246 return fKeys + fKeysBottom;
1247 }
1248
1249 int32_t
addKeyBytes(const char * keyBytes,int32_t length,UErrorCode & errorCode)1250 SRBRoot::addKeyBytes(const char *keyBytes, int32_t length, UErrorCode &errorCode) {
1251 int32_t keypos;
1252
1253 // It is not legal to add new key bytes after compactKeys is run!
1254 U_ASSERT(fKeyMap == nullptr);
1255
1256 if (U_FAILURE(errorCode)) {
1257 return -1;
1258 }
1259 if (length < 0 || (keyBytes == NULL && length != 0)) {
1260 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
1261 return -1;
1262 }
1263 if (length == 0) {
1264 return fKeysTop;
1265 }
1266
1267 keypos = fKeysTop;
1268 fKeysTop += length;
1269 if (fKeysTop >= fKeysCapacity) {
1270 /* overflow - resize the keys buffer */
1271 fKeysCapacity += KEY_SPACE_SIZE;
1272 fKeys = static_cast<char *>(uprv_realloc(fKeys, fKeysCapacity));
1273 if(fKeys == NULL) {
1274 errorCode = U_MEMORY_ALLOCATION_ERROR;
1275 return -1;
1276 }
1277 }
1278
1279 uprv_memcpy(fKeys + keypos, keyBytes, length);
1280
1281 return keypos;
1282 }
1283
1284 int32_t
addTag(const char * tag,UErrorCode & errorCode)1285 SRBRoot::addTag(const char *tag, UErrorCode &errorCode) {
1286 int32_t keypos;
1287
1288 if (U_FAILURE(errorCode)) {
1289 return -1;
1290 }
1291
1292 if (tag == NULL) {
1293 /* no error: the root table and array items have no keys */
1294 return -1;
1295 }
1296
1297 keypos = addKeyBytes(tag, (int32_t)(uprv_strlen(tag) + 1), errorCode);
1298 if (U_SUCCESS(errorCode)) {
1299 ++fKeysCount;
1300 }
1301 return keypos;
1302 }
1303
1304 static int32_t
compareInt32(int32_t lPos,int32_t rPos)1305 compareInt32(int32_t lPos, int32_t rPos) {
1306 /*
1307 * Compare possibly-negative key offsets. Don't just return lPos - rPos
1308 * because that is prone to negative-integer underflows.
1309 */
1310 if (lPos < rPos) {
1311 return -1;
1312 } else if (lPos > rPos) {
1313 return 1;
1314 } else {
1315 return 0;
1316 }
1317 }
1318
1319 static int32_t U_CALLCONV
compareKeySuffixes(const void * context,const void * l,const void * r)1320 compareKeySuffixes(const void *context, const void *l, const void *r) {
1321 const struct SRBRoot *bundle=(const struct SRBRoot *)context;
1322 int32_t lPos = ((const KeyMapEntry *)l)->oldpos;
1323 int32_t rPos = ((const KeyMapEntry *)r)->oldpos;
1324 const char *lStart = bundle->getKeyString(lPos);
1325 const char *lLimit = lStart;
1326 const char *rStart = bundle->getKeyString(rPos);
1327 const char *rLimit = rStart;
1328 int32_t diff;
1329 while (*lLimit != 0) { ++lLimit; }
1330 while (*rLimit != 0) { ++rLimit; }
1331 /* compare keys in reverse character order */
1332 while (lStart < lLimit && rStart < rLimit) {
1333 diff = (int32_t)(uint8_t)*--lLimit - (int32_t)(uint8_t)*--rLimit;
1334 if (diff != 0) {
1335 return diff;
1336 }
1337 }
1338 /* sort equal suffixes by descending key length */
1339 diff = (int32_t)(rLimit - rStart) - (int32_t)(lLimit - lStart);
1340 if (diff != 0) {
1341 return diff;
1342 }
1343 /* Sort pool bundle keys first (negative oldpos), and otherwise keys in parsing order. */
1344 return compareInt32(lPos, rPos);
1345 }
1346
1347 static int32_t U_CALLCONV
compareKeyNewpos(const void *,const void * l,const void * r)1348 compareKeyNewpos(const void * /*context*/, const void *l, const void *r) {
1349 return compareInt32(((const KeyMapEntry *)l)->newpos, ((const KeyMapEntry *)r)->newpos);
1350 }
1351
1352 static int32_t U_CALLCONV
compareKeyOldpos(const void *,const void * l,const void * r)1353 compareKeyOldpos(const void * /*context*/, const void *l, const void *r) {
1354 return compareInt32(((const KeyMapEntry *)l)->oldpos, ((const KeyMapEntry *)r)->oldpos);
1355 }
1356
collectKeys(std::function<void (int32_t)> collector) const1357 void SResource::collectKeys(std::function<void(int32_t)> collector) const {
1358 collector(fKey);
1359 }
1360
collectKeys(std::function<void (int32_t)> collector) const1361 void ContainerResource::collectKeys(std::function<void(int32_t)> collector) const {
1362 collector(fKey);
1363 for (SResource* curr = fFirst; curr != NULL; curr = curr->fNext) {
1364 curr->collectKeys(collector);
1365 }
1366 }
1367
1368 void
compactKeys(UErrorCode & errorCode)1369 SRBRoot::compactKeys(UErrorCode &errorCode) {
1370 KeyMapEntry *map;
1371 char *keys;
1372 int32_t i;
1373
1374 // Except for pool bundles, keys might not be used.
1375 // Do not add unused keys to the final bundle.
1376 std::set<int32_t> keysInUse;
1377 if (!fIsPoolBundle) {
1378 fRoot->collectKeys([&keysInUse](int32_t key) {
1379 if (key >= 0) {
1380 keysInUse.insert(key);
1381 }
1382 });
1383 fKeysCount = static_cast<int32_t>(keysInUse.size());
1384 }
1385
1386 int32_t keysCount = fUsePoolBundle->fKeysCount + fKeysCount;
1387 if (U_FAILURE(errorCode) || fKeyMap != NULL) {
1388 return;
1389 }
1390 map = (KeyMapEntry *)uprv_malloc(keysCount * sizeof(KeyMapEntry));
1391 if (map == NULL) {
1392 errorCode = U_MEMORY_ALLOCATION_ERROR;
1393 return;
1394 }
1395 keys = (char *)fUsePoolBundle->fKeys;
1396 for (i = 0; i < fUsePoolBundle->fKeysCount; ++i) {
1397 map[i].oldpos =
1398 (int32_t)(keys - fUsePoolBundle->fKeys) | 0x80000000; /* negative oldpos */
1399 map[i].newpos = 0;
1400 while (*keys != 0) { ++keys; } /* skip the key */
1401 ++keys; /* skip the NUL */
1402 }
1403 keys = fKeys + fKeysBottom;
1404 while (i < keysCount) {
1405 int32_t keyOffset = static_cast<int32_t>(keys - fKeys);
1406 if (!fIsPoolBundle && keysInUse.count(keyOffset) == 0) {
1407 // Mark the unused key as deleted
1408 while (*keys != 0) { *keys++ = 1; }
1409 *keys++ = 1;
1410 } else {
1411 map[i].oldpos = keyOffset;
1412 map[i].newpos = 0;
1413 while (*keys != 0) { ++keys; } /* skip the key */
1414 ++keys; /* skip the NUL */
1415 i++;
1416 }
1417 }
1418 if (keys != fKeys + fKeysTop) {
1419 // Throw away any unused keys from the end
1420 fKeysTop = static_cast<int32_t>(keys - fKeys);
1421 }
1422 /* Sort the keys so that each one is immediately followed by all of its suffixes. */
1423 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1424 compareKeySuffixes, this, FALSE, &errorCode);
1425 /*
1426 * Make suffixes point into earlier, longer strings that contain them
1427 * and mark the old, now unused suffix bytes as deleted.
1428 */
1429 if (U_SUCCESS(errorCode)) {
1430 keys = fKeys;
1431 for (i = 0; i < keysCount;) {
1432 /*
1433 * This key is not a suffix of the previous one;
1434 * keep this one and delete the following ones that are
1435 * suffixes of this one.
1436 */
1437 const char *key;
1438 const char *keyLimit;
1439 int32_t j = i + 1;
1440 map[i].newpos = map[i].oldpos;
1441 if (j < keysCount && map[j].oldpos < 0) {
1442 /* Key string from the pool bundle, do not delete. */
1443 i = j;
1444 continue;
1445 }
1446 key = getKeyString(map[i].oldpos);
1447 for (keyLimit = key; *keyLimit != 0; ++keyLimit) {}
1448 for (; j < keysCount && map[j].oldpos >= 0; ++j) {
1449 const char *k;
1450 char *suffix;
1451 const char *suffixLimit;
1452 int32_t offset;
1453 suffix = keys + map[j].oldpos;
1454 for (suffixLimit = suffix; *suffixLimit != 0; ++suffixLimit) {}
1455 offset = static_cast<int32_t>((keyLimit - key) - (suffixLimit - suffix));
1456 if (offset < 0) {
1457 break; /* suffix cannot be longer than the original */
1458 }
1459 /* Is it a suffix of the earlier, longer key? */
1460 for (k = keyLimit; suffix < suffixLimit && *--k == *--suffixLimit;) {}
1461 if (suffix == suffixLimit && *k == *suffixLimit) {
1462 map[j].newpos = map[i].oldpos + offset; /* yes, point to the earlier key */
1463 // Mark the suffix as deleted
1464 while (*suffix != 0) { *suffix++ = 1; }
1465 *suffix = 1;
1466 } else {
1467 break; /* not a suffix, restart from here */
1468 }
1469 }
1470 i = j;
1471 }
1472 /*
1473 * Re-sort by newpos, then modify the key characters array in-place
1474 * to squeeze out unused bytes, and readjust the newpos offsets.
1475 */
1476 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1477 compareKeyNewpos, NULL, FALSE, &errorCode);
1478 if (U_SUCCESS(errorCode)) {
1479 int32_t oldpos, newpos, limit;
1480 oldpos = newpos = fKeysBottom;
1481 limit = fKeysTop;
1482 /* skip key offsets that point into the pool bundle rather than this new bundle */
1483 for (i = 0; i < keysCount && map[i].newpos < 0; ++i) {}
1484 if (i < keysCount) {
1485 while (oldpos < limit) {
1486 if (keys[oldpos] == 1) {
1487 ++oldpos; /* skip unused bytes */
1488 } else {
1489 /* adjust the new offsets for keys starting here */
1490 while (i < keysCount && map[i].newpos == oldpos) {
1491 map[i++].newpos = newpos;
1492 }
1493 /* move the key characters to their new position */
1494 keys[newpos++] = keys[oldpos++];
1495 }
1496 }
1497 U_ASSERT(i == keysCount);
1498 }
1499 fKeysTop = newpos;
1500 /* Re-sort once more, by old offsets for binary searching. */
1501 uprv_sortArray(map, keysCount, (int32_t)sizeof(KeyMapEntry),
1502 compareKeyOldpos, NULL, FALSE, &errorCode);
1503 if (U_SUCCESS(errorCode)) {
1504 /* key size reduction by limit - newpos */
1505 fKeyMap = map;
1506 map = NULL;
1507 }
1508 }
1509 }
1510 uprv_free(map);
1511 }
1512
1513 static int32_t U_CALLCONV
compareStringSuffixes(const void *,const void * l,const void * r)1514 compareStringSuffixes(const void * /*context*/, const void *l, const void *r) {
1515 const StringResource *left = *((const StringResource **)l);
1516 const StringResource *right = *((const StringResource **)r);
1517 const UChar *lStart = left->getBuffer();
1518 const UChar *lLimit = lStart + left->length();
1519 const UChar *rStart = right->getBuffer();
1520 const UChar *rLimit = rStart + right->length();
1521 int32_t diff;
1522 /* compare keys in reverse character order */
1523 while (lStart < lLimit && rStart < rLimit) {
1524 diff = (int32_t)*--lLimit - (int32_t)*--rLimit;
1525 if (diff != 0) {
1526 return diff;
1527 }
1528 }
1529 /* sort equal suffixes by descending string length */
1530 return right->length() - left->length();
1531 }
1532
1533 static int32_t U_CALLCONV
compareStringLengths(const void *,const void * l,const void * r)1534 compareStringLengths(const void * /*context*/, const void *l, const void *r) {
1535 const StringResource *left = *((const StringResource **)l);
1536 const StringResource *right = *((const StringResource **)r);
1537 int32_t diff;
1538 /* Make "is suffix of another string" compare greater than a non-suffix. */
1539 diff = (int)(left->fSame != NULL) - (int)(right->fSame != NULL);
1540 if (diff != 0) {
1541 return diff;
1542 }
1543 /* sort by ascending string length */
1544 diff = left->length() - right->length();
1545 if (diff != 0) {
1546 return diff;
1547 }
1548 // sort by descending size reduction
1549 diff = right->fNumUnitsSaved - left->fNumUnitsSaved;
1550 if (diff != 0) {
1551 return diff;
1552 }
1553 // sort lexically
1554 return left->fString.compare(right->fString);
1555 }
1556
1557 void
writeUTF16v2(int32_t base,UnicodeString & dest)1558 StringResource::writeUTF16v2(int32_t base, UnicodeString &dest) {
1559 int32_t len = length();
1560 fRes = URES_MAKE_RESOURCE(URES_STRING_V2, base + dest.length());
1561 fWritten = TRUE;
1562 switch(fNumCharsForLength) {
1563 case 0:
1564 break;
1565 case 1:
1566 dest.append((UChar)(0xdc00 + len));
1567 break;
1568 case 2:
1569 dest.append((UChar)(0xdfef + (len >> 16)));
1570 dest.append((UChar)len);
1571 break;
1572 case 3:
1573 dest.append((UChar)0xdfff);
1574 dest.append((UChar)(len >> 16));
1575 dest.append((UChar)len);
1576 break;
1577 default:
1578 break; /* will not occur */
1579 }
1580 dest.append(fString);
1581 dest.append((UChar)0);
1582 }
1583
1584 void
compactStringsV2(UHashtable * stringSet,UErrorCode & errorCode)1585 SRBRoot::compactStringsV2(UHashtable *stringSet, UErrorCode &errorCode) {
1586 if (U_FAILURE(errorCode)) {
1587 return;
1588 }
1589 // Store the StringResource pointers in an array for
1590 // easy sorting and processing.
1591 // We enumerate a set of strings, so there are no duplicates.
1592 int32_t count = uhash_count(stringSet);
1593 LocalArray<StringResource *> array(new StringResource *[count], errorCode);
1594 if (U_FAILURE(errorCode)) {
1595 return;
1596 }
1597 for (int32_t pos = UHASH_FIRST, i = 0; i < count; ++i) {
1598 array[i] = (StringResource *)uhash_nextElement(stringSet, &pos)->key.pointer;
1599 }
1600 /* Sort the strings so that each one is immediately followed by all of its suffixes. */
1601 uprv_sortArray(array.getAlias(), count, (int32_t)sizeof(struct SResource **),
1602 compareStringSuffixes, NULL, FALSE, &errorCode);
1603 if (U_FAILURE(errorCode)) {
1604 return;
1605 }
1606 /*
1607 * Make suffixes point into earlier, longer strings that contain them.
1608 * Temporarily use fSame and fSuffixOffset for suffix strings to
1609 * refer to the remaining ones.
1610 */
1611 for (int32_t i = 0; i < count;) {
1612 /*
1613 * This string is not a suffix of the previous one;
1614 * write this one and subsume the following ones that are
1615 * suffixes of this one.
1616 */
1617 StringResource *res = array[i];
1618 res->fNumUnitsSaved = (res->fNumCopies - 1) * res->get16BitStringsLength();
1619 // Whole duplicates of pool strings are already account for in fPoolStringIndexLimit,
1620 // see StringResource::handlePreflightStrings().
1621 int32_t j;
1622 for (j = i + 1; j < count; ++j) {
1623 StringResource *suffixRes = array[j];
1624 /* Is it a suffix of the earlier, longer string? */
1625 if (res->fString.endsWith(suffixRes->fString)) {
1626 assert(res->length() != suffixRes->length()); // Set strings are unique.
1627 if (suffixRes->fWritten) {
1628 // Pool string, skip.
1629 } else if (suffixRes->fNumCharsForLength == 0) {
1630 /* yes, point to the earlier string */
1631 suffixRes->fSame = res;
1632 suffixRes->fSuffixOffset = res->length() - suffixRes->length();
1633 if (res->fWritten) {
1634 // Suffix-share res which is a pool string.
1635 // Compute the resource word and collect the maximum.
1636 suffixRes->fRes =
1637 res->fRes + res->fNumCharsForLength + suffixRes->fSuffixOffset;
1638 int32_t poolStringIndex = (int32_t)RES_GET_OFFSET(suffixRes->fRes);
1639 if (poolStringIndex >= fPoolStringIndexLimit) {
1640 fPoolStringIndexLimit = poolStringIndex + 1;
1641 }
1642 suffixRes->fWritten = TRUE;
1643 }
1644 res->fNumUnitsSaved += suffixRes->fNumCopies * suffixRes->get16BitStringsLength();
1645 } else {
1646 /* write the suffix by itself if we need explicit length */
1647 }
1648 } else {
1649 break; /* not a suffix, restart from here */
1650 }
1651 }
1652 i = j;
1653 }
1654 /*
1655 * Re-sort the strings by ascending length (except suffixes last)
1656 * to optimize for URES_TABLE16 and URES_ARRAY16:
1657 * Keep as many as possible within reach of 16-bit offsets.
1658 */
1659 uprv_sortArray(array.getAlias(), count, (int32_t)sizeof(struct SResource **),
1660 compareStringLengths, NULL, FALSE, &errorCode);
1661 if (U_FAILURE(errorCode)) {
1662 return;
1663 }
1664 if (fIsPoolBundle) {
1665 // Write strings that are sufficiently shared.
1666 // Avoid writing other strings.
1667 int32_t numStringsWritten = 0;
1668 int32_t numUnitsSaved = 0;
1669 int32_t numUnitsNotSaved = 0;
1670 for (int32_t i = 0; i < count; ++i) {
1671 StringResource *res = array[i];
1672 // Maximum pool string index when suffix-sharing the last character.
1673 int32_t maxStringIndex =
1674 f16BitUnits.length() + res->fNumCharsForLength + res->length() - 1;
1675 if (res->fNumUnitsSaved >= GENRB_MIN_16BIT_UNITS_SAVED_FOR_POOL_STRING &&
1676 maxStringIndex < RES_MAX_OFFSET) {
1677 res->writeUTF16v2(0, f16BitUnits);
1678 ++numStringsWritten;
1679 numUnitsSaved += res->fNumUnitsSaved;
1680 } else {
1681 numUnitsNotSaved += res->fNumUnitsSaved;
1682 res->fRes = URES_MAKE_EMPTY_RESOURCE(URES_STRING);
1683 res->fWritten = TRUE;
1684 }
1685 }
1686 if (f16BitUnits.isBogus()) {
1687 errorCode = U_MEMORY_ALLOCATION_ERROR;
1688 }
1689 if (getShowWarning()) { // not quiet
1690 printf("number of shared strings: %d\n", (int)numStringsWritten);
1691 printf("16-bit units for strings: %6d = %6d bytes\n",
1692 (int)f16BitUnits.length(), (int)f16BitUnits.length() * 2);
1693 printf("16-bit units saved: %6d = %6d bytes\n",
1694 (int)numUnitsSaved, (int)numUnitsSaved * 2);
1695 printf("16-bit units not saved: %6d = %6d bytes\n",
1696 (int)numUnitsNotSaved, (int)numUnitsNotSaved * 2);
1697 }
1698 } else {
1699 assert(fPoolStringIndexLimit <= fUsePoolBundle->fStringIndexLimit);
1700 /* Write the non-suffix strings. */
1701 int32_t i;
1702 for (i = 0; i < count && array[i]->fSame == NULL; ++i) {
1703 StringResource *res = array[i];
1704 if (!res->fWritten) {
1705 int32_t localStringIndex = f16BitUnits.length();
1706 if (localStringIndex >= fLocalStringIndexLimit) {
1707 fLocalStringIndexLimit = localStringIndex + 1;
1708 }
1709 res->writeUTF16v2(fPoolStringIndexLimit, f16BitUnits);
1710 }
1711 }
1712 if (f16BitUnits.isBogus()) {
1713 errorCode = U_MEMORY_ALLOCATION_ERROR;
1714 return;
1715 }
1716 if (fWritePoolBundle != NULL && gFormatVersion >= 3) {
1717 PseudoListResource *poolStrings =
1718 static_cast<PseudoListResource *>(fWritePoolBundle->fRoot);
1719 for (i = 0; i < count && array[i]->fSame == NULL; ++i) {
1720 assert(!array[i]->fString.isEmpty());
1721 StringResource *poolString =
1722 new StringResource(fWritePoolBundle, array[i]->fString, errorCode);
1723 if (poolString == NULL) {
1724 errorCode = U_MEMORY_ALLOCATION_ERROR;
1725 break;
1726 }
1727 poolStrings->add(poolString);
1728 }
1729 }
1730 /* Write the suffix strings. Make each point to the real string. */
1731 for (; i < count; ++i) {
1732 StringResource *res = array[i];
1733 if (res->fWritten) {
1734 continue;
1735 }
1736 StringResource *same = res->fSame;
1737 assert(res->length() != same->length()); // Set strings are unique.
1738 res->fRes = same->fRes + same->fNumCharsForLength + res->fSuffixOffset;
1739 int32_t localStringIndex = (int32_t)RES_GET_OFFSET(res->fRes) - fPoolStringIndexLimit;
1740 // Suffixes of pool strings have been set already.
1741 assert(localStringIndex >= 0);
1742 if (localStringIndex >= fLocalStringIndexLimit) {
1743 fLocalStringIndexLimit = localStringIndex + 1;
1744 }
1745 res->fWritten = TRUE;
1746 }
1747 }
1748 // +1 to account for the initial zero in f16BitUnits
1749 assert(f16BitUnits.length() <= (f16BitStringsLength + 1));
1750 }
1751
applyFilter(const PathFilter &,ResKeyPath &,const SRBRoot *)1752 void SResource::applyFilter(
1753 const PathFilter& /*filter*/,
1754 ResKeyPath& /*path*/,
1755 const SRBRoot* /*bundle*/) {
1756 // Only a few resource types (tables) are capable of being filtered.
1757 }
1758
applyFilter(const PathFilter & filter,ResKeyPath & path,const SRBRoot * bundle)1759 void TableResource::applyFilter(
1760 const PathFilter& filter,
1761 ResKeyPath& path,
1762 const SRBRoot* bundle) {
1763 SResource* prev = nullptr;
1764 SResource* curr = fFirst;
1765 for (; curr != nullptr;) {
1766 path.push(curr->getKeyString(bundle));
1767 auto inclusion = filter.match(path);
1768 if (inclusion == PathFilter::EInclusion::INCLUDE) {
1769 // Include whole subtree
1770 // no-op
1771 if (isVerbose()) {
1772 std::cout << "genrb subtree: " << bundle->fLocale << ": INCLUDE: " << path << std::endl;
1773 }
1774 } else if (inclusion == PathFilter::EInclusion::EXCLUDE) {
1775 // Reject the whole subtree
1776 // Remove it from the linked list
1777 if (isVerbose()) {
1778 std::cout << "genrb subtree: " << bundle->fLocale << ": DELETE: " << path << std::endl;
1779 }
1780 if (prev == nullptr) {
1781 fFirst = curr->fNext;
1782 } else {
1783 prev->fNext = curr->fNext;
1784 }
1785 fCount--;
1786 delete curr;
1787 curr = prev;
1788 } else {
1789 U_ASSERT(inclusion == PathFilter::EInclusion::PARTIAL);
1790 // Recurse into the child
1791 curr->applyFilter(filter, path, bundle);
1792 }
1793 path.pop();
1794
1795 prev = curr;
1796 if (curr == nullptr) {
1797 curr = fFirst;
1798 } else {
1799 curr = curr->fNext;
1800 }
1801 }
1802 }
1803