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