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