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