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