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