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