]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ********************************************************************** | |
729e4ab9 | 3 | * Copyright (c) 2002-2009, International Business Machines |
b75a7d8f A |
4 | * Corporation and others. All Rights Reserved. |
5 | ********************************************************************** | |
6 | * Author: Alan Liu | |
7 | * Created: October 30 2002 | |
8 | * Since: ICU 2.4 | |
9 | ********************************************************************** | |
10 | */ | |
11 | #include "propname.h" | |
12 | #include "unicode/uchar.h" | |
13 | #include "unicode/udata.h" | |
14 | #include "umutex.h" | |
374ca955 A |
15 | #include "cmemory.h" |
16 | #include "cstring.h" | |
17 | #include "ucln_cmn.h" | |
18 | #include "uarrsort.h" | |
19 | ||
20 | U_CDECL_BEGIN | |
21 | ||
22 | /** | |
23 | * Get the next non-ignorable ASCII character from a property name | |
24 | * and lowercases it. | |
25 | * @return ((advance count for the name)<<8)|character | |
26 | */ | |
27 | static inline int32_t | |
28 | getASCIIPropertyNameChar(const char *name) { | |
29 | int32_t i; | |
30 | char c; | |
31 | ||
32 | /* Ignore delimiters '-', '_', and ASCII White_Space */ | |
33 | for(i=0; | |
34 | (c=name[i++])==0x2d || c==0x5f || | |
35 | c==0x20 || (0x09<=c && c<=0x0d); | |
36 | ) {} | |
37 | ||
38 | if(c!=0) { | |
39 | return (i<<8)|(uint8_t)uprv_asciitolower((char)c); | |
40 | } else { | |
41 | return i<<8; | |
42 | } | |
43 | } | |
44 | ||
45 | /** | |
46 | * Get the next non-ignorable EBCDIC character from a property name | |
47 | * and lowercases it. | |
48 | * @return ((advance count for the name)<<8)|character | |
49 | */ | |
50 | static inline int32_t | |
51 | getEBCDICPropertyNameChar(const char *name) { | |
52 | int32_t i; | |
53 | char c; | |
54 | ||
55 | /* Ignore delimiters '-', '_', and EBCDIC White_Space */ | |
56 | for(i=0; | |
57 | (c=name[i++])==0x60 || c==0x6d || | |
58 | c==0x40 || c==0x05 || c==0x15 || c==0x25 || c==0x0b || c==0x0c || c==0x0d; | |
59 | ) {} | |
60 | ||
61 | if(c!=0) { | |
62 | return (i<<8)|(uint8_t)uprv_ebcdictolower((char)c); | |
63 | } else { | |
64 | return i<<8; | |
65 | } | |
66 | } | |
67 | ||
68 | /** | |
69 | * Unicode property names and property value names are compared "loosely". | |
70 | * | |
71 | * UCD.html 4.0.1 says: | |
72 | * For all property names, property value names, and for property values for | |
73 | * Enumerated, Binary, or Catalog properties, use the following | |
74 | * loose matching rule: | |
75 | * | |
76 | * LM3. Ignore case, whitespace, underscore ('_'), and hyphens. | |
77 | * | |
78 | * This function does just that, for (char *) name strings. | |
79 | * It is almost identical to ucnv_compareNames() but also ignores | |
80 | * C0 White_Space characters (U+0009..U+000d, and U+0085 on EBCDIC). | |
81 | * | |
82 | * @internal | |
83 | */ | |
84 | ||
85 | U_CAPI int32_t U_EXPORT2 | |
86 | uprv_compareASCIIPropertyNames(const char *name1, const char *name2) { | |
87 | int32_t rc, r1, r2; | |
88 | ||
89 | for(;;) { | |
90 | r1=getASCIIPropertyNameChar(name1); | |
91 | r2=getASCIIPropertyNameChar(name2); | |
92 | ||
93 | /* If we reach the ends of both strings then they match */ | |
94 | if(((r1|r2)&0xff)==0) { | |
95 | return 0; | |
96 | } | |
97 | ||
98 | /* Compare the lowercased characters */ | |
99 | if(r1!=r2) { | |
100 | rc=(r1&0xff)-(r2&0xff); | |
101 | if(rc!=0) { | |
102 | return rc; | |
103 | } | |
104 | } | |
105 | ||
106 | name1+=r1>>8; | |
107 | name2+=r2>>8; | |
108 | } | |
109 | } | |
110 | ||
111 | U_CAPI int32_t U_EXPORT2 | |
112 | uprv_compareEBCDICPropertyNames(const char *name1, const char *name2) { | |
113 | int32_t rc, r1, r2; | |
114 | ||
115 | for(;;) { | |
116 | r1=getEBCDICPropertyNameChar(name1); | |
117 | r2=getEBCDICPropertyNameChar(name2); | |
118 | ||
119 | /* If we reach the ends of both strings then they match */ | |
120 | if(((r1|r2)&0xff)==0) { | |
121 | return 0; | |
122 | } | |
123 | ||
124 | /* Compare the lowercased characters */ | |
125 | if(r1!=r2) { | |
126 | rc=(r1&0xff)-(r2&0xff); | |
127 | if(rc!=0) { | |
128 | return rc; | |
129 | } | |
130 | } | |
131 | ||
132 | name1+=r1>>8; | |
133 | name2+=r2>>8; | |
134 | } | |
135 | } | |
136 | ||
137 | U_CDECL_END | |
b75a7d8f A |
138 | |
139 | U_NAMESPACE_BEGIN | |
140 | ||
141 | //---------------------------------------------------------------------- | |
142 | // PropertyAliases implementation | |
143 | ||
144 | const char* | |
145 | PropertyAliases::chooseNameInGroup(Offset offset, | |
146 | UPropertyNameChoice choice) const { | |
147 | int32_t c = choice; | |
148 | if (!offset || c < 0) { | |
149 | return NULL; | |
150 | } | |
151 | const Offset* p = (const Offset*) getPointer(offset); | |
152 | while (c-- > 0) { | |
153 | if (*p++ < 0) return NULL; | |
154 | } | |
155 | Offset a = *p; | |
156 | if (a < 0) a = -a; | |
157 | return (const char*) getPointerNull(a); | |
158 | } | |
159 | ||
160 | const ValueMap* | |
161 | PropertyAliases::getValueMap(EnumValue prop) const { | |
162 | NonContiguousEnumToOffset* e2o = (NonContiguousEnumToOffset*) getPointer(enumToValue_offset); | |
163 | Offset a = e2o->getOffset(prop); | |
164 | return (const ValueMap*) (a ? getPointerNull(a) : NULL); | |
165 | } | |
166 | ||
167 | inline const char* | |
168 | PropertyAliases::getPropertyName(EnumValue prop, | |
169 | UPropertyNameChoice choice) const { | |
170 | NonContiguousEnumToOffset* e2n = (NonContiguousEnumToOffset*) getPointer(enumToName_offset); | |
171 | return chooseNameInGroup(e2n->getOffset(prop), choice); | |
172 | } | |
173 | ||
174 | inline EnumValue | |
175 | PropertyAliases::getPropertyEnum(const char* alias) const { | |
176 | NameToEnum* n2e = (NameToEnum*) getPointer(nameToEnum_offset); | |
177 | return n2e->getEnum(alias, *this); | |
178 | } | |
179 | ||
180 | inline const char* | |
181 | PropertyAliases::getPropertyValueName(EnumValue prop, | |
182 | EnumValue value, | |
183 | UPropertyNameChoice choice) const { | |
184 | const ValueMap* vm = getValueMap(prop); | |
185 | if (!vm) return NULL; | |
186 | Offset a; | |
187 | if (vm->enumToName_offset) { | |
188 | a = ((EnumToOffset*) getPointer(vm->enumToName_offset))-> | |
189 | getOffset(value); | |
190 | } else { | |
191 | a = ((NonContiguousEnumToOffset*) getPointer(vm->ncEnumToName_offset))-> | |
192 | getOffset(value); | |
193 | } | |
194 | return chooseNameInGroup(a, choice); | |
195 | } | |
196 | ||
197 | inline EnumValue | |
198 | PropertyAliases::getPropertyValueEnum(EnumValue prop, | |
199 | const char* alias) const { | |
200 | const ValueMap* vm = getValueMap(prop); | |
201 | if (!vm) return UCHAR_INVALID_CODE; | |
202 | NameToEnum* n2e = (NameToEnum*) getPointer(vm->nameToEnum_offset); | |
203 | return n2e->getEnum(alias, *this); | |
204 | } | |
205 | ||
206 | U_NAMESPACE_END | |
46f4442e | 207 | U_NAMESPACE_USE |
b75a7d8f A |
208 | |
209 | //---------------------------------------------------------------------- | |
210 | // UDataMemory structures | |
211 | ||
212 | static const PropertyAliases* PNAME = NULL; | |
213 | static UDataMemory* UDATA = NULL; | |
214 | ||
215 | //---------------------------------------------------------------------- | |
216 | // UDataMemory loading/unloading | |
217 | ||
218 | /** | |
219 | * udata callback to verify the zone data. | |
220 | */ | |
221 | U_CDECL_BEGIN | |
222 | static UBool U_CALLCONV | |
374ca955 | 223 | isPNameAcceptable(void* /*context*/, |
b75a7d8f A |
224 | const char* /*type*/, const char* /*name*/, |
225 | const UDataInfo* info) { | |
226 | return | |
227 | info->size >= sizeof(UDataInfo) && | |
228 | info->isBigEndian == U_IS_BIG_ENDIAN && | |
229 | info->charsetFamily == U_CHARSET_FAMILY && | |
230 | info->dataFormat[0] == PNAME_SIG_0 && | |
231 | info->dataFormat[1] == PNAME_SIG_1 && | |
232 | info->dataFormat[2] == PNAME_SIG_2 && | |
233 | info->dataFormat[3] == PNAME_SIG_3 && | |
234 | info->formatVersion[0] == PNAME_FORMAT_VERSION; | |
235 | } | |
236 | ||
73c04bcf | 237 | static UBool U_CALLCONV pname_cleanup(void) { |
b75a7d8f A |
238 | if (UDATA) { |
239 | udata_close(UDATA); | |
240 | UDATA = NULL; | |
241 | } | |
242 | PNAME = NULL; | |
243 | return TRUE; | |
244 | } | |
245 | U_CDECL_END | |
246 | ||
247 | /** | |
248 | * Load the property names data. Caller should check that data is | |
249 | * not loaded BEFORE calling this function. Returns TRUE if the load | |
250 | * succeeds. | |
251 | */ | |
252 | static UBool _load() { | |
253 | UErrorCode ec = U_ZERO_ERROR; | |
254 | UDataMemory* data = | |
255 | udata_openChoice(0, PNAME_DATA_TYPE, PNAME_DATA_NAME, | |
374ca955 | 256 | isPNameAcceptable, 0, &ec); |
b75a7d8f A |
257 | if (U_SUCCESS(ec)) { |
258 | umtx_lock(NULL); | |
259 | if (UDATA == NULL) { | |
260 | UDATA = data; | |
261 | PNAME = (const PropertyAliases*) udata_getMemory(UDATA); | |
374ca955 | 262 | ucln_common_registerCleanup(UCLN_COMMON_PNAME, pname_cleanup); |
b75a7d8f A |
263 | data = NULL; |
264 | } | |
265 | umtx_unlock(NULL); | |
266 | } | |
267 | if (data) { | |
268 | udata_close(data); | |
269 | } | |
270 | return PNAME!=NULL; | |
271 | } | |
272 | ||
273 | /** | |
274 | * Inline function that expands to code that does a lazy load of the | |
275 | * property names data. If the data is already loaded, avoids an | |
276 | * unnecessary function call. If the data is not loaded, call _load() | |
277 | * to load it, and return TRUE if the load succeeds. | |
278 | */ | |
279 | static inline UBool load() { | |
73c04bcf A |
280 | UBool f; |
281 | UMTX_CHECK(NULL, (PNAME!=NULL), f); | |
b75a7d8f A |
282 | return f || _load(); |
283 | } | |
284 | ||
285 | //---------------------------------------------------------------------- | |
286 | // Public API implementation | |
287 | ||
288 | // The C API is just a thin wrapper. Each function obtains a pointer | |
289 | // to the singleton PropertyAliases, and calls the appropriate method | |
290 | // on it. If it cannot obtain a pointer, because valid data is not | |
291 | // available, then it returns NULL or UCHAR_INVALID_CODE. | |
292 | ||
293 | U_CAPI const char* U_EXPORT2 | |
294 | u_getPropertyName(UProperty property, | |
295 | UPropertyNameChoice nameChoice) { | |
296 | return load() ? PNAME->getPropertyName(property, nameChoice) | |
297 | : NULL; | |
298 | } | |
299 | ||
300 | U_CAPI UProperty U_EXPORT2 | |
301 | u_getPropertyEnum(const char* alias) { | |
302 | UProperty p = load() ? (UProperty) PNAME->getPropertyEnum(alias) | |
303 | : UCHAR_INVALID_CODE; | |
304 | return p; | |
305 | } | |
306 | ||
307 | U_CAPI const char* U_EXPORT2 | |
308 | u_getPropertyValueName(UProperty property, | |
309 | int32_t value, | |
310 | UPropertyNameChoice nameChoice) { | |
311 | return load() ? PNAME->getPropertyValueName(property, value, nameChoice) | |
312 | : NULL; | |
313 | } | |
314 | ||
315 | U_CAPI int32_t U_EXPORT2 | |
316 | u_getPropertyValueEnum(UProperty property, | |
317 | const char* alias) { | |
318 | return load() ? PNAME->getPropertyValueEnum(property, alias) | |
374ca955 A |
319 | : (int32_t)UCHAR_INVALID_CODE; |
320 | } | |
321 | ||
322 | /* data swapping ------------------------------------------------------------ */ | |
323 | ||
324 | /* | |
325 | * Sub-structure-swappers use the temp array (which is as large as the | |
326 | * actual data) for intermediate storage, | |
327 | * as well as to indicate if a particular structure has been swapped already. | |
328 | * The temp array is initially reset to all 0. | |
329 | * pos is the byte offset of the sub-structure in the inBytes/outBytes/temp arrays. | |
330 | */ | |
331 | ||
332 | int32_t | |
333 | EnumToOffset::swap(const UDataSwapper *ds, | |
334 | const uint8_t *inBytes, int32_t length, uint8_t *outBytes, | |
335 | uint8_t *temp, int32_t pos, | |
336 | UErrorCode *pErrorCode) { | |
337 | const EnumToOffset *inMap; | |
338 | EnumToOffset *outMap, *tempMap; | |
339 | int32_t size; | |
340 | ||
341 | tempMap=(EnumToOffset *)(temp+pos); | |
342 | if(tempMap->enumStart!=0 || tempMap->enumLimit!=0) { | |
343 | /* this map was swapped already */ | |
344 | size=tempMap->getSize(); | |
345 | return size; | |
346 | } | |
347 | ||
348 | inMap=(const EnumToOffset *)(inBytes+pos); | |
349 | outMap=(EnumToOffset *)(outBytes+pos); | |
350 | ||
351 | tempMap->enumStart=udata_readInt32(ds, inMap->enumStart); | |
352 | tempMap->enumLimit=udata_readInt32(ds, inMap->enumLimit); | |
353 | size=tempMap->getSize(); | |
354 | ||
355 | if(length>=0) { | |
356 | if(length<(pos+size)) { | |
357 | if(length<(int32_t)sizeof(PropertyAliases)) { | |
358 | udata_printError(ds, "upname_swap(EnumToOffset): too few bytes (%d after header)\n" | |
359 | " for pnames.icu EnumToOffset{%d..%d} at %d\n", | |
360 | length, tempMap->enumStart, tempMap->enumLimit, pos); | |
361 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
362 | return 0; | |
363 | } | |
364 | } | |
365 | ||
366 | /* swap enumStart and enumLimit */ | |
367 | ds->swapArray32(ds, inMap, 2*sizeof(EnumValue), outMap, pErrorCode); | |
368 | ||
369 | /* swap _offsetArray[] */ | |
370 | ds->swapArray16(ds, inMap->getOffsetArray(), (tempMap->enumLimit-tempMap->enumStart)*sizeof(Offset), | |
371 | outMap->getOffsetArray(), pErrorCode); | |
372 | } | |
373 | ||
374 | return size; | |
375 | } | |
376 | ||
377 | int32_t | |
378 | NonContiguousEnumToOffset::swap(const UDataSwapper *ds, | |
379 | const uint8_t *inBytes, int32_t length, uint8_t *outBytes, | |
380 | uint8_t *temp, int32_t pos, | |
381 | UErrorCode *pErrorCode) { | |
382 | const NonContiguousEnumToOffset *inMap; | |
383 | NonContiguousEnumToOffset *outMap, *tempMap; | |
384 | int32_t size; | |
385 | ||
386 | tempMap=(NonContiguousEnumToOffset *)(temp+pos); | |
387 | if(tempMap->count!=0) { | |
388 | /* this map was swapped already */ | |
389 | size=tempMap->getSize(); | |
390 | return size; | |
391 | } | |
392 | ||
393 | inMap=(const NonContiguousEnumToOffset *)(inBytes+pos); | |
394 | outMap=(NonContiguousEnumToOffset *)(outBytes+pos); | |
395 | ||
396 | tempMap->count=udata_readInt32(ds, inMap->count); | |
397 | size=tempMap->getSize(); | |
398 | ||
399 | if(length>=0) { | |
400 | if(length<(pos+size)) { | |
401 | if(length<(int32_t)sizeof(PropertyAliases)) { | |
402 | udata_printError(ds, "upname_swap(NonContiguousEnumToOffset): too few bytes (%d after header)\n" | |
403 | " for pnames.icu NonContiguousEnumToOffset[%d] at %d\n", | |
404 | length, tempMap->count, pos); | |
405 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
406 | return 0; | |
407 | } | |
408 | } | |
409 | ||
410 | /* swap count and _enumArray[] */ | |
411 | length=(1+tempMap->count)*sizeof(EnumValue); | |
412 | ds->swapArray32(ds, inMap, length, | |
413 | outMap, pErrorCode); | |
414 | ||
415 | /* swap _offsetArray[] */ | |
416 | pos+=length; | |
417 | ds->swapArray16(ds, inBytes+pos, tempMap->count*sizeof(Offset), | |
418 | outBytes+pos, pErrorCode); | |
419 | } | |
420 | ||
421 | return size; | |
422 | } | |
423 | ||
424 | struct NameAndIndex { | |
425 | Offset name, index; | |
426 | }; | |
427 | ||
428 | U_CDECL_BEGIN | |
429 | typedef int32_t U_CALLCONV PropNameCompareFn(const char *name1, const char *name2); | |
430 | ||
431 | struct CompareContext { | |
432 | const char *chars; | |
433 | PropNameCompareFn *propCompare; | |
434 | }; | |
435 | ||
436 | static int32_t U_CALLCONV | |
437 | upname_compareRows(const void *context, const void *left, const void *right) { | |
438 | CompareContext *cmp=(CompareContext *)context; | |
439 | return cmp->propCompare(cmp->chars+((const NameAndIndex *)left)->name, | |
440 | cmp->chars+((const NameAndIndex *)right)->name); | |
441 | } | |
442 | U_CDECL_END | |
443 | ||
444 | int32_t | |
445 | NameToEnum::swap(const UDataSwapper *ds, | |
446 | const uint8_t *inBytes, int32_t length, uint8_t *outBytes, | |
447 | uint8_t *temp, int32_t pos, | |
448 | UErrorCode *pErrorCode) { | |
449 | const NameToEnum *inMap; | |
450 | NameToEnum *outMap, *tempMap; | |
451 | ||
452 | const EnumValue *inEnumArray; | |
453 | EnumValue *outEnumArray; | |
454 | ||
455 | const Offset *inNameArray; | |
456 | Offset *outNameArray; | |
457 | ||
458 | NameAndIndex *sortArray; | |
459 | CompareContext cmp; | |
460 | ||
461 | int32_t i, size, oldIndex; | |
462 | ||
463 | tempMap=(NameToEnum *)(temp+pos); | |
464 | if(tempMap->count!=0) { | |
465 | /* this map was swapped already */ | |
466 | size=tempMap->getSize(); | |
467 | return size; | |
468 | } | |
469 | ||
470 | inMap=(const NameToEnum *)(inBytes+pos); | |
471 | outMap=(NameToEnum *)(outBytes+pos); | |
472 | ||
473 | tempMap->count=udata_readInt32(ds, inMap->count); | |
474 | size=tempMap->getSize(); | |
475 | ||
476 | if(length>=0) { | |
477 | if(length<(pos+size)) { | |
478 | if(length<(int32_t)sizeof(PropertyAliases)) { | |
479 | udata_printError(ds, "upname_swap(NameToEnum): too few bytes (%d after header)\n" | |
480 | " for pnames.icu NameToEnum[%d] at %d\n", | |
481 | length, tempMap->count, pos); | |
482 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
483 | return 0; | |
484 | } | |
485 | } | |
486 | ||
487 | /* swap count */ | |
488 | ds->swapArray32(ds, inMap, 4, outMap, pErrorCode); | |
489 | ||
490 | inEnumArray=inMap->getEnumArray(); | |
491 | outEnumArray=outMap->getEnumArray(); | |
492 | ||
493 | inNameArray=(const Offset *)(inEnumArray+tempMap->count); | |
494 | outNameArray=(Offset *)(outEnumArray+tempMap->count); | |
495 | ||
496 | if(ds->inCharset==ds->outCharset) { | |
497 | /* no need to sort, just swap the enum/name arrays */ | |
498 | ds->swapArray32(ds, inEnumArray, tempMap->count*4, outEnumArray, pErrorCode); | |
499 | ds->swapArray16(ds, inNameArray, tempMap->count*2, outNameArray, pErrorCode); | |
500 | return size; | |
501 | } | |
502 | ||
503 | /* | |
504 | * The name and enum arrays are sorted by names and must be resorted | |
505 | * if inCharset!=outCharset. | |
506 | * We use the corresponding part of the temp array to sort an array | |
507 | * of pairs of name offsets and sorting indexes. | |
508 | * Then the sorting indexes are used to permutate-swap the name and enum arrays. | |
509 | * | |
510 | * The outBytes must already contain the swapped strings. | |
511 | */ | |
512 | sortArray=(NameAndIndex *)tempMap->getEnumArray(); | |
513 | for(i=0; i<tempMap->count; ++i) { | |
514 | sortArray[i].name=udata_readInt16(ds, inNameArray[i]); | |
515 | sortArray[i].index=(Offset)i; | |
516 | } | |
517 | ||
518 | /* | |
519 | * use a stable sort to avoid shuffling of equal strings, | |
520 | * which makes testing harder | |
521 | */ | |
522 | cmp.chars=(const char *)outBytes; | |
73c04bcf A |
523 | if (ds->outCharset==U_ASCII_FAMILY) { |
524 | cmp.propCompare=uprv_compareASCIIPropertyNames; | |
525 | } | |
526 | else { | |
527 | cmp.propCompare=uprv_compareEBCDICPropertyNames; | |
528 | } | |
374ca955 A |
529 | uprv_sortArray(sortArray, tempMap->count, sizeof(NameAndIndex), |
530 | upname_compareRows, &cmp, | |
531 | TRUE, pErrorCode); | |
532 | if(U_FAILURE(*pErrorCode)) { | |
73c04bcf A |
533 | udata_printError(ds, "upname_swap(NameToEnum).uprv_sortArray(%d items) failed\n", |
534 | tempMap->count); | |
374ca955 A |
535 | return 0; |
536 | } | |
537 | ||
538 | /* copy/swap/permutate _enumArray[] and _nameArray[] */ | |
539 | if(inEnumArray!=outEnumArray) { | |
540 | for(i=0; i<tempMap->count; ++i) { | |
541 | oldIndex=sortArray[i].index; | |
542 | ds->swapArray32(ds, inEnumArray+oldIndex, 4, outEnumArray+i, pErrorCode); | |
543 | ds->swapArray16(ds, inNameArray+oldIndex, 2, outNameArray+i, pErrorCode); | |
544 | } | |
545 | } else { | |
546 | /* | |
547 | * in-place swapping: need to permutate into a temporary array | |
548 | * and then copy back to not destroy the data | |
549 | */ | |
550 | EnumValue *tempEnumArray; | |
551 | Offset *oldIndexes; | |
552 | ||
553 | /* write name offsets directly from sortArray */ | |
554 | for(i=0; i<tempMap->count; ++i) { | |
555 | ds->writeUInt16((uint16_t *)outNameArray+i, (uint16_t)sortArray[i].name); | |
556 | } | |
557 | ||
558 | /* | |
559 | * compress the oldIndexes into a separate array to make space for tempEnumArray | |
560 | * the tempMap _nameArray becomes oldIndexes[], getting the index | |
561 | * values from the 2D sortArray[], | |
562 | * while sortArray=tempMap _enumArray[] becomes tempEnumArray[] | |
563 | * this saves us allocating more memory | |
564 | * | |
565 | * it works because sizeof(NameAndIndex)<=sizeof(EnumValue) | |
566 | * and because the nameArray[] can be used for oldIndexes[] | |
567 | */ | |
568 | tempEnumArray=(EnumValue *)sortArray; | |
569 | oldIndexes=(Offset *)(sortArray+tempMap->count); | |
570 | ||
571 | /* copy sortArray[].index values into oldIndexes[] */ | |
572 | for(i=0; i<tempMap->count; ++i) { | |
573 | oldIndexes[i]=sortArray[i].index; | |
574 | } | |
575 | ||
576 | /* permutate inEnumArray[] into tempEnumArray[] */ | |
577 | for(i=0; i<tempMap->count; ++i) { | |
578 | ds->swapArray32(ds, inEnumArray+oldIndexes[i], 4, tempEnumArray+i, pErrorCode); | |
579 | } | |
580 | ||
581 | /* copy tempEnumArray[] to outEnumArray[] */ | |
582 | uprv_memcpy(outEnumArray, tempEnumArray, tempMap->count*4); | |
583 | } | |
584 | } | |
585 | ||
586 | return size; | |
587 | } | |
588 | ||
589 | int32_t | |
590 | PropertyAliases::swap(const UDataSwapper *ds, | |
591 | const uint8_t *inBytes, int32_t length, uint8_t *outBytes, | |
592 | UErrorCode *pErrorCode) { | |
593 | const PropertyAliases *inAliases; | |
594 | PropertyAliases *outAliases; | |
595 | PropertyAliases aliases; | |
596 | ||
597 | const ValueMap *inValueMaps; | |
598 | ValueMap *outValueMaps; | |
599 | ValueMap valueMap; | |
600 | ||
374ca955 A |
601 | int32_t i; |
602 | ||
603 | inAliases=(const PropertyAliases *)inBytes; | |
604 | outAliases=(PropertyAliases *)outBytes; | |
605 | ||
606 | /* read the input PropertyAliases - all 16-bit values */ | |
607 | for(i=0; i<(int32_t)sizeof(PropertyAliases)/2; ++i) { | |
608 | ((uint16_t *)&aliases)[i]=ds->readUInt16(((const uint16_t *)inBytes)[i]); | |
609 | } | |
610 | ||
611 | if(length>=0) { | |
612 | if(length<aliases.total_size) { | |
613 | udata_printError(ds, "upname_swap(): too few bytes (%d after header) for all of pnames.icu\n", | |
614 | length); | |
615 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
616 | return 0; | |
617 | } | |
618 | ||
619 | /* copy the data for inaccessible bytes */ | |
620 | if(inBytes!=outBytes) { | |
621 | uprv_memcpy(outBytes, inBytes, aliases.total_size); | |
622 | } | |
623 | ||
624 | /* swap the PropertyAliases class fields */ | |
625 | ds->swapArray16(ds, inAliases, sizeof(PropertyAliases), outAliases, pErrorCode); | |
626 | ||
627 | /* swap the name groups */ | |
628 | ds->swapArray16(ds, inBytes+aliases.nameGroupPool_offset, | |
629 | aliases.stringPool_offset-aliases.nameGroupPool_offset, | |
630 | outBytes+aliases.nameGroupPool_offset, pErrorCode); | |
631 | ||
632 | /* swap the strings */ | |
633 | udata_swapInvStringBlock(ds, inBytes+aliases.stringPool_offset, | |
634 | aliases.total_size-aliases.stringPool_offset, | |
635 | outBytes+aliases.stringPool_offset, pErrorCode); | |
636 | ||
637 | /* | |
638 | * alloc uint8_t temp[total_size] and reset it | |
639 | * swap each top-level struct, put at least the count fields into temp | |
640 | * use subclass-specific swap() functions | |
641 | * enumerate value maps, for each | |
642 | * if temp does not have count!=0 yet | |
643 | * read count, put it into temp | |
644 | * swap the array(s) | |
645 | * resort strings in name->enum maps | |
646 | * swap value maps | |
647 | */ | |
729e4ab9 A |
648 | LocalMemory<uint8_t> temp; |
649 | if(temp.allocateInsteadAndReset(aliases.total_size)==NULL) { | |
374ca955 A |
650 | udata_printError(ds, "upname_swap(): unable to allocate temp memory (%d bytes)\n", |
651 | aliases.total_size); | |
652 | *pErrorCode=U_MEMORY_ALLOCATION_ERROR; | |
653 | return 0; | |
654 | } | |
374ca955 A |
655 | |
656 | /* swap properties->name groups map */ | |
657 | NonContiguousEnumToOffset::swap(ds, inBytes, length, outBytes, | |
729e4ab9 | 658 | temp.getAlias(), aliases.enumToName_offset, pErrorCode); |
374ca955 A |
659 | |
660 | /* swap name->properties map */ | |
661 | NameToEnum::swap(ds, inBytes, length, outBytes, | |
729e4ab9 | 662 | temp.getAlias(), aliases.nameToEnum_offset, pErrorCode); |
374ca955 A |
663 | |
664 | /* swap properties->value maps map */ | |
665 | NonContiguousEnumToOffset::swap(ds, inBytes, length, outBytes, | |
729e4ab9 | 666 | temp.getAlias(), aliases.enumToValue_offset, pErrorCode); |
374ca955 A |
667 | |
668 | /* enumerate all ValueMaps and swap them */ | |
669 | inValueMaps=(const ValueMap *)(inBytes+aliases.valueMap_offset); | |
670 | outValueMaps=(ValueMap *)(outBytes+aliases.valueMap_offset); | |
671 | ||
672 | for(i=0; i<aliases.valueMap_count; ++i) { | |
673 | valueMap.enumToName_offset=udata_readInt16(ds, inValueMaps[i].enumToName_offset); | |
674 | valueMap.ncEnumToName_offset=udata_readInt16(ds, inValueMaps[i].ncEnumToName_offset); | |
675 | valueMap.nameToEnum_offset=udata_readInt16(ds, inValueMaps[i].nameToEnum_offset); | |
676 | ||
677 | if(valueMap.enumToName_offset!=0) { | |
678 | EnumToOffset::swap(ds, inBytes, length, outBytes, | |
729e4ab9 | 679 | temp.getAlias(), valueMap.enumToName_offset, |
374ca955 A |
680 | pErrorCode); |
681 | } else if(valueMap.ncEnumToName_offset!=0) { | |
682 | NonContiguousEnumToOffset::swap(ds, inBytes, length, outBytes, | |
729e4ab9 | 683 | temp.getAlias(), valueMap.ncEnumToName_offset, |
374ca955 A |
684 | pErrorCode); |
685 | } | |
686 | if(valueMap.nameToEnum_offset!=0) { | |
687 | NameToEnum::swap(ds, inBytes, length, outBytes, | |
729e4ab9 | 688 | temp.getAlias(), valueMap.nameToEnum_offset, |
374ca955 A |
689 | pErrorCode); |
690 | } | |
691 | } | |
692 | ||
693 | /* swap the ValueMaps array itself */ | |
694 | ds->swapArray16(ds, inValueMaps, aliases.valueMap_count*sizeof(ValueMap), | |
695 | outValueMaps, pErrorCode); | |
696 | ||
697 | /* name groups and strings were swapped above */ | |
374ca955 A |
698 | } |
699 | ||
700 | return aliases.total_size; | |
701 | } | |
702 | ||
703 | U_CAPI int32_t U_EXPORT2 | |
704 | upname_swap(const UDataSwapper *ds, | |
705 | const void *inData, int32_t length, void *outData, | |
706 | UErrorCode *pErrorCode) { | |
707 | const UDataInfo *pInfo; | |
708 | int32_t headerSize; | |
709 | ||
710 | const uint8_t *inBytes; | |
711 | uint8_t *outBytes; | |
712 | ||
713 | /* udata_swapDataHeader checks the arguments */ | |
714 | headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); | |
715 | if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { | |
716 | return 0; | |
717 | } | |
718 | ||
719 | /* check data format and format version */ | |
720 | pInfo=(const UDataInfo *)((const char *)inData+4); | |
721 | if(!( | |
722 | pInfo->dataFormat[0]==0x70 && /* dataFormat="pnam" */ | |
723 | pInfo->dataFormat[1]==0x6e && | |
724 | pInfo->dataFormat[2]==0x61 && | |
725 | pInfo->dataFormat[3]==0x6d && | |
726 | pInfo->formatVersion[0]==1 | |
727 | )) { | |
728 | udata_printError(ds, "upname_swap(): data format %02x.%02x.%02x.%02x (format version %02x) is not recognized as pnames.icu\n", | |
729 | pInfo->dataFormat[0], pInfo->dataFormat[1], | |
730 | pInfo->dataFormat[2], pInfo->dataFormat[3], | |
731 | pInfo->formatVersion[0]); | |
732 | *pErrorCode=U_UNSUPPORTED_ERROR; | |
733 | return 0; | |
734 | } | |
735 | ||
736 | inBytes=(const uint8_t *)inData+headerSize; | |
737 | outBytes=(uint8_t *)outData+headerSize; | |
738 | ||
739 | if(length>=0) { | |
740 | length-=headerSize; | |
741 | if(length<(int32_t)sizeof(PropertyAliases)) { | |
742 | udata_printError(ds, "upname_swap(): too few bytes (%d after header) for pnames.icu\n", | |
743 | length); | |
744 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
745 | return 0; | |
746 | } | |
747 | } | |
748 | ||
749 | return headerSize+PropertyAliases::swap(ds, inBytes, length, outBytes, pErrorCode); | |
b75a7d8f A |
750 | } |
751 | ||
752 | //eof |