]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ******************************************************************** | |
374ca955 A |
3 | * COPYRIGHT: |
4 | * Copyright (c) 1996-2004, International Business Machines Corporation and | |
b75a7d8f A |
5 | * others. All Rights Reserved. |
6 | ******************************************************************** | |
7 | * | |
8 | * uconv_bld.c: | |
9 | * | |
10 | * Defines functions that are used in the creation/initialization/deletion | |
11 | * of converters and related structures. | |
12 | * uses uconv_io.h routines to access disk information | |
13 | * is used by ucnv.h to implement public API create/delete/flushCache routines | |
14 | * Modification History: | |
374ca955 | 15 | * |
b75a7d8f | 16 | * Date Name Description |
374ca955 | 17 | * |
b75a7d8f A |
18 | * 06/20/2000 helena OS/400 port changes; mostly typecast. |
19 | * 06/29/2000 helena Major rewrite of the callback interface. | |
20 | */ | |
21 | ||
374ca955 A |
22 | #include "unicode/utypes.h" |
23 | ||
24 | #if !UCONFIG_NO_CONVERSION | |
b75a7d8f A |
25 | |
26 | #include "unicode/udata.h" | |
27 | #include "unicode/ucnv.h" | |
b75a7d8f | 28 | #include "unicode/uloc.h" |
374ca955 | 29 | #include "utracimp.h" |
b75a7d8f A |
30 | #include "ucnv_io.h" |
31 | #include "ucnv_bld.h" | |
374ca955 A |
32 | #include "ucnvmbcs.h" |
33 | #include "ucnv_ext.h" | |
b75a7d8f A |
34 | #include "ucnv_cnv.h" |
35 | #include "ucnv_imp.h" | |
36 | #include "uhash.h" | |
37 | #include "umutex.h" | |
38 | #include "cstring.h" | |
39 | #include "cmemory.h" | |
40 | #include "ucln_cmn.h" | |
374ca955 | 41 | #include "ustr_cnv.h" |
b75a7d8f A |
42 | |
43 | ||
44 | ||
45 | #if 0 | |
46 | #include <stdio.h> | |
47 | extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l); | |
48 | #define UCNV_DEBUG_LOG(x,y,z) UCNV_DEBUG_LOG(x,y,z,__LINE__) | |
49 | #else | |
50 | # define UCNV_DEBUG_LOG(x,y,z) | |
51 | #endif | |
52 | ||
53 | static const UConverterSharedData * const | |
54 | converterData[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES]={ | |
55 | NULL, NULL, | |
56 | ||
57 | #if UCONFIG_NO_LEGACY_CONVERSION | |
58 | NULL, | |
59 | #else | |
60 | &_MBCSData, | |
61 | #endif | |
62 | ||
63 | &_Latin1Data, | |
64 | &_UTF8Data, &_UTF16BEData, &_UTF16LEData, &_UTF32BEData, &_UTF32LEData, | |
65 | NULL, | |
66 | ||
67 | #if UCONFIG_NO_LEGACY_CONVERSION | |
68 | NULL, | |
69 | NULL, NULL, NULL, NULL, NULL, NULL, | |
70 | NULL, NULL, NULL, NULL, NULL, NULL, | |
71 | NULL, | |
72 | #else | |
73 | &_ISO2022Data, | |
74 | &_LMBCSData1,&_LMBCSData2, &_LMBCSData3, &_LMBCSData4, &_LMBCSData5, &_LMBCSData6, | |
75 | &_LMBCSData8,&_LMBCSData11,&_LMBCSData16,&_LMBCSData17,&_LMBCSData18,&_LMBCSData19, | |
76 | &_HZData, | |
77 | #endif | |
78 | ||
79 | &_SCSUData, | |
80 | ||
81 | #if UCONFIG_NO_LEGACY_CONVERSION | |
82 | NULL, | |
83 | #else | |
84 | &_ISCIIData, | |
85 | #endif | |
86 | ||
87 | &_ASCIIData, | |
88 | &_UTF7Data, &_Bocu1Data, &_UTF16Data, &_UTF32Data, &_CESU8Data, &_IMAPData | |
89 | }; | |
90 | ||
91 | /* Please keep this in binary sorted order for getAlgorithmicTypeFromName. | |
92 | Also the name should be in lower case and all spaces, dashes and underscores | |
93 | removed | |
94 | */ | |
95 | static struct { | |
96 | const char *name; | |
97 | const UConverterType type; | |
98 | } const cnvNameType[] = { | |
99 | { "bocu1", UCNV_BOCU1 }, | |
100 | { "cesu8", UCNV_CESU8 }, | |
101 | #if !UCONFIG_NO_LEGACY_CONVERSION | |
102 | { "hz",UCNV_HZ }, | |
374ca955 | 103 | #endif |
b75a7d8f | 104 | { "imapmailboxname", UCNV_IMAP_MAILBOX }, |
374ca955 | 105 | #if !UCONFIG_NO_LEGACY_CONVERSION |
b75a7d8f A |
106 | { "iscii", UCNV_ISCII }, |
107 | { "iso2022", UCNV_ISO_2022 }, | |
108 | #endif | |
109 | { "iso88591", UCNV_LATIN_1 }, | |
110 | #if !UCONFIG_NO_LEGACY_CONVERSION | |
111 | { "lmbcs1", UCNV_LMBCS_1 }, | |
112 | { "lmbcs11",UCNV_LMBCS_11 }, | |
113 | { "lmbcs16",UCNV_LMBCS_16 }, | |
114 | { "lmbcs17",UCNV_LMBCS_17 }, | |
115 | { "lmbcs18",UCNV_LMBCS_18 }, | |
116 | { "lmbcs19",UCNV_LMBCS_19 }, | |
117 | { "lmbcs2", UCNV_LMBCS_2 }, | |
118 | { "lmbcs3", UCNV_LMBCS_3 }, | |
119 | { "lmbcs4", UCNV_LMBCS_4 }, | |
120 | { "lmbcs5", UCNV_LMBCS_5 }, | |
121 | { "lmbcs6", UCNV_LMBCS_6 }, | |
122 | { "lmbcs8", UCNV_LMBCS_8 }, | |
123 | #endif | |
124 | { "scsu", UCNV_SCSU }, | |
125 | { "usascii", UCNV_US_ASCII }, | |
126 | { "utf16", UCNV_UTF16 }, | |
127 | { "utf16be", UCNV_UTF16_BigEndian }, | |
128 | { "utf16le", UCNV_UTF16_LittleEndian }, | |
129 | #if U_IS_BIG_ENDIAN | |
130 | { "utf16oppositeendian", UCNV_UTF16_LittleEndian }, | |
131 | { "utf16platformendian", UCNV_UTF16_BigEndian }, | |
132 | #else | |
133 | { "utf16oppositeendian", UCNV_UTF16_BigEndian}, | |
134 | { "utf16platformendian", UCNV_UTF16_LittleEndian }, | |
135 | #endif | |
136 | { "utf32", UCNV_UTF32 }, | |
137 | { "utf32be", UCNV_UTF32_BigEndian }, | |
138 | { "utf32le", UCNV_UTF32_LittleEndian }, | |
139 | #if U_IS_BIG_ENDIAN | |
140 | { "utf32oppositeendian", UCNV_UTF32_LittleEndian }, | |
141 | { "utf32platformendian", UCNV_UTF32_BigEndian }, | |
142 | #else | |
143 | { "utf32oppositeendian", UCNV_UTF32_BigEndian }, | |
144 | { "utf32platformendian", UCNV_UTF32_LittleEndian }, | |
145 | #endif | |
146 | { "utf7", UCNV_UTF7 }, | |
147 | { "utf8", UCNV_UTF8 } | |
148 | }; | |
149 | ||
150 | ||
151 | /*initializes some global variables */ | |
152 | static UHashtable *SHARED_DATA_HASHTABLE = NULL; | |
153 | static UMTX cnvCacheMutex = NULL; /* Mutex for synchronizing cnv cache access. */ | |
154 | /* Note: the global mutex is used for */ | |
155 | /* reference count updates. */ | |
156 | ||
157 | ||
158 | static const char DATA_TYPE[] = "cnv"; | |
159 | ||
160 | /* ucnv_cleanup - delete all storage held by the converter cache, except any in use */ | |
161 | /* by open converters. */ | |
162 | /* Not thread safe. */ | |
163 | /* Not supported API. Marked U_CAPI only for use by test programs. */ | |
374ca955 | 164 | static UBool U_CALLCONV ucnv_cleanup(void) { |
b75a7d8f A |
165 | if (SHARED_DATA_HASHTABLE != NULL) { |
166 | ucnv_flushCache(); | |
167 | if (SHARED_DATA_HASHTABLE != NULL && uhash_count(SHARED_DATA_HASHTABLE) == 0) { | |
168 | uhash_close(SHARED_DATA_HASHTABLE); | |
169 | SHARED_DATA_HASHTABLE = NULL; | |
170 | } | |
171 | } | |
172 | ||
173 | umtx_destroy(&cnvCacheMutex); /* Don't worry about destroying the mutex even */ | |
174 | /* if the hash table still exists. The mutex */ | |
175 | /* will lazily re-init itself if needed. */ | |
176 | return (SHARED_DATA_HASHTABLE == NULL); | |
177 | } | |
178 | ||
b75a7d8f A |
179 | static UBool U_CALLCONV |
180 | isCnvAcceptable(void *context, | |
181 | const char *type, const char *name, | |
182 | const UDataInfo *pInfo) { | |
183 | return (UBool)( | |
184 | pInfo->size>=20 && | |
185 | pInfo->isBigEndian==U_IS_BIG_ENDIAN && | |
186 | pInfo->charsetFamily==U_CHARSET_FAMILY && | |
187 | pInfo->sizeofUChar==U_SIZEOF_UCHAR && | |
188 | pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ | |
189 | pInfo->dataFormat[1]==0x6e && | |
190 | pInfo->dataFormat[2]==0x76 && | |
191 | pInfo->dataFormat[3]==0x74 && | |
192 | pInfo->formatVersion[0]==6); /* Everything will be version 6 */ | |
193 | } | |
194 | ||
195 | /** | |
196 | * Un flatten shared data from a UDATA.. | |
197 | */ | |
198 | static UConverterSharedData* | |
374ca955 | 199 | ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCode *status) |
b75a7d8f A |
200 | { |
201 | /* UDataInfo info; -- necessary only if some converters have different formatVersion */ | |
202 | const uint8_t *raw = (const uint8_t *)udata_getMemory(pData); | |
203 | const UConverterStaticData *source = (const UConverterStaticData *) raw; | |
204 | UConverterSharedData *data; | |
205 | UConverterType type = (UConverterType)source->conversionType; | |
206 | ||
207 | if(U_FAILURE(*status)) | |
208 | return NULL; | |
209 | ||
210 | if( (uint16_t)type >= UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES || | |
211 | converterData[type] == NULL || | |
212 | converterData[type]->referenceCounter != 1 || | |
213 | source->structSize != sizeof(UConverterStaticData)) | |
214 | { | |
215 | *status = U_INVALID_TABLE_FORMAT; | |
216 | return NULL; | |
217 | } | |
218 | ||
219 | data = (UConverterSharedData *)uprv_malloc(sizeof(UConverterSharedData)); | |
220 | if(data == NULL) { | |
221 | *status = U_MEMORY_ALLOCATION_ERROR; | |
222 | return NULL; | |
223 | } | |
224 | ||
225 | /* copy initial values from the static structure for this type */ | |
226 | uprv_memcpy(data, converterData[type], sizeof(UConverterSharedData)); | |
227 | ||
374ca955 | 228 | #if 0 /* made UConverterMBCSTable part of UConverterSharedData -- markus 20031107 */ |
b75a7d8f A |
229 | /* |
230 | * It would be much more efficient if the table were a direct member, not a pointer. | |
231 | * However, that would add to the size of all UConverterSharedData objects | |
232 | * even if they do not use this table (especially algorithmic ones). | |
233 | * If this changes, then the static templates from converterData[type] | |
234 | * need more entries. | |
374ca955 A |
235 | * |
236 | * In principle, it would be cleaner if the load() function below | |
237 | * allocated the table. | |
b75a7d8f A |
238 | */ |
239 | data->table = (UConverterTable *)uprv_malloc(sizeof(UConverterTable)); | |
240 | if(data->table == NULL) { | |
241 | uprv_free(data); | |
242 | *status = U_MEMORY_ALLOCATION_ERROR; | |
243 | return NULL; | |
244 | } | |
245 | uprv_memset(data->table, 0, sizeof(UConverterTable)); | |
374ca955 A |
246 | #endif |
247 | ||
b75a7d8f | 248 | data->staticData = source; |
374ca955 | 249 | |
b75a7d8f A |
250 | data->sharedDataCached = FALSE; |
251 | ||
252 | /* fill in fields from the loaded data */ | |
253 | data->dataMemory = (void*)pData; /* for future use */ | |
254 | ||
255 | if(data->impl->load != NULL) { | |
374ca955 | 256 | data->impl->load(data, pArgs, raw + source->structSize, status); |
b75a7d8f A |
257 | if(U_FAILURE(*status)) { |
258 | uprv_free(data->table); | |
259 | uprv_free(data); | |
260 | return NULL; | |
261 | } | |
262 | } | |
263 | return data; | |
264 | } | |
265 | ||
266 | /*Takes an alias name gets an actual converter file name | |
267 | *goes to disk and opens it. | |
268 | *allocates the memory and returns a new UConverter object | |
269 | */ | |
374ca955 | 270 | static UConverterSharedData *createConverterFromFile(UConverterLoadArgs *pArgs, UErrorCode * err) |
b75a7d8f A |
271 | { |
272 | UDataMemory *data; | |
273 | UConverterSharedData *sharedData; | |
274 | ||
374ca955 A |
275 | UTRACE_ENTRY_OC(UTRACE_UCNV_LOAD); |
276 | ||
b75a7d8f | 277 | if (err == NULL || U_FAILURE (*err)) { |
374ca955 | 278 | UTRACE_EXIT_STATUS(*err); |
b75a7d8f A |
279 | return NULL; |
280 | } | |
281 | ||
374ca955 A |
282 | UTRACE_DATA2(UTRACE_OPEN_CLOSE, "load converter %s from package %s", pArgs->name, pArgs->pkg); |
283 | ||
284 | data = udata_openChoice(pArgs->pkg, DATA_TYPE, pArgs->name, isCnvAcceptable, NULL, err); | |
b75a7d8f A |
285 | if(U_FAILURE(*err)) |
286 | { | |
374ca955 | 287 | UTRACE_EXIT_STATUS(*err); |
b75a7d8f A |
288 | return NULL; |
289 | } | |
290 | ||
374ca955 | 291 | sharedData = ucnv_data_unFlattenClone(pArgs, data, err); |
b75a7d8f A |
292 | if(U_FAILURE(*err)) |
293 | { | |
294 | udata_close(data); | |
374ca955 | 295 | UTRACE_EXIT_STATUS(*err); |
b75a7d8f A |
296 | return NULL; |
297 | } | |
298 | ||
374ca955 A |
299 | /* |
300 | * TODO Store pkg in a field in the shared data so that delta-only converters | |
301 | * can load base converters from the same package. | |
302 | * If the pkg name is longer than the field, then either do not load the converter | |
303 | * in the first place, or just set the pkg field to "". | |
304 | */ | |
305 | ||
306 | UTRACE_EXIT_PTR_STATUS(sharedData, *err); | |
b75a7d8f A |
307 | return sharedData; |
308 | } | |
309 | ||
374ca955 | 310 | int32_t |
b75a7d8f A |
311 | ucnv_copyPlatformString(char *platformString, UConverterPlatform pltfrm) |
312 | { | |
313 | switch (pltfrm) | |
314 | { | |
315 | case UCNV_IBM: | |
316 | uprv_strcpy(platformString, "ibm-"); | |
317 | return 4; | |
318 | case UCNV_UNKNOWN: | |
319 | break; | |
320 | } | |
321 | ||
322 | /* default to empty string */ | |
323 | *platformString = 0; | |
324 | return 0; | |
325 | } | |
326 | ||
327 | /*returns a converter type from a string | |
328 | */ | |
329 | static const UConverterSharedData * | |
330 | getAlgorithmicTypeFromName(const char *realName) | |
331 | { | |
332 | uint32_t mid, start, limit; | |
374ca955 | 333 | uint32_t lastMid; |
b75a7d8f A |
334 | int result; |
335 | char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH]; | |
336 | ||
337 | /* Lower case and remove ignoreable characters. */ | |
338 | ucnv_io_stripForCompare(strippedName, realName); | |
339 | ||
340 | /* do a binary search for the alias */ | |
341 | start = 0; | |
342 | limit = sizeof(cnvNameType)/sizeof(cnvNameType[0]); | |
343 | mid = limit; | |
374ca955 | 344 | lastMid = UINT32_MAX; |
b75a7d8f A |
345 | |
346 | for (;;) { | |
347 | mid = (uint32_t)((start + limit) / 2); | |
374ca955 A |
348 | if (lastMid == mid) { /* Have we moved? */ |
349 | break; /* We haven't moved, and it wasn't found. */ | |
350 | } | |
351 | lastMid = mid; | |
b75a7d8f A |
352 | result = uprv_strcmp(strippedName, cnvNameType[mid].name); |
353 | ||
354 | if (result < 0) { | |
355 | limit = mid; | |
356 | } else if (result > 0) { | |
357 | start = mid; | |
358 | } else { | |
359 | return converterData[cnvNameType[mid].type]; | |
360 | } | |
361 | } | |
362 | ||
363 | return NULL; | |
364 | } | |
365 | ||
366 | /* Puts the shared data in the static hashtable SHARED_DATA_HASHTABLE */ | |
367 | /* Will always be called with the cnvCacheMutex alrady being held */ | |
368 | /* by the calling function. */ | |
369 | /* Stores the shared data in the SHARED_DATA_HASHTABLE | |
370 | * @param data The shared data | |
371 | */ | |
372 | static void | |
373 | ucnv_shareConverterData(UConverterSharedData * data) | |
374 | { | |
375 | UErrorCode err = U_ZERO_ERROR; | |
376 | /*Lazy evaluates the Hashtable itself */ | |
377 | /*void *sanity = NULL;*/ | |
378 | ||
379 | if (SHARED_DATA_HASHTABLE == NULL) | |
380 | { | |
381 | SHARED_DATA_HASHTABLE = uhash_openSize(uhash_hashChars, uhash_compareChars, | |
382 | ucnv_io_countAvailableAliases(&err), | |
383 | &err); | |
374ca955 A |
384 | ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); |
385 | ||
386 | if (U_FAILURE(err)) | |
b75a7d8f A |
387 | return; |
388 | } | |
389 | ||
390 | /* ### check to see if the element is not already there! */ | |
391 | ||
392 | /* | |
393 | sanity = ucnv_getSharedConverterData (data->staticData->name); | |
394 | if(sanity != NULL) | |
395 | { | |
396 | UCNV_DEBUG_LOG("put:overwrite!",data->staticData->name,sanity); | |
397 | } | |
398 | UCNV_DEBUG_LOG("put:chk",data->staticData->name,sanity); | |
399 | */ | |
374ca955 | 400 | |
b75a7d8f A |
401 | /* Mark it shared */ |
402 | data->sharedDataCached = TRUE; | |
403 | ||
404 | uhash_put(SHARED_DATA_HASHTABLE, | |
405 | (void*) data->staticData->name, /* Okay to cast away const as long as | |
406 | keyDeleter == NULL */ | |
407 | data, | |
408 | &err); | |
409 | UCNV_DEBUG_LOG("put", data->staticData->name,data); | |
410 | ||
411 | } | |
412 | ||
413 | /* Look up a converter name in the shared data cache. */ | |
414 | /* cnvCacheMutex must be held by the caller to protect the hash table. */ | |
415 | /* gets the shared data from the SHARED_DATA_HASHTABLE (might return NULL if it isn't there) | |
416 | * @param name The name of the shared data | |
417 | * @return the shared data from the SHARED_DATA_HASHTABLE | |
418 | */ | |
419 | static UConverterSharedData * | |
420 | ucnv_getSharedConverterData(const char *name) | |
421 | { | |
422 | /*special case when no Table has yet been created we return NULL */ | |
423 | if (SHARED_DATA_HASHTABLE == NULL) | |
424 | { | |
425 | return NULL; | |
426 | } | |
427 | else | |
428 | { | |
429 | UConverterSharedData *rc; | |
430 | ||
431 | rc = (UConverterSharedData*)uhash_get(SHARED_DATA_HASHTABLE, name); | |
432 | UCNV_DEBUG_LOG("get",name,rc); | |
433 | return rc; | |
434 | } | |
435 | } | |
436 | ||
437 | /*frees the string of memory blocks associates with a sharedConverter | |
438 | *if and only if the referenceCounter == 0 | |
439 | */ | |
440 | /* Deletes (frees) the Shared data it's passed. first it checks the referenceCounter to | |
441 | * see if anyone is using it, if not it frees all the memory stemming from sharedConverterData and | |
442 | * returns TRUE, | |
443 | * otherwise returns FALSE | |
444 | * @param sharedConverterData The shared data | |
445 | * @return if not it frees all the memory stemming from sharedConverterData and | |
446 | * returns TRUE, otherwise returns FALSE | |
447 | */ | |
448 | static UBool | |
449 | ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData) | |
450 | { | |
374ca955 A |
451 | UTRACE_ENTRY_OC(UTRACE_UCNV_UNLOAD); |
452 | UTRACE_DATA2(UTRACE_OPEN_CLOSE, "unload converter %s shared data %p", deadSharedData->staticData->name, deadSharedData); | |
453 | ||
454 | if (deadSharedData->referenceCounter > 0) { | |
455 | UTRACE_EXIT_VALUE((int32_t)FALSE); | |
b75a7d8f | 456 | return FALSE; |
374ca955 | 457 | } |
b75a7d8f A |
458 | |
459 | if (deadSharedData->impl->unload != NULL) { | |
460 | deadSharedData->impl->unload(deadSharedData); | |
461 | } | |
374ca955 | 462 | |
b75a7d8f A |
463 | if(deadSharedData->dataMemory != NULL) |
464 | { | |
465 | UDataMemory *data = (UDataMemory*)deadSharedData->dataMemory; | |
466 | udata_close(data); | |
467 | } | |
468 | ||
469 | if(deadSharedData->table != NULL) | |
470 | { | |
471 | uprv_free(deadSharedData->table); | |
472 | } | |
473 | ||
474 | #if 0 | |
475 | /* if the static data is actually owned by the shared data */ | |
476 | /* enable if we ever have this situation. */ | |
477 | if(deadSharedData->staticDataOwned == TRUE) /* see ucnv_bld.h */ | |
478 | { | |
479 | uprv_free((void*)deadSharedData->staticData); | |
480 | } | |
481 | #endif | |
482 | ||
483 | #if 0 | |
484 | /* Zap it ! */ | |
485 | uprv_memset(deadSharedData->0, sizeof(*deadSharedData)); | |
486 | #endif | |
487 | ||
488 | uprv_free(deadSharedData); | |
374ca955 A |
489 | |
490 | UTRACE_EXIT_VALUE((int32_t)TRUE); | |
b75a7d8f A |
491 | return TRUE; |
492 | } | |
493 | ||
374ca955 A |
494 | /** |
495 | * Load a non-algorithmic converter. | |
496 | * If pkg==NULL, then this function must be called inside umtx_lock(&cnvCacheMutex). | |
497 | */ | |
498 | UConverterSharedData * | |
499 | ucnv_load(UConverterLoadArgs *pArgs, UErrorCode *err) { | |
500 | UConverterSharedData *mySharedConverterData; | |
501 | ||
502 | if(err == NULL || U_FAILURE(*err)) { | |
503 | return NULL; | |
504 | } | |
505 | ||
506 | if(pArgs->pkg != NULL && *pArgs->pkg != 0) { | |
507 | /* application-provided converters are not currently cached */ | |
508 | return createConverterFromFile(pArgs, err); | |
509 | } | |
510 | ||
511 | mySharedConverterData = ucnv_getSharedConverterData(pArgs->name); | |
512 | if (mySharedConverterData == NULL) | |
513 | { | |
514 | /*Not cached, we need to stream it in from file */ | |
515 | mySharedConverterData = createConverterFromFile(pArgs, err); | |
516 | if (U_FAILURE (*err) || (mySharedConverterData == NULL)) | |
517 | { | |
518 | return NULL; | |
519 | } | |
520 | else | |
521 | { | |
522 | /* share it with other library clients */ | |
523 | ucnv_shareConverterData(mySharedConverterData); | |
524 | } | |
525 | } | |
526 | else | |
527 | { | |
528 | /* The data for this converter was already in the cache. */ | |
529 | /* Update the reference counter on the shared data: one more client */ | |
530 | mySharedConverterData->referenceCounter++; | |
531 | } | |
532 | ||
533 | return mySharedConverterData; | |
534 | } | |
535 | ||
536 | /** | |
537 | * Unload a non-algorithmic converter. | |
538 | * It must be sharedData->referenceCounter != ~0 | |
539 | * and this function must be called inside umtx_lock(&cnvCacheMutex). | |
540 | */ | |
b75a7d8f | 541 | void |
374ca955 A |
542 | ucnv_unload(UConverterSharedData *sharedData) { |
543 | if(sharedData != NULL) { | |
b75a7d8f A |
544 | if (sharedData->referenceCounter > 0) { |
545 | sharedData->referenceCounter--; | |
546 | } | |
374ca955 | 547 | |
b75a7d8f A |
548 | if((sharedData->referenceCounter <= 0)&&(sharedData->sharedDataCached == FALSE)) { |
549 | ucnv_deleteSharedConverterData(sharedData); | |
550 | } | |
551 | } | |
b75a7d8f A |
552 | } |
553 | ||
554 | void | |
374ca955 | 555 | ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData) |
b75a7d8f | 556 | { |
b75a7d8f | 557 | /* |
374ca955 A |
558 | Checking whether it's an algorithic converter is okay |
559 | in multithreaded applications because the value never changes. | |
560 | Don't check referenceCounter for any other value. | |
b75a7d8f | 561 | */ |
374ca955 A |
562 | if(sharedData != NULL && sharedData->referenceCounter != ~0) { |
563 | umtx_lock(&cnvCacheMutex); | |
564 | ucnv_unload(sharedData); | |
565 | umtx_unlock(&cnvCacheMutex); | |
566 | } | |
567 | } | |
568 | ||
569 | void | |
570 | ucnv_incrementRefCount(UConverterSharedData *sharedData) | |
571 | { | |
572 | if(sharedData != NULL && sharedData->referenceCounter != ~0) { | |
573 | umtx_lock(&cnvCacheMutex); | |
b75a7d8f | 574 | sharedData->referenceCounter++; |
374ca955 | 575 | umtx_unlock(&cnvCacheMutex); |
b75a7d8f | 576 | } |
b75a7d8f A |
577 | } |
578 | ||
579 | static void | |
580 | parseConverterOptions(const char *inName, | |
581 | char *cnvName, | |
582 | char *locale, | |
583 | uint32_t *pFlags, | |
584 | UErrorCode *err) | |
585 | { | |
586 | char c; | |
587 | int32_t len = 0; | |
588 | ||
589 | /* copy the converter name itself to cnvName */ | |
590 | while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { | |
591 | if (++len>=UCNV_MAX_CONVERTER_NAME_LENGTH) { | |
592 | *err = U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ | |
593 | *cnvName=0; | |
594 | return; | |
595 | } | |
596 | *cnvName++=c; | |
597 | inName++; | |
598 | } | |
599 | *cnvName=0; | |
600 | ||
601 | /* parse options. No more name copying should occur. */ | |
602 | while((c=*inName)!=0) { | |
603 | if(c==UCNV_OPTION_SEP_CHAR) { | |
604 | ++inName; | |
605 | } | |
606 | ||
607 | /* inName is behind an option separator */ | |
608 | if(uprv_strncmp(inName, "locale=", 7)==0) { | |
609 | /* do not modify locale itself in case we have multiple locale options */ | |
610 | char *dest=locale; | |
611 | ||
612 | /* copy the locale option value */ | |
613 | inName+=7; | |
614 | len=0; | |
615 | while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { | |
616 | ++inName; | |
617 | ||
618 | if(++len>=ULOC_FULLNAME_CAPACITY) { | |
619 | *err=U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ | |
620 | *locale=0; | |
621 | return; | |
622 | } | |
623 | ||
624 | *dest++=c; | |
625 | } | |
626 | *dest=0; | |
627 | } else if(uprv_strncmp(inName, "version=", 8)==0) { | |
628 | /* copy the version option value into bits 3..0 of *pFlags */ | |
629 | inName+=8; | |
630 | c=*inName; | |
631 | if(c==0) { | |
632 | *pFlags&=~UCNV_OPTION_VERSION; | |
633 | return; | |
634 | } else if((uint8_t)(c-'0')<10) { | |
635 | *pFlags=(*pFlags&~UCNV_OPTION_VERSION)|(uint32_t)(c-'0'); | |
636 | ++inName; | |
637 | } | |
638 | } else if(uprv_strncmp(inName, "swaplfnl", 8)==0) { | |
639 | inName+=8; | |
640 | *pFlags|=UCNV_OPTION_SWAP_LFNL; | |
641 | /* add processing for new options here with another } else if(uprv_strncmp(inName, "option-name=", XX)==0) { */ | |
642 | } else { | |
643 | /* ignore any other options until we define some */ | |
644 | while(((c = *inName++) != 0) && (c != UCNV_OPTION_SEP_CHAR)) { | |
645 | } | |
646 | if(c==0) { | |
647 | return; | |
648 | } | |
649 | } | |
650 | } | |
651 | } | |
652 | ||
653 | /*Logic determines if the converter is Algorithmic AND/OR cached | |
654 | *depending on that: | |
655 | * -we either go to get data from disk and cache it (Data=TRUE, Cached=False) | |
656 | * -Get it from a Hashtable (Data=X, Cached=TRUE) | |
657 | * -Call dataConverter initializer (Data=TRUE, Cached=TRUE) | |
658 | * -Call AlgorithmicConverter initializer (Data=FALSE, Cached=TRUE) | |
659 | */ | |
374ca955 A |
660 | UConverterSharedData * |
661 | ucnv_loadSharedData(const char *converterName, UConverterLookupData *lookup, UErrorCode * err) { | |
662 | UConverterLookupData stackLookup; | |
b75a7d8f A |
663 | UConverterSharedData *mySharedConverterData = NULL; |
664 | UErrorCode internalErrorCode = U_ZERO_ERROR; | |
374ca955 A |
665 | |
666 | if (U_FAILURE (*err)) { | |
b75a7d8f | 667 | return NULL; |
374ca955 A |
668 | } |
669 | ||
670 | if(lookup == NULL) { | |
671 | lookup = &stackLookup; | |
672 | } | |
b75a7d8f | 673 | |
374ca955 A |
674 | lookup->locale[0] = 0; |
675 | lookup->options = 0; | |
b75a7d8f A |
676 | |
677 | /* In case "name" is NULL we want to open the default converter. */ | |
678 | if (converterName == NULL) { | |
374ca955 A |
679 | lookup->realName = ucnv_io_getDefaultConverterName(); |
680 | if (lookup->realName == NULL) { | |
b75a7d8f A |
681 | *err = U_MISSING_RESOURCE_ERROR; |
682 | return NULL; | |
683 | } | |
684 | /* the default converter name is already canonical */ | |
685 | } else { | |
686 | /* separate the converter name from the options */ | |
374ca955 | 687 | parseConverterOptions(converterName, lookup->cnvName, lookup->locale, &lookup->options, err); |
b75a7d8f A |
688 | if (U_FAILURE(*err)) { |
689 | /* Very bad name used. */ | |
690 | return NULL; | |
691 | } | |
692 | ||
693 | /* get the canonical converter name */ | |
374ca955 A |
694 | lookup->realName = ucnv_io_getConverterName(lookup->cnvName, &internalErrorCode); |
695 | if (U_FAILURE(internalErrorCode) || lookup->realName == NULL) { | |
b75a7d8f A |
696 | /* |
697 | * set the input name in case the converter was added | |
698 | * without updating the alias table, or when there is no alias table | |
699 | */ | |
374ca955 | 700 | lookup->realName = lookup->cnvName; |
b75a7d8f A |
701 | } |
702 | } | |
703 | ||
704 | /* separate the converter name from the options */ | |
374ca955 A |
705 | if(lookup->realName != lookup->cnvName) { |
706 | parseConverterOptions(lookup->realName, lookup->cnvName, lookup->locale, &lookup->options, err); | |
707 | lookup->realName = lookup->cnvName; | |
b75a7d8f | 708 | } |
374ca955 | 709 | |
b75a7d8f | 710 | /* get the shared data for an algorithmic converter, if it is one */ |
374ca955 | 711 | mySharedConverterData = (UConverterSharedData *)getAlgorithmicTypeFromName(lookup->realName); |
b75a7d8f A |
712 | if (mySharedConverterData == NULL) |
713 | { | |
714 | /* it is a data-based converter, get its shared data. */ | |
715 | /* Hold the cnvCacheMutex through the whole process of checking the */ | |
716 | /* converter data cache, and adding new entries to the cache */ | |
717 | /* to prevent other threads from modifying the cache during the */ | |
718 | /* process. */ | |
374ca955 A |
719 | UConverterLoadArgs args={ 0 }; |
720 | ||
721 | args.size=sizeof(UConverterLoadArgs); | |
722 | args.nestedLoads=1; | |
723 | args.options=lookup->options; | |
724 | args.pkg=NULL; | |
725 | args.name=lookup->realName; | |
726 | ||
b75a7d8f | 727 | umtx_lock(&cnvCacheMutex); |
374ca955 A |
728 | mySharedConverterData = ucnv_load(&args, err); |
729 | umtx_unlock(&cnvCacheMutex); | |
730 | if (U_FAILURE (*err) || (mySharedConverterData == NULL)) | |
b75a7d8f | 731 | { |
374ca955 | 732 | return NULL; |
b75a7d8f | 733 | } |
b75a7d8f A |
734 | } |
735 | ||
374ca955 A |
736 | return mySharedConverterData; |
737 | } | |
b75a7d8f | 738 | |
374ca955 A |
739 | UConverter * |
740 | ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err) | |
741 | { | |
742 | UConverterLookupData stackLookup; | |
743 | UConverterSharedData *mySharedConverterData; | |
744 | ||
745 | UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); | |
746 | ||
747 | if(U_SUCCESS(*err)) { | |
748 | UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open converter %s", converterName); | |
749 | ||
750 | mySharedConverterData = ucnv_loadSharedData(converterName, &stackLookup, err); | |
751 | ||
752 | if(U_SUCCESS(*err)) { | |
753 | myUConverter = ucnv_createConverterFromSharedData( | |
754 | myUConverter, mySharedConverterData, | |
755 | stackLookup.realName, stackLookup.locale, stackLookup.options, | |
756 | err); | |
757 | ||
758 | if(U_SUCCESS(*err)) { | |
759 | UTRACE_EXIT_PTR_STATUS(myUConverter, *err); | |
760 | return myUConverter; | |
761 | } else { | |
762 | ucnv_unloadSharedDataIfReady(mySharedConverterData); | |
763 | } | |
b75a7d8f | 764 | } |
b75a7d8f A |
765 | } |
766 | ||
374ca955 A |
767 | /* exit with error */ |
768 | UTRACE_EXIT_STATUS(*err); | |
769 | return NULL; | |
b75a7d8f A |
770 | } |
771 | ||
772 | UConverter * | |
773 | ucnv_createAlgorithmicConverter(UConverter *myUConverter, | |
774 | UConverterType type, | |
775 | const char *locale, uint32_t options, | |
776 | UErrorCode *err) { | |
374ca955 | 777 | UConverter *cnv; |
b75a7d8f A |
778 | const UConverterSharedData *sharedData; |
779 | UBool isAlgorithmicConverter; | |
780 | ||
374ca955 A |
781 | UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_ALGORITHMIC); |
782 | UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open algorithmic converter type %d", (int32_t)type); | |
783 | ||
b75a7d8f A |
784 | if(type<0 || UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES<=type) { |
785 | *err = U_ILLEGAL_ARGUMENT_ERROR; | |
374ca955 | 786 | UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); |
b75a7d8f A |
787 | return NULL; |
788 | } | |
789 | ||
790 | sharedData = converterData[type]; | |
791 | umtx_lock(&cnvCacheMutex); | |
792 | isAlgorithmicConverter = (UBool)(sharedData == NULL || sharedData->referenceCounter != ~0); | |
793 | umtx_unlock(&cnvCacheMutex); | |
794 | if (isAlgorithmicConverter) { | |
795 | /* not a valid type, or not an algorithmic converter */ | |
796 | *err = U_ILLEGAL_ARGUMENT_ERROR; | |
374ca955 | 797 | UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); |
b75a7d8f A |
798 | return NULL; |
799 | } | |
800 | ||
374ca955 | 801 | cnv = ucnv_createConverterFromSharedData(myUConverter, (UConverterSharedData *)sharedData, "", |
b75a7d8f | 802 | locale != NULL ? locale : "", options, err); |
374ca955 A |
803 | |
804 | UTRACE_EXIT_PTR_STATUS(cnv, *err); | |
805 | return cnv; | |
b75a7d8f A |
806 | } |
807 | ||
808 | UConverter* | |
809 | ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode * err) | |
810 | { | |
811 | char cnvName[UCNV_MAX_CONVERTER_NAME_LENGTH], locale[ULOC_FULLNAME_CAPACITY]; | |
b75a7d8f | 812 | UConverter *myUConverter; |
374ca955 A |
813 | UConverterSharedData *mySharedConverterData; |
814 | ||
815 | UConverterLoadArgs args={ 0 }; | |
816 | ||
817 | UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_PACKAGE); | |
b75a7d8f A |
818 | |
819 | if(U_FAILURE(*err)) { | |
374ca955 A |
820 | UTRACE_EXIT_STATUS(*err); |
821 | return NULL; | |
b75a7d8f A |
822 | } |
823 | ||
374ca955 A |
824 | UTRACE_DATA2(UTRACE_OPEN_CLOSE, "open converter %s from package %s", converterName, packageName); |
825 | ||
826 | args.size=sizeof(UConverterLoadArgs); | |
827 | args.nestedLoads=1; | |
828 | args.pkg=packageName; | |
829 | ||
830 | /* first, get the options out of the converterName string */ | |
831 | parseConverterOptions(converterName, cnvName, locale, &args.options, err); | |
b75a7d8f A |
832 | if (U_FAILURE(*err)) { |
833 | /* Very bad name used. */ | |
374ca955 | 834 | UTRACE_EXIT_STATUS(*err); |
b75a7d8f A |
835 | return NULL; |
836 | } | |
374ca955 A |
837 | args.name=cnvName; |
838 | ||
b75a7d8f | 839 | /* open the data, unflatten the shared structure */ |
374ca955 A |
840 | mySharedConverterData = createConverterFromFile(&args, err); |
841 | ||
b75a7d8f | 842 | if (U_FAILURE(*err)) { |
374ca955 A |
843 | UTRACE_EXIT_STATUS(*err); |
844 | return NULL; | |
b75a7d8f A |
845 | } |
846 | ||
847 | /* create the actual converter */ | |
374ca955 A |
848 | myUConverter = ucnv_createConverterFromSharedData(NULL, mySharedConverterData, cnvName, locale, args.options, err); |
849 | ||
b75a7d8f A |
850 | if (U_FAILURE(*err)) { |
851 | ucnv_close(myUConverter); | |
374ca955 A |
852 | UTRACE_EXIT_STATUS(*err); |
853 | return NULL; | |
b75a7d8f | 854 | } |
374ca955 A |
855 | |
856 | UTRACE_EXIT_PTR_STATUS(myUConverter, *err); | |
b75a7d8f A |
857 | return myUConverter; |
858 | } | |
859 | ||
860 | ||
861 | UConverter* | |
862 | ucnv_createConverterFromSharedData(UConverter *myUConverter, | |
863 | UConverterSharedData *mySharedConverterData, | |
864 | const char *realName, const char *locale, uint32_t options, | |
865 | UErrorCode *err) | |
866 | { | |
867 | UBool isCopyLocal; | |
868 | ||
869 | if(myUConverter == NULL) | |
870 | { | |
871 | myUConverter = (UConverter *) uprv_malloc (sizeof (UConverter)); | |
872 | if(myUConverter == NULL) | |
873 | { | |
874 | *err = U_MEMORY_ALLOCATION_ERROR; | |
875 | return NULL; | |
876 | } | |
877 | isCopyLocal = FALSE; | |
878 | } else { | |
879 | isCopyLocal = TRUE; | |
880 | } | |
881 | ||
882 | /* initialize the converter */ | |
883 | uprv_memset(myUConverter, 0, sizeof(UConverter)); | |
884 | myUConverter->isCopyLocal = isCopyLocal; | |
885 | myUConverter->isExtraLocal = FALSE; | |
886 | myUConverter->sharedData = mySharedConverterData; | |
887 | myUConverter->options = options; | |
b75a7d8f A |
888 | myUConverter->fromCharErrorBehaviour = (UConverterToUCallback) UCNV_TO_U_CALLBACK_SUBSTITUTE; |
889 | myUConverter->fromUCharErrorBehaviour = (UConverterFromUCallback) UCNV_FROM_U_CALLBACK_SUBSTITUTE; | |
890 | myUConverter->toUnicodeStatus = myUConverter->sharedData->toUnicodeStatus; | |
374ca955 | 891 | myUConverter->maxBytesPerUChar = myUConverter->sharedData->staticData->maxBytesPerChar; |
b75a7d8f A |
892 | myUConverter->subChar1 = myUConverter->sharedData->staticData->subChar1; |
893 | myUConverter->subCharLen = myUConverter->sharedData->staticData->subCharLen; | |
894 | uprv_memcpy (myUConverter->subChar, myUConverter->sharedData->staticData->subChar, myUConverter->subCharLen); | |
374ca955 | 895 | myUConverter->preFromUFirstCP = U_SENTINEL; |
b75a7d8f A |
896 | |
897 | if(myUConverter != NULL && myUConverter->sharedData->impl->open != NULL) { | |
898 | myUConverter->sharedData->impl->open(myUConverter, realName, locale,options, err); | |
899 | if(U_FAILURE(*err)) { | |
900 | ucnv_close(myUConverter); | |
901 | return NULL; | |
902 | } | |
903 | } | |
904 | ||
905 | return myUConverter; | |
906 | } | |
907 | ||
908 | /*Frees all shared immutable objects that aren't referred to (reference count = 0) | |
909 | */ | |
910 | U_CAPI int32_t U_EXPORT2 | |
911 | ucnv_flushCache () | |
912 | { | |
913 | UConverterSharedData *mySharedData = NULL; | |
374ca955 | 914 | int32_t pos; |
b75a7d8f A |
915 | int32_t tableDeletedNum = 0; |
916 | const UHashElement *e; | |
917 | UErrorCode status = U_ILLEGAL_ARGUMENT_ERROR; | |
374ca955 A |
918 | int32_t i, remaining; |
919 | ||
920 | UTRACE_ENTRY_OC(UTRACE_UCNV_FLUSH_CACHE); | |
b75a7d8f A |
921 | |
922 | /* Close the default converter without creating a new one so that everything will be flushed. */ | |
923 | ucnv_close(u_getDefaultConverter(&status)); | |
924 | ||
925 | /*if shared data hasn't even been lazy evaluated yet | |
926 | * return 0 | |
927 | */ | |
374ca955 A |
928 | if (SHARED_DATA_HASHTABLE == NULL) { |
929 | UTRACE_EXIT_VALUE((int32_t)0); | |
b75a7d8f | 930 | return 0; |
374ca955 | 931 | } |
b75a7d8f A |
932 | |
933 | /*creates an enumeration to iterate through every element in the | |
934 | * table | |
935 | * | |
936 | * Synchronization: holding cnvCacheMutex will prevent any other thread from | |
937 | * accessing or modifying the hash table during the iteration. | |
938 | * The reference count of an entry may be decremented by | |
939 | * ucnv_close while the iteration is in process, but this is | |
940 | * benign. It can't be incremented (in ucnv_createConverter()) | |
941 | * because the sequence of looking up in the cache + incrementing | |
942 | * is protected by cnvCacheMutex. | |
943 | */ | |
944 | umtx_lock(&cnvCacheMutex); | |
374ca955 A |
945 | /* |
946 | * double loop: A delta/extension-only converter has a pointer to its base table's | |
947 | * shared data; the first iteration of the outer loop may see the delta converter | |
948 | * before the base converter, and unloading the delta converter may get the base | |
949 | * converter's reference counter down to 0. | |
950 | */ | |
951 | i = 0; | |
952 | do { | |
953 | remaining = 0; | |
954 | pos = -1; | |
955 | while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL) | |
b75a7d8f | 956 | { |
374ca955 A |
957 | mySharedData = (UConverterSharedData *) e->value.pointer; |
958 | /*deletes only if reference counter == 0 */ | |
959 | if (mySharedData->referenceCounter == 0) | |
960 | { | |
961 | tableDeletedNum++; | |
962 | ||
963 | UCNV_DEBUG_LOG("del",mySharedData->staticData->name,mySharedData); | |
964 | ||
965 | uhash_removeElement(SHARED_DATA_HASHTABLE, e); | |
966 | mySharedData->sharedDataCached = FALSE; | |
967 | ucnv_deleteSharedConverterData (mySharedData); | |
968 | } else { | |
969 | ++remaining; | |
970 | } | |
b75a7d8f | 971 | } |
374ca955 | 972 | } while(++i == 1 && remaining > 0); |
b75a7d8f A |
973 | umtx_unlock(&cnvCacheMutex); |
974 | ||
374ca955 A |
975 | UTRACE_DATA1(UTRACE_INFO, "ucnv_flushCache() exits with %d converters remaining", remaining); |
976 | ||
b75a7d8f A |
977 | ucnv_io_flushAvailableConverterCache(); |
978 | ||
374ca955 | 979 | UTRACE_EXIT_VALUE(tableDeletedNum); |
b75a7d8f A |
980 | return tableDeletedNum; |
981 | } | |
982 | ||
374ca955 A |
983 | /* data swapping ------------------------------------------------------------ */ |
984 | ||
985 | /* most of this might belong more properly into ucnvmbcs.c, but that is so large */ | |
986 | ||
987 | #if !UCONFIG_NO_LEGACY_CONVERSION | |
988 | ||
989 | U_CAPI int32_t U_EXPORT2 | |
990 | ucnv_swap(const UDataSwapper *ds, | |
991 | const void *inData, int32_t length, void *outData, | |
992 | UErrorCode *pErrorCode) { | |
993 | const UDataInfo *pInfo; | |
994 | int32_t headerSize; | |
995 | ||
996 | const uint8_t *inBytes; | |
997 | uint8_t *outBytes; | |
998 | ||
999 | uint32_t offset, count, staticDataSize; | |
1000 | int32_t size; | |
1001 | ||
1002 | const UConverterStaticData *inStaticData; | |
1003 | UConverterStaticData *outStaticData; | |
1004 | ||
1005 | const _MBCSHeader *inMBCSHeader; | |
1006 | _MBCSHeader *outMBCSHeader; | |
1007 | _MBCSHeader mbcsHeader; | |
1008 | uint8_t outputType; | |
1009 | ||
1010 | const int32_t *inExtIndexes; | |
1011 | int32_t extOffset; | |
1012 | ||
1013 | /* udata_swapDataHeader checks the arguments */ | |
1014 | headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); | |
1015 | if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { | |
1016 | return 0; | |
1017 | } | |
1018 | ||
1019 | /* check data format and format version */ | |
1020 | pInfo=(const UDataInfo *)((const char *)inData+4); | |
1021 | if(!( | |
1022 | pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ | |
1023 | pInfo->dataFormat[1]==0x6e && | |
1024 | pInfo->dataFormat[2]==0x76 && | |
1025 | pInfo->dataFormat[3]==0x74 && | |
1026 | pInfo->formatVersion[0]==6 && | |
1027 | pInfo->formatVersion[1]>=2 | |
1028 | )) { | |
1029 | udata_printError(ds, "ucnv_swap(): data format %02x.%02x.%02x.%02x (format version %02x.%02x) is not recognized as an ICU .cnv conversion table\n", | |
1030 | pInfo->dataFormat[0], pInfo->dataFormat[1], | |
1031 | pInfo->dataFormat[2], pInfo->dataFormat[3], | |
1032 | pInfo->formatVersion[0], pInfo->formatVersion[1]); | |
1033 | *pErrorCode=U_UNSUPPORTED_ERROR; | |
1034 | return 0; | |
1035 | } | |
1036 | ||
1037 | inBytes=(const uint8_t *)inData+headerSize; | |
1038 | outBytes=(uint8_t *)outData+headerSize; | |
1039 | ||
1040 | /* read the initial UConverterStaticData structure after the UDataInfo header */ | |
1041 | inStaticData=(const UConverterStaticData *)inBytes; | |
1042 | outStaticData=(UConverterStaticData *)outBytes; | |
1043 | ||
1044 | if(length<0) { | |
1045 | staticDataSize=ds->readUInt32(inStaticData->structSize); | |
1046 | } else { | |
1047 | length-=headerSize; | |
1048 | if( length<sizeof(UConverterStaticData) || | |
1049 | (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize)) | |
1050 | ) { | |
1051 | udata_printError(ds, "ucnv_swap(): too few bytes (%d after header) for an ICU .cnv conversion table\n", | |
1052 | length); | |
1053 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
1054 | return 0; | |
1055 | } | |
1056 | } | |
1057 | ||
1058 | if(length>=0) { | |
1059 | /* swap the static data */ | |
1060 | if(inStaticData!=outStaticData) { | |
1061 | uprv_memcpy(outStaticData, inStaticData, staticDataSize); | |
1062 | } | |
1063 | ||
1064 | ds->swapArray32(ds, &inStaticData->structSize, 4, | |
1065 | &outStaticData->structSize, pErrorCode); | |
1066 | ds->swapArray32(ds, &inStaticData->codepage, 4, | |
1067 | &outStaticData->codepage, pErrorCode); | |
1068 | ||
1069 | ds->swapInvChars(ds, inStaticData->name, uprv_strlen(inStaticData->name), | |
1070 | outStaticData->name, pErrorCode); | |
1071 | if(U_FAILURE(*pErrorCode)) { | |
1072 | udata_printError(ds, "ucnv_swap(): error swapping converter name - %s\n", | |
1073 | u_errorName(*pErrorCode)); | |
1074 | return 0; | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | inBytes+=staticDataSize; | |
1079 | outBytes+=staticDataSize; | |
1080 | if(length>=0) { | |
1081 | length-=(int32_t)staticDataSize; | |
1082 | } | |
1083 | ||
1084 | /* check for supported conversionType values */ | |
1085 | if(inStaticData->conversionType==UCNV_MBCS) { | |
1086 | /* swap MBCS data */ | |
1087 | inMBCSHeader=(const _MBCSHeader *)inBytes; | |
1088 | outMBCSHeader=(_MBCSHeader *)outBytes; | |
1089 | ||
1090 | if(!(inMBCSHeader->version[0]==4 || inMBCSHeader->version[1]>=1)) { | |
1091 | udata_printError(ds, "ucnv_swap(): unsupported _MBCSHeader.version %d.%d\n", | |
1092 | inMBCSHeader->version[0], inMBCSHeader->version[1]); | |
1093 | *pErrorCode=U_UNSUPPORTED_ERROR; | |
1094 | return 0; | |
1095 | } | |
1096 | ||
1097 | uprv_memcpy(mbcsHeader.version, inMBCSHeader->version, 4); | |
1098 | mbcsHeader.countStates= ds->readUInt32(inMBCSHeader->countStates); | |
1099 | mbcsHeader.countToUFallbacks= ds->readUInt32(inMBCSHeader->countToUFallbacks); | |
1100 | mbcsHeader.offsetToUCodeUnits= ds->readUInt32(inMBCSHeader->offsetToUCodeUnits); | |
1101 | mbcsHeader.offsetFromUTable= ds->readUInt32(inMBCSHeader->offsetFromUTable); | |
1102 | mbcsHeader.offsetFromUBytes= ds->readUInt32(inMBCSHeader->offsetFromUBytes); | |
1103 | mbcsHeader.flags= ds->readUInt32(inMBCSHeader->flags); | |
1104 | mbcsHeader.fromUBytesLength= ds->readUInt32(inMBCSHeader->fromUBytesLength); | |
1105 | ||
1106 | extOffset=(int32_t)mbcsHeader.flags>>8; | |
1107 | outputType=(uint8_t)mbcsHeader.flags; | |
1108 | ||
1109 | /* make sure that the output type is known */ | |
1110 | switch(outputType) { | |
1111 | case MBCS_OUTPUT_1: | |
1112 | case MBCS_OUTPUT_2: | |
1113 | case MBCS_OUTPUT_3: | |
1114 | case MBCS_OUTPUT_4: | |
1115 | case MBCS_OUTPUT_3_EUC: | |
1116 | case MBCS_OUTPUT_4_EUC: | |
1117 | case MBCS_OUTPUT_2_SISO: | |
1118 | case MBCS_OUTPUT_EXT_ONLY: | |
1119 | /* OK */ | |
1120 | break; | |
1121 | default: | |
1122 | udata_printError(ds, "ucnv_swap(): unsupported MBCS output type 0x%x\n", | |
1123 | outputType); | |
1124 | *pErrorCode=U_UNSUPPORTED_ERROR; | |
1125 | return 0; | |
1126 | } | |
1127 | ||
1128 | /* calculate the length of the MBCS data */ | |
1129 | if(extOffset==0) { | |
1130 | size=(int32_t)(mbcsHeader.offsetFromUBytes+mbcsHeader.fromUBytesLength); | |
1131 | ||
1132 | /* avoid compiler warnings - not otherwise necessary, and the value does not matter */ | |
1133 | inExtIndexes=NULL; | |
1134 | } else { | |
1135 | /* there is extension data after the base data, see ucnv_ext.h */ | |
1136 | if(length>=0 && length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) { | |
1137 | udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n", | |
1138 | length); | |
1139 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
1140 | return 0; | |
1141 | } | |
1142 | ||
1143 | inExtIndexes=(const int32_t *)(inBytes+extOffset); | |
1144 | size=extOffset+udata_readInt32(ds, inExtIndexes[UCNV_EXT_SIZE]); | |
1145 | } | |
1146 | ||
1147 | if(length>=0) { | |
1148 | if(length<size) { | |
1149 | udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", | |
1150 | length); | |
1151 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; | |
1152 | return 0; | |
1153 | } | |
1154 | ||
1155 | /* copy the data for inaccessible bytes */ | |
1156 | if(inBytes!=outBytes) { | |
1157 | uprv_memcpy(outBytes, inBytes, size); | |
1158 | } | |
1159 | ||
1160 | /* swap the MBCSHeader */ | |
1161 | ds->swapArray32(ds, &inMBCSHeader->countStates, 7*4, | |
1162 | &outMBCSHeader->countStates, pErrorCode); | |
1163 | ||
1164 | if(outputType==MBCS_OUTPUT_EXT_ONLY) { | |
1165 | /* | |
1166 | * extension-only file, | |
1167 | * contains a base name instead of normal base table data | |
1168 | */ | |
1169 | ||
1170 | /* swap the base name, between the header and the extension data */ | |
1171 | ds->swapInvChars(ds, inMBCSHeader+1, uprv_strlen((const char *)(inMBCSHeader+1)), | |
1172 | outMBCSHeader+1, pErrorCode); | |
1173 | } else { | |
1174 | /* normal file with base table data */ | |
1175 | ||
1176 | /* swap the state table, 1kB per state */ | |
1177 | ds->swapArray32(ds, inMBCSHeader+1, (int32_t)(mbcsHeader.countStates*1024), | |
1178 | outMBCSHeader+1, pErrorCode); | |
1179 | ||
1180 | /* swap the toUFallbacks[] */ | |
1181 | offset=sizeof(_MBCSHeader)+mbcsHeader.countStates*1024; | |
1182 | ds->swapArray32(ds, inBytes+offset, (int32_t)(mbcsHeader.countToUFallbacks*8), | |
1183 | outBytes+offset, pErrorCode); | |
1184 | ||
1185 | /* swap the unicodeCodeUnits[] */ | |
1186 | offset=mbcsHeader.offsetToUCodeUnits; | |
1187 | count=mbcsHeader.offsetFromUTable-offset; | |
1188 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, | |
1189 | outBytes+offset, pErrorCode); | |
1190 | ||
1191 | /* offset to the stage 1 table, independent of the outputType */ | |
1192 | offset=mbcsHeader.offsetFromUTable; | |
1193 | ||
1194 | if(outputType==MBCS_OUTPUT_1) { | |
1195 | /* SBCS: swap the fromU tables, all 16 bits wide */ | |
1196 | count=(mbcsHeader.offsetFromUBytes-offset)+mbcsHeader.fromUBytesLength; | |
1197 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, | |
1198 | outBytes+offset, pErrorCode); | |
1199 | } else { | |
1200 | /* otherwise: swap the stage tables separately */ | |
1201 | ||
1202 | /* stage 1 table: uint16_t[0x440 or 0x40] */ | |
1203 | if(inStaticData->unicodeMask&UCNV_HAS_SUPPLEMENTARY) { | |
1204 | count=0x440*2; /* for all of Unicode */ | |
1205 | } else { | |
1206 | count=0x40*2; /* only BMP */ | |
1207 | } | |
1208 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, | |
1209 | outBytes+offset, pErrorCode); | |
1210 | ||
1211 | /* stage 2 table: uint32_t[] */ | |
1212 | offset+=count; | |
1213 | count=mbcsHeader.offsetFromUBytes-offset; | |
1214 | ds->swapArray32(ds, inBytes+offset, (int32_t)count, | |
1215 | outBytes+offset, pErrorCode); | |
1216 | ||
1217 | /* stage 3/result bytes: sometimes uint16_t[] or uint32_t[] */ | |
1218 | offset=mbcsHeader.offsetFromUBytes; | |
1219 | count=mbcsHeader.fromUBytesLength; | |
1220 | switch(outputType) { | |
1221 | case MBCS_OUTPUT_2: | |
1222 | case MBCS_OUTPUT_3_EUC: | |
1223 | case MBCS_OUTPUT_2_SISO: | |
1224 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, | |
1225 | outBytes+offset, pErrorCode); | |
1226 | break; | |
1227 | case MBCS_OUTPUT_4: | |
1228 | ds->swapArray32(ds, inBytes+offset, (int32_t)count, | |
1229 | outBytes+offset, pErrorCode); | |
1230 | break; | |
1231 | default: | |
1232 | /* just uint8_t[], nothing to swap */ | |
1233 | break; | |
1234 | } | |
1235 | } | |
1236 | } | |
1237 | ||
1238 | if(extOffset!=0) { | |
1239 | /* swap the extension data */ | |
1240 | inBytes+=extOffset; | |
1241 | outBytes+=extOffset; | |
1242 | ||
1243 | /* swap toUTable[] */ | |
1244 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_INDEX]); | |
1245 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_LENGTH]); | |
1246 | ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); | |
1247 | ||
1248 | /* swap toUUChars[] */ | |
1249 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_INDEX]); | |
1250 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_LENGTH]); | |
1251 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); | |
1252 | ||
1253 | /* swap fromUTableUChars[] */ | |
1254 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_UCHARS_INDEX]); | |
1255 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_LENGTH]); | |
1256 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); | |
1257 | ||
1258 | /* swap fromUTableValues[] */ | |
1259 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_VALUES_INDEX]); | |
1260 | /* same length as for fromUTableUChars[] */ | |
1261 | ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); | |
1262 | ||
1263 | /* no need to swap fromUBytes[] */ | |
1264 | ||
1265 | /* swap fromUStage12[] */ | |
1266 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_INDEX]); | |
1267 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_LENGTH]); | |
1268 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); | |
1269 | ||
1270 | /* swap fromUStage3[] */ | |
1271 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_INDEX]); | |
1272 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_LENGTH]); | |
1273 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); | |
1274 | ||
1275 | /* swap fromUStage3b[] */ | |
1276 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_INDEX]); | |
1277 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_LENGTH]); | |
1278 | ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); | |
1279 | ||
1280 | /* swap indexes[] */ | |
1281 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_INDEXES_LENGTH]); | |
1282 | ds->swapArray32(ds, inBytes, length*4, outBytes, pErrorCode); | |
1283 | } | |
1284 | } | |
1285 | } else { | |
1286 | udata_printError(ds, "ucnv_swap(): unknown conversionType=%d!=UCNV_MBCS\n", | |
1287 | inStaticData->conversionType); | |
1288 | *pErrorCode=U_UNSUPPORTED_ERROR; | |
1289 | return 0; | |
1290 | } | |
1291 | ||
1292 | return headerSize+(int32_t)staticDataSize+size; | |
1293 | } | |
1294 | ||
1295 | #endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ | |
1296 | ||
1297 | #endif |