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