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