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