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