tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

uresdata.cpp (50934B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 *******************************************************************************
      5 * Copyright (C) 1999-2016, International Business Machines Corporation
      6 *               and others. All Rights Reserved.
      7 *******************************************************************************
      8 *   file name:  uresdata.cpp
      9 *   encoding:   UTF-8
     10 *   tab size:   8 (not used)
     11 *   indentation:4
     12 *
     13 *   created on: 1999dec08
     14 *   created by: Markus W. Scherer
     15 * Modification History:
     16 *
     17 *   Date        Name        Description
     18 *   06/20/2000  helena      OS/400 port changes; mostly typecast.
     19 *   06/24/02    weiv        Added support for resource sharing
     20 */
     21 
     22 #include "unicode/utypes.h"
     23 #include "unicode/udata.h"
     24 #include "unicode/ustring.h"
     25 #include "unicode/utf16.h"
     26 #include "cmemory.h"
     27 #include "cstring.h"
     28 #include "resource.h"
     29 #include "uarrsort.h"
     30 #include "uassert.h"
     31 #include "ucol_swp.h"
     32 #include "udataswp.h"
     33 #include "uinvchar.h"
     34 #include "uresdata.h"
     35 #include "uresimp.h"
     36 #include "utracimp.h"
     37 
     38 /*
     39 * Resource access helpers
     40 */
     41 
     42 /* get a const char* pointer to the key with the keyOffset byte offset from pRoot */
     43 #define RES_GET_KEY16(pResData, keyOffset) \
     44    ((keyOffset)<(pResData)->localKeyLimit ? \
     45        (const char *)(pResData)->pRoot+(keyOffset) : \
     46        (pResData)->poolBundleKeys+(keyOffset)-(pResData)->localKeyLimit)
     47 
     48 #define RES_GET_KEY32(pResData, keyOffset) \
     49    ((keyOffset)>=0 ? \
     50        (const char *)(pResData)->pRoot+(keyOffset) : \
     51        (pResData)->poolBundleKeys+((keyOffset)&0x7fffffff))
     52 
     53 #define URESDATA_ITEM_NOT_FOUND -1
     54 
     55 /* empty resources, returned when the resource offset is 0 */
     56 static const uint16_t gEmpty16=0;
     57 
     58 static const struct {
     59    int32_t length;
     60    int32_t res;
     61 } gEmpty32={ 0, 0 };
     62 
     63 static const struct {
     64    int32_t length;
     65    char16_t nul;
     66    char16_t pad;
     67 } gEmptyString={ 0, 0, 0 };
     68 
     69 /*
     70 * All the type-access functions assume that
     71 * the resource is of the expected type.
     72 */
     73 
     74 static int32_t
     75 _res_findTableItem(const ResourceData *pResData, const uint16_t *keyOffsets, int32_t length,
     76                   const char *key, const char **realKey) {
     77    const char *tableKey;
     78    int32_t mid, start, limit;
     79    int result;
     80 
     81    /* do a binary search for the key */
     82    start=0;
     83    limit=length;
     84    while(start<limit) {
     85        mid = (start + limit) / 2;
     86        tableKey = RES_GET_KEY16(pResData, keyOffsets[mid]);
     87        if (pResData->useNativeStrcmp) {
     88            result = uprv_strcmp(key, tableKey);
     89        } else {
     90            result = uprv_compareInvCharsAsAscii(key, tableKey);
     91        }
     92        if (result < 0) {
     93            limit = mid;
     94        } else if (result > 0) {
     95            start = mid + 1;
     96        } else {
     97            /* We found it! */
     98            *realKey=tableKey;
     99            return mid;
    100        }
    101    }
    102    return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
    103 }
    104 
    105 static int32_t
    106 _res_findTable32Item(const ResourceData *pResData, const int32_t *keyOffsets, int32_t length,
    107                     const char *key, const char **realKey) {
    108    const char *tableKey;
    109    int32_t mid, start, limit;
    110    int result;
    111 
    112    /* do a binary search for the key */
    113    start=0;
    114    limit=length;
    115    while(start<limit) {
    116        mid = (start + limit) / 2;
    117        tableKey = RES_GET_KEY32(pResData, keyOffsets[mid]);
    118        if (pResData->useNativeStrcmp) {
    119            result = uprv_strcmp(key, tableKey);
    120        } else {
    121            result = uprv_compareInvCharsAsAscii(key, tableKey);
    122        }
    123        if (result < 0) {
    124            limit = mid;
    125        } else if (result > 0) {
    126            start = mid + 1;
    127        } else {
    128            /* We found it! */
    129            *realKey=tableKey;
    130            return mid;
    131        }
    132    }
    133    return URESDATA_ITEM_NOT_FOUND;  /* not found or table is empty. */
    134 }
    135 
    136 /* helper for res_load() ---------------------------------------------------- */
    137 
    138 static UBool U_CALLCONV
    139 isAcceptable(void *context,
    140             const char * /*type*/, const char * /*name*/,
    141             const UDataInfo *pInfo) {
    142    uprv_memcpy(context, pInfo->formatVersion, 4);
    143    return
    144        pInfo->size>=20 &&
    145        pInfo->isBigEndian==U_IS_BIG_ENDIAN &&
    146        pInfo->charsetFamily==U_CHARSET_FAMILY &&
    147        pInfo->sizeofUChar==U_SIZEOF_UCHAR &&
    148        pInfo->dataFormat[0]==0x52 &&   /* dataFormat="ResB" */
    149        pInfo->dataFormat[1]==0x65 &&
    150        pInfo->dataFormat[2]==0x73 &&
    151        pInfo->dataFormat[3]==0x42 &&
    152        (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3);
    153 }
    154 
    155 /* semi-public functions ---------------------------------------------------- */
    156 
    157 static void
    158 res_init(ResourceData *pResData,
    159         UVersionInfo formatVersion, const void *inBytes, int32_t length,
    160         UErrorCode *errorCode) {
    161    UResType rootType;
    162 
    163    /* get the root resource */
    164    pResData->pRoot = static_cast<const int32_t*>(inBytes);
    165    pResData->rootRes = static_cast<Resource>(*pResData->pRoot);
    166    pResData->p16BitUnits=&gEmpty16;
    167 
    168    /* formatVersion 1.1 must have a root item and at least 5 indexes */
    169    if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) {
    170        *errorCode=U_INVALID_FORMAT_ERROR;
    171        res_unload(pResData);
    172        return;
    173    }
    174 
    175    /* currently, we accept only resources that have a Table as their roots */
    176    rootType = static_cast<UResType>(RES_GET_TYPE(pResData->rootRes));
    177    if(!URES_IS_TABLE(rootType)) {
    178        *errorCode=U_INVALID_FORMAT_ERROR;
    179        res_unload(pResData);
    180        return;
    181    }
    182 
    183    if(formatVersion[0]==1 && formatVersion[1]==0) {
    184        pResData->localKeyLimit=0x10000;  /* greater than any 16-bit key string offset */
    185    } else {
    186        /* bundles with formatVersion 1.1 and later contain an indexes[] array */
    187        const int32_t *indexes=pResData->pRoot+1;
    188        int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff;
    189        if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
    190            *errorCode=U_INVALID_FORMAT_ERROR;
    191            res_unload(pResData);
    192            return;
    193        }
    194        if( length>=0 &&
    195            (length<((1+indexLength)<<2) ||
    196             length<(indexes[URES_INDEX_BUNDLE_TOP]<<2))
    197        ) {
    198            *errorCode=U_INVALID_FORMAT_ERROR;
    199            res_unload(pResData);
    200            return;
    201        }
    202        if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) {
    203            pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2;
    204        }
    205        if(formatVersion[0]>=3) {
    206            // In formatVersion 1, the indexLength took up this whole int.
    207            // In version 2, bits 31..8 were reserved and always 0.
    208            // In version 3, they contain bits 23..0 of the poolStringIndexLimit.
    209            // Bits 27..24 are in indexes[URES_INDEX_ATTRIBUTES] bits 15..12.
    210            pResData->poolStringIndexLimit = static_cast<int32_t>(static_cast<uint32_t>(indexes[URES_INDEX_LENGTH]) >> 8);
    211        }
    212        if(indexLength>URES_INDEX_ATTRIBUTES) {
    213            int32_t att=indexes[URES_INDEX_ATTRIBUTES];
    214            pResData->noFallback = static_cast<UBool>(att & URES_ATT_NO_FALLBACK);
    215            pResData->isPoolBundle = static_cast<UBool>((att & URES_ATT_IS_POOL_BUNDLE) != 0);
    216            pResData->usesPoolBundle = static_cast<UBool>((att & URES_ATT_USES_POOL_BUNDLE) != 0);
    217            pResData->poolStringIndexLimit|=(att&0xf000)<<12;  // bits 15..12 -> 27..24
    218            pResData->poolStringIndex16Limit = static_cast<int32_t>(static_cast<uint32_t>(att) >> 16);
    219        }
    220        if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength<=URES_INDEX_POOL_CHECKSUM) {
    221            *errorCode=U_INVALID_FORMAT_ERROR;
    222            res_unload(pResData);
    223            return;
    224        }
    225        if( indexLength>URES_INDEX_16BIT_TOP &&
    226            indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP]
    227        ) {
    228            pResData->p16BitUnits = reinterpret_cast<const uint16_t*>(pResData->pRoot + indexes[URES_INDEX_KEYS_TOP]);
    229        }
    230    }
    231 
    232    if(formatVersion[0]==1 || U_CHARSET_FAMILY==U_ASCII_FAMILY) {
    233        /*
    234         * formatVersion 1: compare key strings in native-charset order
    235         * formatVersion 2 and up: compare key strings in ASCII order
    236         */
    237        pResData->useNativeStrcmp=true;
    238    }
    239 }
    240 
    241 U_CAPI void U_EXPORT2
    242 res_read(ResourceData *pResData,
    243         const UDataInfo *pInfo, const void *inBytes, int32_t length,
    244         UErrorCode *errorCode) {
    245    UVersionInfo formatVersion;
    246 
    247    uprv_memset(pResData, 0, sizeof(ResourceData));
    248    if(U_FAILURE(*errorCode)) {
    249        return;
    250    }
    251    if(!isAcceptable(formatVersion, nullptr, nullptr, pInfo)) {
    252        *errorCode=U_INVALID_FORMAT_ERROR;
    253        return;
    254    }
    255    res_init(pResData, formatVersion, inBytes, length, errorCode);
    256 }
    257 
    258 U_CFUNC void
    259 res_load(ResourceData *pResData,
    260         const char *path, const char *name, UErrorCode *errorCode) {
    261    UVersionInfo formatVersion;
    262 
    263    uprv_memset(pResData, 0, sizeof(ResourceData));
    264 
    265    /* load the ResourceBundle file */
    266    pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVersion, errorCode);
    267    if(U_FAILURE(*errorCode)) {
    268        return;
    269    }
    270 
    271    /* get its memory and initialize *pResData */
    272    res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, errorCode);
    273 }
    274 
    275 U_CFUNC void
    276 res_unload(ResourceData *pResData) {
    277    if(pResData->data!=nullptr) {
    278        udata_close(pResData->data);
    279        pResData->data=nullptr;
    280    }
    281 }
    282 
    283 static const int8_t gPublicTypes[URES_LIMIT] = {
    284    URES_STRING,
    285    URES_BINARY,
    286    URES_TABLE,
    287    URES_ALIAS,
    288 
    289    URES_TABLE,     /* URES_TABLE32 */
    290    URES_TABLE,     /* URES_TABLE16 */
    291    URES_STRING,    /* URES_STRING_V2 */
    292    URES_INT,
    293 
    294    URES_ARRAY,
    295    URES_ARRAY,     /* URES_ARRAY16 */
    296    URES_NONE,
    297    URES_NONE,
    298 
    299    URES_NONE,
    300    URES_NONE,
    301    URES_INT_VECTOR,
    302    URES_NONE
    303 };
    304 
    305 U_CAPI UResType U_EXPORT2
    306 res_getPublicType(Resource res) {
    307    return (UResType)gPublicTypes[RES_GET_TYPE(res)];
    308 }
    309 
    310 U_CAPI const char16_t * U_EXPORT2
    311 res_getStringNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) {
    312    const char16_t *p;
    313    uint32_t offset=RES_GET_OFFSET(res);
    314    int32_t length;
    315    if(RES_GET_TYPE(res)==URES_STRING_V2) {
    316        int32_t first;
    317        if((int32_t)offset<pResData->poolStringIndexLimit) {
    318            p=(const char16_t *)pResData->poolBundleStrings+offset;
    319        } else {
    320            p=(const char16_t *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit);
    321        }
    322        first=*p;
    323        if(!U16_IS_TRAIL(first)) {
    324            length=u_strlen(p);
    325        } else if(first<0xdfef) {
    326            length=first&0x3ff;
    327            ++p;
    328        } else if(first<0xdfff) {
    329            length=((first-0xdfef)<<16)|p[1];
    330            p+=2;
    331        } else {
    332            length=((int32_t)p[1]<<16)|p[2];
    333            p+=3;
    334        }
    335    } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ {
    336        const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res;
    337        length=*p32++;
    338        p=(const char16_t *)p32;
    339    } else {
    340        p=nullptr;
    341        length=0;
    342    }
    343    if(pLength) {
    344        *pLength=length;
    345    }
    346    return p;
    347 }
    348 
    349 namespace {
    350 
    351 /**
    352 * CLDR string value (three empty-set symbols)=={2205, 2205, 2205}
    353 * prevents fallback to the parent bundle.
    354 * TODO: combine with other code that handles this marker, use EMPTY_SET constant.
    355 * TODO: maybe move to uresbund.cpp?
    356 */
    357 UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) {
    358    uint32_t offset=RES_GET_OFFSET(res);
    359    if (offset == 0) {
    360        // empty string
    361    } else if (res == offset) {
    362        const int32_t *p32=pResData->pRoot+res;
    363        int32_t length=*p32;
    364        const char16_t* p = reinterpret_cast<const char16_t*>(p32);
    365        return length == 3 && p[2] == 0x2205 && p[3] == 0x2205 && p[4] == 0x2205;
    366    } else if (RES_GET_TYPE(res) == URES_STRING_V2) {
    367        const char16_t *p;
    368        if (static_cast<int32_t>(offset) < pResData->poolStringIndexLimit) {
    369            p = reinterpret_cast<const char16_t*>(pResData->poolBundleStrings) + offset;
    370        } else {
    371            p = reinterpret_cast<const char16_t*>(pResData->p16BitUnits) + (offset - pResData->poolStringIndexLimit);
    372        }
    373        int32_t first=*p;
    374        if (first == 0x2205) {  // implicit length
    375            return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0;
    376        } else if (first == 0xdc03) {  // explicit length 3 (should not occur)
    377            return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0x2205;
    378        } else {
    379            // Assume that the string has not been stored with more length units than necessary.
    380            return false;
    381        }
    382    }
    383    return false;
    384 }
    385 
    386 int32_t getStringArray(const ResourceData *pResData, const icu::ResourceArray &array,
    387                       icu::UnicodeString *dest, int32_t capacity,
    388                       UErrorCode &errorCode) {
    389    if(U_FAILURE(errorCode)) {
    390        return 0;
    391    }
    392    if(dest == nullptr ? capacity != 0 : capacity < 0) {
    393        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    394        return 0;
    395    }
    396    int32_t length = array.getSize();
    397    if(length == 0) {
    398        return 0;
    399    }
    400    if(length > capacity) {
    401        errorCode = U_BUFFER_OVERFLOW_ERROR;
    402        return length;
    403    }
    404    for(int32_t i = 0; i < length; ++i) {
    405        int32_t sLength;
    406        // No tracing: handled by the caller
    407        const char16_t *s = res_getStringNoTrace(pResData, array.internalGetResource(pResData, i), &sLength);
    408        if(s == nullptr) {
    409            errorCode = U_RESOURCE_TYPE_MISMATCH;
    410            return 0;
    411        }
    412        dest[i].setTo(true, s, sLength);
    413    }
    414    return length;
    415 }
    416 
    417 }  // namespace
    418 
    419 U_CAPI const char16_t * U_EXPORT2
    420 res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) {
    421    const char16_t *p;
    422    uint32_t offset=RES_GET_OFFSET(res);
    423    int32_t length;
    424    if(RES_GET_TYPE(res)==URES_ALIAS) {
    425        const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+offset;
    426        length=*p32++;
    427        p=(const char16_t *)p32;
    428    } else {
    429        p=nullptr;
    430        length=0;
    431    }
    432    if(pLength) {
    433        *pLength=length;
    434    }
    435    return p;
    436 }
    437 
    438 U_CAPI const uint8_t * U_EXPORT2
    439 res_getBinaryNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) {
    440    const uint8_t *p;
    441    uint32_t offset=RES_GET_OFFSET(res);
    442    int32_t length;
    443    if(RES_GET_TYPE(res)==URES_BINARY) {
    444        const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->pRoot+offset;
    445        length=*p32++;
    446        p=(const uint8_t *)p32;
    447    } else {
    448        p=nullptr;
    449        length=0;
    450    }
    451    if(pLength) {
    452        *pLength=length;
    453    }
    454    return p;
    455 }
    456 
    457 
    458 U_CAPI const int32_t * U_EXPORT2
    459 res_getIntVectorNoTrace(const ResourceData *pResData, Resource res, int32_t *pLength) {
    460    const int32_t *p;
    461    uint32_t offset=RES_GET_OFFSET(res);
    462    int32_t length;
    463    if(RES_GET_TYPE(res)==URES_INT_VECTOR) {
    464        p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset;
    465        length=*p++;
    466    } else {
    467        p=nullptr;
    468        length=0;
    469    }
    470    if(pLength) {
    471        *pLength=length;
    472    }
    473    return p;
    474 }
    475 
    476 U_CAPI int32_t U_EXPORT2
    477 res_countArrayItems(const ResourceData *pResData, Resource res) {
    478    uint32_t offset=RES_GET_OFFSET(res);
    479    switch(RES_GET_TYPE(res)) {
    480    case URES_STRING:
    481    case URES_STRING_V2:
    482    case URES_BINARY:
    483    case URES_ALIAS:
    484    case URES_INT:
    485    case URES_INT_VECTOR:
    486        return 1;
    487    case URES_ARRAY:
    488    case URES_TABLE32:
    489        return offset==0 ? 0 : *(pResData->pRoot+offset);
    490    case URES_TABLE:
    491        return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset));
    492    case URES_ARRAY16:
    493    case URES_TABLE16:
    494        return pResData->p16BitUnits[offset];
    495    default:
    496        return 0;
    497    }
    498 }
    499 
    500 U_NAMESPACE_BEGIN
    501 
    502 ResourceDataValue::~ResourceDataValue() {}
    503 
    504 UResType ResourceDataValue::getType() const {
    505    return res_getPublicType(res);
    506 }
    507 
    508 const char16_t *ResourceDataValue::getString(int32_t &length, UErrorCode &errorCode) const {
    509    if(U_FAILURE(errorCode)) {
    510        return nullptr;
    511    }
    512    const char16_t *s = res_getString(fTraceInfo, &getData(), res, &length);
    513    if(s == nullptr) {
    514        errorCode = U_RESOURCE_TYPE_MISMATCH;
    515    }
    516    return s;
    517 }
    518 
    519 const char16_t *ResourceDataValue::getAliasString(int32_t &length, UErrorCode &errorCode) const {
    520    if(U_FAILURE(errorCode)) {
    521        return nullptr;
    522    }
    523    const char16_t *s = res_getAlias(&getData(), res, &length);
    524    if(s == nullptr) {
    525        errorCode = U_RESOURCE_TYPE_MISMATCH;
    526    }
    527    return s;
    528 }
    529 
    530 int32_t ResourceDataValue::getInt(UErrorCode &errorCode) const {
    531    if(U_FAILURE(errorCode)) {
    532        return 0;
    533    }
    534    if(RES_GET_TYPE(res) != URES_INT) {
    535        errorCode = U_RESOURCE_TYPE_MISMATCH;
    536    }
    537    return res_getInt(fTraceInfo, res);
    538 }
    539 
    540 uint32_t ResourceDataValue::getUInt(UErrorCode &errorCode) const {
    541    if(U_FAILURE(errorCode)) {
    542        return 0;
    543    }
    544    if(RES_GET_TYPE(res) != URES_INT) {
    545        errorCode = U_RESOURCE_TYPE_MISMATCH;
    546    }
    547    return res_getUInt(fTraceInfo, res);
    548 }
    549 
    550 const int32_t *ResourceDataValue::getIntVector(int32_t &length, UErrorCode &errorCode) const {
    551    if(U_FAILURE(errorCode)) {
    552        return nullptr;
    553    }
    554    const int32_t *iv = res_getIntVector(fTraceInfo, &getData(), res, &length);
    555    if(iv == nullptr) {
    556        errorCode = U_RESOURCE_TYPE_MISMATCH;
    557    }
    558    return iv;
    559 }
    560 
    561 const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorCode) const {
    562    if(U_FAILURE(errorCode)) {
    563        return nullptr;
    564    }
    565    const uint8_t *b = res_getBinary(fTraceInfo, &getData(), res, &length);
    566    if(b == nullptr) {
    567        errorCode = U_RESOURCE_TYPE_MISMATCH;
    568    }
    569    return b;
    570 }
    571 
    572 ResourceArray ResourceDataValue::getArray(UErrorCode &errorCode) const {
    573    if(U_FAILURE(errorCode)) {
    574        return {};
    575    }
    576    const uint16_t *items16 = nullptr;
    577    const Resource *items32 = nullptr;
    578    uint32_t offset=RES_GET_OFFSET(res);
    579    int32_t length = 0;
    580    switch(RES_GET_TYPE(res)) {
    581    case URES_ARRAY:
    582        if (offset!=0) {  // empty if offset==0
    583            items32 = reinterpret_cast<const Resource*>(getData().pRoot) + offset;
    584            length = *items32++;
    585        }
    586        break;
    587    case URES_ARRAY16:
    588        items16 = getData().p16BitUnits+offset;
    589        length = *items16++;
    590        break;
    591    default:
    592        errorCode = U_RESOURCE_TYPE_MISMATCH;
    593        return {};
    594    }
    595    return ResourceArray(items16, items32, length, fTraceInfo);
    596 }
    597 
    598 ResourceTable ResourceDataValue::getTable(UErrorCode &errorCode) const {
    599    if(U_FAILURE(errorCode)) {
    600        return {};
    601    }
    602    const uint16_t *keys16 = nullptr;
    603    const int32_t *keys32 = nullptr;
    604    const uint16_t *items16 = nullptr;
    605    const Resource *items32 = nullptr;
    606    uint32_t offset = RES_GET_OFFSET(res);
    607    int32_t length = 0;
    608    switch(RES_GET_TYPE(res)) {
    609    case URES_TABLE:
    610        if (offset != 0) {  // empty if offset==0
    611            keys16 = reinterpret_cast<const uint16_t*>(getData().pRoot + offset);
    612            length = *keys16++;
    613            items32 = reinterpret_cast<const Resource*>(keys16 + length + (~length & 1));
    614        }
    615        break;
    616    case URES_TABLE16:
    617        keys16 = getData().p16BitUnits+offset;
    618        length = *keys16++;
    619        items16 = keys16 + length;
    620        break;
    621    case URES_TABLE32:
    622        if (offset != 0) {  // empty if offset==0
    623            keys32 = getData().pRoot+offset;
    624            length = *keys32++;
    625            items32 = reinterpret_cast<const Resource*>(keys32) + length;
    626        }
    627        break;
    628    default:
    629        errorCode = U_RESOURCE_TYPE_MISMATCH;
    630        return {};
    631    }
    632    return ResourceTable(keys16, keys32, items16, items32, length, fTraceInfo);
    633 }
    634 
    635 UBool ResourceDataValue::isNoInheritanceMarker() const {
    636    return ::isNoInheritanceMarker(&getData(), res);
    637 }
    638 
    639 int32_t ResourceDataValue::getStringArray(UnicodeString *dest, int32_t capacity,
    640                                          UErrorCode &errorCode) const {
    641    return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode);
    642 }
    643 
    644 int32_t ResourceDataValue::getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity,
    645                                                         UErrorCode &errorCode) const {
    646    if(URES_IS_ARRAY(res)) {
    647        return ::getStringArray(&getData(), getArray(errorCode), dest, capacity, errorCode);
    648    }
    649    if(U_FAILURE(errorCode)) {
    650        return 0;
    651    }
    652    if(dest == nullptr ? capacity != 0 : capacity < 0) {
    653        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    654        return 0;
    655    }
    656    if(capacity < 1) {
    657        errorCode = U_BUFFER_OVERFLOW_ERROR;
    658        return 1;
    659    }
    660    int32_t sLength;
    661    const char16_t *s = res_getString(fTraceInfo, &getData(), res, &sLength);
    662    if(s != nullptr) {
    663        dest[0].setTo(true, s, sLength);
    664        return 1;
    665    }
    666    errorCode = U_RESOURCE_TYPE_MISMATCH;
    667    return 0;
    668 }
    669 
    670 UnicodeString ResourceDataValue::getStringOrFirstOfArray(UErrorCode &errorCode) const {
    671    UnicodeString us;
    672    if(U_FAILURE(errorCode)) {
    673        return us;
    674    }
    675    int32_t sLength;
    676    const char16_t *s = res_getString(fTraceInfo, &getData(), res, &sLength);
    677    if(s != nullptr) {
    678        us.setTo(true, s, sLength);
    679        return us;
    680    }
    681    ResourceArray array = getArray(errorCode);
    682    if(U_FAILURE(errorCode)) {
    683        return us;
    684    }
    685    if(array.getSize() > 0) {
    686        // Tracing is already performed above (unimportant for trace that this is an array)
    687        s = res_getStringNoTrace(&getData(), array.internalGetResource(&getData(), 0), &sLength);
    688        if(s != nullptr) {
    689            us.setTo(true, s, sLength);
    690            return us;
    691        }
    692    }
    693    errorCode = U_RESOURCE_TYPE_MISMATCH;
    694    return us;
    695 }
    696 
    697 U_NAMESPACE_END
    698 
    699 static Resource
    700 makeResourceFrom16(const ResourceData *pResData, int32_t res16) {
    701    if(res16<pResData->poolStringIndex16Limit) {
    702        // Pool string, nothing to do.
    703    } else {
    704        // Local string, adjust the 16-bit offset to a regular one,
    705        // with a larger pool string index limit.
    706        res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexLimit;
    707    }
    708    return URES_MAKE_RESOURCE(URES_STRING_V2, res16);
    709 }
    710 
    711 U_CAPI Resource U_EXPORT2
    712 res_getTableItemByKey(const ResourceData *pResData, Resource table,
    713                      int32_t *indexR, const char **key) {
    714    uint32_t offset=RES_GET_OFFSET(table);
    715    int32_t length;
    716    int32_t idx;
    717    if(key == nullptr || *key == nullptr) {
    718        return RES_BOGUS;
    719    }
    720    switch(RES_GET_TYPE(table)) {
    721    case URES_TABLE: {
    722        if (offset!=0) { /* empty if offset==0 */
    723            const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
    724            length=*p++;
    725            *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
    726            if(idx>=0) {
    727                const Resource *p32=(const Resource *)(p+length+(~length&1));
    728                return p32[idx];
    729            }
    730        }
    731        break;
    732    }
    733    case URES_TABLE16: {
    734        const uint16_t *p=pResData->p16BitUnits+offset;
    735        length=*p++;
    736        *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
    737        if(idx>=0) {
    738            return makeResourceFrom16(pResData, p[length+idx]);
    739        }
    740        break;
    741    }
    742    case URES_TABLE32: {
    743        if (offset!=0) { /* empty if offset==0 */
    744            const int32_t *p= pResData->pRoot+offset;
    745            length=*p++;
    746            *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key);
    747            if(idx>=0) {
    748                return (Resource)p[length+idx];
    749            }
    750        }
    751        break;
    752    }
    753    default:
    754        break;
    755    }
    756    return RES_BOGUS;
    757 }
    758 
    759 U_CAPI Resource U_EXPORT2
    760 res_getTableItemByIndex(const ResourceData *pResData, Resource table,
    761                        int32_t indexR, const char **key) {
    762    uint32_t offset=RES_GET_OFFSET(table);
    763    int32_t length;
    764    if (indexR < 0) {
    765        return RES_BOGUS;
    766    }
    767    switch(RES_GET_TYPE(table)) {
    768    case URES_TABLE: {
    769        if (offset != 0) { /* empty if offset==0 */
    770            const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
    771            length=*p++;
    772            if(indexR<length) {
    773                const Resource *p32=(const Resource *)(p+length+(~length&1));
    774                if(key!=nullptr) {
    775                    *key=RES_GET_KEY16(pResData, p[indexR]);
    776                }
    777                return p32[indexR];
    778            }
    779        }
    780        break;
    781    }
    782    case URES_TABLE16: {
    783        const uint16_t *p=pResData->p16BitUnits+offset;
    784        length=*p++;
    785        if(indexR<length) {
    786            if(key!=nullptr) {
    787                *key=RES_GET_KEY16(pResData, p[indexR]);
    788            }
    789            return makeResourceFrom16(pResData, p[length+indexR]);
    790        }
    791        break;
    792    }
    793    case URES_TABLE32: {
    794        if (offset != 0) { /* empty if offset==0 */
    795            const int32_t *p= pResData->pRoot+offset;
    796            length=*p++;
    797            if(indexR<length) {
    798                if(key!=nullptr) {
    799                    *key=RES_GET_KEY32(pResData, p[indexR]);
    800                }
    801                return (Resource)p[length+indexR];
    802            }
    803        }
    804        break;
    805    }
    806    default:
    807        break;
    808    }
    809    return RES_BOGUS;
    810 }
    811 
    812 U_CAPI Resource U_EXPORT2
    813 res_getResource(const ResourceData *pResData, const char *key) {
    814    const char *realKey=key;
    815    int32_t idx;
    816    return res_getTableItemByKey(pResData, pResData->rootRes, &idx, &realKey);
    817 }
    818 
    819 
    820 UBool icu::ResourceTable::getKeyAndValue(int32_t i,
    821                                         const char *&key, icu::ResourceValue &value) const {
    822    if(0 <= i && i < length) {
    823        icu::ResourceDataValue &rdValue = static_cast<icu::ResourceDataValue &>(value);
    824        if (keys16 != nullptr) {
    825            key = RES_GET_KEY16(&rdValue.getData(), keys16[i]);
    826        } else {
    827            key = RES_GET_KEY32(&rdValue.getData(), keys32[i]);
    828        }
    829        Resource res;
    830        if (items16 != nullptr) {
    831            res = makeResourceFrom16(&rdValue.getData(), items16[i]);
    832        } else {
    833            res = items32[i];
    834        }
    835        // Note: the ResourceTracer keeps a reference to the field of this
    836        // ResourceTable. This is OK because the ResourceTable should remain
    837        // alive for the duration that fields are being read from it
    838        // (including nested fields).
    839        rdValue.setResource(res, ResourceTracer(fTraceInfo, key));
    840        return true;
    841    }
    842    return false;
    843 }
    844 
    845 UBool icu::ResourceTable::findValue(const char *key, ResourceValue &value) const {
    846    icu::ResourceDataValue &rdValue = static_cast<icu::ResourceDataValue &>(value);
    847    const char *realKey = nullptr;
    848    int32_t i;
    849    if (keys16 != nullptr) {
    850        i = _res_findTableItem(&rdValue.getData(), keys16, length, key, &realKey);
    851    } else {
    852        i = _res_findTable32Item(&rdValue.getData(), keys32, length, key, &realKey);
    853    }
    854    if (i >= 0) {
    855        Resource res;
    856        if (items16 != nullptr) {
    857            res = makeResourceFrom16(&rdValue.getData(), items16[i]);
    858        } else {
    859            res = items32[i];
    860        }
    861        // Same note about lifetime as in getKeyAndValue().
    862        rdValue.setResource(res, ResourceTracer(fTraceInfo, key));
    863        return true;
    864    }
    865    return false;
    866 }
    867 
    868 U_CAPI Resource U_EXPORT2
    869 res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) {
    870    uint32_t offset=RES_GET_OFFSET(array);
    871    if (indexR < 0) {
    872        return RES_BOGUS;
    873    }
    874    switch(RES_GET_TYPE(array)) {
    875    case URES_ARRAY: {
    876        if (offset!=0) { /* empty if offset==0 */
    877            const int32_t *p= pResData->pRoot+offset;
    878            if(indexR<*p) {
    879                return (Resource)p[1+indexR];
    880            }
    881        }
    882        break;
    883    }
    884    case URES_ARRAY16: {
    885        const uint16_t *p=pResData->p16BitUnits+offset;
    886        if(indexR<*p) {
    887            return makeResourceFrom16(pResData, p[1+indexR]);
    888        }
    889        break;
    890    }
    891    default:
    892        break;
    893    }
    894    return RES_BOGUS;
    895 }
    896 
    897 uint32_t icu::ResourceArray::internalGetResource(const ResourceData *pResData, int32_t i) const {
    898    if (items16 != nullptr) {
    899        return makeResourceFrom16(pResData, items16[i]);
    900    } else {
    901        return items32[i];
    902    }
    903 }
    904 
    905 UBool icu::ResourceArray::getValue(int32_t i, icu::ResourceValue &value) const {
    906    if(0 <= i && i < length) {
    907        icu::ResourceDataValue &rdValue = static_cast<icu::ResourceDataValue &>(value);
    908        // Note: the ResourceTracer keeps a reference to the field of this
    909        // ResourceArray. This is OK because the ResourceArray should remain
    910        // alive for the duration that fields are being read from it
    911        // (including nested fields).
    912        rdValue.setResource(
    913            internalGetResource(&rdValue.getData(), i),
    914            ResourceTracer(fTraceInfo, i));
    915        return true;
    916    }
    917    return false;
    918 }
    919 
    920 U_CFUNC Resource
    921 res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) {
    922  char *pathP = *path, *nextSepP = *path;
    923  char *closeIndex = nullptr;
    924  Resource t1 = r;
    925  Resource t2;
    926  int32_t indexR = 0;
    927  UResType type = (UResType)RES_GET_TYPE(t1);
    928 
    929  /* if you come in with an empty path, you'll be getting back the same resource */
    930  if(!uprv_strlen(pathP)) {
    931      return r;
    932  }
    933 
    934  /* one needs to have an aggregate resource in order to search in it */
    935  if(!URES_IS_CONTAINER(type)) {
    936      return RES_BOGUS;
    937  }
    938  
    939  while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) {
    940    /* Iteration stops if: the path has been consumed, we found a non-existing
    941     * resource (t1 == RES_BOGUS) or we found a scalar resource (including alias)
    942     */
    943    nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR);
    944    /* if there are more separators, terminate string 
    945     * and set path to the remaining part of the string
    946     */
    947    if(nextSepP != nullptr) {
    948      if(nextSepP == pathP) {
    949        // Empty key string.
    950        return RES_BOGUS;
    951      }
    952      *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */
    953      *path = nextSepP+1;
    954    } else {
    955      *path = uprv_strchr(pathP, 0);
    956    }
    957 
    958    /* if the resource is a table */
    959    /* try the key based access */
    960    if(URES_IS_TABLE(type)) {
    961      *key = pathP;
    962      t2 = res_getTableItemByKey(pResData, t1, &indexR, key);
    963    } else if(URES_IS_ARRAY(type)) {
    964      indexR = uprv_strtol(pathP, &closeIndex, 10);
    965      if(indexR >= 0 && *closeIndex == 0) {
    966        t2 = res_getArrayItem(pResData, t1, indexR);
    967      } else {
    968        t2 = RES_BOGUS; /* have an array, but don't have a valid index */
    969      }
    970      *key = nullptr;
    971    } else { /* can't do much here, except setting t2 to bogus */
    972      t2 = RES_BOGUS;
    973    }
    974    t1 = t2;
    975    type = (UResType)RES_GET_TYPE(t1);
    976    /* position pathP to next resource key/index */
    977    pathP = *path;
    978  }
    979 
    980  return t1;
    981 }
    982 
    983 /* resource bundle swapping ------------------------------------------------- */
    984 
    985 /*
    986 * Need to always enumerate the entire item tree,
    987 * track the lowest address of any item to use as the limit for char keys[],
    988 * track the highest address of any item to return the size of the data.
    989 *
    990 * We should have thought of storing those in the data...
    991 * It is possible to extend the data structure by putting additional values
    992 * in places that are inaccessible by ordinary enumeration of the item tree.
    993 * For example, additional integers could be stored at the beginning or
    994 * end of the key strings; this could be indicated by a minor version number,
    995 * and the data swapping would have to know about these values.
    996 *
    997 * The data structure does not forbid keys to be shared, so we must swap
    998 * all keys once instead of each key when it is referenced.
    999 *
   1000 * These swapping functions assume that a resource bundle always has a length
   1001 * that is a multiple of 4 bytes.
   1002 * Currently, this is trivially true because genrb writes bundle tree leaves
   1003 * physically first, before their branches, so that the root table with its
   1004 * array of resource items (uint32_t values) is always last.
   1005 */
   1006 
   1007 /* definitions for table sorting ------------------------ */
   1008 
   1009 /*
   1010 * row of a temporary array
   1011 *
   1012 * gets platform-endian key string indexes and sorting indexes;
   1013 * after sorting this array by keys, the actual key/value arrays are permutated
   1014 * according to the sorting indexes
   1015 */
   1016 typedef struct Row {
   1017    int32_t keyIndex, sortIndex;
   1018 } Row;
   1019 
   1020 static int32_t U_CALLCONV
   1021 ures_compareRows(const void *context, const void *left, const void *right) {
   1022    const char* keyChars = static_cast<const char*>(context);
   1023    return static_cast<int32_t>(uprv_strcmp(keyChars + static_cast<const Row*>(left)->keyIndex,
   1024                                            keyChars + static_cast<const Row*>(right)->keyIndex));
   1025 }
   1026 
   1027 typedef struct TempTable {
   1028    const char *keyChars;
   1029    Row *rows;
   1030    int32_t *resort;
   1031    uint32_t *resFlags;
   1032    int32_t localKeyLimit;
   1033    uint8_t majorFormatVersion;
   1034 } TempTable;
   1035 
   1036 enum {
   1037    STACK_ROW_CAPACITY=200
   1038 };
   1039 
   1040 /* The table item key string is not locally available. */
   1041 static const char *const gUnknownKey="";
   1042 
   1043 #if !UCONFIG_NO_COLLATION
   1044 // resource table key for collation binaries
   1045 static const char16_t gCollationBinKey[]=u"%%CollationBin";
   1046 #endif
   1047 
   1048 /*
   1049 * swap one resource item
   1050 */
   1051 static void
   1052 ures_swapResource(const UDataSwapper *ds,
   1053                  const Resource *inBundle, Resource *outBundle,
   1054                  Resource res, /* caller swaps res itself */
   1055                  const char *key,
   1056                  TempTable *pTempTable,
   1057                  UErrorCode *pErrorCode) {
   1058    const Resource *p;
   1059    Resource *q;
   1060    int32_t offset, count;
   1061 
   1062    switch(RES_GET_TYPE(res)) {
   1063    case URES_TABLE16:
   1064    case URES_STRING_V2:
   1065    case URES_INT:
   1066    case URES_ARRAY16:
   1067        /* integer, or points to 16-bit units, nothing to do here */
   1068        return;
   1069    default:
   1070        break;
   1071    }
   1072 
   1073    /* all other types use an offset to point to their data */
   1074    offset = static_cast<int32_t>(RES_GET_OFFSET(res));
   1075    if(offset==0) {
   1076        /* special offset indicating an empty item */
   1077        return;
   1078    }
   1079    if (pTempTable->resFlags[offset >> 5] & (static_cast<uint32_t>(1) << (offset & 0x1f))) {
   1080        /* we already swapped this resource item */
   1081        return;
   1082    } else {
   1083        /* mark it as swapped now */
   1084        pTempTable->resFlags[offset >> 5] |= static_cast<uint32_t>(1) << (offset & 0x1f);
   1085    }
   1086 
   1087    p=inBundle+offset;
   1088    q=outBundle+offset;
   1089 
   1090    switch(RES_GET_TYPE(res)) {
   1091    case URES_ALIAS:
   1092        /* physically same value layout as string, fall through */
   1093        U_FALLTHROUGH;
   1094    case URES_STRING:
   1095        count = udata_readInt32(ds, static_cast<int32_t>(*p));
   1096        /* swap length */
   1097        ds->swapArray32(ds, p, 4, q, pErrorCode);
   1098        /* swap each char16_t (the terminating NUL would not change) */
   1099        ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode);
   1100        break;
   1101    case URES_BINARY:
   1102        count = udata_readInt32(ds, static_cast<int32_t>(*p));
   1103        /* swap length */
   1104        ds->swapArray32(ds, p, 4, q, pErrorCode);
   1105        /* no need to swap or copy bytes - ures_swap() copied them all */
   1106 
   1107        /* swap known formats */
   1108 #if !UCONFIG_NO_COLLATION
   1109        if( key!=nullptr &&  /* the binary is in a table */
   1110            (key!=gUnknownKey ?
   1111                /* its table key string is "%%CollationBin" */
   1112                0==ds->compareInvChars(ds, key, -1,
   1113                                       gCollationBinKey, UPRV_LENGTHOF(gCollationBinKey)-1) :
   1114                /* its table key string is unknown but it looks like a collation binary */
   1115                ucol_looksLikeCollationBinary(ds, p+1, count))
   1116        ) {
   1117            ucol_swap(ds, p+1, count, q+1, pErrorCode);
   1118        }
   1119 #endif
   1120        break;
   1121    case URES_TABLE:
   1122    case URES_TABLE32:
   1123        {
   1124            const uint16_t *pKey16;
   1125            uint16_t *qKey16;
   1126 
   1127            const int32_t *pKey32;
   1128            int32_t *qKey32;
   1129 
   1130            Resource item;
   1131            int32_t i, oldIndex;
   1132 
   1133            if(RES_GET_TYPE(res)==URES_TABLE) {
   1134                /* get table item count */
   1135                pKey16 = reinterpret_cast<const uint16_t*>(p);
   1136                qKey16 = reinterpret_cast<uint16_t*>(q);
   1137                count=ds->readUInt16(*pKey16);
   1138 
   1139                pKey32=qKey32=nullptr;
   1140 
   1141                /* swap count */
   1142                ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode);
   1143 
   1144                offset+=((1+count)+1)/2;
   1145            } else {
   1146                /* get table item count */
   1147                pKey32 = reinterpret_cast<const int32_t*>(p);
   1148                qKey32 = reinterpret_cast<int32_t*>(q);
   1149                count=udata_readInt32(ds, *pKey32);
   1150 
   1151                pKey16=qKey16=nullptr;
   1152 
   1153                /* swap count */
   1154                ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode);
   1155 
   1156                offset+=1+count;
   1157            }
   1158 
   1159            if(count==0) {
   1160                break;
   1161            }
   1162 
   1163            p=inBundle+offset; /* pointer to table resources */
   1164            q=outBundle+offset;
   1165 
   1166            /* recurse */
   1167            for(i=0; i<count; ++i) {
   1168                const char *itemKey=gUnknownKey;
   1169                if(pKey16!=nullptr) {
   1170                    int32_t keyOffset=ds->readUInt16(pKey16[i]);
   1171                    if(keyOffset<pTempTable->localKeyLimit) {
   1172                        itemKey = reinterpret_cast<const char*>(outBundle) + keyOffset;
   1173                    }
   1174                } else {
   1175                    int32_t keyOffset=udata_readInt32(ds, pKey32[i]);
   1176                    if(keyOffset>=0) {
   1177                        itemKey = reinterpret_cast<const char*>(outBundle) + keyOffset;
   1178                    }
   1179                }
   1180                item=ds->readUInt32(p[i]);
   1181                ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode);
   1182                if(U_FAILURE(*pErrorCode)) {
   1183                    udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n",
   1184                                     res, i, item);
   1185                    return;
   1186                }
   1187            }
   1188 
   1189            if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) {
   1190                /* no need to sort, just swap the offset/value arrays */
   1191                if(pKey16!=nullptr) {
   1192                    ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode);
   1193                    ds->swapArray32(ds, p, count*4, q, pErrorCode);
   1194                } else {
   1195                    /* swap key offsets and items as one array */
   1196                    ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode);
   1197                }
   1198                break;
   1199            }
   1200 
   1201            /*
   1202             * We need to sort tables by outCharset key strings because they
   1203             * sort differently for different charset families.
   1204             * ures_swap() already set pTempTable->keyChars appropriately.
   1205             * First we set up a temporary table with the key indexes and
   1206             * sorting indexes and sort that.
   1207             * Then we permutate and copy/swap the actual values.
   1208             */
   1209            if(pKey16!=nullptr) {
   1210                for(i=0; i<count; ++i) {
   1211                    pTempTable->rows[i].keyIndex=ds->readUInt16(pKey16[i]);
   1212                    pTempTable->rows[i].sortIndex=i;
   1213                }
   1214            } else {
   1215                for(i=0; i<count; ++i) {
   1216                    pTempTable->rows[i].keyIndex=udata_readInt32(ds, pKey32[i]);
   1217                    pTempTable->rows[i].sortIndex=i;
   1218                }
   1219            }
   1220            uprv_sortArray(pTempTable->rows, count, sizeof(Row),
   1221                           ures_compareRows, pTempTable->keyChars,
   1222                           false, pErrorCode);
   1223            if(U_FAILURE(*pErrorCode)) {
   1224                udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n",
   1225                                 res, count);
   1226                return;
   1227            }
   1228 
   1229            /*
   1230             * copy/swap/permutate items
   1231             *
   1232             * If we swap in-place, then the permutation must use another
   1233             * temporary array (pTempTable->resort)
   1234             * before the results are copied to the outBundle.
   1235             */
   1236            /* keys */
   1237            if(pKey16!=nullptr) {
   1238                uint16_t *rKey16;
   1239 
   1240                if(pKey16!=qKey16) {
   1241                    rKey16=qKey16;
   1242                } else {
   1243                    rKey16 = reinterpret_cast<uint16_t*>(pTempTable->resort);
   1244                }
   1245                for(i=0; i<count; ++i) {
   1246                    oldIndex=pTempTable->rows[i].sortIndex;
   1247                    ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode);
   1248                }
   1249                if(qKey16!=rKey16) {
   1250                    uprv_memcpy(qKey16, rKey16, 2*count);
   1251                }
   1252            } else {
   1253                int32_t *rKey32;
   1254 
   1255                if(pKey32!=qKey32) {
   1256                    rKey32=qKey32;
   1257                } else {
   1258                    rKey32=pTempTable->resort;
   1259                }
   1260                for(i=0; i<count; ++i) {
   1261                    oldIndex=pTempTable->rows[i].sortIndex;
   1262                    ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode);
   1263                }
   1264                if(qKey32!=rKey32) {
   1265                    uprv_memcpy(qKey32, rKey32, 4*count);
   1266                }
   1267            }
   1268 
   1269            /* resources */
   1270            {
   1271                Resource *r;
   1272 
   1273 
   1274                if(p!=q) {
   1275                    r=q;
   1276                } else {
   1277                    r = reinterpret_cast<Resource*>(pTempTable->resort);
   1278                }
   1279                for(i=0; i<count; ++i) {
   1280                    oldIndex=pTempTable->rows[i].sortIndex;
   1281                    ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode);
   1282                }
   1283                if(q!=r) {
   1284                    uprv_memcpy(q, r, 4*count);
   1285                }
   1286            }
   1287        }
   1288        break;
   1289    case URES_ARRAY:
   1290        {
   1291            Resource item;
   1292            int32_t i;
   1293 
   1294            count = udata_readInt32(ds, static_cast<int32_t>(*p));
   1295            /* swap length */
   1296            ds->swapArray32(ds, p++, 4, q++, pErrorCode);
   1297 
   1298            /* recurse */
   1299            for(i=0; i<count; ++i) {
   1300                item=ds->readUInt32(p[i]);
   1301                ures_swapResource(ds, inBundle, outBundle, item, nullptr, pTempTable, pErrorCode);
   1302                if(U_FAILURE(*pErrorCode)) {
   1303                    udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n",
   1304                                     res, i, item);
   1305                    return;
   1306                }
   1307            }
   1308 
   1309            /* swap items */
   1310            ds->swapArray32(ds, p, 4*count, q, pErrorCode);
   1311        }
   1312        break;
   1313    case URES_INT_VECTOR:
   1314        count = udata_readInt32(ds, static_cast<int32_t>(*p));
   1315        /* swap length and each integer */
   1316        ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode);
   1317        break;
   1318    default:
   1319        /* also catches RES_BOGUS */
   1320        *pErrorCode=U_UNSUPPORTED_ERROR;
   1321        break;
   1322    }
   1323 }
   1324 
   1325 U_CAPI int32_t U_EXPORT2
   1326 ures_swap(const UDataSwapper *ds,
   1327          const void *inData, int32_t length, void *outData,
   1328          UErrorCode *pErrorCode) {
   1329    const UDataInfo *pInfo;
   1330    const Resource *inBundle;
   1331    Resource rootRes;
   1332    int32_t headerSize, maxTableLength;
   1333 
   1334    Row rows[STACK_ROW_CAPACITY];
   1335    int32_t resort[STACK_ROW_CAPACITY];
   1336    TempTable tempTable;
   1337 
   1338    const int32_t *inIndexes;
   1339 
   1340    /* the following integers count Resource item offsets (4 bytes each), not bytes */
   1341    int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top;
   1342 
   1343    /* udata_swapDataHeader checks the arguments */
   1344    headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
   1345    if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) {
   1346        return 0;
   1347    }
   1348 
   1349    /* check data format and format version */
   1350    pInfo=(const UDataInfo *)((const char *)inData+4);
   1351    if(!(
   1352        pInfo->dataFormat[0]==0x52 &&   /* dataFormat="ResB" */
   1353        pInfo->dataFormat[1]==0x65 &&
   1354        pInfo->dataFormat[2]==0x73 &&
   1355        pInfo->dataFormat[3]==0x42 &&
   1356        /* formatVersion 1.1+ or 2.x or 3.x */
   1357        ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) ||
   1358            pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3)
   1359    )) {
   1360        udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n",
   1361                         pInfo->dataFormat[0], pInfo->dataFormat[1],
   1362                         pInfo->dataFormat[2], pInfo->dataFormat[3],
   1363                         pInfo->formatVersion[0], pInfo->formatVersion[1]);
   1364        *pErrorCode=U_UNSUPPORTED_ERROR;
   1365        return 0;
   1366    }
   1367    tempTable.majorFormatVersion=pInfo->formatVersion[0];
   1368 
   1369    /* a resource bundle must contain at least one resource item */
   1370    if(length<0) {
   1371        bundleLength=-1;
   1372    } else {
   1373        bundleLength=(length-headerSize)/4;
   1374 
   1375        /* formatVersion 1.1 must have a root item and at least 5 indexes */
   1376        if(bundleLength<(1+5)) {
   1377            udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n",
   1378                             length-headerSize);
   1379            *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
   1380            return 0;
   1381        }
   1382    }
   1383 
   1384    inBundle=(const Resource *)((const char *)inData+headerSize);
   1385    rootRes=ds->readUInt32(*inBundle);
   1386 
   1387    /* formatVersion 1.1 adds the indexes[] array */
   1388    inIndexes=(const int32_t *)(inBundle+1);
   1389 
   1390    indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff;
   1391    if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
   1392        udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n");
   1393        *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
   1394        return 0;
   1395    }
   1396    keysBottom=1+indexLength;
   1397    keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]);
   1398    if(indexLength>URES_INDEX_16BIT_TOP) {
   1399        resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]);
   1400    } else {
   1401        resBottom=keysTop;
   1402    }
   1403    top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]);
   1404    maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]);
   1405 
   1406    if(0<=bundleLength && bundleLength<top) {
   1407        udata_printError(ds, "ures_swap(): resource top %d exceeds bundle length %d\n",
   1408                         top, bundleLength);
   1409        *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
   1410        return 0;
   1411    }
   1412    if(keysTop>(1+indexLength)) {
   1413        tempTable.localKeyLimit=keysTop<<2;
   1414    } else {
   1415        tempTable.localKeyLimit=0;
   1416    }
   1417 
   1418    if(length>=0) {
   1419        Resource *outBundle=(Resource *)((char *)outData+headerSize);
   1420 
   1421        /* track which resources we have already swapped */
   1422        uint32_t stackResFlags[STACK_ROW_CAPACITY];
   1423        int32_t resFlagsLength;
   1424 
   1425        /*
   1426         * We need one bit per 4 resource bundle bytes so that we can track
   1427         * every possible Resource for whether we have swapped it already.
   1428         * Multiple Resource words can refer to the same bundle offsets
   1429         * for sharing identical values.
   1430         * We could optimize this by allocating only for locations above
   1431         * where Resource values are stored (above keys & strings).
   1432         */
   1433        resFlagsLength=(length+31)>>5;          /* number of bytes needed */
   1434        resFlagsLength=(resFlagsLength+3)&~3;   /* multiple of 4 bytes for uint32_t */
   1435        if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) {
   1436            tempTable.resFlags=stackResFlags;
   1437        } else {
   1438            tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength);
   1439            if(tempTable.resFlags==nullptr) {
   1440                udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n");
   1441                *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
   1442                return 0;
   1443            }
   1444        }
   1445        uprv_memset(tempTable.resFlags, 0, resFlagsLength);
   1446 
   1447        /* copy the bundle for binary and inaccessible data */
   1448        if(inData!=outData) {
   1449            uprv_memcpy(outBundle, inBundle, 4*top);
   1450        }
   1451 
   1452        /* swap the key strings, but not the padding bytes (0xaa) after the last string and its NUL */
   1453        udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom),
   1454                                    outBundle+keysBottom, pErrorCode);
   1455        if(U_FAILURE(*pErrorCode)) {
   1456            udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom));
   1457            if(tempTable.resFlags!=stackResFlags) {
   1458                uprv_free(tempTable.resFlags);
   1459            }
   1460            return 0;
   1461        }
   1462 
   1463        /* swap the 16-bit units (strings, table16, array16) */
   1464        if(keysTop<resBottom) {
   1465            ds->swapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode);
   1466            if(U_FAILURE(*pErrorCode)) {
   1467                udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop));
   1468                if(tempTable.resFlags!=stackResFlags) {
   1469                    uprv_free(tempTable.resFlags);
   1470                }
   1471                return 0;
   1472            }
   1473        }
   1474 
   1475        /* allocate the temporary table for sorting resource tables */
   1476        tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */
   1477        if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) {
   1478            tempTable.rows=rows;
   1479            tempTable.resort=resort;
   1480        } else {
   1481            tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4);
   1482            if(tempTable.rows==nullptr) {
   1483                udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n",
   1484                                 maxTableLength);
   1485                *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
   1486                if(tempTable.resFlags!=stackResFlags) {
   1487                    uprv_free(tempTable.resFlags);
   1488                }
   1489                return 0;
   1490            }
   1491            tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength);
   1492        }
   1493 
   1494        /* swap the resources */
   1495        ures_swapResource(ds, inBundle, outBundle, rootRes, nullptr, &tempTable, pErrorCode);
   1496        if(U_FAILURE(*pErrorCode)) {
   1497            udata_printError(ds, "ures_swapResource(root res=%08x) failed\n",
   1498                             rootRes);
   1499        }
   1500 
   1501        if(tempTable.rows!=rows) {
   1502            uprv_free(tempTable.rows);
   1503        }
   1504        if(tempTable.resFlags!=stackResFlags) {
   1505            uprv_free(tempTable.resFlags);
   1506        }
   1507 
   1508        /* swap the root resource and indexes */
   1509        ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode);
   1510    }
   1511 
   1512    return headerSize+4*top;
   1513 }