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