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