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