1 /*
2 *******************************************************************************
3 * Copyright (C) 1999-2015, International Business Machines Corporation
4 *               and others. All Rights Reserved.
5 *******************************************************************************
6 *   file name:  uresdata.cpp
7 *   encoding:   US-ASCII
8 *   tab size:   8 (not used)
9 *   indentation:4
10 *
11 *   created on: 1999dec08
12 *   created by: Markus W. Scherer
13 * Modification History:
14 *
15 *   Date        Name        Description
16 *   06/20/2000  helena      OS/400 port changes; mostly typecast.
17 *   06/24/02    weiv        Added support for resource sharing
18 */
19 
20 #include "unicode/utypes.h"
21 #include "unicode/udata.h"
22 #include "unicode/ustring.h"
23 #include "unicode/utf16.h"
24 #include "cmemory.h"
25 #include "cstring.h"
26 #include "resource.h"
27 #include "uarrsort.h"
28 #include "uassert.h"
29 #include "ucol_swp.h"
30 #include "udataswp.h"
31 #include "uinvchar.h"
32 #include "uresdata.h"
33 #include "uresimp.h"
34 
35 /*
36  * Resource access helpers
37  */
38 
39 /* get a const char* pointer to the key with the keyOffset byte offset from pRoot */
40 #define RES_GET_KEY16(pResData, keyOffset) \
41     ((keyOffset)<(pResData)->localKeyLimit ? \
42         (const char *)(pResData)->pRoot+(keyOffset) : \
43         (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit)
44 
45 #define RES_GET_KEY32(pResData, keyOffset) \
46     ((keyOffset)>=0 ? \
47         (const char *)(pResData)->pRoot+(keyOffset) : \
48         (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff))
49 
50 #define URESDATA_ITEM_NOT_FOUND -1
51 
52 /* empty resources, returned when the resource offset is 0 */
53 static const uint16_t gEmpty16=0;
54 
55 static const struct {
56     int32_t length;
57     int32_t res;
58 } gEmpty32={ 0, 0 };
59 
60 static const struct {
61     int32_t length;
62     UChar nul;
63     UChar pad;
64 } gEmptyString={ 0, 0, 0 };
65 
66 /*
67  * All the type-access functions assume that
68  * the resource is of the expected type.
69  */
70 
71 static int32_t
_res_findTableItem(const ResourceData * pResData,const uint16_t * keyOffsets,int32_t length,const char * key,const char ** realKey)72 _res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, int32_t length,
73                    const char *key, const char **realKey) {
74     const char *tableKey;
75     int32_t mid, start, limit;
76     int result;
77 
78     /* do a binary search for the key */
79     start=0;
80     limit=length;
81     while(start<limit) {
82         mid = (start + limit) / 2;
83         tableKey = RES_GET_KEY16(pResData, keyOffsets[mid]);
84         if (pResData->useNativeStrcmp) {
85             result = uprv_strcmp(key, tableKey);
86         } else {
87             result = uprv_compareInvCharsAsAscii(key, tableKey);
88         }
89         if (result < 0) {
90             limit = mid;
91         } else if (result > 0) {
92             start = mid + 1;
93         } else {
94             /* We found it! */
95             *realKey=tableKey;
96             return mid;
97         }
98     }
99     return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
100 }
101 
102 static int32_t
_res_findTable32Item(const ResourceData * pResData,const int32_t * keyOffsets,int32_t length,const char * key,const char ** realKey)103 _res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, int32_t length,
104                      const char *key, const char **realKey) {
105     const char *tableKey;
106     int32_t mid, start, limit;
107     int result;
108 
109     /* do a binary search for the key */
110     start=0;
111     limit=length;
112     while(start<limit) {
113         mid = (start + limit) / 2;
114         tableKey = RES_GET_KEY32(pResData, keyOffsets[mid]);
115         if (pResData->useNativeStrcmp) {
116             result = uprv_strcmp(key, tableKey);
117         } else {
118             result = uprv_compareInvCharsAsAscii(key, tableKey);
119         }
120         if (result < 0) {
121             limit = mid;
122         } else if (result > 0) {
123             start = mid + 1;
124         } else {
125             /* We found it! */
126             *realKey=tableKey;
127             return mid;
128         }
129     }
130     return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
131 }
132 
133 /* helper for res_load() ---------------------------------------------------- */
134 
135 static UBool U_CALLCONV
isAcceptable(void * context,const char *,const char *,const UDataInfo * pInfo)136 isAcceptable(void *context,
137              const char * /*type*/, const char * /*name*/,
138              const UDataInfo *pInfo) {
139     uprv_memcpy(context, pInfo->formatVersion, 4);
140     return (UBool)(
141         pInfo->size>=20 &&
142         pInfo->isBigEndian==U_IS_BIG_ENDIAN &&
143         pInfo->charsetFamily==U_CHARSET_FAMILY &&
144         pInfo->sizeofUChar==U_SIZEOF_UCHAR &&
145         pInfo->dataFormat[0]==0x52 &&   /* dataFormat="ResB" */
146         pInfo->dataFormat[1]==0x65 &&
147         pInfo->dataFormat[2]==0x73 &&
148         pInfo->dataFormat[3]==0x42 &&
149         (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3));
150 }
151 
152 /* semi-public functions ---------------------------------------------------- */
153 
154 static void
res_init(ResourceData * pResData,UVersionInfo formatVersion,const void * inBytes,int32_t length,UErrorCode * errorCode)155 res_init(ResourceData *pResData,
156          UVersionInfo formatVersion, const void *inBytes, int32_t length,
157          UErrorCode *errorCode) {
158     UResType rootType;
159 
160     /* get the root resource */
161     pResData->pRoot=(const int32_t *)inBytes;
162     pResData->rootRes=(Resource)*pResData->pRoot;
163     pResData->p16BitUnits=&gEmpty16;
164 
165     /* formatVersion 1.1 must have a root item and at least 5 indexes */
166     if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) {
167         *errorCode=U_INVALID_FORMAT_ERROR;
168         res_unload(pResData);
169         return;
170     }
171 
172     /* currently, we accept only resources that have a Table as their roots */
173     rootType=(UResType)RES_GET_TYPE(pResData->rootRes);
174     if(!URES_IS_TABLE(rootType)) {
175         *errorCode=U_INVALID_FORMAT_ERROR;
176         res_unload(pResData);
177         return;
178     }
179 
180     if(formatVersion[0]==1 && formatVersion[1]==0) {
181         pResData->localKeyLimit=0x10000;  /* greater than any 16-bit key string offset */
182     } else {
183         /* bundles with formatVersion 1.1 and later contain an indexes[] array */
184         const int32_t *indexes=pResData->pRoot+1;
185         int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff;
186         if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
187             *errorCode=U_INVALID_FORMAT_ERROR;
188             res_unload(pResData);
189             return;
190         }
191         if( length>=0 &&
192             (length<((1+indexLength)<<2) ||
193              length<(indexes[URES_INDEX_BUNDLE_TOP]<<2))
194         ) {
195             *errorCode=U_INVALID_FORMAT_ERROR;
196             res_unload(pResData);
197             return;
198         }
199         if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) {
200             pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2;
201         }
202         if(formatVersion[0]>=3) {
203             // In formatVersion 1, the indexLength took up this whole int.
204             // In version 2, bits 31..8 were reserved and always 0.
205             // In version 3, they contain bits 23..0 of the poolStringIndexLimit.
206             // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12.
207             pResData->poolStringIndexLimit=(int32_t)((uint32_t)indexes[URES_INDEX_LENGTH]>>8);
208         }
209         if(indexLength>URES_INDEX_ATTRIBUTES) {
210             int32_t att=indexes[URES_INDEX_ATTRIBUTES];
211             pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK);
212             pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0);
213             pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0);
214             pResData->poolStringIndexLimit|=(att&0xf000)<<12;  // bits 15..12 -> 27..24
215             pResData->poolStringIndex16Limit=(int32_t)((uint32_t)att>>16);
216         }
217         if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength<=URES_INDEX_POOL_CHECKSUM) {
218             *errorCode=U_INVALID_FORMAT_ERROR;
219             res_unload(pResData);
220             return;
221         }
222         if( indexLength>URES_INDEX_16BIT_TOP &&
223             indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP]
224         ) {
225             pResData->p16BitUnits=(const uint16_t *)(pResData->pRoot+indexes[URES_INDEX_KEYS_TOP]);
226         }
227     }
228 
229     if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) {
230         /*
231          * formatVersion 1: compare key strings in native-charset order
232          * formatVersion 2 and up: compare key strings in ASCII order
233          */
234         pResData->useNativeStrcmp=TRUE;
235     }
236 }
237 
238 U_CAPI void U_EXPORT2
res_read(ResourceData * pResData,const UDataInfo * pInfo,const void * inBytes,int32_t length,UErrorCode * errorCode)239 res_read(ResourceData *pResData,
240          const UDataInfo *pInfo, const void *inBytes, int32_t length,
241          UErrorCode *errorCode) {
242     UVersionInfo formatVersion;
243 
244     uprv_memset(pResData, 0, sizeof(ResourceData));
245     if(U_FAILURE(*errorCode)) {
246         return;
247     }
248     if(!isAcceptable(formatVersion, NULL, NULL, pInfo)) {
249         *errorCode=U_INVALID_FORMAT_ERROR;
250         return;
251     }
252     res_init(pResData, formatVersion, inBytes, length, errorCode);
253 }
254 
255 U_CFUNC void
res_load(ResourceData * pResData,const char * path,const char * name,UErrorCode * errorCode)256 res_load(ResourceData *pResData,
257          const char *path, const char *name, UErrorCode *errorCode) {
258     UVersionInfo formatVersion;
259 
260     uprv_memset(pResData, 0, sizeof(ResourceData));
261 
262     /* load the ResourceBundle file */
263     pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVersion, errorCode);
264     if(U_FAILURE(*errorCode)) {
265         return;
266     }
267 
268     /* get its memory and initialize *pResData */
269     res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, errorCode);
270 }
271 
272 U_CFUNC void
res_unload(ResourceData * pResData)273 res_unload(ResourceData *pResData) {
274     if(pResData->data!=NULL) {
275         udata_close(pResData->data);
276         pResData->data=NULL;
277     }
278 }
279 
280 static const int8_t gPublicTypes[URES_LIMIT] = {
281     URES_STRING,
282     URES_BINARY,
283     URES_TABLE,
284     URES_ALIAS,
285 
286     URES_TABLE,     /* URES_TABLE32 */
287     URES_TABLE,     /* URES_TABLE16 */
288     URES_STRING,    /* URES_STRING_V2 */
289     URES_INT,
290 
291     URES_ARRAY,
292     URES_ARRAY,     /* URES_ARRAY16 */
293     URES_NONE,
294     URES_NONE,
295 
296     URES_NONE,
297     URES_NONE,
298     URES_INT_VECTOR,
299     URES_NONE
300 };
301 
302 U_CAPI UResType U_EXPORT2
res_getPublicType(Resource res)303 res_getPublicType(Resource res) {
304     return (UResType)gPublicTypes[RES_GET_TYPE(res)];
305 }
306 
307 U_CAPI const UChar * U_EXPORT2
res_getString(const ResourceData * pResData,Resource res,int32_t * pLength)308 res_getString(const ResourceData *pResData, Resource res, int32_t *pLength) {
309     const UChar *p;
310     uint32_t offset=RES_GET_OFFSET(res);
311     int32_t length;
312     if(RES_GET_TYPE(res)==URES_STRING_V2) {
313         int32_t first;
314         if((int32_t)offset<pResData->poolStringIndexLimit) {
315             p=(const UChar *)pResData->poolBundleStrings+offset;
316         } else {
317             p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit);
318         }
319         first=*p;
320         if(!U16_IS_TRAIL(first)) {
321             length=u_strlen(p);
322         } else if(first<0xdfef) {
323             length=first&0x3ff;
324             ++p;
325         } else if(first<0xdfff) {
326             length=((first-0xdfef)<<16)|p[1];
327             p+=2;
328         } else {
329             length=((int32_t)p[1]<<16)|p[2];
330             p+=3;
331         }
332     } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ {
333         const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res;
334         length=*p32++;
335         p=(const UChar *)p32;
336     } else {
337         p=NULL;
338         length=0;
339     }
340     if(pLength) {
341         *pLength=length;
342     }
343     return p;
344 }
345 
346 namespace {
347 
348 /**
349  * CLDR string value (three empty-set symbols)=={2205, 2205, 2205}
350  * prevents fallback to the parent bundle.
351  * TODO: combine with other code that handles this marker, use EMPTY_SET constant.
352  * TODO: maybe move to uresbund.cpp?
353  */
isNoInheritanceMarker(const ResourceData * pResData,Resource res)354 UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) {
355     uint32_t offset=RES_GET_OFFSET(res);
356     if (offset == 0) {
357         // empty string
358     } else if (res == offset) {
359         const int32_t *p32=pResData->pRoot+res;
360         int32_t length=*p32;
361         const UChar *p=(const UChar *)p32;
362         return length == 3 && p[2] == 0x2205 && p[3] == 0x2205 && p[4] == 0x2205;
363     } else if (RES_GET_TYPE(res) == URES_STRING_V2) {
364         const UChar *p;
365         if((int32_t)offset<pResData->poolStringIndexLimit) {
366             p=(const UChar *)pResData->poolBundleStrings+offset;
367         } else {
368             p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit);
369         }
370         int32_t first=*p;
371         if (first == 0x2205) {  // implicit length
372             return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0;
373         } else if (first == 0xdc03) {  // explicit length 3 (should not occur)
374             return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0x2205;
375         } else {
376             // Assume that the string has not been stored with more length units than necessary.
377             return FALSE;
378         }
379     }
380     return FALSE;
381 }
382 
383 }  // namespace
384 
385 U_CAPI const UChar * U_EXPORT2
res_getAlias(const ResourceData * pResData,Resource res,int32_t * pLength)386 res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) {
387     const UChar *p;
388     uint32_t offset=RES_GET_OFFSET(res);
389     int32_t length;
390     if(RES_GET_TYPE(res)==URES_ALIAS) {
391         const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+offset;
392         length=*p32++;
393         p=(const UChar *)p32;
394     } else {
395         p=NULL;
396         length=0;
397     }
398     if(pLength) {
399         *pLength=length;
400     }
401     return p;
402 }
403 
404 U_CAPI const uint8_t * U_EXPORT2
res_getBinary(const ResourceData * pResData,Resource res,int32_t * pLength)405 res_getBinary(const ResourceData *pResData, Resource res, int32_t *pLength) {
406     const uint8_t *p;
407     uint32_t offset=RES_GET_OFFSET(res);
408     int32_t length;
409     if(RES_GET_TYPE(res)==URES_BINARY) {
410         const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->pRoot+offset;
411         length=*p32++;
412         p=(const uint8_t *)p32;
413     } else {
414         p=NULL;
415         length=0;
416     }
417     if(pLength) {
418         *pLength=length;
419     }
420     return p;
421 }
422 
423 
424 U_CAPI const int32_t * U_EXPORT2
res_getIntVector(const ResourceData * pResData,Resource res,int32_t * pLength)425 res_getIntVector(const ResourceData *pResData, Resource res, int32_t *pLength) {
426     const int32_t *p;
427     uint32_t offset=RES_GET_OFFSET(res);
428     int32_t length;
429     if(RES_GET_TYPE(res)==URES_INT_VECTOR) {
430         p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset;
431         length=*p++;
432     } else {
433         p=NULL;
434         length=0;
435     }
436     if(pLength) {
437         *pLength=length;
438     }
439     return p;
440 }
441 
442 U_CAPI int32_t U_EXPORT2
res_countArrayItems(const ResourceData * pResData,Resource res)443 res_countArrayItems(const ResourceData *pResData, Resource res) {
444     uint32_t offset=RES_GET_OFFSET(res);
445     switch(RES_GET_TYPE(res)) {
446     case URES_STRING:
447     case URES_STRING_V2:
448     case URES_BINARY:
449     case URES_ALIAS:
450     case URES_INT:
451     case URES_INT_VECTOR:
452         return 1;
453     case URES_ARRAY:
454     case URES_TABLE32:
455         return offset==0 ? 0 : *(pResData->pRoot+offset);
456     case URES_TABLE:
457         return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset));
458     case URES_ARRAY16:
459     case URES_TABLE16:
460         return pResData->p16BitUnits[offset];
461     default:
462         return 0;
463     }
464 }
465 
466 namespace {
467 
getArrayLength(const ResourceData * pResData,Resource res)468 int32_t getArrayLength(const ResourceData *pResData, Resource res) {
469     uint32_t offset=RES_GET_OFFSET(res);
470     if(offset == 0) {
471         return 0;
472     }
473     int32_t type = RES_GET_TYPE(res);
474     if(type == URES_ARRAY) {
475         return *(pResData->pRoot+offset);
476     } else if(type == URES_ARRAY16) {
477         return pResData->p16BitUnits[offset];
478     } else {
479         return 0;
480     }
481 }
482 
getTableLength(const ResourceData * pResData,Resource res)483 int32_t getTableLength(const ResourceData *pResData, Resource res) {
484     uint32_t offset=RES_GET_OFFSET(res);
485     if(offset == 0) {
486         return 0;
487     }
488     int32_t type = RES_GET_TYPE(res);
489     if(type == URES_TABLE) {
490         return *((const uint16_t *)(pResData->pRoot+offset));
491     } else if(type == URES_TABLE16) {
492         return pResData->p16BitUnits[offset];
493     } else if(type == URES_TABLE32) {
494         return *(pResData->pRoot+offset);
495     } else {
496         return 0;
497     }
498 }
499 
500 }  // namespace
501 
502 U_NAMESPACE_BEGIN
503 
~ResourceDataValue()504 ResourceDataValue::~ResourceDataValue() {}
505 
getType() const506 UResType ResourceDataValue::getType() const {
507     return res_getPublicType(res);
508 }
509 
getString(int32_t & length,UErrorCode & errorCode) const510 const UChar *ResourceDataValue::getString(int32_t &length, UErrorCode &errorCode) const {
511     if(U_FAILURE(errorCode)) {
512         return NULL;
513     }
514     const UChar *s = res_getString(pResData, res, &length);
515     if(s == NULL) {
516         errorCode = U_RESOURCE_TYPE_MISMATCH;
517     }
518     return s;
519 }
520 
getAliasString(int32_t & length,UErrorCode & errorCode) const521 const UChar *ResourceDataValue::getAliasString(int32_t &length, UErrorCode &errorCode) const {
522     if(U_FAILURE(errorCode)) {
523         return NULL;
524     }
525     const UChar *s = res_getAlias(pResData, res, &length);
526     if(s == NULL) {
527         errorCode = U_RESOURCE_TYPE_MISMATCH;
528     }
529     return s;
530 }
531 
getInt(UErrorCode & errorCode) const532 int32_t ResourceDataValue::getInt(UErrorCode &errorCode) const {
533     if(U_FAILURE(errorCode)) {
534         return 0;
535     }
536     if(RES_GET_TYPE(res) != URES_INT) {
537         errorCode = U_RESOURCE_TYPE_MISMATCH;
538     }
539     return RES_GET_INT(res);
540 }
541 
getUInt(UErrorCode & errorCode) const542 uint32_t ResourceDataValue::getUInt(UErrorCode &errorCode) const {
543     if(U_FAILURE(errorCode)) {
544         return 0;
545     }
546     if(RES_GET_TYPE(res) != URES_INT) {
547         errorCode = U_RESOURCE_TYPE_MISMATCH;
548     }
549     return RES_GET_UINT(res);
550 }
551 
getIntVector(int32_t & length,UErrorCode & errorCode) const552 const int32_t *ResourceDataValue::getIntVector(int32_t &length, UErrorCode &errorCode) const {
553     if(U_FAILURE(errorCode)) {
554         return NULL;
555     }
556     const int32_t *iv = res_getIntVector(pResData, res, &length);
557     if(iv == NULL) {
558         errorCode = U_RESOURCE_TYPE_MISMATCH;
559     }
560     return iv;
561 }
562 
getBinary(int32_t & length,UErrorCode & errorCode) const563 const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorCode) const {
564     if(U_FAILURE(errorCode)) {
565         return NULL;
566     }
567     const uint8_t *b = res_getBinary(pResData, res, &length);
568     if(b == NULL) {
569         errorCode = U_RESOURCE_TYPE_MISMATCH;
570     }
571     return b;
572 }
573 
574 U_NAMESPACE_END
575 
576 static Resource
makeResourceFrom16(const ResourceData * pResData,int32_t res16)577 makeResourceFrom16(const ResourceData *pResData, int32_t res16) {
578     if(res16<pResData->poolStringIndex16Limit) {
579         // Pool string, nothing to do.
580     } else {
581         // Local string, adjust the 16-bit offset to a regular one,
582         // with a larger pool string index limit.
583         res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexLimit;
584     }
585     return URES_MAKE_RESOURCE(URES_STRING_V2, res16);
586 }
587 
588 U_CAPI Resource U_EXPORT2
res_getTableItemByKey(const ResourceData * pResData,Resource table,int32_t * indexR,const char ** key)589 res_getTableItemByKey(const ResourceData *pResData, Resource table,
590                       int32_t *indexR, const char **key) {
591     uint32_t offset=RES_GET_OFFSET(table);
592     int32_t length;
593     int32_t idx;
594     if(key == NULL || *key == NULL) {
595         return RES_BOGUS;
596     }
597     switch(RES_GET_TYPE(table)) {
598     case URES_TABLE: {
599         if (offset!=0) { /* empty if offset==0 */
600             const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
601             length=*p++;
602             *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
603             if(idx>=0) {
604                 const Resource *p32=(const Resource *)(p+length+(~length&1));
605                 return p32[idx];
606             }
607         }
608         break;
609     }
610     case URES_TABLE16: {
611         const uint16_t *p=pResData->p16BitUnits+offset;
612         length=*p++;
613         *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
614         if(idx>=0) {
615             return makeResourceFrom16(pResData, p[length+idx]);
616         }
617         break;
618     }
619     case URES_TABLE32: {
620         if (offset!=0) { /* empty if offset==0 */
621             const int32_t *p= pResData->pRoot+offset;
622             length=*p++;
623             *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key);
624             if(idx>=0) {
625                 return (Resource)p[length+idx];
626             }
627         }
628         break;
629     }
630     default:
631         break;
632     }
633     return RES_BOGUS;
634 }
635 
636 U_CAPI Resource U_EXPORT2
res_getTableItemByIndex(const ResourceData * pResData,Resource table,int32_t indexR,const char ** key)637 res_getTableItemByIndex(const ResourceData *pResData, Resource table,
638                         int32_t indexR, const char **key) {
639     uint32_t offset=RES_GET_OFFSET(table);
640     int32_t length;
641     U_ASSERT(indexR>=0); /* to ensure the index is not negative */
642     switch(RES_GET_TYPE(table)) {
643     case URES_TABLE: {
644         if (offset != 0) { /* empty if offset==0 */
645             const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
646             length=*p++;
647             if(indexR<length) {
648                 const Resource *p32=(const Resource *)(p+length+(~length&1));
649                 if(key!=NULL) {
650                     *key=RES_GET_KEY16(pResData, p[indexR]);
651                 }
652                 return p32[indexR];
653             }
654         }
655         break;
656     }
657     case URES_TABLE16: {
658         const uint16_t *p=pResData->p16BitUnits+offset;
659         length=*p++;
660         if(indexR<length) {
661             if(key!=NULL) {
662                 *key=RES_GET_KEY16(pResData, p[indexR]);
663             }
664             return makeResourceFrom16(pResData, p[length+indexR]);
665         }
666         break;
667     }
668     case URES_TABLE32: {
669         if (offset != 0) { /* empty if offset==0 */
670             const int32_t *p= pResData->pRoot+offset;
671             length=*p++;
672             if(indexR<length) {
673                 if(key!=NULL) {
674                     *key=RES_GET_KEY32(pResData, p[indexR]);
675                 }
676                 return (Resource)p[length+indexR];
677             }
678         }
679         break;
680     }
681     default:
682         break;
683     }
684     return RES_BOGUS;
685 }
686 
687 U_CAPI Resource U_EXPORT2
res_getResource(const ResourceData * pResData,const char * key)688 res_getResource(const ResourceData *pResData, const char *key) {
689     const char *realKey=key;
690     int32_t idx;
691     return res_getTableItemByKey(pResData, pResData->rootRes, &idx, &realKey);
692 }
693 
694 // TODO: Ported from Java, but enumerating at this low level may prevent us
695 // from doing necessary things, like resolving aliases,
696 // which need access to higher-level UResourceBundle code.
697 // Consider porting the low-level Container/Array/Table classes from Java,
698 // with getters for keys and values,
699 // and doing the enumeration in the higher-level code on top of those accessors.
700 U_CFUNC void
ures_getAllTableItems(const ResourceData * pResData,Resource table,icu::ResourceDataValue & value,icu::ResourceTableSink & sink,UErrorCode & errorCode)701 ures_getAllTableItems(const ResourceData *pResData, Resource table,
702                       icu::ResourceDataValue &value, icu::ResourceTableSink &sink,
703                       UErrorCode &errorCode) {
704     if(U_FAILURE(errorCode)) { return; }
705     const uint16_t *keys16 = NULL;
706     const int32_t *keys32 = NULL;
707     const uint16_t *items16 = NULL;
708     const Resource *items32 = NULL;
709     uint32_t offset = RES_GET_OFFSET(table);
710     int32_t length = 0;
711     switch(RES_GET_TYPE(table)) {
712     case URES_TABLE: {
713         if (offset != 0) { /* empty if offset==0 */
714             keys16 = (const uint16_t *)(pResData->pRoot+offset);
715             length = *keys16++;
716             items32 = (const Resource *)(keys16+length+(~length&1));
717         }
718         break;
719     }
720     case URES_TABLE16: {
721         keys16 = pResData->p16BitUnits+offset;
722         length = *keys16++;
723         items16 = keys16 + length;
724         break;
725     }
726     case URES_TABLE32: {
727         if (offset != 0) { /* empty if offset==0 */
728             keys32 = pResData->pRoot+offset;
729             length = *keys32++;
730             items32 = (const Resource *)keys32 + length;
731         }
732         break;
733     }
734     default:
735         errorCode = U_RESOURCE_TYPE_MISMATCH;
736         return;
737     }
738 
739     for (int32_t i = 0; i < length; ++i) {
740         const char *key;
741         if (keys16 != NULL) {
742             key=RES_GET_KEY16(pResData, keys16[i]);
743         } else {
744             key=RES_GET_KEY32(pResData, keys32[i]);
745         }
746         Resource res;
747         if (items16 != NULL) {
748             res = makeResourceFrom16(pResData, items16[i]);
749         } else {
750             res = items32[i];
751         }
752         int32_t type = RES_GET_TYPE(res);
753         if (URES_IS_ARRAY(type)) {
754             int32_t numItems = getArrayLength(pResData, res);
755             icu::ResourceArraySink *subSink = sink.getOrCreateArraySink(key, numItems, errorCode);
756             if (subSink != NULL) {
757                 ures_getAllArrayItems(pResData, res, value, *subSink, errorCode);
758             }
759         } else if (URES_IS_TABLE(type)) {
760             int32_t numItems = getTableLength(pResData, res);
761             icu::ResourceTableSink *subSink = sink.getOrCreateTableSink(key, numItems, errorCode);
762             if (subSink != NULL) {
763                 ures_getAllTableItems(pResData, res, value, *subSink, errorCode);
764             }
765         /* TODO: settle on how to deal with aliases, port to Java
766         } else if (type == URES_ALIAS) {
767             // aliases not handled in resource enumeration
768             errorCode = U_UNSUPPORTED_ERROR;
769             return; */
770         } else if (isNoInheritanceMarker(pResData, res)) {
771             sink.putNoFallback(key, errorCode);
772         } else {
773             value.setResource(res);
774             sink.put(key, value, errorCode);
775         }
776         if(U_FAILURE(errorCode)) { return; }
777     }
778     sink.leave(errorCode);
779 }
780 
781 U_CAPI Resource U_EXPORT2
res_getArrayItem(const ResourceData * pResData,Resource array,int32_t indexR)782 res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) {
783     uint32_t offset=RES_GET_OFFSET(array);
784     U_ASSERT(indexR>=0); /* to ensure the index is not negative */
785     switch(RES_GET_TYPE(array)) {
786     case URES_ARRAY: {
787         if (offset!=0) { /* empty if offset==0 */
788             const int32_t *p= pResData->pRoot+offset;
789             if(indexR<*p) {
790                 return (Resource)p[1+indexR];
791             }
792         }
793         break;
794     }
795     case URES_ARRAY16: {
796         const uint16_t *p=pResData->p16BitUnits+offset;
797         if(indexR<*p) {
798             return makeResourceFrom16(pResData, p[1+indexR]);
799         }
800         break;
801     }
802     default:
803         break;
804     }
805     return RES_BOGUS;
806 }
807 
808 U_CFUNC void
ures_getAllArrayItems(const ResourceData * pResData,Resource array,icu::ResourceDataValue & value,icu::ResourceArraySink & sink,UErrorCode & errorCode)809 ures_getAllArrayItems(const ResourceData *pResData, Resource array,
810                       icu::ResourceDataValue &value, icu::ResourceArraySink &sink,
811                       UErrorCode &errorCode) {
812     if(U_FAILURE(errorCode)) { return; }
813     const uint16_t *items16 = NULL;
814     const Resource *items32 = NULL;
815     uint32_t offset=RES_GET_OFFSET(array);
816     int32_t length = 0;
817     switch(RES_GET_TYPE(array)) {
818     case URES_ARRAY: {
819         if (offset!=0) { /* empty if offset==0 */
820             items32 = (const Resource *)pResData->pRoot+offset;
821             length = *items32++;
822         }
823         break;
824     }
825     case URES_ARRAY16: {
826         items16 = pResData->p16BitUnits+offset;
827         length = *items16++;
828         break;
829     }
830     default:
831         errorCode = U_RESOURCE_TYPE_MISMATCH;
832         return;
833     }
834 
835     for (int32_t i = 0; i < length; ++i) {
836         Resource res;
837         if (items16 != NULL) {
838             res = makeResourceFrom16(pResData, items16[i]);
839         } else {
840             res = items32[i];
841         }
842         int32_t type = RES_GET_TYPE(res);
843         if (URES_IS_ARRAY(type)) {
844             int32_t numItems = getArrayLength(pResData, res);
845             icu::ResourceArraySink *subSink = sink.getOrCreateArraySink(i, numItems, errorCode);
846             if (subSink != NULL) {
847                 ures_getAllArrayItems(pResData, res, value, *subSink, errorCode);
848             }
849         } else if (URES_IS_TABLE(type)) {
850             int32_t numItems = getTableLength(pResData, res);
851             icu::ResourceTableSink *subSink = sink.getOrCreateTableSink(i, numItems, errorCode);
852             if (subSink != NULL) {
853                 ures_getAllTableItems(pResData, res, value, *subSink, errorCode);
854             }
855         /* TODO: settle on how to deal with aliases, port to Java
856         } else if (type == URES_ALIAS) {
857             // aliases not handled in resource enumeration
858             errorCode = U_UNSUPPORTED_ERROR;
859             return; */
860         } else {
861             value.setResource(res);
862             sink.put(i, value, errorCode);
863         }
864         if(U_FAILURE(errorCode)) { return; }
865     }
866     sink.leave(errorCode);
867 }
868 
869 U_CFUNC Resource
res_findResource(const ResourceData * pResData,Resource r,char ** path,const char ** key)870 res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) {
871   char *pathP = *path, *nextSepP = *path;
872   char *closeIndex = NULL;
873   Resource t1 = r;
874   Resource t2;
875   int32_t indexR = 0;
876   UResType type = (UResType)RES_GET_TYPE(t1);
877 
878   /* if you come in with an empty path, you'll be getting back the same resource */
879   if(!uprv_strlen(pathP)) {
880       return r;
881   }
882 
883   /* one needs to have an aggregate resource in order to search in it */
884   if(!URES_IS_CONTAINER(type)) {
885       return RES_BOGUS;
886   }
887 
888   while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) {
889     /* Iteration stops if: the path has been consumed, we found a non-existing
890      * resource (t1 == RES_BOGUS) or we found a scalar resource (including alias)
891      */
892     nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR);
893     /* if there are more separators, terminate string
894      * and set path to the remaining part of the string
895      */
896     if(nextSepP != NULL) {
897       if(nextSepP == pathP) {
898         // Empty key string.
899         return RES_BOGUS;
900       }
901       *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */
902       *path = nextSepP+1;
903     } else {
904       *path = uprv_strchr(pathP, 0);
905     }
906 
907     /* if the resource is a table */
908     /* try the key based access */
909     if(URES_IS_TABLE(type)) {
910       *key = pathP;
911       t2 = res_getTableItemByKey(pResData, t1, &indexR, key);
912       if(t2 == RES_BOGUS) {
913         /* if we fail to get the resource by key, maybe we got an index */
914         indexR = uprv_strtol(pathP, &closeIndex, 10);
915         if(*closeIndex == 0) {
916           /* if we indeed have an index, try to get the item by index */
917           t2 = res_getTableItemByIndex(pResData, t1, indexR, key);
918         }
919       }
920     } else if(URES_IS_ARRAY(type)) {
921       indexR = uprv_strtol(pathP, &closeIndex, 10);
922       if(*closeIndex == 0) {
923         t2 = res_getArrayItem(pResData, t1, indexR);
924       } else {
925         t2 = RES_BOGUS; /* have an array, but don't have a valid index */
926       }
927       *key = NULL;
928     } else { /* can't do much here, except setting t2 to bogus */
929       t2 = RES_BOGUS;
930     }
931     t1 = t2;
932     type = (UResType)RES_GET_TYPE(t1);
933     /* position pathP to next resource key/index */
934     pathP = *path;
935   }
936 
937   return t1;
938 }
939 
940 /* resource bundle swapping ------------------------------------------------- */
941 
942 /*
943  * Need to always enumerate the entire item tree,
944  * track the lowest address of any item to use as the limit for char keys[],
945  * track the highest address of any item to return the size of the data.
946  *
947  * We should have thought of storing those in the data...
948  * It is possible to extend the data structure by putting additional values
949  * in places that are inaccessible by ordinary enumeration of the item tree.
950  * For example, additional integers could be stored at the beginning or
951  * end of the key strings; this could be indicated by a minor version number,
952  * and the data swapping would have to know about these values.
953  *
954  * The data structure does not forbid keys to be shared, so we must swap
955  * all keys once instead of each key when it is referenced.
956  *
957  * These swapping functions assume that a resource bundle always has a length
958  * that is a multiple of 4 bytes.
959  * Currently, this is trivially true because genrb writes bundle tree leaves
960  * physically first, before their branches, so that the root table with its
961  * array of resource items (uint32_t values) is always last.
962  */
963 
964 /* definitions for table sorting ------------------------ */
965 
966 /*
967  * row of a temporary array
968  *
969  * gets platform-endian key string indexes and sorting indexes;
970  * after sorting this array by keys, the actual key/value arrays are permutated
971  * according to the sorting indexes
972  */
973 typedef struct Row {
974     int32_t keyIndex, sortIndex;
975 } Row;
976 
977 static int32_t
ures_compareRows(const void * context,const void * left,const void * right)978 ures_compareRows(const void *context, const void *left, const void *right) {
979     const char *keyChars=(const char *)context;
980     return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex,
981                                 keyChars+((const Row *)right)->keyIndex);
982 }
983 
984 typedef struct TempTable {
985     const char *keyChars;
986     Row *rows;
987     int32_t *resort;
988     uint32_t *resFlags;
989     int32_t localKeyLimit;
990     uint8_t majorFormatVersion;
991 } TempTable;
992 
993 enum {
994     STACK_ROW_CAPACITY=200
995 };
996 
997 /* The table item key string is not locally available. */
998 static const char *const gUnknownKey="";
999 
1000 /* resource table key for collation binaries: "%%CollationBin" */
1001 static const UChar gCollationBinKey[]={
1002     0x25, 0x25,
1003     0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
1004     0x42, 0x69, 0x6e,
1005     0
1006 };
1007 
1008 /*
1009  * swap one resource item
1010  */
1011 static void
ures_swapResource(const UDataSwapper * ds,const Resource * inBundle,Resource * outBundle,Resource res,const char * key,TempTable * pTempTable,UErrorCode * pErrorCode)1012 ures_swapResource(const UDataSwapper *ds,
1013                   const Resource *inBundle, Resource *outBundle,
1014                   Resource res, /* caller swaps res itself */
1015                   const char *key,
1016                   TempTable *pTempTable,
1017                   UErrorCode *pErrorCode) {
1018     const Resource *p;
1019     Resource *q;
1020     int32_t offset, count;
1021 
1022     switch(RES_GET_TYPE(res)) {
1023     case URES_TABLE16:
1024     case URES_STRING_V2:
1025     case URES_INT:
1026     case URES_ARRAY16:
1027         /* integer, or points to 16-bit units, nothing to do here */
1028         return;
1029     default:
1030         break;
1031     }
1032 
1033     /* all other types use an offset to point to their data */
1034     offset=(int32_t)RES_GET_OFFSET(res);
1035     if(offset==0) {
1036         /* special offset indicating an empty item */
1037         return;
1038     }
1039     if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) {
1040         /* we already swapped this resource item */
1041         return;
1042     } else {
1043         /* mark it as swapped now */
1044         pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f));
1045     }
1046 
1047     p=inBundle+offset;
1048     q=outBundle+offset;
1049 
1050     switch(RES_GET_TYPE(res)) {
1051     case URES_ALIAS:
1052         /* physically same value layout as string, fall through */
1053     case URES_STRING:
1054         count=udata_readInt32(ds, (int32_t)*p);
1055         /* swap length */
1056         ds->swapArray32(ds, p, 4, q, pErrorCode);
1057         /* swap each UChar (the terminating NUL would not change) */
1058         ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode);
1059         break;
1060     case URES_BINARY:
1061         count=udata_readInt32(ds, (int32_t)*p);
1062         /* swap length */
1063         ds->swapArray32(ds, p, 4, q, pErrorCode);
1064         /* no need to swap or copy bytes - ures_swap() copied them all */
1065 
1066         /* swap known formats */
1067 #if !UCONFIG_NO_COLLATION
1068         if( key!=NULL &&  /* the binary is in a table */
1069             (key!=gUnknownKey ?
1070                 /* its table key string is "%%CollationBin" */
1071                 0==ds->compareInvChars(ds, key, -1,
1072                                        gCollationBinKey, UPRV_LENGTHOF(gCollationBinKey)-1) :
1073                 /* its table key string is unknown but it looks like a collation binary */
1074                 ucol_looksLikeCollationBinary(ds, p+1, count))
1075         ) {
1076             ucol_swap(ds, p+1, count, q+1, pErrorCode);
1077         }
1078 #endif
1079         break;
1080     case URES_TABLE:
1081     case URES_TABLE32:
1082         {
1083             const uint16_t *pKey16;
1084             uint16_t *qKey16;
1085 
1086             const int32_t *pKey32;
1087             int32_t *qKey32;
1088 
1089             Resource item;
1090             int32_t i, oldIndex;
1091 
1092             if(RES_GET_TYPE(res)==URES_TABLE) {
1093                 /* get table item count */
1094                 pKey16=(const uint16_t *)p;
1095                 qKey16=(uint16_t *)q;
1096                 count=ds->readUInt16(*pKey16);
1097 
1098                 pKey32=qKey32=NULL;
1099 
1100                 /* swap count */
1101                 ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode);
1102 
1103                 offset+=((1+count)+1)/2;
1104             } else {
1105                 /* get table item count */
1106                 pKey32=(const int32_t *)p;
1107                 qKey32=(int32_t *)q;
1108                 count=udata_readInt32(ds, *pKey32);
1109 
1110                 pKey16=qKey16=NULL;
1111 
1112                 /* swap count */
1113                 ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode);
1114 
1115                 offset+=1+count;
1116             }
1117 
1118             if(count==0) {
1119                 break;
1120             }
1121 
1122             p=inBundle+offset; /* pointer to table resources */
1123             q=outBundle+offset;
1124 
1125             /* recurse */
1126             for(i=0; i<count; ++i) {
1127                 const char *itemKey=gUnknownKey;
1128                 if(pKey16!=NULL) {
1129                     int32_t keyOffset=ds->readUInt16(pKey16[i]);
1130                     if(keyOffset<pTempTable->localKeyLimit) {
1131                         itemKey=(const char *)outBundle+keyOffset;
1132                     }
1133                 } else {
1134                     int32_t keyOffset=udata_readInt32(ds, pKey32[i]);
1135                     if(keyOffset>=0) {
1136                         itemKey=(const char *)outBundle+keyOffset;
1137                     }
1138                 }
1139                 item=ds->readUInt32(p[i]);
1140                 ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode);
1141                 if(U_FAILURE(*pErrorCode)) {
1142                     udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n",
1143                                      res, i, item);
1144                     return;
1145                 }
1146             }
1147 
1148             if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) {
1149                 /* no need to sort, just swap the offset/value arrays */
1150                 if(pKey16!=NULL) {
1151                     ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode);
1152                     ds->swapArray32(ds, p, count*4, q, pErrorCode);
1153                 } else {
1154                     /* swap key offsets and items as one array */
1155                     ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode);
1156                 }
1157                 break;
1158             }
1159 
1160             /*
1161              * We need to sort tables by outCharset key strings because they
1162              * sort differently for different charset families.
1163              * ures_swap() already set pTempTable->keyChars appropriately.
1164              * First we set up a temporary table with the key indexes and
1165              * sorting indexes and sort that.
1166              * Then we permutate and copy/swap the actual values.
1167              */
1168             if(pKey16!=NULL) {
1169                 for(i=0; i<count; ++i) {
1170                     pTempTable->rows[i].keyIndex=ds->readUInt16(pKey16[i]);
1171                     pTempTable->rows[i].sortIndex=i;
1172                 }
1173             } else {
1174                 for(i=0; i<count; ++i) {
1175                     pTempTable->rows[i].keyIndex=udata_readInt32(ds, pKey32[i]);
1176                     pTempTable->rows[i].sortIndex=i;
1177                 }
1178             }
1179             uprv_sortArray(pTempTable->rows, count, sizeof(Row),
1180                            ures_compareRows, pTempTable->keyChars,
1181                            FALSE, pErrorCode);
1182             if(U_FAILURE(*pErrorCode)) {
1183                 udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n",
1184                                  res, count);
1185                 return;
1186             }
1187 
1188             /*
1189              * copy/swap/permutate items
1190              *
1191              * If we swap in-place, then the permutation must use another
1192              * temporary array (pTempTable->resort)
1193              * before the results are copied to the outBundle.
1194              */
1195             /* keys */
1196             if(pKey16!=NULL) {
1197                 uint16_t *rKey16;
1198 
1199                 if(pKey16!=qKey16) {
1200                     rKey16=qKey16;
1201                 } else {
1202                     rKey16=(uint16_t *)pTempTable->resort;
1203                 }
1204                 for(i=0; i<count; ++i) {
1205                     oldIndex=pTempTable->rows[i].sortIndex;
1206                     ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode);
1207                 }
1208                 if(qKey16!=rKey16) {
1209                     uprv_memcpy(qKey16, rKey16, 2*count);
1210                 }
1211             } else {
1212                 int32_t *rKey32;
1213 
1214                 if(pKey32!=qKey32) {
1215                     rKey32=qKey32;
1216                 } else {
1217                     rKey32=pTempTable->resort;
1218                 }
1219                 for(i=0; i<count; ++i) {
1220                     oldIndex=pTempTable->rows[i].sortIndex;
1221                     ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode);
1222                 }
1223                 if(qKey32!=rKey32) {
1224                     uprv_memcpy(qKey32, rKey32, 4*count);
1225                 }
1226             }
1227 
1228             /* resources */
1229             {
1230                 Resource *r;
1231 
1232 
1233                 if(p!=q) {
1234                     r=q;
1235                 } else {
1236                     r=(Resource *)pTempTable->resort;
1237                 }
1238                 for(i=0; i<count; ++i) {
1239                     oldIndex=pTempTable->rows[i].sortIndex;
1240                     ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode);
1241                 }
1242                 if(q!=r) {
1243                     uprv_memcpy(q, r, 4*count);
1244                 }
1245             }
1246         }
1247         break;
1248     case URES_ARRAY:
1249         {
1250             Resource item;
1251             int32_t i;
1252 
1253             count=udata_readInt32(ds, (int32_t)*p);
1254             /* swap length */
1255             ds->swapArray32(ds, p++, 4, q++, pErrorCode);
1256 
1257             /* recurse */
1258             for(i=0; i<count; ++i) {
1259                 item=ds->readUInt32(p[i]);
1260                 ures_swapResource(ds, inBundle, outBundle, item, NULL, pTempTable, pErrorCode);
1261                 if(U_FAILURE(*pErrorCode)) {
1262                     udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n",
1263                                      res, i, item);
1264                     return;
1265                 }
1266             }
1267 
1268             /* swap items */
1269             ds->swapArray32(ds, p, 4*count, q, pErrorCode);
1270         }
1271         break;
1272     case URES_INT_VECTOR:
1273         count=udata_readInt32(ds, (int32_t)*p);
1274         /* swap length and each integer */
1275         ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode);
1276         break;
1277     default:
1278         /* also catches RES_BOGUS */
1279         *pErrorCode=U_UNSUPPORTED_ERROR;
1280         break;
1281     }
1282 }
1283 
1284 U_CAPI int32_t U_EXPORT2
ures_swap(const UDataSwapper * ds,const void * inData,int32_t length,void * outData,UErrorCode * pErrorCode)1285 ures_swap(const UDataSwapper *ds,
1286           const void *inData, int32_t length, void *outData,
1287           UErrorCode *pErrorCode) {
1288     const UDataInfo *pInfo;
1289     const Resource *inBundle;
1290     Resource rootRes;
1291     int32_t headerSize, maxTableLength;
1292 
1293     Row rows[STACK_ROW_CAPACITY];
1294     int32_t resort[STACK_ROW_CAPACITY];
1295     TempTable tempTable;
1296 
1297     const int32_t *inIndexes;
1298 
1299     /* the following integers count Resource item offsets (4 bytes each), not bytes */
1300     int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top;
1301 
1302     /* udata_swapDataHeader checks the arguments */
1303     headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
1304     if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
1305         return 0;
1306     }
1307 
1308     /* check data format and format version */
1309     pInfo=(const UDataInfo *)((const char *)inData+4);
1310     if(!(
1311         pInfo->dataFormat[0]==0x52 &&   /* dataFormat="ResB" */
1312         pInfo->dataFormat[1]==0x65 &&
1313         pInfo->dataFormat[2]==0x73 &&
1314         pInfo->dataFormat[3]==0x42 &&
1315         /* formatVersion 1.1+ or 2.x or 3.x */
1316         ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) ||
1317             pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3)
1318     )) {
1319         udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n",
1320                          pInfo->dataFormat[0], pInfo->dataFormat[1],
1321                          pInfo->dataFormat[2], pInfo->dataFormat[3],
1322                          pInfo->formatVersion[0], pInfo->formatVersion[1]);
1323         *pErrorCode=U_UNSUPPORTED_ERROR;
1324         return 0;
1325     }
1326     tempTable.majorFormatVersion=pInfo->formatVersion[0];
1327 
1328     /* a resource bundle must contain at least one resource item */
1329     if(length<0) {
1330         bundleLength=-1;
1331     } else {
1332         bundleLength=(length-headerSize)/4;
1333 
1334         /* formatVersion 1.1 must have a root item and at least 5 indexes */
1335         if(bundleLength<(1+5)) {
1336             udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n",
1337                              length-headerSize);
1338             *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1339             return 0;
1340         }
1341     }
1342 
1343     inBundle=(const Resource *)((const char *)inData+headerSize);
1344     rootRes=ds->readUInt32(*inBundle);
1345 
1346     /* formatVersion 1.1 adds the indexes[] array */
1347     inIndexes=(const int32_t *)(inBundle+1);
1348 
1349     indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff;
1350     if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
1351         udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n");
1352         *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1353         return 0;
1354     }
1355     keysBottom=1+indexLength;
1356     keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]);
1357     if(indexLength>URES_INDEX_16BIT_TOP) {
1358         resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]);
1359     } else {
1360         resBottom=keysTop;
1361     }
1362     top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]);
1363     maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]);
1364 
1365     if(0<=bundleLength && bundleLength<top) {
1366         udata_printError(ds, "ures_swap(): resource top %d exceeds bundle length %d\n",
1367                          top, bundleLength);
1368         *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1369         return 0;
1370     }
1371     if(keysTop>(1+indexLength)) {
1372         tempTable.localKeyLimit=keysTop<<2;
1373     } else {
1374         tempTable.localKeyLimit=0;
1375     }
1376 
1377     if(length>=0) {
1378         Resource *outBundle=(Resource *)((char *)outData+headerSize);
1379 
1380         /* track which resources we have already swapped */
1381         uint32_t stackResFlags[STACK_ROW_CAPACITY];
1382         int32_t resFlagsLength;
1383 
1384         /*
1385          * We need one bit per 4 resource bundle bytes so that we can track
1386          * every possible Resource for whether we have swapped it already.
1387          * Multiple Resource words can refer to the same bundle offsets
1388          * for sharing identical values.
1389          * We could optimize this by allocating only for locations above
1390          * where Resource values are stored (above keys & strings).
1391          */
1392         resFlagsLength=(length+31)>>5;          /* number of bytes needed */
1393         resFlagsLength=(resFlagsLength+3)&~3;   /* multiple of 4 bytes for uint32_t */
1394         if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) {
1395             tempTable.resFlags=stackResFlags;
1396         } else {
1397             tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength);
1398             if(tempTable.resFlags==NULL) {
1399                 udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n");
1400                 *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1401                 return 0;
1402             }
1403         }
1404         uprv_memset(tempTable.resFlags, 0, resFlagsLength);
1405 
1406         /* copy the bundle for binary and inaccessible data */
1407         if(inData!=outData) {
1408             uprv_memcpy(outBundle, inBundle, 4*top);
1409         }
1410 
1411         /* swap the key strings, but not the padding bytes (0xaa) after the last string and its NUL */
1412         udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom),
1413                                     outBundle+keysBottom, pErrorCode);
1414         if(U_FAILURE(*pErrorCode)) {
1415             udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom));
1416             return 0;
1417         }
1418 
1419         /* swap the 16-bit units (strings, table16, array16) */
1420         if(keysTop<resBottom) {
1421             ds->swapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode);
1422             if(U_FAILURE(*pErrorCode)) {
1423                 udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop));
1424                 return 0;
1425             }
1426         }
1427 
1428         /* allocate the temporary table for sorting resource tables */
1429         tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */
1430         if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) {
1431             tempTable.rows=rows;
1432             tempTable.resort=resort;
1433         } else {
1434             tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4);
1435             if(tempTable.rows==NULL) {
1436                 udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n",
1437                                  maxTableLength);
1438                 *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1439                 if(tempTable.resFlags!=stackResFlags) {
1440                     uprv_free(tempTable.resFlags);
1441                 }
1442                 return 0;
1443             }
1444             tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength);
1445         }
1446 
1447         /* swap the resources */
1448         ures_swapResource(ds, inBundle, outBundle, rootRes, NULL, &tempTable, pErrorCode);
1449         if(U_FAILURE(*pErrorCode)) {
1450             udata_printError(ds, "ures_swapResource(root res=%08x) failed\n",
1451                              rootRes);
1452         }
1453 
1454         if(tempTable.rows!=rows) {
1455             uprv_free(tempTable.rows);
1456         }
1457         if(tempTable.resFlags!=stackResFlags) {
1458             uprv_free(tempTable.resFlags);
1459         }
1460 
1461         /* swap the root resource and indexes */
1462         ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode);
1463     }
1464 
1465     return headerSize+4*top;
1466 }
1467