Line data Source code
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 5 : _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 5 : start=0;
82 5 : limit=length;
83 107 : while(start<limit) {
84 53 : mid = (start + limit) / 2;
85 53 : tableKey = RES_GET_KEY16(pResData, keyOffsets[mid]);
86 53 : if (pResData->useNativeStrcmp) {
87 53 : result = uprv_strcmp(key, tableKey);
88 : } else {
89 0 : result = uprv_compareInvCharsAsAscii(key, tableKey);
90 : }
91 53 : if (result < 0) {
92 33 : limit = mid;
93 20 : } else if (result > 0) {
94 18 : start = mid + 1;
95 : } else {
96 : /* We found it! */
97 2 : *realKey=tableKey;
98 2 : return mid;
99 : }
100 : }
101 3 : return URESDATA_ITEM_NOT_FOUND; /* not found or table is empty. */
102 : }
103 :
104 : static int32_t
105 0 : _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 0 : start=0;
113 0 : limit=length;
114 0 : while(start<limit) {
115 0 : mid = (start + limit) / 2;
116 0 : tableKey = RES_GET_KEY32(pResData, keyOffsets[mid]);
117 0 : if (pResData->useNativeStrcmp) {
118 0 : result = uprv_strcmp(key, tableKey);
119 : } else {
120 0 : result = uprv_compareInvCharsAsAscii(key, tableKey);
121 : }
122 0 : if (result < 0) {
123 0 : limit = mid;
124 0 : } else if (result > 0) {
125 0 : start = mid + 1;
126 : } else {
127 : /* We found it! */
128 0 : *realKey=tableKey;
129 0 : return mid;
130 : }
131 : }
132 0 : 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 1 : isAcceptable(void *context,
139 : const char * /*type*/, const char * /*name*/,
140 : const UDataInfo *pInfo) {
141 1 : uprv_memcpy(context, pInfo->formatVersion, 4);
142 : return (UBool)(
143 2 : pInfo->size>=20 &&
144 2 : pInfo->isBigEndian==U_IS_BIG_ENDIAN &&
145 2 : pInfo->charsetFamily==U_CHARSET_FAMILY &&
146 2 : pInfo->sizeofUChar==U_SIZEOF_UCHAR &&
147 2 : pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */
148 2 : pInfo->dataFormat[1]==0x65 &&
149 2 : pInfo->dataFormat[2]==0x73 &&
150 4 : pInfo->dataFormat[3]==0x42 &&
151 3 : (1<=pInfo->formatVersion[0] && pInfo->formatVersion[0]<=3));
152 : }
153 :
154 : /* semi-public functions ---------------------------------------------------- */
155 :
156 : static void
157 1 : 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 1 : pResData->pRoot=(const int32_t *)inBytes;
164 1 : pResData->rootRes=(Resource)*pResData->pRoot;
165 1 : pResData->p16BitUnits=&gEmpty16;
166 :
167 : /* formatVersion 1.1 must have a root item and at least 5 indexes */
168 1 : if(length>=0 && (length/4)<((formatVersion[0]==1 && formatVersion[1]==0) ? 1 : 1+5)) {
169 0 : *errorCode=U_INVALID_FORMAT_ERROR;
170 0 : res_unload(pResData);
171 0 : return;
172 : }
173 :
174 : /* currently, we accept only resources that have a Table as their roots */
175 1 : rootType=(UResType)RES_GET_TYPE(pResData->rootRes);
176 1 : if(!URES_IS_TABLE(rootType)) {
177 0 : *errorCode=U_INVALID_FORMAT_ERROR;
178 0 : res_unload(pResData);
179 0 : return;
180 : }
181 :
182 1 : if(formatVersion[0]==1 && formatVersion[1]==0) {
183 0 : 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 1 : const int32_t *indexes=pResData->pRoot+1;
187 1 : int32_t indexLength=indexes[URES_INDEX_LENGTH]&0xff;
188 1 : if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
189 0 : *errorCode=U_INVALID_FORMAT_ERROR;
190 0 : res_unload(pResData);
191 0 : return;
192 : }
193 1 : if( length>=0 &&
194 0 : (length<((1+indexLength)<<2) ||
195 0 : length<(indexes[URES_INDEX_BUNDLE_TOP]<<2))
196 : ) {
197 0 : *errorCode=U_INVALID_FORMAT_ERROR;
198 0 : res_unload(pResData);
199 0 : return;
200 : }
201 1 : if(indexes[URES_INDEX_KEYS_TOP]>(1+indexLength)) {
202 1 : pResData->localKeyLimit=indexes[URES_INDEX_KEYS_TOP]<<2;
203 : }
204 1 : 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 0 : pResData->poolStringIndexLimit=(int32_t)((uint32_t)indexes[URES_INDEX_LENGTH]>>8);
210 : }
211 1 : if(indexLength>URES_INDEX_ATTRIBUTES) {
212 1 : int32_t att=indexes[URES_INDEX_ATTRIBUTES];
213 1 : pResData->noFallback=(UBool)(att&URES_ATT_NO_FALLBACK);
214 1 : pResData->isPoolBundle=(UBool)((att&URES_ATT_IS_POOL_BUNDLE)!=0);
215 1 : pResData->usesPoolBundle=(UBool)((att&URES_ATT_USES_POOL_BUNDLE)!=0);
216 1 : pResData->poolStringIndexLimit|=(att&0xf000)<<12; // bits 15..12 -> 27..24
217 1 : pResData->poolStringIndex16Limit=(int32_t)((uint32_t)att>>16);
218 : }
219 1 : if((pResData->isPoolBundle || pResData->usesPoolBundle) && indexLength<=URES_INDEX_POOL_CHECKSUM) {
220 0 : *errorCode=U_INVALID_FORMAT_ERROR;
221 0 : res_unload(pResData);
222 0 : return;
223 : }
224 2 : if( indexLength>URES_INDEX_16BIT_TOP &&
225 1 : indexes[URES_INDEX_16BIT_TOP]>indexes[URES_INDEX_KEYS_TOP]
226 : ) {
227 1 : 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 1 : pResData->useNativeStrcmp=TRUE;
237 : }
238 : }
239 :
240 : U_CAPI void U_EXPORT2
241 0 : res_read(ResourceData *pResData,
242 : const UDataInfo *pInfo, const void *inBytes, int32_t length,
243 : UErrorCode *errorCode) {
244 : UVersionInfo formatVersion;
245 :
246 0 : uprv_memset(pResData, 0, sizeof(ResourceData));
247 0 : if(U_FAILURE(*errorCode)) {
248 0 : return;
249 : }
250 0 : if(!isAcceptable(formatVersion, NULL, NULL, pInfo)) {
251 0 : *errorCode=U_INVALID_FORMAT_ERROR;
252 0 : return;
253 : }
254 0 : res_init(pResData, formatVersion, inBytes, length, errorCode);
255 : }
256 :
257 : U_CFUNC void
258 1 : res_load(ResourceData *pResData,
259 : const char *path, const char *name, UErrorCode *errorCode) {
260 : UVersionInfo formatVersion;
261 :
262 1 : uprv_memset(pResData, 0, sizeof(ResourceData));
263 :
264 : /* load the ResourceBundle file */
265 1 : pResData->data=udata_openChoice(path, "res", name, isAcceptable, formatVersion, errorCode);
266 1 : if(U_FAILURE(*errorCode)) {
267 0 : return;
268 : }
269 :
270 : /* get its memory and initialize *pResData */
271 1 : res_init(pResData, formatVersion, udata_getMemory(pResData->data), -1, errorCode);
272 : }
273 :
274 : U_CFUNC void
275 0 : res_unload(ResourceData *pResData) {
276 0 : if(pResData->data!=NULL) {
277 0 : udata_close(pResData->data);
278 0 : pResData->data=NULL;
279 : }
280 0 : }
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 0 : res_getPublicType(Resource res) {
306 0 : return (UResType)gPublicTypes[RES_GET_TYPE(res)];
307 : }
308 :
309 : U_CAPI const UChar * U_EXPORT2
310 2 : res_getString(const ResourceData *pResData, Resource res, int32_t *pLength) {
311 : const UChar *p;
312 2 : uint32_t offset=RES_GET_OFFSET(res);
313 : int32_t length;
314 2 : if(RES_GET_TYPE(res)==URES_STRING_V2) {
315 : int32_t first;
316 2 : if((int32_t)offset<pResData->poolStringIndexLimit) {
317 0 : p=(const UChar *)pResData->poolBundleStrings+offset;
318 : } else {
319 2 : p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit);
320 : }
321 2 : first=*p;
322 2 : if(!U16_IS_TRAIL(first)) {
323 2 : length=u_strlen(p);
324 0 : } else if(first<0xdfef) {
325 0 : length=first&0x3ff;
326 0 : ++p;
327 0 : } else if(first<0xdfff) {
328 0 : length=((first-0xdfef)<<16)|p[1];
329 0 : p+=2;
330 : } else {
331 0 : length=((int32_t)p[1]<<16)|p[2];
332 0 : p+=3;
333 : }
334 0 : } else if(res==offset) /* RES_GET_TYPE(res)==URES_STRING */ {
335 0 : const int32_t *p32= res==0 ? &gEmptyString.length : pResData->pRoot+res;
336 0 : length=*p32++;
337 0 : p=(const UChar *)p32;
338 : } else {
339 0 : p=NULL;
340 0 : length=0;
341 : }
342 2 : if(pLength) {
343 2 : *pLength=length;
344 : }
345 2 : 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 0 : UBool isNoInheritanceMarker(const ResourceData *pResData, Resource res) {
357 0 : uint32_t offset=RES_GET_OFFSET(res);
358 0 : if (offset == 0) {
359 : // empty string
360 0 : } else if (res == offset) {
361 0 : const int32_t *p32=pResData->pRoot+res;
362 0 : int32_t length=*p32;
363 0 : const UChar *p=(const UChar *)p32;
364 0 : return length == 3 && p[2] == 0x2205 && p[3] == 0x2205 && p[4] == 0x2205;
365 0 : } else if (RES_GET_TYPE(res) == URES_STRING_V2) {
366 : const UChar *p;
367 0 : if((int32_t)offset<pResData->poolStringIndexLimit) {
368 0 : p=(const UChar *)pResData->poolBundleStrings+offset;
369 : } else {
370 0 : p=(const UChar *)pResData->p16BitUnits+(offset-pResData->poolStringIndexLimit);
371 : }
372 0 : int32_t first=*p;
373 0 : if (first == 0x2205) { // implicit length
374 0 : return p[1] == 0x2205 && p[2] == 0x2205 && p[3] == 0;
375 0 : } else if (first == 0xdc03) { // explicit length 3 (should not occur)
376 0 : 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 0 : return FALSE;
380 : }
381 : }
382 0 : return FALSE;
383 : }
384 :
385 0 : int32_t getStringArray(const ResourceData *pResData, const icu::ResourceArray &array,
386 : icu::UnicodeString *dest, int32_t capacity,
387 : UErrorCode &errorCode) {
388 0 : if(U_FAILURE(errorCode)) {
389 0 : return 0;
390 : }
391 0 : if(dest == NULL ? capacity != 0 : capacity < 0) {
392 0 : errorCode = U_ILLEGAL_ARGUMENT_ERROR;
393 0 : return 0;
394 : }
395 0 : int32_t length = array.getSize();
396 0 : if(length == 0) {
397 0 : return 0;
398 : }
399 0 : if(length > capacity) {
400 0 : errorCode = U_BUFFER_OVERFLOW_ERROR;
401 0 : return length;
402 : }
403 0 : for(int32_t i = 0; i < length; ++i) {
404 : int32_t sLength;
405 0 : const UChar *s = res_getString(pResData, array.internalGetResource(pResData, i), &sLength);
406 0 : if(s == NULL) {
407 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
408 0 : return 0;
409 : }
410 0 : dest[i].setTo(TRUE, s, sLength);
411 : }
412 0 : return length;
413 : }
414 :
415 : } // namespace
416 :
417 : U_CAPI const UChar * U_EXPORT2
418 0 : res_getAlias(const ResourceData *pResData, Resource res, int32_t *pLength) {
419 : const UChar *p;
420 0 : uint32_t offset=RES_GET_OFFSET(res);
421 : int32_t length;
422 0 : if(RES_GET_TYPE(res)==URES_ALIAS) {
423 0 : const int32_t *p32= offset==0 ? &gEmptyString.length : pResData->pRoot+offset;
424 0 : length=*p32++;
425 0 : p=(const UChar *)p32;
426 : } else {
427 0 : p=NULL;
428 0 : length=0;
429 : }
430 0 : if(pLength) {
431 0 : *pLength=length;
432 : }
433 0 : return p;
434 : }
435 :
436 : U_CAPI const uint8_t * U_EXPORT2
437 0 : res_getBinary(const ResourceData *pResData, Resource res, int32_t *pLength) {
438 : const uint8_t *p;
439 0 : uint32_t offset=RES_GET_OFFSET(res);
440 : int32_t length;
441 0 : if(RES_GET_TYPE(res)==URES_BINARY) {
442 0 : const int32_t *p32= offset==0 ? (const int32_t*)&gEmpty32 : pResData->pRoot+offset;
443 0 : length=*p32++;
444 0 : p=(const uint8_t *)p32;
445 : } else {
446 0 : p=NULL;
447 0 : length=0;
448 : }
449 0 : if(pLength) {
450 0 : *pLength=length;
451 : }
452 0 : return p;
453 : }
454 :
455 :
456 : U_CAPI const int32_t * U_EXPORT2
457 0 : res_getIntVector(const ResourceData *pResData, Resource res, int32_t *pLength) {
458 : const int32_t *p;
459 0 : uint32_t offset=RES_GET_OFFSET(res);
460 : int32_t length;
461 0 : if(RES_GET_TYPE(res)==URES_INT_VECTOR) {
462 0 : p= offset==0 ? (const int32_t *)&gEmpty32 : pResData->pRoot+offset;
463 0 : length=*p++;
464 : } else {
465 0 : p=NULL;
466 0 : length=0;
467 : }
468 0 : if(pLength) {
469 0 : *pLength=length;
470 : }
471 0 : return p;
472 : }
473 :
474 : U_CAPI int32_t U_EXPORT2
475 4 : res_countArrayItems(const ResourceData *pResData, Resource res) {
476 4 : uint32_t offset=RES_GET_OFFSET(res);
477 4 : 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 0 : return 1;
485 : case URES_ARRAY:
486 : case URES_TABLE32:
487 0 : return offset==0 ? 0 : *(pResData->pRoot+offset);
488 : case URES_TABLE:
489 0 : return offset==0 ? 0 : *((const uint16_t *)(pResData->pRoot+offset));
490 : case URES_ARRAY16:
491 : case URES_TABLE16:
492 4 : return pResData->p16BitUnits[offset];
493 : default:
494 0 : return 0;
495 : }
496 : }
497 :
498 : U_NAMESPACE_BEGIN
499 :
500 0 : ResourceDataValue::~ResourceDataValue() {}
501 :
502 0 : UResType ResourceDataValue::getType() const {
503 0 : return res_getPublicType(res);
504 : }
505 :
506 0 : const UChar *ResourceDataValue::getString(int32_t &length, UErrorCode &errorCode) const {
507 0 : if(U_FAILURE(errorCode)) {
508 0 : return NULL;
509 : }
510 0 : const UChar *s = res_getString(pResData, res, &length);
511 0 : if(s == NULL) {
512 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
513 : }
514 0 : return s;
515 : }
516 :
517 0 : const UChar *ResourceDataValue::getAliasString(int32_t &length, UErrorCode &errorCode) const {
518 0 : if(U_FAILURE(errorCode)) {
519 0 : return NULL;
520 : }
521 0 : const UChar *s = res_getAlias(pResData, res, &length);
522 0 : if(s == NULL) {
523 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
524 : }
525 0 : return s;
526 : }
527 :
528 0 : int32_t ResourceDataValue::getInt(UErrorCode &errorCode) const {
529 0 : if(U_FAILURE(errorCode)) {
530 0 : return 0;
531 : }
532 0 : if(RES_GET_TYPE(res) != URES_INT) {
533 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
534 : }
535 0 : return RES_GET_INT(res);
536 : }
537 :
538 0 : uint32_t ResourceDataValue::getUInt(UErrorCode &errorCode) const {
539 0 : if(U_FAILURE(errorCode)) {
540 0 : return 0;
541 : }
542 0 : if(RES_GET_TYPE(res) != URES_INT) {
543 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
544 : }
545 0 : return RES_GET_UINT(res);
546 : }
547 :
548 0 : const int32_t *ResourceDataValue::getIntVector(int32_t &length, UErrorCode &errorCode) const {
549 0 : if(U_FAILURE(errorCode)) {
550 0 : return NULL;
551 : }
552 0 : const int32_t *iv = res_getIntVector(pResData, res, &length);
553 0 : if(iv == NULL) {
554 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
555 : }
556 0 : return iv;
557 : }
558 :
559 0 : const uint8_t *ResourceDataValue::getBinary(int32_t &length, UErrorCode &errorCode) const {
560 0 : if(U_FAILURE(errorCode)) {
561 0 : return NULL;
562 : }
563 0 : const uint8_t *b = res_getBinary(pResData, res, &length);
564 0 : if(b == NULL) {
565 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
566 : }
567 0 : return b;
568 : }
569 :
570 0 : ResourceArray ResourceDataValue::getArray(UErrorCode &errorCode) const {
571 0 : if(U_FAILURE(errorCode)) {
572 0 : return ResourceArray();
573 : }
574 0 : const uint16_t *items16 = NULL;
575 0 : const Resource *items32 = NULL;
576 0 : uint32_t offset=RES_GET_OFFSET(res);
577 0 : int32_t length = 0;
578 0 : switch(RES_GET_TYPE(res)) {
579 : case URES_ARRAY:
580 0 : if (offset!=0) { // empty if offset==0
581 0 : items32 = (const Resource *)pResData->pRoot+offset;
582 0 : length = *items32++;
583 : }
584 0 : break;
585 : case URES_ARRAY16:
586 0 : items16 = pResData->p16BitUnits+offset;
587 0 : length = *items16++;
588 0 : break;
589 : default:
590 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
591 0 : return ResourceArray();
592 : }
593 0 : return ResourceArray(items16, items32, length);
594 : }
595 :
596 0 : ResourceTable ResourceDataValue::getTable(UErrorCode &errorCode) const {
597 0 : if(U_FAILURE(errorCode)) {
598 0 : return ResourceTable();
599 : }
600 0 : const uint16_t *keys16 = NULL;
601 0 : const int32_t *keys32 = NULL;
602 0 : const uint16_t *items16 = NULL;
603 0 : const Resource *items32 = NULL;
604 0 : uint32_t offset = RES_GET_OFFSET(res);
605 0 : int32_t length = 0;
606 0 : switch(RES_GET_TYPE(res)) {
607 : case URES_TABLE:
608 0 : if (offset != 0) { // empty if offset==0
609 0 : keys16 = (const uint16_t *)(pResData->pRoot+offset);
610 0 : length = *keys16++;
611 0 : items32 = (const Resource *)(keys16+length+(~length&1));
612 : }
613 0 : break;
614 : case URES_TABLE16:
615 0 : keys16 = pResData->p16BitUnits+offset;
616 0 : length = *keys16++;
617 0 : items16 = keys16 + length;
618 0 : break;
619 : case URES_TABLE32:
620 0 : if (offset != 0) { // empty if offset==0
621 0 : keys32 = pResData->pRoot+offset;
622 0 : length = *keys32++;
623 0 : items32 = (const Resource *)keys32 + length;
624 : }
625 0 : break;
626 : default:
627 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
628 0 : return ResourceTable();
629 : }
630 0 : return ResourceTable(keys16, keys32, items16, items32, length);
631 : }
632 :
633 0 : UBool ResourceDataValue::isNoInheritanceMarker() const {
634 0 : return ::isNoInheritanceMarker(pResData, res);
635 : }
636 :
637 0 : int32_t ResourceDataValue::getStringArray(UnicodeString *dest, int32_t capacity,
638 : UErrorCode &errorCode) const {
639 0 : return ::getStringArray(pResData, getArray(errorCode), dest, capacity, errorCode);
640 : }
641 :
642 0 : int32_t ResourceDataValue::getStringArrayOrStringAsArray(UnicodeString *dest, int32_t capacity,
643 : UErrorCode &errorCode) const {
644 0 : if(URES_IS_ARRAY(res)) {
645 0 : return ::getStringArray(pResData, getArray(errorCode), dest, capacity, errorCode);
646 : }
647 0 : if(U_FAILURE(errorCode)) {
648 0 : return 0;
649 : }
650 0 : if(dest == NULL ? capacity != 0 : capacity < 0) {
651 0 : errorCode = U_ILLEGAL_ARGUMENT_ERROR;
652 0 : return 0;
653 : }
654 0 : if(capacity < 1) {
655 0 : errorCode = U_BUFFER_OVERFLOW_ERROR;
656 0 : return 1;
657 : }
658 : int32_t sLength;
659 0 : const UChar *s = res_getString(pResData, res, &sLength);
660 0 : if(s != NULL) {
661 0 : dest[0].setTo(TRUE, s, sLength);
662 0 : return 1;
663 : }
664 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
665 0 : return 0;
666 : }
667 :
668 0 : UnicodeString ResourceDataValue::getStringOrFirstOfArray(UErrorCode &errorCode) const {
669 0 : UnicodeString us;
670 0 : if(U_FAILURE(errorCode)) {
671 0 : return us;
672 : }
673 : int32_t sLength;
674 0 : const UChar *s = res_getString(pResData, res, &sLength);
675 0 : if(s != NULL) {
676 0 : us.setTo(TRUE, s, sLength);
677 0 : return us;
678 : }
679 0 : ResourceArray array = getArray(errorCode);
680 0 : if(U_FAILURE(errorCode)) {
681 0 : return us;
682 : }
683 0 : if(array.getSize() > 0) {
684 0 : s = res_getString(pResData, array.internalGetResource(pResData, 0), &sLength);
685 0 : if(s != NULL) {
686 0 : us.setTo(TRUE, s, sLength);
687 0 : return us;
688 : }
689 : }
690 0 : errorCode = U_RESOURCE_TYPE_MISMATCH;
691 0 : return us;
692 : }
693 :
694 : U_NAMESPACE_END
695 :
696 : static Resource
697 2 : makeResourceFrom16(const ResourceData *pResData, int32_t res16) {
698 2 : 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 2 : res16=res16-pResData->poolStringIndex16Limit+pResData->poolStringIndexLimit;
704 : }
705 2 : return URES_MAKE_RESOURCE(URES_STRING_V2, res16);
706 : }
707 :
708 : U_CAPI Resource U_EXPORT2
709 5 : res_getTableItemByKey(const ResourceData *pResData, Resource table,
710 : int32_t *indexR, const char **key) {
711 5 : uint32_t offset=RES_GET_OFFSET(table);
712 : int32_t length;
713 : int32_t idx;
714 5 : if(key == NULL || *key == NULL) {
715 0 : return RES_BOGUS;
716 : }
717 5 : switch(RES_GET_TYPE(table)) {
718 : case URES_TABLE: {
719 0 : if (offset!=0) { /* empty if offset==0 */
720 0 : const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
721 0 : length=*p++;
722 0 : *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
723 0 : if(idx>=0) {
724 0 : const Resource *p32=(const Resource *)(p+length+(~length&1));
725 0 : return p32[idx];
726 : }
727 : }
728 0 : break;
729 : }
730 : case URES_TABLE16: {
731 5 : const uint16_t *p=pResData->p16BitUnits+offset;
732 5 : length=*p++;
733 5 : *indexR=idx=_res_findTableItem(pResData, p, length, *key, key);
734 5 : if(idx>=0) {
735 2 : return makeResourceFrom16(pResData, p[length+idx]);
736 : }
737 3 : break;
738 : }
739 : case URES_TABLE32: {
740 0 : if (offset!=0) { /* empty if offset==0 */
741 0 : const int32_t *p= pResData->pRoot+offset;
742 0 : length=*p++;
743 0 : *indexR=idx=_res_findTable32Item(pResData, p, length, *key, key);
744 0 : if(idx>=0) {
745 0 : return (Resource)p[length+idx];
746 : }
747 : }
748 0 : break;
749 : }
750 : default:
751 0 : break;
752 : }
753 3 : return RES_BOGUS;
754 : }
755 :
756 : U_CAPI Resource U_EXPORT2
757 0 : res_getTableItemByIndex(const ResourceData *pResData, Resource table,
758 : int32_t indexR, const char **key) {
759 0 : uint32_t offset=RES_GET_OFFSET(table);
760 : int32_t length;
761 0 : if (indexR < 0) {
762 0 : return RES_BOGUS;
763 : }
764 0 : switch(RES_GET_TYPE(table)) {
765 : case URES_TABLE: {
766 0 : if (offset != 0) { /* empty if offset==0 */
767 0 : const uint16_t *p= (const uint16_t *)(pResData->pRoot+offset);
768 0 : length=*p++;
769 0 : if(indexR<length) {
770 0 : const Resource *p32=(const Resource *)(p+length+(~length&1));
771 0 : if(key!=NULL) {
772 0 : *key=RES_GET_KEY16(pResData, p[indexR]);
773 : }
774 0 : return p32[indexR];
775 : }
776 : }
777 0 : break;
778 : }
779 : case URES_TABLE16: {
780 0 : const uint16_t *p=pResData->p16BitUnits+offset;
781 0 : length=*p++;
782 0 : if(indexR<length) {
783 0 : if(key!=NULL) {
784 0 : *key=RES_GET_KEY16(pResData, p[indexR]);
785 : }
786 0 : return makeResourceFrom16(pResData, p[length+indexR]);
787 : }
788 0 : break;
789 : }
790 : case URES_TABLE32: {
791 0 : if (offset != 0) { /* empty if offset==0 */
792 0 : const int32_t *p= pResData->pRoot+offset;
793 0 : length=*p++;
794 0 : if(indexR<length) {
795 0 : if(key!=NULL) {
796 0 : *key=RES_GET_KEY32(pResData, p[indexR]);
797 : }
798 0 : return (Resource)p[length+indexR];
799 : }
800 : }
801 0 : break;
802 : }
803 : default:
804 0 : break;
805 : }
806 0 : return RES_BOGUS;
807 : }
808 :
809 : U_CAPI Resource U_EXPORT2
810 1 : res_getResource(const ResourceData *pResData, const char *key) {
811 1 : const char *realKey=key;
812 : int32_t idx;
813 1 : return res_getTableItemByKey(pResData, pResData->rootRes, &idx, &realKey);
814 : }
815 :
816 :
817 0 : UBool icu::ResourceTable::getKeyAndValue(int32_t i,
818 : const char *&key, icu::ResourceValue &value) const {
819 0 : if(0 <= i && i < length) {
820 0 : icu::ResourceDataValue &rdValue = static_cast<icu::ResourceDataValue &>(value);
821 0 : if (keys16 != NULL) {
822 0 : key = RES_GET_KEY16(rdValue.pResData, keys16[i]);
823 : } else {
824 0 : key = RES_GET_KEY32(rdValue.pResData, keys32[i]);
825 : }
826 : Resource res;
827 0 : if (items16 != NULL) {
828 0 : res = makeResourceFrom16(rdValue.pResData, items16[i]);
829 : } else {
830 0 : res = items32[i];
831 : }
832 0 : rdValue.setResource(res);
833 0 : return TRUE;
834 : }
835 0 : return FALSE;
836 : }
837 :
838 : U_CAPI Resource U_EXPORT2
839 0 : res_getArrayItem(const ResourceData *pResData, Resource array, int32_t indexR) {
840 0 : uint32_t offset=RES_GET_OFFSET(array);
841 0 : if (indexR < 0) {
842 0 : return RES_BOGUS;
843 : }
844 0 : switch(RES_GET_TYPE(array)) {
845 : case URES_ARRAY: {
846 0 : if (offset!=0) { /* empty if offset==0 */
847 0 : const int32_t *p= pResData->pRoot+offset;
848 0 : if(indexR<*p) {
849 0 : return (Resource)p[1+indexR];
850 : }
851 : }
852 0 : break;
853 : }
854 : case URES_ARRAY16: {
855 0 : const uint16_t *p=pResData->p16BitUnits+offset;
856 0 : if(indexR<*p) {
857 0 : return makeResourceFrom16(pResData, p[1+indexR]);
858 : }
859 0 : break;
860 : }
861 : default:
862 0 : break;
863 : }
864 0 : return RES_BOGUS;
865 : }
866 :
867 0 : uint32_t icu::ResourceArray::internalGetResource(const ResourceData *pResData, int32_t i) const {
868 0 : if (items16 != NULL) {
869 0 : return makeResourceFrom16(pResData, items16[i]);
870 : } else {
871 0 : return items32[i];
872 : }
873 : }
874 :
875 0 : UBool icu::ResourceArray::getValue(int32_t i, icu::ResourceValue &value) const {
876 0 : if(0 <= i && i < length) {
877 0 : icu::ResourceDataValue &rdValue = static_cast<icu::ResourceDataValue &>(value);
878 0 : rdValue.setResource(internalGetResource(rdValue.pResData, i));
879 0 : return TRUE;
880 : }
881 0 : return FALSE;
882 : }
883 :
884 : U_CFUNC Resource
885 0 : res_findResource(const ResourceData *pResData, Resource r, char** path, const char** key) {
886 0 : char *pathP = *path, *nextSepP = *path;
887 0 : char *closeIndex = NULL;
888 0 : Resource t1 = r;
889 : Resource t2;
890 0 : int32_t indexR = 0;
891 0 : 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 0 : if(!uprv_strlen(pathP)) {
895 0 : return r;
896 : }
897 :
898 : /* one needs to have an aggregate resource in order to search in it */
899 0 : if(!URES_IS_CONTAINER(type)) {
900 0 : return RES_BOGUS;
901 : }
902 :
903 0 : 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 0 : 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 0 : if(nextSepP != NULL) {
912 0 : if(nextSepP == pathP) {
913 : // Empty key string.
914 0 : return RES_BOGUS;
915 : }
916 0 : *nextSepP = 0; /* overwrite the separator with a NUL to terminate the key */
917 0 : *path = nextSepP+1;
918 : } else {
919 0 : *path = uprv_strchr(pathP, 0);
920 : }
921 :
922 : /* if the resource is a table */
923 : /* try the key based access */
924 0 : if(URES_IS_TABLE(type)) {
925 0 : *key = pathP;
926 0 : t2 = res_getTableItemByKey(pResData, t1, &indexR, key);
927 0 : if(t2 == RES_BOGUS) {
928 : /* if we fail to get the resource by key, maybe we got an index */
929 0 : indexR = uprv_strtol(pathP, &closeIndex, 10);
930 0 : if(indexR >= 0 && *closeIndex == 0) {
931 : /* if we indeed have an index, try to get the item by index */
932 0 : t2 = res_getTableItemByIndex(pResData, t1, indexR, key);
933 : } // else t2 is already RES_BOGUS
934 : }
935 0 : } else if(URES_IS_ARRAY(type)) {
936 0 : indexR = uprv_strtol(pathP, &closeIndex, 10);
937 0 : if(indexR >= 0 && *closeIndex == 0) {
938 0 : t2 = res_getArrayItem(pResData, t1, indexR);
939 : } else {
940 0 : t2 = RES_BOGUS; /* have an array, but don't have a valid index */
941 : }
942 0 : *key = NULL;
943 : } else { /* can't do much here, except setting t2 to bogus */
944 0 : t2 = RES_BOGUS;
945 : }
946 0 : t1 = t2;
947 0 : type = (UResType)RES_GET_TYPE(t1);
948 : /* position pathP to next resource key/index */
949 0 : pathP = *path;
950 : }
951 :
952 0 : 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 0 : ures_compareRows(const void *context, const void *left, const void *right) {
994 0 : const char *keyChars=(const char *)context;
995 0 : 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 0 : 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 0 : 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 0 : return;
1044 : default:
1045 0 : break;
1046 : }
1047 :
1048 : /* all other types use an offset to point to their data */
1049 0 : offset=(int32_t)RES_GET_OFFSET(res);
1050 0 : if(offset==0) {
1051 : /* special offset indicating an empty item */
1052 0 : return;
1053 : }
1054 0 : if(pTempTable->resFlags[offset>>5]&((uint32_t)1<<(offset&0x1f))) {
1055 : /* we already swapped this resource item */
1056 0 : return;
1057 : } else {
1058 : /* mark it as swapped now */
1059 0 : pTempTable->resFlags[offset>>5]|=((uint32_t)1<<(offset&0x1f));
1060 : }
1061 :
1062 0 : p=inBundle+offset;
1063 0 : q=outBundle+offset;
1064 :
1065 0 : 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 0 : count=udata_readInt32(ds, (int32_t)*p);
1071 : /* swap length */
1072 0 : ds->swapArray32(ds, p, 4, q, pErrorCode);
1073 : /* swap each UChar (the terminating NUL would not change) */
1074 0 : ds->swapArray16(ds, p+1, 2*count, q+1, pErrorCode);
1075 0 : break;
1076 : case URES_BINARY:
1077 0 : count=udata_readInt32(ds, (int32_t)*p);
1078 : /* swap length */
1079 0 : 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 0 : if( key!=NULL && /* the binary is in a table */
1085 : (key!=gUnknownKey ?
1086 : /* its table key string is "%%CollationBin" */
1087 0 : 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 0 : ucol_looksLikeCollationBinary(ds, p+1, count))
1091 : ) {
1092 0 : ucol_swap(ds, p+1, count, q+1, pErrorCode);
1093 : }
1094 : #endif
1095 0 : 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 0 : if(RES_GET_TYPE(res)==URES_TABLE) {
1109 : /* get table item count */
1110 0 : pKey16=(const uint16_t *)p;
1111 0 : qKey16=(uint16_t *)q;
1112 0 : count=ds->readUInt16(*pKey16);
1113 :
1114 0 : pKey32=qKey32=NULL;
1115 :
1116 : /* swap count */
1117 0 : ds->swapArray16(ds, pKey16++, 2, qKey16++, pErrorCode);
1118 :
1119 0 : offset+=((1+count)+1)/2;
1120 : } else {
1121 : /* get table item count */
1122 0 : pKey32=(const int32_t *)p;
1123 0 : qKey32=(int32_t *)q;
1124 0 : count=udata_readInt32(ds, *pKey32);
1125 :
1126 0 : pKey16=qKey16=NULL;
1127 :
1128 : /* swap count */
1129 0 : ds->swapArray32(ds, pKey32++, 4, qKey32++, pErrorCode);
1130 :
1131 0 : offset+=1+count;
1132 : }
1133 :
1134 0 : if(count==0) {
1135 0 : break;
1136 : }
1137 :
1138 0 : p=inBundle+offset; /* pointer to table resources */
1139 0 : q=outBundle+offset;
1140 :
1141 : /* recurse */
1142 0 : for(i=0; i<count; ++i) {
1143 0 : const char *itemKey=gUnknownKey;
1144 0 : if(pKey16!=NULL) {
1145 0 : int32_t keyOffset=ds->readUInt16(pKey16[i]);
1146 0 : if(keyOffset<pTempTable->localKeyLimit) {
1147 0 : itemKey=(const char *)outBundle+keyOffset;
1148 : }
1149 : } else {
1150 0 : int32_t keyOffset=udata_readInt32(ds, pKey32[i]);
1151 0 : if(keyOffset>=0) {
1152 0 : itemKey=(const char *)outBundle+keyOffset;
1153 : }
1154 : }
1155 0 : item=ds->readUInt32(p[i]);
1156 0 : ures_swapResource(ds, inBundle, outBundle, item, itemKey, pTempTable, pErrorCode);
1157 0 : if(U_FAILURE(*pErrorCode)) {
1158 : udata_printError(ds, "ures_swapResource(table res=%08x)[%d].recurse(%08x) failed\n",
1159 0 : res, i, item);
1160 0 : return;
1161 : }
1162 : }
1163 :
1164 0 : if(pTempTable->majorFormatVersion>1 || ds->inCharset==ds->outCharset) {
1165 : /* no need to sort, just swap the offset/value arrays */
1166 0 : if(pKey16!=NULL) {
1167 0 : ds->swapArray16(ds, pKey16, count*2, qKey16, pErrorCode);
1168 0 : ds->swapArray32(ds, p, count*4, q, pErrorCode);
1169 : } else {
1170 : /* swap key offsets and items as one array */
1171 0 : ds->swapArray32(ds, pKey32, count*2*4, qKey32, pErrorCode);
1172 : }
1173 0 : 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 0 : if(pKey16!=NULL) {
1185 0 : for(i=0; i<count; ++i) {
1186 0 : pTempTable->rows[i].keyIndex=ds->readUInt16(pKey16[i]);
1187 0 : pTempTable->rows[i].sortIndex=i;
1188 : }
1189 : } else {
1190 0 : for(i=0; i<count; ++i) {
1191 0 : pTempTable->rows[i].keyIndex=udata_readInt32(ds, pKey32[i]);
1192 0 : pTempTable->rows[i].sortIndex=i;
1193 : }
1194 : }
1195 0 : uprv_sortArray(pTempTable->rows, count, sizeof(Row),
1196 0 : ures_compareRows, pTempTable->keyChars,
1197 0 : FALSE, pErrorCode);
1198 0 : if(U_FAILURE(*pErrorCode)) {
1199 : udata_printError(ds, "ures_swapResource(table res=%08x).uprv_sortArray(%d items) failed\n",
1200 0 : res, count);
1201 0 : 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 0 : if(pKey16!=NULL) {
1213 : uint16_t *rKey16;
1214 :
1215 0 : if(pKey16!=qKey16) {
1216 0 : rKey16=qKey16;
1217 : } else {
1218 0 : rKey16=(uint16_t *)pTempTable->resort;
1219 : }
1220 0 : for(i=0; i<count; ++i) {
1221 0 : oldIndex=pTempTable->rows[i].sortIndex;
1222 0 : ds->swapArray16(ds, pKey16+oldIndex, 2, rKey16+i, pErrorCode);
1223 : }
1224 0 : if(qKey16!=rKey16) {
1225 0 : uprv_memcpy(qKey16, rKey16, 2*count);
1226 : }
1227 : } else {
1228 : int32_t *rKey32;
1229 :
1230 0 : if(pKey32!=qKey32) {
1231 0 : rKey32=qKey32;
1232 : } else {
1233 0 : rKey32=pTempTable->resort;
1234 : }
1235 0 : for(i=0; i<count; ++i) {
1236 0 : oldIndex=pTempTable->rows[i].sortIndex;
1237 0 : ds->swapArray32(ds, pKey32+oldIndex, 4, rKey32+i, pErrorCode);
1238 : }
1239 0 : if(qKey32!=rKey32) {
1240 0 : uprv_memcpy(qKey32, rKey32, 4*count);
1241 : }
1242 : }
1243 :
1244 : /* resources */
1245 : {
1246 : Resource *r;
1247 :
1248 :
1249 0 : if(p!=q) {
1250 0 : r=q;
1251 : } else {
1252 0 : r=(Resource *)pTempTable->resort;
1253 : }
1254 0 : for(i=0; i<count; ++i) {
1255 0 : oldIndex=pTempTable->rows[i].sortIndex;
1256 0 : ds->swapArray32(ds, p+oldIndex, 4, r+i, pErrorCode);
1257 : }
1258 0 : if(q!=r) {
1259 0 : uprv_memcpy(q, r, 4*count);
1260 : }
1261 : }
1262 : }
1263 0 : break;
1264 : case URES_ARRAY:
1265 : {
1266 : Resource item;
1267 : int32_t i;
1268 :
1269 0 : count=udata_readInt32(ds, (int32_t)*p);
1270 : /* swap length */
1271 0 : ds->swapArray32(ds, p++, 4, q++, pErrorCode);
1272 :
1273 : /* recurse */
1274 0 : for(i=0; i<count; ++i) {
1275 0 : item=ds->readUInt32(p[i]);
1276 0 : ures_swapResource(ds, inBundle, outBundle, item, NULL, pTempTable, pErrorCode);
1277 0 : if(U_FAILURE(*pErrorCode)) {
1278 : udata_printError(ds, "ures_swapResource(array res=%08x)[%d].recurse(%08x) failed\n",
1279 0 : res, i, item);
1280 0 : return;
1281 : }
1282 : }
1283 :
1284 : /* swap items */
1285 0 : ds->swapArray32(ds, p, 4*count, q, pErrorCode);
1286 : }
1287 0 : break;
1288 : case URES_INT_VECTOR:
1289 0 : count=udata_readInt32(ds, (int32_t)*p);
1290 : /* swap length and each integer */
1291 0 : ds->swapArray32(ds, p, 4*(1+count), q, pErrorCode);
1292 0 : break;
1293 : default:
1294 : /* also catches RES_BOGUS */
1295 0 : *pErrorCode=U_UNSUPPORTED_ERROR;
1296 0 : break;
1297 : }
1298 : }
1299 :
1300 : U_CAPI int32_t U_EXPORT2
1301 0 : 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 0 : headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode);
1320 0 : if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
1321 0 : return 0;
1322 : }
1323 :
1324 : /* check data format and format version */
1325 0 : pInfo=(const UDataInfo *)((const char *)inData+4);
1326 0 : if(!(
1327 0 : pInfo->dataFormat[0]==0x52 && /* dataFormat="ResB" */
1328 0 : pInfo->dataFormat[1]==0x65 &&
1329 0 : pInfo->dataFormat[2]==0x73 &&
1330 0 : pInfo->dataFormat[3]==0x42 &&
1331 : /* formatVersion 1.1+ or 2.x or 3.x */
1332 0 : ((pInfo->formatVersion[0]==1 && pInfo->formatVersion[1]>=1) ||
1333 0 : pInfo->formatVersion[0]==2 || pInfo->formatVersion[0]==3)
1334 : )) {
1335 0 : udata_printError(ds, "ures_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not a resource bundle\n",
1336 0 : pInfo->dataFormat[0], pInfo->dataFormat[1],
1337 0 : pInfo->dataFormat[2], pInfo->dataFormat[3],
1338 0 : pInfo->formatVersion[0], pInfo->formatVersion[1]);
1339 0 : *pErrorCode=U_UNSUPPORTED_ERROR;
1340 0 : return 0;
1341 : }
1342 0 : tempTable.majorFormatVersion=pInfo->formatVersion[0];
1343 :
1344 : /* a resource bundle must contain at least one resource item */
1345 0 : if(length<0) {
1346 0 : bundleLength=-1;
1347 : } else {
1348 0 : bundleLength=(length-headerSize)/4;
1349 :
1350 : /* formatVersion 1.1 must have a root item and at least 5 indexes */
1351 0 : if(bundleLength<(1+5)) {
1352 0 : udata_printError(ds, "ures_swap(): too few bytes (%d after header) for a resource bundle\n",
1353 0 : length-headerSize);
1354 0 : *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1355 0 : return 0;
1356 : }
1357 : }
1358 :
1359 0 : inBundle=(const Resource *)((const char *)inData+headerSize);
1360 0 : rootRes=ds->readUInt32(*inBundle);
1361 :
1362 : /* formatVersion 1.1 adds the indexes[] array */
1363 0 : inIndexes=(const int32_t *)(inBundle+1);
1364 :
1365 0 : indexLength=udata_readInt32(ds, inIndexes[URES_INDEX_LENGTH])&0xff;
1366 0 : if(indexLength<=URES_INDEX_MAX_TABLE_LENGTH) {
1367 0 : udata_printError(ds, "ures_swap(): too few indexes for a 1.1+ resource bundle\n");
1368 0 : *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1369 0 : return 0;
1370 : }
1371 0 : keysBottom=1+indexLength;
1372 0 : keysTop=udata_readInt32(ds, inIndexes[URES_INDEX_KEYS_TOP]);
1373 0 : if(indexLength>URES_INDEX_16BIT_TOP) {
1374 0 : resBottom=udata_readInt32(ds, inIndexes[URES_INDEX_16BIT_TOP]);
1375 : } else {
1376 0 : resBottom=keysTop;
1377 : }
1378 0 : top=udata_readInt32(ds, inIndexes[URES_INDEX_BUNDLE_TOP]);
1379 0 : maxTableLength=udata_readInt32(ds, inIndexes[URES_INDEX_MAX_TABLE_LENGTH]);
1380 :
1381 0 : if(0<=bundleLength && bundleLength<top) {
1382 : udata_printError(ds, "ures_swap(): resource top %d exceeds bundle length %d\n",
1383 0 : top, bundleLength);
1384 0 : *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
1385 0 : return 0;
1386 : }
1387 0 : if(keysTop>(1+indexLength)) {
1388 0 : tempTable.localKeyLimit=keysTop<<2;
1389 : } else {
1390 0 : tempTable.localKeyLimit=0;
1391 : }
1392 :
1393 0 : if(length>=0) {
1394 0 : 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 0 : resFlagsLength=(length+31)>>5; /* number of bytes needed */
1409 0 : resFlagsLength=(resFlagsLength+3)&~3; /* multiple of 4 bytes for uint32_t */
1410 0 : if(resFlagsLength<=(int32_t)sizeof(stackResFlags)) {
1411 0 : tempTable.resFlags=stackResFlags;
1412 : } else {
1413 0 : tempTable.resFlags=(uint32_t *)uprv_malloc(resFlagsLength);
1414 0 : if(tempTable.resFlags==NULL) {
1415 0 : udata_printError(ds, "ures_swap(): unable to allocate memory for tracking resources\n");
1416 0 : *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1417 0 : return 0;
1418 : }
1419 : }
1420 0 : uprv_memset(tempTable.resFlags, 0, resFlagsLength);
1421 :
1422 : /* copy the bundle for binary and inaccessible data */
1423 0 : if(inData!=outData) {
1424 0 : 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 0 : udata_swapInvStringBlock(ds, inBundle+keysBottom, 4*(keysTop-keysBottom),
1429 0 : outBundle+keysBottom, pErrorCode);
1430 0 : if(U_FAILURE(*pErrorCode)) {
1431 0 : udata_printError(ds, "ures_swap().udata_swapInvStringBlock(keys[%d]) failed\n", 4*(keysTop-keysBottom));
1432 0 : return 0;
1433 : }
1434 :
1435 : /* swap the 16-bit units (strings, table16, array16) */
1436 0 : if(keysTop<resBottom) {
1437 0 : ds->swapArray16(ds, inBundle+keysTop, (resBottom-keysTop)*4, outBundle+keysTop, pErrorCode);
1438 0 : if(U_FAILURE(*pErrorCode)) {
1439 0 : udata_printError(ds, "ures_swap().swapArray16(16-bit units[%d]) failed\n", 2*(resBottom-keysTop));
1440 0 : return 0;
1441 : }
1442 : }
1443 :
1444 : /* allocate the temporary table for sorting resource tables */
1445 0 : tempTable.keyChars=(const char *)outBundle; /* sort by outCharset */
1446 0 : if(tempTable.majorFormatVersion>1 || maxTableLength<=STACK_ROW_CAPACITY) {
1447 0 : tempTable.rows=rows;
1448 0 : tempTable.resort=resort;
1449 : } else {
1450 0 : tempTable.rows=(Row *)uprv_malloc(maxTableLength*sizeof(Row)+maxTableLength*4);
1451 0 : if(tempTable.rows==NULL) {
1452 : udata_printError(ds, "ures_swap(): unable to allocate memory for sorting tables (max length: %d)\n",
1453 0 : maxTableLength);
1454 0 : *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
1455 0 : if(tempTable.resFlags!=stackResFlags) {
1456 0 : uprv_free(tempTable.resFlags);
1457 : }
1458 0 : return 0;
1459 : }
1460 0 : tempTable.resort=(int32_t *)(tempTable.rows+maxTableLength);
1461 : }
1462 :
1463 : /* swap the resources */
1464 0 : ures_swapResource(ds, inBundle, outBundle, rootRes, NULL, &tempTable, pErrorCode);
1465 0 : if(U_FAILURE(*pErrorCode)) {
1466 : udata_printError(ds, "ures_swapResource(root res=%08x) failed\n",
1467 0 : rootRes);
1468 : }
1469 :
1470 0 : if(tempTable.rows!=rows) {
1471 0 : uprv_free(tempTable.rows);
1472 : }
1473 0 : if(tempTable.resFlags!=stackResFlags) {
1474 0 : uprv_free(tempTable.resFlags);
1475 : }
1476 :
1477 : /* swap the root resource and indexes */
1478 0 : ds->swapArray32(ds, inBundle, keysBottom*4, outBundle, pErrorCode);
1479 : }
1480 :
1481 0 : return headerSize+4*top;
1482 : }
|