1 // 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: UTF-8 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 if (indexR < 0) { 762 return RES_BOGUS; 763 } 764 switch(RES_GET_TYPE(table)) { 765 case URES_TABLE: { 766 if (offset != 0) { /* empty if offset==0 */ 767 const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset); 768 length=*p++; 769 if(indexR<length) { 770 const Resource *p32=(const Resource *)(p+length+(~length&1)); 771 if(key!=NULL) { 772 *key=RES_GET_KEY16(pResData, p[indexR]); 773 } 774 return p32[indexR]; 775 } 776 } 777 break; 778 } 779 case URES_TABLE16: { 780 const uint16_t *p=pResData->p16BitUnits+offset; 781 length=*p++; 782 if(indexR<length) { 783 if(key!=NULL) { 784 *key=RES_GET_KEY16(pResData, p[indexR]); 785 } 786 return makeResourceFrom16(pResData, p[length+indexR]); 787 } 788 break; 789 } 790 case URES_TABLE32: { 791 if (offset != 0) { /* empty if offset==0 */ 792 const int32_t *p= pResData->pRoot+offset; 793 length=*p++; 794 if(indexR<length) { 795 if(key!=NULL) { 796 *key=RES_GET_KEY32(pResData, p[indexR]); 797 } 798 return (Resource)p[length+indexR]; 799 } 800 } 801 break; 802 } 803 default: 804 break; 805 } 806 return RES_BOGUS; 807 } 808 809 U_CAPI Resource U_EXPORT2 810 res_getResource(const ResourceData *pResData, const char *key) { 811 const char *realKey=key; 812 int32_t idx; 813 return res_getTableItemByKey(pResData, pResData->rootRes, &idx, &realKey); 814 } 815 816 817 UBool icu::ResourceTable::getKeyAndValue(int32_t i, 818 const char *&key, icu::ResourceValue &value) const { 819 if(0 <= i && i < length) { 820 icu::ResourceDataValue &rdValue = static_cast<icu::ResourceDataValue &>(value); 821 if (keys16 != NULL) { 822 key = RES_GET_KEY16(rdValue.pResData, keys16[i]); 823 } else { 824 key = RES_GET_KEY32(rdValue.pResData, keys32[i]); 825 } 826 Resource res; 827 if (items16 != NULL) { 828 res = makeResourceFrom16(rdValue.pResData, items16[i]); 829 } else { 830 res = items32[i]; 831 } 832 rdValue.setResource(res); 833 return TRUE; 834 } 835 return FALSE; 836 } 837 838 U_CAPI Resource U_EXPORT2 839 res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) { 840 uint32_t offset=RES_GET_OFFSET(array); 841 if (indexR < 0) { 842 return RES_BOGUS; 843 } 844 switch(RES_GET_TYPE(array)) { 845 case URES_ARRAY: { 846 if (offset!=0) { /* empty if offset==0 */ 847 const int32_t *p= pResData->pRoot+offset; 848 if(indexR<*p) { 849 return (Resource)p[1+indexR]; 850 } 851 } 852 break; 853 } 854 case URES_ARRAY16: { 855 const uint16_t *p=pResData->p16BitUnits+offset; 856 if(indexR<*p) { 857 return makeResourceFrom16(pResData, p[1+indexR]); 858 } 859 break; 860 } 861 default: 862 break; 863 } 864 return RES_BOGUS; 865 } 866 867 uint32_t icu::ResourceArray::internalGetResource(const ResourceData *pResData, int32_t i) const { 868 if (items16 != NULL) { 869 return makeResourceFrom16(pResData, items16[i]); 870 } else { 871 return items32[i]; 872 } 873 } 874 875 UBool icu::ResourceArray::getValue(int32_t i, icu::ResourceValue &value) const { 876 if(0 <= i && i < length) { 877 icu::ResourceDataValue &rdValue = static_cast<icu::ResourceDataValue &>(value); 878 rdValue.setResource(internalGetResource(rdValue.pResData, i)); 879 return TRUE; 880 } 881 return FALSE; 882 } 883 884 U_CFUNC Resource 885 res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) { 886 char *pathP = *path, *nextSepP = *path; 887 char *closeIndex = NULL; 888 Resource t1 = r; 889 Resource t2; 890 int32_t indexR = 0; 891 UResType type = (UResType)RES_GET_TYPE(t1); 892 893 /* if you come in with an empty path, you'll be getting back the same resource */ 894 if(!uprv_strlen(pathP)) { 895 return r; 896 } 897 898 /* one needs to have an aggregate resource in order to search in it */ 899 if(!URES_IS_CONTAINER(type)) { 900 return RES_BOGUS; 901 } 902 903 while(nextSepP && *pathP && t1 != RES_BOGUS && URES_IS_CONTAINER(type)) { 904 /* Iteration stops if: the path has been consumed, we found a non-existing 905 * resource (t1 == RES_BOGUS) or we found a scalar resource (including alias) 906 */ 907 nextSepP = uprv_strchr(pathP, RES_PATH_SEPARATOR); 908 /* if there are more separators, terminate string 909 * and set path to the remaining part of the string 910 */ 911 if(nextSepP != NULL) { 912 if(nextSepP == pathP) { 913 // Empty key string. 914 return RES_BOGUS; 915 } 916 *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */ 917 *path = nextSepP+1; 918 } else { 919 *path = uprv_strchr(pathP, 0); 920 } 921 922 /* if the resource is a table */ 923 /* try the key based access */ 924 if(URES_IS_TABLE(type)) { 925 *key = pathP; 926 t2 = res_getTableItemByKey(pResData, t1, &indexR, key); 927 if(t2 == RES_BOGUS) { 928 /* if we fail to get the resource by key, maybe we got an index */ 929 indexR = uprv_strtol(pathP, &closeIndex, 10); 930 if(indexR >= 0 && *closeIndex == 0) { 931 /* if we indeed have an index, try to get the item by index */ 932 t2 = res_getTableItemByIndex(pResData, t1, indexR, key); 933 } // else t2 is already RES_BOGUS 934 } 935 } else if(URES_IS_ARRAY(type)) { 936 indexR = uprv_strtol(pathP, &closeIndex, 10); 937 if(indexR >= 0 && *closeIndex == 0) { 938 t2 = res_getArrayItem(pResData, t1, indexR); 939 } else { 940 t2 = RES_BOGUS; /* have an array, but don't have a valid index */ 941 } 942 *key = NULL; 943 } else { /* can't do much here, except setting t2 to bogus */ 944 t2 = RES_BOGUS; 945 } 946 t1 = t2; 947 type = (UResType)RES_GET_TYPE(t1); 948 /* position pathP to next resource key/index */ 949 pathP = *path; 950 } 951 952 return t1; 953 } 954 955 /* resource bundle swapping ------------------------------------------------- */ 956 957 /* 958 * Need to always enumerate the entire item tree, 959 * track the lowest address of any item to use as the limit for char keys[], 960 * track the highest address of any item to return the size of the data. 961 * 962 * We should have thought of storing those in the data... 963 * It is possible to extend the data structure by putting additional values 964 * in places that are inaccessible by ordinary enumeration of the item tree. 965 * For example, additional integers could be stored at the beginning or 966 * end of the key strings; this could be indicated by a minor version number, 967 * and the data swapping would have to know about these values. 968 * 969 * The data structure does not forbid keys to be shared, so we must swap 970 * all keys once instead of each key when it is referenced. 971 * 972 * These swapping functions assume that a resource bundle always has a length 973 * that is a multiple of 4 bytes. 974 * Currently, this is trivially true because genrb writes bundle tree leaves 975 * physically first, before their branches, so that the root table with its 976 * array of resource items (uint32_t values) is always last. 977 */ 978 979 /* definitions for table sorting ------------------------ */ 980 981 /* 982 * row of a temporary array 983 * 984 * gets platform-endian key string indexes and sorting indexes; 985 * after sorting this array by keys, the actual key/value arrays are permutated 986 * according to the sorting indexes 987 */ 988 typedef struct Row { 989 int32_t keyIndex, sortIndex; 990 } Row; 991 992 static int32_t U_CALLCONV 993 ures_compareRows(const void *context, const void *left, const void *right) { 994 const char *keyChars=(const char *)context; 995 return (int32_t)uprv_strcmp(keyChars+((const Row *)left)->keyIndex, 996 keyChars+((const Row *)right)->keyIndex); 997 } 998 999 typedef struct TempTable { 1000 const char *keyChars; 1001 Row *rows; 1002 int32_t *resort; 1003 uint32_t *resFlags; 1004 int32_t localKeyLimit; 1005 uint8_t majorFormatVersion; 1006 } TempTable; 1007 1008 enum { 1009 STACK_ROW_CAPACITY=200 1010 }; 1011 1012 /* The table item key string is not locally available. */ 1013 static const char *const gUnknownKey=""; 1014 1015 /* resource table key for collation binaries: "%%CollationBin" */ 1016 static const UChar gCollationBinKey[]={ 1017 0x25, 0x25, 1018 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 1019 0x42, 0x69, 0x6e, 1020 0 1021 }; 1022 1023 /* 1024 * swap one resource item 1025 */ 1026 static void 1027 ures_swapResource(const UDataSwapper *ds, 1028 const Resource *inBundle, Resource *outBundle, 1029 Resource res, /* caller swaps res itself */ 1030 const char *key, 1031 TempTable *pTempTable, 1032 UErrorCode *pErrorCode) { 1033 const Resource *p; 1034 Resource *q; 1035 int32_t offset, count; 1036 1037 switch(RES_GET_TYPE(res)) { 1038 case URES_TABLE16: 1039 case URES_STRING_V2: 1040 case URES_INT: 1041 case URES_ARRAY16: 1042 /* integer, or points to 16-bit units, nothing to do here */ 1043 return; 1044 default: 1045 break; 1046 } 1047 1048 /* all other types use an offset to point to their data */ 1049 offset=(int32_t)RES_GET_OFFSET(res); 1050 if(offset==0) { 1051 /* special offset indicating an empty item */ 1052 return; 1053 } 1054 if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) { 1055 /* we already swapped this resource item */ 1056 return; 1057 } else { 1058 /* mark it as swapped now */ 1059 pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f)); 1060 } 1061 1062 p=inBundle+offset; 1063 q=outBundle+offset; 1064 1065 switch(RES_GET_TYPE(res)) { 1066 case URES_ALIAS: 1067 /* physically same value layout as string, fall through */ 1068 U_FALLTHROUGH; 1069 case URES_STRING: 1070 count=udata_readInt32(ds, (int32_t)*p); 1071 /* swap length */ 1072 ds->swapArray32(ds, p, 4, q, pErrorCode); 1073 /* swap each UChar (the terminating NUL would not change) */ 1074 ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode); 1075 break; 1076 case URES_BINARY: 1077 count=udata_readInt32(ds, (int32_t)*p); 1078 /* swap length */ 1079 ds->swapArray32(ds, p, 4, q, pErrorCode); 1080 /* no need to swap or copy bytes - ures_swap() copied them all */ 1081 1082 /* swap known formats */ 1083 #if !UCONFIG_NO_COLLATION 1084 if( key!=NULL && /* the binary is in a table */ 1085 (key!=gUnknownKey ? 1086 /* its table key string is "%%CollationBin" */ 1087 0==ds->compareInvChars(ds, key, -1, 1088 gCollationBinKey, UPRV_LENGTHOF(gCollationBinKey)-1) : 1089 /* its table key string is unknown but it looks like a collation binary */ 1090 ucol_looksLikeCollationBinary(ds, p+1, count)) 1091 ) { 1092 ucol_swap(ds, p+1, count, q+1, pErrorCode); 1093 } 1094 #endif 1095 break; 1096 case URES_TABLE: 1097 case URES_TABLE32: 1098 { 1099 const uint16_t *pKey16; 1100 uint16_t *qKey16; 1101 1102 const int32_t *pKey32; 1103 int32_t *qKey32; 1104 1105 Resource item; 1106 int32_t i, oldIndex; 1107 1108 if(RES_GET_TYPE(res)==URES_TABLE) { 1109 /* get table item count */ 1110 pKey16=(const uint16_t *)p; 1111 qKey16=(uint16_t *)q; 1112 count=ds->readUInt16(*pKey16); 1113 1114 pKey32=qKey32=NULL; 1115 1116 /* swap count */ 1117 ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode); 1118 1119 offset+=((1+count)+1)/2; 1120 } else { 1121 /* get table item count */ 1122 pKey32=(const int32_t *)p; 1123 qKey32=(int32_t *)q; 1124 count=udata_readInt32(ds, *pKey32); 1125 1126 pKey16=qKey16=NULL; 1127 1128 /* swap count */ 1129 ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode); 1130 1131 offset+=1+count; 1132 } 1133 1134 if(count==0) { 1135 break; 1136 } 1137 1138 p=inBundle+offset; /* pointer to table resources */ 1139 q=outBundle+offset; 1140 1141 /* recurse */ 1142 for(i=0; i<count; ++i) { 1143 const char *itemKey=gUnknownKey; 1144 if(pKey16!=NULL) { 1145 int32_t keyOffset=ds->readUInt16(pKey16[i]); 1146 if(keyOffset<pTempTable->localKeyLimit) { 1147 itemKey=(const char *)outBundle+keyOffset; 1148 } 1149 } else { 1150 int32_t keyOffset=udata_readInt32(ds, pKey32[i]); 1151 if(keyOffset>=0) { 1152 itemKey=(const char *)outBundle+keyOffset; 1153 } 1154 } 1155 item=ds->readUInt32(p[i]); 1156 ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode); 1157 if(U_FAILURE(*pErrorCode)) { 1158 udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n", 1159 res, i, item); 1160 return; 1161 } 1162 } 1163 1164 if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) { 1165 /* no need to sort, just swap the offset/value arrays */ 1166 if(pKey16!=NULL) { 1167 ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode); 1168 ds->swapArray32(ds, p, count*4, q, pErrorCode); 1169 } else { 1170 /* swap key offsets and items as one array */ 1171 ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode); 1172 } 1173 break; 1174 } 1175 1176 /* 1177 * We need to sort tables by outCharset key strings because they 1178 * sort differently for different charset families. 1179 * ures_swap() already set pTempTable->keyChars appropriately. 1180 * First we set up a temporary table with the key indexes and 1181 * sorting indexes and sort that. 1182 * Then we permutate and copy/swap the actual values. 1183 */ 1184 if(pKey16!=NULL) { 1185 for(i=0; i<count; ++i) { 1186 pTempTable->rows[i].keyIndex=ds->readUInt16(pKey16[i]); 1187 pTempTable->rows[i].sortIndex=i; 1188 } 1189 } else { 1190 for(i=0; i<count; ++i) { 1191 pTempTable->rows[i].keyIndex=udata_readInt32(ds, pKey32[i]); 1192 pTempTable->rows[i].sortIndex=i; 1193 } 1194 } 1195 uprv_sortArray(pTempTable->rows, count, sizeof(Row), 1196 ures_compareRows, pTempTable->keyChars, 1197 FALSE, pErrorCode); 1198 if(U_FAILURE(*pErrorCode)) { 1199 udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n", 1200 res, count); 1201 return; 1202 } 1203 1204 /* 1205 * copy/swap/permutate items 1206 * 1207 * If we swap in-place, then the permutation must use another 1208 * temporary array (pTempTable->resort) 1209 * before the results are copied to the outBundle. 1210 */ 1211 /* keys */ 1212 if(pKey16!=NULL) { 1213 uint16_t *rKey16; 1214 1215 if(pKey16!=qKey16) { 1216 rKey16=qKey16; 1217 } else { 1218 rKey16=(uint16_t *)pTempTable->resort; 1219 } 1220 for(i=0; i<count; ++i) { 1221 oldIndex=pTempTable->rows[i].sortIndex; 1222 ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode); 1223 } 1224 if(qKey16!=rKey16) { 1225 uprv_memcpy(qKey16, rKey16, 2*count); 1226 } 1227 } else { 1228 int32_t *rKey32; 1229 1230 if(pKey32!=qKey32) { 1231 rKey32=qKey32; 1232 } else { 1233 rKey32=pTempTable->resort; 1234 } 1235 for(i=0; i<count; ++i) { 1236 oldIndex=pTempTable->rows[i].sortIndex; 1237 ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode); 1238 } 1239 if(qKey32!=rKey32) { 1240 uprv_memcpy(qKey32, rKey32, 4*count); 1241 } 1242 } 1243 1244 /* resources */ 1245 { 1246 Resource *r; 1247 1248 1249 if(p!=q) { 1250 r=q; 1251 } else { 1252 r=(Resource *)pTempTable->resort; 1253 } 1254 for(i=0; i<count; ++i) { 1255 oldIndex=pTempTable->rows[i].sortIndex; 1256 ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode); 1257 } 1258 if(q!=r) { 1259 uprv_memcpy(q, r, 4*count); 1260 } 1261 } 1262 } 1263 break; 1264 case URES_ARRAY: 1265 { 1266 Resource item; 1267 int32_t i; 1268 1269 count=udata_readInt32(ds, (int32_t)*p); 1270 /* swap length */ 1271 ds->swapArray32(ds, p++, 4, q++, pErrorCode); 1272 1273 /* recurse */ 1274 for(i=0; i<count; ++i) { 1275 item=ds->readUInt32(p[i]); 1276 ures_swapResource(ds, inBundle, outBundle, item, NULL, pTempTable, pErrorCode); 1277 if(U_FAILURE(*pErrorCode)) { 1278 udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n", 1279 res, i, item); 1280 return; 1281 } 1282 } 1283 1284 /* swap items */ 1285 ds->swapArray32(ds, p, 4*count, q, pErrorCode); 1286 } 1287 break; 1288 case URES_INT_VECTOR: 1289 count=udata_readInt32(ds, (int32_t)*p); 1290 /* swap length and each integer */ 1291 ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode); 1292 break; 1293 default: 1294 /* also catches RES_BOGUS */ 1295 *pErrorCode=U_UNSUPPORTED_ERROR; 1296 break; 1297 } 1298 } 1299 1300 U_CAPI int32_t U_EXPORT2 1301 ures_swap(const UDataSwapper *ds, 1302 const void *inData, int32_t length, void *outData, 1303 UErrorCode *pErrorCode) { 1304 const UDataInfo *pInfo; 1305 const Resource *inBundle; 1306 Resource rootRes; 1307 int32_t headerSize, maxTableLength; 1308 1309 Row rows[STACK_ROW_CAPACITY]; 1310 int32_t resort[STACK_ROW_CAPACITY]; 1311 TempTable tempTable; 1312 1313 const int32_t *inIndexes; 1314 1315 /* the following integers count Resource item offsets (4 bytes each), not bytes */ 1316 int32_t bundleLength, indexLength, keysBottom, keysTop, resBottom, top; 1317 1318 /* udata_swapDataHeader checks the arguments */ 1319 headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); 1320 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { 1321 return 0; 1322 } 1323 1324 /* check data format and format version */ 1325 pInfo=(const UDataInfo *)((const char *)inData+4); 1326 if(!( 1327 pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */ 1328 pInfo->dataFormat[1]==0x65 && 1329 pInfo->dataFormat[2]==0x73 && 1330 pInfo->dataFormat[3]==0x42 && 1331 /* formatVersion 1.1+ or 2.x or 3.x */ 1332 ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) || 1333 pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3) 1334 )) { 1335 udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n", 1336 pInfo->dataFormat[0], pInfo->dataFormat[1], 1337 pInfo->dataFormat[2], pInfo->dataFormat[3], 1338 pInfo->formatVersion[0], pInfo->formatVersion[1]); 1339 *pErrorCode=U_UNSUPPORTED_ERROR; 1340 return 0; 1341 } 1342 tempTable.majorFormatVersion=pInfo->formatVersion[0]; 1343 1344 /* a resource bundle must contain at least one resource item */ 1345 if(length<0) { 1346 bundleLength=-1; 1347 } else { 1348 bundleLength=(length-headerSize)/4; 1349 1350 /* formatVersion 1.1 must have a root item and at least 5 indexes */ 1351 if(bundleLength<(1+5)) { 1352 udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n", 1353 length-headerSize); 1354 *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; 1355 return 0; 1356 } 1357 } 1358 1359 inBundle=(const Resource *)((const char *)inData+headerSize); 1360 rootRes=ds->readUInt32(*inBundle); 1361 1362 /* formatVersion 1.1 adds the indexes[] array */ 1363 inIndexes=(const int32_t *)(inBundle+1); 1364 1365 indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff; 1366 if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) { 1367 udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n"); 1368 *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; 1369 return 0; 1370 } 1371 keysBottom=1+indexLength; 1372 keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]); 1373 if(indexLength>URES_INDEX_16BIT_TOP) { 1374 resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]); 1375 } else { 1376 resBottom=keysTop; 1377 } 1378 top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]); 1379 maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]); 1380 1381 if(0<=bundleLength && bundleLength<top) { 1382 udata_printError(ds, "ures_swap(): resource top %d exceeds bundle length %d\n", 1383 top, bundleLength); 1384 *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; 1385 return 0; 1386 } 1387 if(keysTop>(1+indexLength)) { 1388 tempTable.localKeyLimit=keysTop<<2; 1389 } else { 1390 tempTable.localKeyLimit=0; 1391 } 1392 1393 if(length>=0) { 1394 Resource *outBundle=(Resource *)((char *)outData+headerSize); 1395 1396 /* track which resources we have already swapped */ 1397 uint32_t stackResFlags[STACK_ROW_CAPACITY]; 1398 int32_t resFlagsLength; 1399 1400 /* 1401 * We need one bit per 4 resource bundle bytes so that we can track 1402 * every possible Resource for whether we have swapped it already. 1403 * Multiple Resource words can refer to the same bundle offsets 1404 * for sharing identical values. 1405 * We could optimize this by allocating only for locations above 1406 * where Resource values are stored (above keys & strings). 1407 */ 1408 resFlagsLength=(length+31)>>5; /* number of bytes needed */ 1409 resFlagsLength=(resFlagsLength+3)&~3; /* multiple of 4 bytes for uint32_t */ 1410 if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) { 1411 tempTable.resFlags=stackResFlags; 1412 } else { 1413 tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength); 1414 if(tempTable.resFlags==NULL) { 1415 udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n"); 1416 *pErrorCode=U_MEMORY_ALLOCATION_ERROR; 1417 return 0; 1418 } 1419 } 1420 uprv_memset(tempTable.resFlags, 0, resFlagsLength); 1421 1422 /* copy the bundle for binary and inaccessible data */ 1423 if(inData!=outData) { 1424 uprv_memcpy(outBundle, inBundle, 4*top); 1425 } 1426 1427 /* swap the key strings, but not the padding bytes (0xaa) after the last string and its NUL */ 1428 udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom), 1429 outBundle+keysBottom, pErrorCode); 1430 if(U_FAILURE(*pErrorCode)) { 1431 udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom)); 1432 return 0; 1433 } 1434 1435 /* swap the 16-bit units (strings, table16, array16) */ 1436 if(keysTop<resBottom) { 1437 ds->swapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode); 1438 if(U_FAILURE(*pErrorCode)) { 1439 udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop)); 1440 return 0; 1441 } 1442 } 1443 1444 /* allocate the temporary table for sorting resource tables */ 1445 tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */ 1446 if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) { 1447 tempTable.rows=rows; 1448 tempTable.resort=resort; 1449 } else { 1450 tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4); 1451 if(tempTable.rows==NULL) { 1452 udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n", 1453 maxTableLength); 1454 *pErrorCode=U_MEMORY_ALLOCATION_ERROR; 1455 if(tempTable.resFlags!=stackResFlags) { 1456 uprv_free(tempTable.resFlags); 1457 } 1458 return 0; 1459 } 1460 tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength); 1461 } 1462 1463 /* swap the resources */ 1464 ures_swapResource(ds, inBundle, outBundle, rootRes, NULL, &tempTable, pErrorCode); 1465 if(U_FAILURE(*pErrorCode)) { 1466 udata_printError(ds, "ures_swapResource(root res=%08x) failed\n", 1467 rootRes); 1468 } 1469 1470 if(tempTable.rows!=rows) { 1471 uprv_free(tempTable.rows); 1472 } 1473 if(tempTable.resFlags!=stackResFlags) { 1474 uprv_free(tempTable.resFlags); 1475 } 1476 1477 /* swap the root resource and indexes */ 1478 ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode); 1479 } 1480 1481 return headerSize+4*top; 1482 } 1483