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