1/*
2*******************************************************************************
3*                                                                             *
4* Copyright (C) 1999-2010, International Business Machines Corporation        *
5*               and others. All Rights Reserved.                              *
6*                                                                             *
7*******************************************************************************
8*   file name:  uresdata.c
9*   encoding:   US-ASCII
10*   tab size:   8 (not used)
11*   indentation:4
12*
13*   created on: 1999dec08
14*   created by: Markus W. Scherer
15* Modification History:
16*
17*   Date        Name        Description
18*   06/20/2000  helena      OS/400 port changes; mostly typecast.
19*   06/24/02    weiv        Added support for resource sharing
20*/
21
22#include "unicode/utypes.h"
23#include "unicode/udata.h"
24#include "unicode/ustring.h"
25#include "cmemory.h"
26#include "cstring.h"
27#include "uarrsort.h"
28#include "udataswp.h"
29#include "ucol_swp.h"
30#include "uinvchar.h"
31#include "uresdata.h"
32#include "uresimp.h"
33
34#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
35
36/*
37 * Resource access helpers
38 */
39
40/* get a const char* pointer to the key with the keyOffset byte offset from pRoot */
41#define RES_GET_KEY16(pResData, keyOffset) \
42    ((keyOffset)<(pResData)->localKeyLimit ? \
43        (const char *)(pResData)->pRoot+(keyOffset) : \
44        (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit)
45
46#define RES_GET_KEY32(pResData, keyOffset) \
47    ((keyOffset)>=0 ? \
48        (const char *)(pResData)->pRoot+(keyOffset) : \
49        (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff))
50
51#define URESDATA_ITEM_NOT_FOUND -1
52
53/* empty resources, returned when the resource offset is 0 */
54static const uint16_t gEmpty16=0;
55static const int32_t gEmpty32=0;
56static const struct {
57    int32_t length;
58    UChar nul;
59    UChar pad;
60} gEmptyString={ 0, 0, 0 };
61
62/*
63 * All the type-access functions assume that
64 * the resource is of the expected type.
65 */
66
67static int32_t
68_res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, int32_t length,
69                   const char *key, const char **realKey) {
70    const char *tableKey;
71    int32_t mid, start, limit;
72    int result;
73
74    /* do a binary search for the key */
75    start=0;
76    limit=length;
77    while(start<limit) {
78        mid = (start + limit) / 2;
79        tableKey = RES_GET_KEY16(pResData, keyOffsets[mid]);
80        if (pResData->useNativeStrcmp) {
81            result = uprv_strcmp(key, tableKey);
82        } else {
83            result = uprv_compareInvCharsAsAscii(key, tableKey);
84        }
85        if (result < 0) {
86            limit = mid;
87        } else if (result > 0) {
88            start = mid + 1;
89        } else {
90            /* We found it! */
91            *realKey=tableKey;
92            return mid;
93        }
94    }
95    return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
96}
97
98static int32_t
99_res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, int32_t length,
100                     const char *key, const char **realKey) {
101    const char *tableKey;
102    int32_t mid, start, limit;
103    int result;
104
105    /* do a binary search for the key */
106    start=0;
107    limit=length;
108    while(start<limit) {
109        mid = (start + limit) / 2;
110        tableKey = RES_GET_KEY32(pResData, keyOffsets[mid]);
111        if (pResData->useNativeStrcmp) {
112            result = uprv_strcmp(key, tableKey);
113        } else {
114            result = uprv_compareInvCharsAsAscii(key, tableKey);
115        }
116        if (result < 0) {
117            limit = mid;
118        } else if (result > 0) {
119            start = mid + 1;
120        } else {
121            /* We found it! */
122            *realKey=tableKey;
123            return mid;
124        }
125    }
126    return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
127}
128
129/* helper for res_load() ---------------------------------------------------- */
130
131static UBool U_CALLCONV
132isAcceptable(void *context,
133             const char *type, const char *name,
134             const UDataInfo *pInfo) {
135    uprv_memcpy(context, pInfo->formatVersion, 4);
136    return (UBool)(
137        pInfo->size>=20 &&
138        pInfo->isBigEndian==U_IS_BIG_ENDIAN &&
139        pInfo->charsetFamily==U_CHARSET_FAMILY &&
140        pInfo->sizeofUChar==U_SIZEOF_UCHAR &&
141        pInfo->dataFormat[0]==0x52 &&   /* dataFormat="ResB" */
142        pInfo->dataFormat[1]==0x65 &&
143        pInfo->dataFormat[2]==0x73 &&
144        pInfo->dataFormat[3]==0x42 &&
145        (pInfo->formatVersion[0]==1 || pInfo->formatVersion[0]==2));
146}
147
148/* semi-public functions ---------------------------------------------------- */
149
150static void
151res_init(ResourceData *pResData,
152         UVersionInfo formatVersion, const void *inBytes, int32_t length,
153         UErrorCode *errorCode) {
154    UResType rootType;
155
156    /* get the root resource */
157    pResData->pRoot=(const int32_t *)inBytes;
158    pResData->rootRes=(Resource)*pResData->pRoot;
159    pResData->p16BitUnits=&gEmpty16;
160
161    /* formatVersion 1.1 must have a root item and at least 5 indexes */
162    if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) {
163        *errorCode=U_INVALID_FORMAT_ERROR;
164        res_unload(pResData);
165        return;
166    }
167
168    /* currently, we accept only resources that have a Table as their roots */
169    rootType=RES_GET_TYPE(pResData->rootRes);
170    if(!URES_IS_TABLE(rootType)) {
171        *errorCode=U_INVALID_FORMAT_ERROR;
172        res_unload(pResData);
173        return;
174    }
175
176    if(formatVersion[0]==1 && formatVersion[1]==0) {
177        pResData->localKeyLimit=0x10000;  /* greater than any 16-bit key string offset */
178    } else {
179        /* bundles with formatVersion 1.1 and later contain an indexes[] array */
180        const int32_t *indexes=pResData->pRoot+1;
181        int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff;
182        if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
183            *errorCode=U_INVALID_FORMAT_ERROR;
184            res_unload(pResData);
185            return;
186        }
187        if( length>=0 &&
188            (length<((1+indexLength)<<2) ||
189             length<(indexes[URES_INDEX_BUNDLE_TOP]<<2))
190        ) {
191            *errorCode=U_INVALID_FORMAT_ERROR;
192            res_unload(pResData);
193            return;
194        }
195        if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) {
196            pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2;
197        }
198        if(indexLength>URES_INDEX_ATTRIBUTES) {
199            int32_t att=indexes[URES_INDEX_ATTRIBUTES];
200            pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK);
201            pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0);
202            pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0);
203        }
204        if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength<=URES_INDEX_POOL_CHECKSUM) {
205            *errorCode=U_INVALID_FORMAT_ERROR;
206            res_unload(pResData);
207            return;
208        }
209        if( indexLength>URES_INDEX_16BIT_TOP &&
210            indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP]
211        ) {
212            pResData->p16BitUnits=(const uint16_t *)(pResData->pRoot+indexes[URES_INDEX_KEYS_TOP]);
213        }
214    }
215
216    if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) {
217        /*
218         * formatVersion 1: compare key strings in native-charset order
219         * formatVersion 2 and up: compare key strings in ASCII order
220         */
221        pResData->useNativeStrcmp=TRUE;
222    }
223}
224
225U_CAPI void U_EXPORT2
226res_read(ResourceData *pResData,
227         const UDataInfo *pInfo, const void *inBytes, int32_t length,
228         UErrorCode *errorCode) {
229    UVersionInfo formatVersion;
230
231    uprv_memset(pResData, 0, sizeof(ResourceData));
232    if(U_FAILURE(*errorCode)) {
233        return;
234    }
235    if(!isAcceptable(formatVersion, NULL, NULL, pInfo)) {
236        *errorCode=U_INVALID_FORMAT_ERROR;
237        return;
238    }
239    res_init(pResData, formatVersion, inBytes, length, errorCode);
240}
241
242U_CFUNC void
243res_load(ResourceData *pResData,
244         const char *path, const char *name, UErrorCode *errorCode) {
245    UVersionInfo formatVersion;
246
247    uprv_memset(pResData, 0, sizeof(ResourceData));
248
249    /* load the ResourceBundle file */
250    pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVersion, errorCode);
251    if(U_FAILURE(*errorCode)) {
252        return;
253    }
254
255    /* get its memory and initialize *pResData */
256    res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, errorCode);
257}
258
259U_CFUNC void
260res_unload(ResourceData *pResData) {
261    if(pResData->data!=NULL) {
262        udata_close(pResData->data);
263        pResData->data=NULL;
264    }
265}
266
267static const int8_t gPublicTypes[URES_LIMIT] = {
268    URES_STRING,
269    URES_BINARY,
270    URES_TABLE,
271    URES_ALIAS,
272
273    URES_TABLE,     /* URES_TABLE32 */
274    URES_TABLE,     /* URES_TABLE16 */
275    URES_STRING,    /* URES_STRING_V2 */
276    URES_INT,
277
278    URES_ARRAY,
279    URES_ARRAY,     /* URES_ARRAY16 */
280    URES_NONE,
281    URES_NONE,
282
283    URES_NONE,
284    URES_NONE,
285    URES_INT_VECTOR,
286    URES_NONE
287};
288
289U_CAPI UResType U_EXPORT2
290res_getPublicType(Resource res) {
291    return (UResType)gPublicTypes[RES_GET_TYPE(res)];
292}
293
294U_CAPI const UChar * U_EXPORT2
295res_getString(const ResourceData *pResData, Resource res, int32_t *pLength) {
296    const UChar *p;
297    uint32_t offset=RES_GET_OFFSET(res);
298    int32_t length;
299    if(RES_GET_TYPE(res)==URES_STRING_V2) {
300        int32_t first;
301        p=(const UChar *)(pResData->p16BitUnits+offset);
302        first=*p;
303        if(!U16_IS_TRAIL(first)) {
304            length=u_strlen(p);
305        } else if(first<0xdfef) {
306            length=first&0x3ff;
307            ++p;
308        } else if(first<0xdfff) {
309            length=((first-0xdfef)<<16)|p[1];
310            p+=2;
311        } else {
312            length=((int32_t)p[1]<<16)|p[2];
313            p+=3;
314        }
315    } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ {
316        const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res;
317        length=*p32++;
318        p=(const UChar *)p32;
319    } else {
320        p=NULL;
321        length=0;
322    }
323    if(pLength) {
324        *pLength=length;
325    }
326    return p;
327}
328
329U_CAPI const UChar * U_EXPORT2
330res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) {
331    const UChar *p;
332    uint32_t offset=RES_GET_OFFSET(res);
333    int32_t length;
334    if(RES_GET_TYPE(res)==URES_ALIAS) {
335        const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+offset;
336        length=*p32++;
337        p=(const UChar *)p32;
338    } else {
339        p=NULL;
340        length=0;
341    }
342    if(pLength) {
343        *pLength=length;
344    }
345    return p;
346}
347
348U_CAPI const uint8_t * U_EXPORT2
349res_getBinary(const ResourceData *pResData, Resource res, int32_t *pLength) {
350    const uint8_t *p;
351    uint32_t offset=RES_GET_OFFSET(res);
352    int32_t length;
353    if(RES_GET_TYPE(res)==URES_BINARY) {
354        const int32_t *p32= offset==0 ? &gEmpty32 : pResData->pRoot+offset;
355        length=*p32++;
356        p=(const uint8_t *)p32;
357    } else {
358        p=NULL;
359        length=0;
360    }
361    if(pLength) {
362        *pLength=length;
363    }
364    return p;
365}
366
367
368U_CAPI const int32_t * U_EXPORT2
369res_getIntVector(const ResourceData *pResData, Resource res, int32_t *pLength) {
370    const int32_t *p;
371    uint32_t offset=RES_GET_OFFSET(res);
372    int32_t length;
373    if(RES_GET_TYPE(res)==URES_INT_VECTOR) {
374        p= offset==0 ? &gEmpty32 : pResData->pRoot+offset;
375        length=*p++;
376    } else {
377        p=NULL;
378        length=0;
379    }
380    if(pLength) {
381        *pLength=length;
382    }
383    return p;
384}
385
386U_CAPI int32_t U_EXPORT2
387res_countArrayItems(const ResourceData *pResData, Resource res) {
388    uint32_t offset=RES_GET_OFFSET(res);
389    switch(RES_GET_TYPE(res)) {
390    case URES_STRING:
391    case URES_STRING_V2:
392    case URES_BINARY:
393    case URES_ALIAS:
394    case URES_INT:
395    case URES_INT_VECTOR:
396        return 1;
397    case URES_ARRAY:
398    case URES_TABLE32:
399        return offset==0 ? 0 : *(pResData->pRoot+offset);
400    case URES_TABLE:
401        return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset));
402    case URES_ARRAY16:
403    case URES_TABLE16:
404        return pResData->p16BitUnits[offset];
405    default:
406        return 0;
407    }
408}
409
410U_CAPI Resource U_EXPORT2
411res_getTableItemByKey(const ResourceData *pResData, Resource table,
412                      int32_t *indexR, const char **key) {
413    uint32_t offset=RES_GET_OFFSET(table);
414    int32_t length;
415    int32_t idx;
416    if(key == NULL || *key == NULL) {
417        return RES_BOGUS;
418    }
419    switch(RES_GET_TYPE(table)) {
420    case URES_TABLE: {
421        const uint16_t *p= offset==0 ? &gEmpty16 : (const uint16_t *)(pResData->pRoot+offset);
422        length=*p++;
423        *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
424        if(idx>=0) {
425            const Resource *p32=(const Resource *)(p+length+(~length&1));
426            return p32[idx];
427        }
428        break;
429    }
430    case URES_TABLE16: {
431        const uint16_t *p=pResData->p16BitUnits+offset;
432        length=*p++;
433        *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
434        if(idx>=0) {
435            return URES_MAKE_RESOURCE(URES_STRING_V2, p[length+idx]);
436        }
437        break;
438    }
439    case URES_TABLE32: {
440        const int32_t *p= offset==0 ? &gEmpty32 : pResData->pRoot+offset;
441        length=*p++;
442        *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key);
443        if(idx>=0) {
444            return (Resource)p[length+idx];
445        }
446        break;
447    }
448    default:
449        break;
450    }
451    return RES_BOGUS;
452}
453
454U_CAPI Resource U_EXPORT2
455res_getTableItemByIndex(const ResourceData *pResData, Resource table,
456                        int32_t indexR, const char **key) {
457    uint32_t offset=RES_GET_OFFSET(table);
458    int32_t length;
459    switch(RES_GET_TYPE(table)) {
460    case URES_TABLE: {
461        const uint16_t *p= offset==0 ? &gEmpty16 : (const uint16_t *)(pResData->pRoot+offset);
462        length=*p++;
463        if(indexR<length) {
464            const Resource *p32=(const Resource *)(p+length+(~length&1));
465            if(key!=NULL) {
466                *key=RES_GET_KEY16(pResData, p[indexR]);
467            }
468            return p32[indexR];
469        }
470        break;
471    }
472    case URES_TABLE16: {
473        const uint16_t *p=pResData->p16BitUnits+offset;
474        length=*p++;
475        if(indexR<length) {
476            if(key!=NULL) {
477                *key=RES_GET_KEY16(pResData, p[indexR]);
478            }
479            return URES_MAKE_RESOURCE(URES_STRING_V2, p[length+indexR]);
480        }
481        break;
482    }
483    case URES_TABLE32: {
484        const int32_t *p= offset==0 ? &gEmpty32 : pResData->pRoot+offset;
485        length=*p++;
486        if(indexR<length) {
487            if(key!=NULL) {
488                *key=RES_GET_KEY32(pResData, p[indexR]);
489            }
490            return (Resource)p[length+indexR];
491        }
492        break;
493    }
494    default:
495        break;
496    }
497    return RES_BOGUS;
498}
499
500U_CAPI Resource U_EXPORT2
501res_getResource(const ResourceData *pResData, const char *key) {
502    const char *realKey=key;
503    int32_t idx;
504    return res_getTableItemByKey(pResData, pResData->rootRes, &idx, &realKey);
505}
506
507U_CAPI Resource U_EXPORT2
508res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) {
509    uint32_t offset=RES_GET_OFFSET(array);
510    switch(RES_GET_TYPE(array)) {
511    case URES_ARRAY: {
512        const int32_t *p= offset==0 ? &gEmpty32 : pResData->pRoot+offset;
513        if(indexR<*p) {
514            return (Resource)p[1+indexR];
515        }
516        break;
517    }
518    case URES_ARRAY16: {
519        const uint16_t *p=pResData->p16BitUnits+offset;
520        if(indexR<*p) {
521            return URES_MAKE_RESOURCE(URES_STRING_V2, p[1+indexR]);
522        }
523        break;
524    }
525    default:
526        break;
527    }
528    return RES_BOGUS;
529}
530
531U_CFUNC Resource
532res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) {
533  /* we pass in a path. CollationElements/Sequence or zoneStrings/3/2 etc.
534   * iterates over a path and stops when a scalar resource is found. This
535   * CAN be an alias. Path gets set to the part that has not yet been processed.
536   */
537
538  char *pathP = *path, *nextSepP = *path;
539  char *closeIndex = NULL;
540  Resource t1 = r;
541  Resource t2;
542  int32_t indexR = 0;
543  UResType type = RES_GET_TYPE(t1);
544
545  /* if you come in with an empty path, you'll be getting back the same resource */
546  if(!uprv_strlen(pathP)) {
547      return r;
548  }
549
550  /* one needs to have an aggregate resource in order to search in it */
551  if(!URES_IS_CONTAINER(type)) {
552      return RES_BOGUS;
553  }
554
555  while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) {
556    /* Iteration stops if: the path has been consumed, we found a non-existing
557     * resource (t1 == RES_BOGUS) or we found a scalar resource (including alias)
558     */
559    nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR);
560    /* if there are more separators, terminate string
561     * and set path to the remaining part of the string
562     */
563    if(nextSepP != NULL) {
564      *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */
565      *path = nextSepP+1;
566    } else {
567      *path = uprv_strchr(pathP, 0);
568    }
569
570    /* if the resource is a table */
571    /* try the key based access */
572    if(URES_IS_TABLE(type)) {
573      *key = pathP;
574      t2 = res_getTableItemByKey(pResData, t1, &indexR, key);
575      if(t2 == RES_BOGUS) {
576        /* if we fail to get the resource by key, maybe we got an index */
577        indexR = uprv_strtol(pathP, &closeIndex, 10);
578        if(closeIndex != pathP) {
579          /* if we indeed have an index, try to get the item by index */
580          t2 = res_getTableItemByIndex(pResData, t1, indexR, key);
581        }
582      }
583    } else if(URES_IS_ARRAY(type)) {
584      indexR = uprv_strtol(pathP, &closeIndex, 10);
585      if(closeIndex != pathP) {
586        t2 = res_getArrayItem(pResData, t1, indexR);
587      } else {
588        t2 = RES_BOGUS; /* have an array, but don't have a valid index */
589      }
590      *key = NULL;
591    } else { /* can't do much here, except setting t2 to bogus */
592      t2 = RES_BOGUS;
593    }
594    t1 = t2;
595    type = RES_GET_TYPE(t1);
596    /* position pathP to next resource key/index */
597    pathP = *path;
598  }
599
600  return t1;
601}
602
603/* resource bundle swapping ------------------------------------------------- */
604
605/*
606 * Need to always enumerate the entire item tree,
607 * track the lowest address of any item to use as the limit for char keys[],
608 * track the highest address of any item to return the size of the data.
609 *
610 * We should have thought of storing those in the data...
611 * It is possible to extend the data structure by putting additional values
612 * in places that are inaccessible by ordinary enumeration of the item tree.
613 * For example, additional integers could be stored at the beginning or
614 * end of the key strings; this could be indicated by a minor version number,
615 * and the data swapping would have to know about these values.
616 *
617 * The data structure does not forbid keys to be shared, so we must swap
618 * all keys once instead of each key when it is referenced.
619 *
620 * These swapping functions assume that a resource bundle always has a length
621 * that is a multiple of 4 bytes.
622 * Currently, this is trivially true because genrb writes bundle tree leaves
623 * physically first, before their branches, so that the root table with its
624 * array of resource items (uint32_t values) is always last.
625 */
626
627/* definitions for table sorting ------------------------ */
628
629/*
630 * row of a temporary array
631 *
632 * gets platform-endian key string indexes and sorting indexes;
633 * after sorting this array by keys, the actual key/value arrays are permutated
634 * according to the sorting indexes
635 */
636typedef struct Row {
637    int32_t keyIndex, sortIndex;
638} Row;
639
640static int32_t
641ures_compareRows(const void *context, const void *left, const void *right) {
642    const char *keyChars=(const char *)context;
643    return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex,
644                                keyChars+((const Row *)right)->keyIndex);
645}
646
647typedef struct TempTable {
648    const char *keyChars;
649    Row *rows;
650    int32_t *resort;
651    uint32_t *resFlags;
652    int32_t localKeyLimit;
653    uint8_t majorFormatVersion;
654} TempTable;
655
656enum {
657    STACK_ROW_CAPACITY=200
658};
659
660/* The table item key string is not locally available. */
661static const char *const gUnknownKey="";
662
663/* resource table key for collation binaries: "%%CollationBin" */
664static const UChar gCollationBinKey[]={
665    0x25, 0x25,
666    0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
667    0x42, 0x69, 0x6e,
668    0
669};
670
671/*
672 * swap one resource item
673 */
674static void
675ures_swapResource(const UDataSwapper *ds,
676                  const Resource *inBundle, Resource *outBundle,
677                  Resource res, /* caller swaps res itself */
678                  const char *key,
679                  TempTable *pTempTable,
680                  UErrorCode *pErrorCode) {
681    const Resource *p;
682    Resource *q;
683    int32_t offset, count;
684
685    switch(RES_GET_TYPE(res)) {
686    case URES_TABLE16:
687    case URES_STRING_V2:
688    case URES_INT:
689    case URES_ARRAY16:
690        /* integer, or points to 16-bit units, nothing to do here */
691        return;
692    default:
693        break;
694    }
695
696    /* all other types use an offset to point to their data */
697    offset=(int32_t)RES_GET_OFFSET(res);
698    if(offset==0) {
699        /* special offset indicating an empty item */
700        return;
701    }
702    if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) {
703        /* we already swapped this resource item */
704        return;
705    } else {
706        /* mark it as swapped now */
707        pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f));
708    }
709
710    p=inBundle+offset;
711    q=outBundle+offset;
712
713    switch(RES_GET_TYPE(res)) {
714    case URES_ALIAS:
715        /* physically same value layout as string, fall through */
716    case URES_STRING:
717        count=udata_readInt32(ds, (int32_t)*p);
718        /* swap length */
719        ds->swapArray32(ds, p, 4, q, pErrorCode);
720        /* swap each UChar (the terminating NUL would not change) */
721        ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode);
722        break;
723    case URES_BINARY:
724        count=udata_readInt32(ds, (int32_t)*p);
725        /* swap length */
726        ds->swapArray32(ds, p, 4, q, pErrorCode);
727        /* no need to swap or copy bytes - ures_swap() copied them all */
728
729        /* swap known formats */
730#if !UCONFIG_NO_COLLATION
731        if( key!=NULL &&  /* the binary is in a table */
732            (key!=gUnknownKey ?
733                /* its table key string is "%%CollationBin" */
734                0==ds->compareInvChars(ds, key, -1,
735                                       gCollationBinKey, LENGTHOF(gCollationBinKey)-1) :
736                /* its table key string is unknown but it looks like a collation binary */
737                ucol_looksLikeCollationBinary(ds, p+1, count))
738        ) {
739            ucol_swapBinary(ds, p+1, count, q+1, pErrorCode);
740        }
741#endif
742        break;
743    case URES_TABLE:
744    case URES_TABLE32:
745        {
746            const uint16_t *pKey16;
747            uint16_t *qKey16;
748
749            const int32_t *pKey32;
750            int32_t *qKey32;
751
752            Resource item;
753            int32_t i, oldIndex;
754
755            if(RES_GET_TYPE(res)==URES_TABLE) {
756                /* get table item count */
757                pKey16=(const uint16_t *)p;
758                qKey16=(uint16_t *)q;
759                count=ds->readUInt16(*pKey16);
760
761                pKey32=qKey32=NULL;
762
763                /* swap count */
764                ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode);
765
766                offset+=((1+count)+1)/2;
767            } else {
768                /* get table item count */
769                pKey32=(const int32_t *)p;
770                qKey32=(int32_t *)q;
771                count=udata_readInt32(ds, *pKey32);
772
773                pKey16=qKey16=NULL;
774
775                /* swap count */
776                ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode);
777
778                offset+=1+count;
779            }
780
781            if(count==0) {
782                break;
783            }
784
785            p=inBundle+offset; /* pointer to table resources */
786            q=outBundle+offset;
787
788            /* recurse */
789            for(i=0; i<count; ++i) {
790                const char *itemKey=gUnknownKey;
791                if(pKey16!=NULL) {
792                    int32_t keyOffset=ds->readUInt16(pKey16[i]);
793                    if(keyOffset<pTempTable->localKeyLimit) {
794                        itemKey=(const char *)outBundle+keyOffset;
795                    }
796                } else {
797                    int32_t keyOffset=udata_readInt32(ds, pKey32[i]);
798                    if(keyOffset>=0) {
799                        itemKey=(const char *)outBundle+keyOffset;
800                    }
801                }
802                item=ds->readUInt32(p[i]);
803                ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode);
804                if(U_FAILURE(*pErrorCode)) {
805                    udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n",
806                                     res, i, item);
807                    return;
808                }
809            }
810
811            if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) {
812                /* no need to sort, just swap the offset/value arrays */
813                if(pKey16!=NULL) {
814                    ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode);
815                    ds->swapArray32(ds, p, count*4, q, pErrorCode);
816                } else {
817                    /* swap key offsets and items as one array */
818                    ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode);
819                }
820                break;
821            }
822
823            /*
824             * We need to sort tables by outCharset key strings because they
825             * sort differently for different charset families.
826             * ures_swap() already set pTempTable->keyChars appropriately.
827             * First we set up a temporary table with the key indexes and
828             * sorting indexes and sort that.
829             * Then we permutate and copy/swap the actual values.
830             */
831            if(pKey16!=NULL) {
832                for(i=0; i<count; ++i) {
833                    pTempTable->rows[i].keyIndex=ds->readUInt16(pKey16[i]);
834                    pTempTable->rows[i].sortIndex=i;
835                }
836            } else {
837                for(i=0; i<count; ++i) {
838                    pTempTable->rows[i].keyIndex=udata_readInt32(ds, pKey32[i]);
839                    pTempTable->rows[i].sortIndex=i;
840                }
841            }
842            uprv_sortArray(pTempTable->rows, count, sizeof(Row),
843                           ures_compareRows, pTempTable->keyChars,
844                           FALSE, pErrorCode);
845            if(U_FAILURE(*pErrorCode)) {
846                udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n",
847                                 res, count);
848                return;
849            }
850
851            /*
852             * copy/swap/permutate items
853             *
854             * If we swap in-place, then the permutation must use another
855             * temporary array (pTempTable->resort)
856             * before the results are copied to the outBundle.
857             */
858            /* keys */
859            if(pKey16!=NULL) {
860                uint16_t *rKey16;
861
862                if(pKey16!=qKey16) {
863                    rKey16=qKey16;
864                } else {
865                    rKey16=(uint16_t *)pTempTable->resort;
866                }
867                for(i=0; i<count; ++i) {
868                    oldIndex=pTempTable->rows[i].sortIndex;
869                    ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode);
870                }
871                if(qKey16!=rKey16) {
872                    uprv_memcpy(qKey16, rKey16, 2*count);
873                }
874            } else {
875                int32_t *rKey32;
876
877                if(pKey32!=qKey32) {
878                    rKey32=qKey32;
879                } else {
880                    rKey32=pTempTable->resort;
881                }
882                for(i=0; i<count; ++i) {
883                    oldIndex=pTempTable->rows[i].sortIndex;
884                    ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode);
885                }
886                if(qKey32!=rKey32) {
887                    uprv_memcpy(qKey32, rKey32, 4*count);
888                }
889            }
890
891            /* resources */
892            {
893                Resource *r;
894
895
896                if(p!=q) {
897                    r=q;
898                } else {
899                    r=(Resource *)pTempTable->resort;
900                }
901                for(i=0; i<count; ++i) {
902                    oldIndex=pTempTable->rows[i].sortIndex;
903                    ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode);
904                }
905                if(q!=r) {
906                    uprv_memcpy(q, r, 4*count);
907                }
908            }
909        }
910        break;
911    case URES_ARRAY:
912        {
913            Resource item;
914            int32_t i;
915
916            count=udata_readInt32(ds, (int32_t)*p);
917            /* swap length */
918            ds->swapArray32(ds, p++, 4, q++, pErrorCode);
919
920            /* recurse */
921            for(i=0; i<count; ++i) {
922                item=ds->readUInt32(p[i]);
923                ures_swapResource(ds, inBundle, outBundle, item, NULL, pTempTable, pErrorCode);
924                if(U_FAILURE(*pErrorCode)) {
925                    udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n",
926                                     res, i, item);
927                    return;
928                }
929            }
930
931            /* swap items */
932            ds->swapArray32(ds, p, 4*count, q, pErrorCode);
933        }
934        break;
935    case URES_INT_VECTOR:
936        count=udata_readInt32(ds, (int32_t)*p);
937        /* swap length and each integer */
938        ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode);
939        break;
940    default:
941        /* also catches RES_BOGUS */
942        *pErrorCode=U_UNSUPPORTED_ERROR;
943        break;
944    }
945}
946
947U_CAPI int32_t U_EXPORT2
948ures_swap(const UDataSwapper *ds,
949          const void *inData, int32_t length, void *outData,
950          UErrorCode *pErrorCode) {
951    const UDataInfo *pInfo;
952    const Resource *inBundle;
953    Resource rootRes;
954    int32_t headerSize, maxTableLength;
955
956    Row rows[STACK_ROW_CAPACITY];
957    int32_t resort[STACK_ROW_CAPACITY];
958    TempTable tempTable;
959
960    const int32_t *inIndexes;
961
962    /* the following integers count Resource item offsets (4 bytes each), not bytes */
963    int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top;
964
965    /* udata_swapDataHeader checks the arguments */
966    headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
967    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
968        return 0;
969    }
970
971    /* check data format and format version */
972    pInfo=(const UDataInfo *)((const char *)inData+4);
973    if(!(
974        pInfo->dataFormat[0]==0x52 &&   /* dataFormat="ResB" */
975        pInfo->dataFormat[1]==0x65 &&
976        pInfo->dataFormat[2]==0x73 &&
977        pInfo->dataFormat[3]==0x42 &&
978        ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) ||  /* formatVersion 1.1+ or 2.x */
979         pInfo->formatVersion[0]==2)
980    )) {
981        udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n",
982                         pInfo->dataFormat[0], pInfo->dataFormat[1],
983                         pInfo->dataFormat[2], pInfo->dataFormat[3],
984                         pInfo->formatVersion[0], pInfo->formatVersion[1]);
985        *pErrorCode=U_UNSUPPORTED_ERROR;
986        return 0;
987    }
988    tempTable.majorFormatVersion=pInfo->formatVersion[0];
989
990    /* a resource bundle must contain at least one resource item */
991    if(length<0) {
992        bundleLength=-1;
993    } else {
994        bundleLength=(length-headerSize)/4;
995
996        /* formatVersion 1.1 must have a root item and at least 5 indexes */
997        if(bundleLength<(1+5)) {
998            udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n",
999                             length-headerSize);
1000            *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1001            return 0;
1002        }
1003    }
1004
1005    inBundle=(const Resource *)((const char *)inData+headerSize);
1006    rootRes=ds->readUInt32(*inBundle);
1007
1008    /* formatVersion 1.1 adds the indexes[] array */
1009    inIndexes=(const int32_t *)(inBundle+1);
1010
1011    indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff;
1012    if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
1013        udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n");
1014        *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1015        return 0;
1016    }
1017    keysBottom=1+indexLength;
1018    keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]);
1019    if(indexLength>URES_INDEX_16BIT_TOP) {
1020        resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]);
1021    } else {
1022        resBottom=keysTop;
1023    }
1024    top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]);
1025    maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]);
1026
1027    if(0<=bundleLength && bundleLength<top) {
1028        udata_printError(ds, "ures_swap(): resource top %d exceeds bundle length %d\n",
1029                         top, bundleLength);
1030        *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1031        return 0;
1032    }
1033    if(keysTop>(1+indexLength)) {
1034        tempTable.localKeyLimit=keysTop<<2;
1035    } else {
1036        tempTable.localKeyLimit=0;
1037    }
1038
1039    if(length>=0) {
1040        Resource *outBundle=(Resource *)((char *)outData+headerSize);
1041
1042        /* track which resources we have already swapped */
1043        uint32_t stackResFlags[STACK_ROW_CAPACITY];
1044        int32_t resFlagsLength;
1045
1046        /*
1047         * We need one bit per 4 resource bundle bytes so that we can track
1048         * every possible Resource for whether we have swapped it already.
1049         * Multiple Resource words can refer to the same bundle offsets
1050         * for sharing identical values.
1051         * We could optimize this by allocating only for locations above
1052         * where Resource values are stored (above keys & strings).
1053         */
1054        resFlagsLength=(length+31)>>5;          /* number of bytes needed */
1055        resFlagsLength=(resFlagsLength+3)&~3;   /* multiple of 4 bytes for uint32_t */
1056        if(resFlagsLength<=sizeof(stackResFlags)) {
1057            tempTable.resFlags=stackResFlags;
1058        } else {
1059            tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength);
1060            if(tempTable.resFlags==NULL) {
1061                udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n");
1062                *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1063                return 0;
1064            }
1065        }
1066        uprv_memset(tempTable.resFlags, 0, resFlagsLength);
1067
1068        /* copy the bundle for binary and inaccessible data */
1069        if(inData!=outData) {
1070            uprv_memcpy(outBundle, inBundle, 4*top);
1071        }
1072
1073        /* swap the key strings, but not the padding bytes (0xaa) after the last string and its NUL */
1074        udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom),
1075                                    outBundle+keysBottom, pErrorCode);
1076        if(U_FAILURE(*pErrorCode)) {
1077            udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom));
1078            return 0;
1079        }
1080
1081        /* swap the 16-bit units (strings, table16, array16) */
1082        if(keysTop<resBottom) {
1083            ds->swapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode);
1084            if(U_FAILURE(*pErrorCode)) {
1085                udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop));
1086                return 0;
1087            }
1088        }
1089
1090        /* allocate the temporary table for sorting resource tables */
1091        tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */
1092        if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) {
1093            tempTable.rows=rows;
1094            tempTable.resort=resort;
1095        } else {
1096            tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4);
1097            if(tempTable.rows==NULL) {
1098                udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n",
1099                                 maxTableLength);
1100                *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1101                if(tempTable.resFlags!=stackResFlags) {
1102                    uprv_free(tempTable.resFlags);
1103                }
1104                return 0;
1105            }
1106            tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength);
1107        }
1108
1109        /* swap the resources */
1110        ures_swapResource(ds, inBundle, outBundle, rootRes, NULL, &tempTable, pErrorCode);
1111        if(U_FAILURE(*pErrorCode)) {
1112            udata_printError(ds, "ures_swapResource(root res=%08x) failed\n",
1113                             rootRes);
1114        }
1115
1116        if(tempTable.rows!=rows) {
1117            uprv_free(tempTable.rows);
1118        }
1119        if(tempTable.resFlags!=stackResFlags) {
1120            uprv_free(tempTable.resFlags);
1121        }
1122
1123        /* swap the root resource and indexes */
1124        ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode);
1125    }
1126
1127    return headerSize+4*top;
1128}
1129