Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* |
michael@0 | 2 | ******************************************************************** |
michael@0 | 3 | * COPYRIGHT: |
michael@0 | 4 | * Copyright (c) 1996-2013, International Business Machines Corporation and |
michael@0 | 5 | * others. All Rights Reserved. |
michael@0 | 6 | ******************************************************************** |
michael@0 | 7 | * |
michael@0 | 8 | * uconv_bld.cpp: |
michael@0 | 9 | * |
michael@0 | 10 | * Defines functions that are used in the creation/initialization/deletion |
michael@0 | 11 | * of converters and related structures. |
michael@0 | 12 | * uses uconv_io.h routines to access disk information |
michael@0 | 13 | * is used by ucnv.h to implement public API create/delete/flushCache routines |
michael@0 | 14 | * Modification History: |
michael@0 | 15 | * |
michael@0 | 16 | * Date Name Description |
michael@0 | 17 | * |
michael@0 | 18 | * 06/20/2000 helena OS/400 port changes; mostly typecast. |
michael@0 | 19 | * 06/29/2000 helena Major rewrite of the callback interface. |
michael@0 | 20 | */ |
michael@0 | 21 | |
michael@0 | 22 | #include "unicode/utypes.h" |
michael@0 | 23 | |
michael@0 | 24 | #if !UCONFIG_NO_CONVERSION |
michael@0 | 25 | |
michael@0 | 26 | #include "unicode/putil.h" |
michael@0 | 27 | #include "unicode/udata.h" |
michael@0 | 28 | #include "unicode/ucnv.h" |
michael@0 | 29 | #include "unicode/uloc.h" |
michael@0 | 30 | #include "mutex.h" |
michael@0 | 31 | #include "putilimp.h" |
michael@0 | 32 | #include "uassert.h" |
michael@0 | 33 | #include "utracimp.h" |
michael@0 | 34 | #include "ucnv_io.h" |
michael@0 | 35 | #include "ucnv_bld.h" |
michael@0 | 36 | #include "ucnvmbcs.h" |
michael@0 | 37 | #include "ucnv_ext.h" |
michael@0 | 38 | #include "ucnv_cnv.h" |
michael@0 | 39 | #include "ucnv_imp.h" |
michael@0 | 40 | #include "uhash.h" |
michael@0 | 41 | #include "umutex.h" |
michael@0 | 42 | #include "cstring.h" |
michael@0 | 43 | #include "cmemory.h" |
michael@0 | 44 | #include "ucln_cmn.h" |
michael@0 | 45 | #include "ustr_cnv.h" |
michael@0 | 46 | |
michael@0 | 47 | |
michael@0 | 48 | #if 0 |
michael@0 | 49 | #include <stdio.h> |
michael@0 | 50 | extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l); |
michael@0 | 51 | #define UCNV_DEBUG_LOG(x,y,z) UCNV_DEBUG_LOG(x,y,z,__LINE__) |
michael@0 | 52 | #else |
michael@0 | 53 | # define UCNV_DEBUG_LOG(x,y,z) |
michael@0 | 54 | #endif |
michael@0 | 55 | |
michael@0 | 56 | static const UConverterSharedData * const |
michael@0 | 57 | converterData[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES]={ |
michael@0 | 58 | NULL, NULL, |
michael@0 | 59 | |
michael@0 | 60 | #if UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 61 | NULL, |
michael@0 | 62 | #else |
michael@0 | 63 | &_MBCSData, |
michael@0 | 64 | #endif |
michael@0 | 65 | |
michael@0 | 66 | &_Latin1Data, |
michael@0 | 67 | &_UTF8Data, &_UTF16BEData, &_UTF16LEData, &_UTF32BEData, &_UTF32LEData, |
michael@0 | 68 | NULL, |
michael@0 | 69 | |
michael@0 | 70 | #if UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 71 | NULL, |
michael@0 | 72 | NULL, NULL, NULL, NULL, NULL, NULL, |
michael@0 | 73 | NULL, NULL, NULL, NULL, NULL, NULL, |
michael@0 | 74 | NULL, |
michael@0 | 75 | #else |
michael@0 | 76 | &_ISO2022Data, |
michael@0 | 77 | &_LMBCSData1,&_LMBCSData2, &_LMBCSData3, &_LMBCSData4, &_LMBCSData5, &_LMBCSData6, |
michael@0 | 78 | &_LMBCSData8,&_LMBCSData11,&_LMBCSData16,&_LMBCSData17,&_LMBCSData18,&_LMBCSData19, |
michael@0 | 79 | &_HZData, |
michael@0 | 80 | #endif |
michael@0 | 81 | |
michael@0 | 82 | &_SCSUData, |
michael@0 | 83 | |
michael@0 | 84 | #if UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 85 | NULL, |
michael@0 | 86 | #else |
michael@0 | 87 | &_ISCIIData, |
michael@0 | 88 | #endif |
michael@0 | 89 | |
michael@0 | 90 | &_ASCIIData, |
michael@0 | 91 | &_UTF7Data, &_Bocu1Data, &_UTF16Data, &_UTF32Data, &_CESU8Data, &_IMAPData, |
michael@0 | 92 | |
michael@0 | 93 | #if UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 94 | NULL, |
michael@0 | 95 | #else |
michael@0 | 96 | &_CompoundTextData |
michael@0 | 97 | #endif |
michael@0 | 98 | }; |
michael@0 | 99 | |
michael@0 | 100 | /* Please keep this in binary sorted order for getAlgorithmicTypeFromName. |
michael@0 | 101 | Also the name should be in lower case and all spaces, dashes and underscores |
michael@0 | 102 | removed |
michael@0 | 103 | */ |
michael@0 | 104 | static struct { |
michael@0 | 105 | const char *name; |
michael@0 | 106 | const UConverterType type; |
michael@0 | 107 | } const cnvNameType[] = { |
michael@0 | 108 | { "bocu1", UCNV_BOCU1 }, |
michael@0 | 109 | { "cesu8", UCNV_CESU8 }, |
michael@0 | 110 | #if !UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 111 | { "hz",UCNV_HZ }, |
michael@0 | 112 | #endif |
michael@0 | 113 | { "imapmailboxname", UCNV_IMAP_MAILBOX }, |
michael@0 | 114 | #if !UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 115 | { "iscii", UCNV_ISCII }, |
michael@0 | 116 | { "iso2022", UCNV_ISO_2022 }, |
michael@0 | 117 | #endif |
michael@0 | 118 | { "iso88591", UCNV_LATIN_1 }, |
michael@0 | 119 | #if !UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 120 | { "lmbcs1", UCNV_LMBCS_1 }, |
michael@0 | 121 | { "lmbcs11",UCNV_LMBCS_11 }, |
michael@0 | 122 | { "lmbcs16",UCNV_LMBCS_16 }, |
michael@0 | 123 | { "lmbcs17",UCNV_LMBCS_17 }, |
michael@0 | 124 | { "lmbcs18",UCNV_LMBCS_18 }, |
michael@0 | 125 | { "lmbcs19",UCNV_LMBCS_19 }, |
michael@0 | 126 | { "lmbcs2", UCNV_LMBCS_2 }, |
michael@0 | 127 | { "lmbcs3", UCNV_LMBCS_3 }, |
michael@0 | 128 | { "lmbcs4", UCNV_LMBCS_4 }, |
michael@0 | 129 | { "lmbcs5", UCNV_LMBCS_5 }, |
michael@0 | 130 | { "lmbcs6", UCNV_LMBCS_6 }, |
michael@0 | 131 | { "lmbcs8", UCNV_LMBCS_8 }, |
michael@0 | 132 | #endif |
michael@0 | 133 | { "scsu", UCNV_SCSU }, |
michael@0 | 134 | { "usascii", UCNV_US_ASCII }, |
michael@0 | 135 | { "utf16", UCNV_UTF16 }, |
michael@0 | 136 | { "utf16be", UCNV_UTF16_BigEndian }, |
michael@0 | 137 | { "utf16le", UCNV_UTF16_LittleEndian }, |
michael@0 | 138 | #if U_IS_BIG_ENDIAN |
michael@0 | 139 | { "utf16oppositeendian", UCNV_UTF16_LittleEndian }, |
michael@0 | 140 | { "utf16platformendian", UCNV_UTF16_BigEndian }, |
michael@0 | 141 | #else |
michael@0 | 142 | { "utf16oppositeendian", UCNV_UTF16_BigEndian}, |
michael@0 | 143 | { "utf16platformendian", UCNV_UTF16_LittleEndian }, |
michael@0 | 144 | #endif |
michael@0 | 145 | { "utf32", UCNV_UTF32 }, |
michael@0 | 146 | { "utf32be", UCNV_UTF32_BigEndian }, |
michael@0 | 147 | { "utf32le", UCNV_UTF32_LittleEndian }, |
michael@0 | 148 | #if U_IS_BIG_ENDIAN |
michael@0 | 149 | { "utf32oppositeendian", UCNV_UTF32_LittleEndian }, |
michael@0 | 150 | { "utf32platformendian", UCNV_UTF32_BigEndian }, |
michael@0 | 151 | #else |
michael@0 | 152 | { "utf32oppositeendian", UCNV_UTF32_BigEndian }, |
michael@0 | 153 | { "utf32platformendian", UCNV_UTF32_LittleEndian }, |
michael@0 | 154 | #endif |
michael@0 | 155 | { "utf7", UCNV_UTF7 }, |
michael@0 | 156 | { "utf8", UCNV_UTF8 }, |
michael@0 | 157 | { "x11compoundtext", UCNV_COMPOUND_TEXT} |
michael@0 | 158 | }; |
michael@0 | 159 | |
michael@0 | 160 | |
michael@0 | 161 | /*initializes some global variables */ |
michael@0 | 162 | static UHashtable *SHARED_DATA_HASHTABLE = NULL; |
michael@0 | 163 | static UMutex cnvCacheMutex = U_MUTEX_INITIALIZER; /* Mutex for synchronizing cnv cache access. */ |
michael@0 | 164 | /* Note: the global mutex is used for */ |
michael@0 | 165 | /* reference count updates. */ |
michael@0 | 166 | |
michael@0 | 167 | static const char **gAvailableConverters = NULL; |
michael@0 | 168 | static uint16_t gAvailableConverterCount = 0; |
michael@0 | 169 | static icu::UInitOnce gAvailableConvertersInitOnce = U_INITONCE_INITIALIZER; |
michael@0 | 170 | |
michael@0 | 171 | #if !U_CHARSET_IS_UTF8 |
michael@0 | 172 | |
michael@0 | 173 | /* This contains the resolved converter name. So no further alias lookup is needed again. */ |
michael@0 | 174 | static char gDefaultConverterNameBuffer[UCNV_MAX_CONVERTER_NAME_LENGTH + 1]; /* +1 for NULL */ |
michael@0 | 175 | static const char *gDefaultConverterName = NULL; |
michael@0 | 176 | |
michael@0 | 177 | /* |
michael@0 | 178 | If the default converter is an algorithmic converter, this is the cached value. |
michael@0 | 179 | We don't cache a full UConverter and clone it because ucnv_clone doesn't have |
michael@0 | 180 | less overhead than an algorithmic open. We don't cache non-algorithmic converters |
michael@0 | 181 | because ucnv_flushCache must be able to unload the default converter and its table. |
michael@0 | 182 | */ |
michael@0 | 183 | static const UConverterSharedData *gDefaultAlgorithmicSharedData = NULL; |
michael@0 | 184 | |
michael@0 | 185 | /* Does gDefaultConverterName have a converter option and require extra parsing? */ |
michael@0 | 186 | static UBool gDefaultConverterContainsOption; |
michael@0 | 187 | |
michael@0 | 188 | #endif /* !U_CHARSET_IS_UTF8 */ |
michael@0 | 189 | |
michael@0 | 190 | static const char DATA_TYPE[] = "cnv"; |
michael@0 | 191 | |
michael@0 | 192 | /* ucnv_flushAvailableConverterCache. This is only called from ucnv_cleanup(). |
michael@0 | 193 | * If it is ever to be called from elsewhere, synchronization |
michael@0 | 194 | * will need to be considered. |
michael@0 | 195 | */ |
michael@0 | 196 | static void |
michael@0 | 197 | ucnv_flushAvailableConverterCache() { |
michael@0 | 198 | gAvailableConverterCount = 0; |
michael@0 | 199 | if (gAvailableConverters) { |
michael@0 | 200 | uprv_free((char **)gAvailableConverters); |
michael@0 | 201 | gAvailableConverters = NULL; |
michael@0 | 202 | } |
michael@0 | 203 | gAvailableConvertersInitOnce.reset(); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | /* ucnv_cleanup - delete all storage held by the converter cache, except any */ |
michael@0 | 207 | /* in use by open converters. */ |
michael@0 | 208 | /* Not thread safe. */ |
michael@0 | 209 | /* Not supported API. */ |
michael@0 | 210 | static UBool U_CALLCONV ucnv_cleanup(void) { |
michael@0 | 211 | ucnv_flushCache(); |
michael@0 | 212 | if (SHARED_DATA_HASHTABLE != NULL && uhash_count(SHARED_DATA_HASHTABLE) == 0) { |
michael@0 | 213 | uhash_close(SHARED_DATA_HASHTABLE); |
michael@0 | 214 | SHARED_DATA_HASHTABLE = NULL; |
michael@0 | 215 | } |
michael@0 | 216 | |
michael@0 | 217 | /* Isn't called from flushCache because other threads may have preexisting references to the table. */ |
michael@0 | 218 | ucnv_flushAvailableConverterCache(); |
michael@0 | 219 | |
michael@0 | 220 | #if !U_CHARSET_IS_UTF8 |
michael@0 | 221 | gDefaultConverterName = NULL; |
michael@0 | 222 | gDefaultConverterNameBuffer[0] = 0; |
michael@0 | 223 | gDefaultConverterContainsOption = FALSE; |
michael@0 | 224 | gDefaultAlgorithmicSharedData = NULL; |
michael@0 | 225 | #endif |
michael@0 | 226 | |
michael@0 | 227 | return (SHARED_DATA_HASHTABLE == NULL); |
michael@0 | 228 | } |
michael@0 | 229 | |
michael@0 | 230 | static UBool U_CALLCONV |
michael@0 | 231 | isCnvAcceptable(void * /*context*/, |
michael@0 | 232 | const char * /*type*/, const char * /*name*/, |
michael@0 | 233 | const UDataInfo *pInfo) { |
michael@0 | 234 | return (UBool)( |
michael@0 | 235 | pInfo->size>=20 && |
michael@0 | 236 | pInfo->isBigEndian==U_IS_BIG_ENDIAN && |
michael@0 | 237 | pInfo->charsetFamily==U_CHARSET_FAMILY && |
michael@0 | 238 | pInfo->sizeofUChar==U_SIZEOF_UCHAR && |
michael@0 | 239 | pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ |
michael@0 | 240 | pInfo->dataFormat[1]==0x6e && |
michael@0 | 241 | pInfo->dataFormat[2]==0x76 && |
michael@0 | 242 | pInfo->dataFormat[3]==0x74 && |
michael@0 | 243 | pInfo->formatVersion[0]==6); /* Everything will be version 6 */ |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | /** |
michael@0 | 247 | * Un flatten shared data from a UDATA.. |
michael@0 | 248 | */ |
michael@0 | 249 | static UConverterSharedData* |
michael@0 | 250 | ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCode *status) |
michael@0 | 251 | { |
michael@0 | 252 | /* UDataInfo info; -- necessary only if some converters have different formatVersion */ |
michael@0 | 253 | const uint8_t *raw = (const uint8_t *)udata_getMemory(pData); |
michael@0 | 254 | const UConverterStaticData *source = (const UConverterStaticData *) raw; |
michael@0 | 255 | UConverterSharedData *data; |
michael@0 | 256 | UConverterType type = (UConverterType)source->conversionType; |
michael@0 | 257 | |
michael@0 | 258 | if(U_FAILURE(*status)) |
michael@0 | 259 | return NULL; |
michael@0 | 260 | |
michael@0 | 261 | if( (uint16_t)type >= UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES || |
michael@0 | 262 | converterData[type] == NULL || |
michael@0 | 263 | converterData[type]->referenceCounter != 1 || |
michael@0 | 264 | source->structSize != sizeof(UConverterStaticData)) |
michael@0 | 265 | { |
michael@0 | 266 | *status = U_INVALID_TABLE_FORMAT; |
michael@0 | 267 | return NULL; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | data = (UConverterSharedData *)uprv_malloc(sizeof(UConverterSharedData)); |
michael@0 | 271 | if(data == NULL) { |
michael@0 | 272 | *status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 273 | return NULL; |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | /* copy initial values from the static structure for this type */ |
michael@0 | 277 | uprv_memcpy(data, converterData[type], sizeof(UConverterSharedData)); |
michael@0 | 278 | |
michael@0 | 279 | #if 0 /* made UConverterMBCSTable part of UConverterSharedData -- markus 20031107 */ |
michael@0 | 280 | /* |
michael@0 | 281 | * It would be much more efficient if the table were a direct member, not a pointer. |
michael@0 | 282 | * However, that would add to the size of all UConverterSharedData objects |
michael@0 | 283 | * even if they do not use this table (especially algorithmic ones). |
michael@0 | 284 | * If this changes, then the static templates from converterData[type] |
michael@0 | 285 | * need more entries. |
michael@0 | 286 | * |
michael@0 | 287 | * In principle, it would be cleaner if the load() function below |
michael@0 | 288 | * allocated the table. |
michael@0 | 289 | */ |
michael@0 | 290 | data->table = (UConverterTable *)uprv_malloc(sizeof(UConverterTable)); |
michael@0 | 291 | if(data->table == NULL) { |
michael@0 | 292 | uprv_free(data); |
michael@0 | 293 | *status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 294 | return NULL; |
michael@0 | 295 | } |
michael@0 | 296 | uprv_memset(data->table, 0, sizeof(UConverterTable)); |
michael@0 | 297 | #endif |
michael@0 | 298 | |
michael@0 | 299 | data->staticData = source; |
michael@0 | 300 | |
michael@0 | 301 | data->sharedDataCached = FALSE; |
michael@0 | 302 | |
michael@0 | 303 | /* fill in fields from the loaded data */ |
michael@0 | 304 | data->dataMemory = (void*)pData; /* for future use */ |
michael@0 | 305 | |
michael@0 | 306 | if(data->impl->load != NULL) { |
michael@0 | 307 | data->impl->load(data, pArgs, raw + source->structSize, status); |
michael@0 | 308 | if(U_FAILURE(*status)) { |
michael@0 | 309 | uprv_free(data->table); |
michael@0 | 310 | uprv_free(data); |
michael@0 | 311 | return NULL; |
michael@0 | 312 | } |
michael@0 | 313 | } |
michael@0 | 314 | return data; |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | /*Takes an alias name gets an actual converter file name |
michael@0 | 318 | *goes to disk and opens it. |
michael@0 | 319 | *allocates the memory and returns a new UConverter object |
michael@0 | 320 | */ |
michael@0 | 321 | static UConverterSharedData *createConverterFromFile(UConverterLoadArgs *pArgs, UErrorCode * err) |
michael@0 | 322 | { |
michael@0 | 323 | UDataMemory *data; |
michael@0 | 324 | UConverterSharedData *sharedData; |
michael@0 | 325 | |
michael@0 | 326 | UTRACE_ENTRY_OC(UTRACE_UCNV_LOAD); |
michael@0 | 327 | |
michael@0 | 328 | if (U_FAILURE (*err)) { |
michael@0 | 329 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 330 | return NULL; |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | UTRACE_DATA2(UTRACE_OPEN_CLOSE, "load converter %s from package %s", pArgs->name, pArgs->pkg); |
michael@0 | 334 | |
michael@0 | 335 | data = udata_openChoice(pArgs->pkg, DATA_TYPE, pArgs->name, isCnvAcceptable, NULL, err); |
michael@0 | 336 | if(U_FAILURE(*err)) |
michael@0 | 337 | { |
michael@0 | 338 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 339 | return NULL; |
michael@0 | 340 | } |
michael@0 | 341 | |
michael@0 | 342 | sharedData = ucnv_data_unFlattenClone(pArgs, data, err); |
michael@0 | 343 | if(U_FAILURE(*err)) |
michael@0 | 344 | { |
michael@0 | 345 | udata_close(data); |
michael@0 | 346 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 347 | return NULL; |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | /* |
michael@0 | 351 | * TODO Store pkg in a field in the shared data so that delta-only converters |
michael@0 | 352 | * can load base converters from the same package. |
michael@0 | 353 | * If the pkg name is longer than the field, then either do not load the converter |
michael@0 | 354 | * in the first place, or just set the pkg field to "". |
michael@0 | 355 | */ |
michael@0 | 356 | |
michael@0 | 357 | UTRACE_EXIT_PTR_STATUS(sharedData, *err); |
michael@0 | 358 | return sharedData; |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | /*returns a converter type from a string |
michael@0 | 362 | */ |
michael@0 | 363 | static const UConverterSharedData * |
michael@0 | 364 | getAlgorithmicTypeFromName(const char *realName) |
michael@0 | 365 | { |
michael@0 | 366 | uint32_t mid, start, limit; |
michael@0 | 367 | uint32_t lastMid; |
michael@0 | 368 | int result; |
michael@0 | 369 | char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH]; |
michael@0 | 370 | |
michael@0 | 371 | /* Lower case and remove ignoreable characters. */ |
michael@0 | 372 | ucnv_io_stripForCompare(strippedName, realName); |
michael@0 | 373 | |
michael@0 | 374 | /* do a binary search for the alias */ |
michael@0 | 375 | start = 0; |
michael@0 | 376 | limit = sizeof(cnvNameType)/sizeof(cnvNameType[0]); |
michael@0 | 377 | mid = limit; |
michael@0 | 378 | lastMid = UINT32_MAX; |
michael@0 | 379 | |
michael@0 | 380 | for (;;) { |
michael@0 | 381 | mid = (uint32_t)((start + limit) / 2); |
michael@0 | 382 | if (lastMid == mid) { /* Have we moved? */ |
michael@0 | 383 | break; /* We haven't moved, and it wasn't found. */ |
michael@0 | 384 | } |
michael@0 | 385 | lastMid = mid; |
michael@0 | 386 | result = uprv_strcmp(strippedName, cnvNameType[mid].name); |
michael@0 | 387 | |
michael@0 | 388 | if (result < 0) { |
michael@0 | 389 | limit = mid; |
michael@0 | 390 | } else if (result > 0) { |
michael@0 | 391 | start = mid; |
michael@0 | 392 | } else { |
michael@0 | 393 | return converterData[cnvNameType[mid].type]; |
michael@0 | 394 | } |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | return NULL; |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | /* |
michael@0 | 401 | * Based on the number of known converters, this determines how many times larger |
michael@0 | 402 | * the shared data hash table should be. When on small platforms, or just a couple |
michael@0 | 403 | * of converters are used, this number should be 2. When memory is plentiful, or |
michael@0 | 404 | * when ucnv_countAvailable is ever used with a lot of available converters, |
michael@0 | 405 | * this should be 4. |
michael@0 | 406 | * Larger numbers reduce the number of hash collisions, but use more memory. |
michael@0 | 407 | */ |
michael@0 | 408 | #define UCNV_CACHE_LOAD_FACTOR 2 |
michael@0 | 409 | |
michael@0 | 410 | /* Puts the shared data in the static hashtable SHARED_DATA_HASHTABLE */ |
michael@0 | 411 | /* Will always be called with the cnvCacheMutex alrady being held */ |
michael@0 | 412 | /* by the calling function. */ |
michael@0 | 413 | /* Stores the shared data in the SHARED_DATA_HASHTABLE |
michael@0 | 414 | * @param data The shared data |
michael@0 | 415 | */ |
michael@0 | 416 | static void |
michael@0 | 417 | ucnv_shareConverterData(UConverterSharedData * data) |
michael@0 | 418 | { |
michael@0 | 419 | UErrorCode err = U_ZERO_ERROR; |
michael@0 | 420 | /*Lazy evaluates the Hashtable itself */ |
michael@0 | 421 | /*void *sanity = NULL;*/ |
michael@0 | 422 | |
michael@0 | 423 | if (SHARED_DATA_HASHTABLE == NULL) |
michael@0 | 424 | { |
michael@0 | 425 | SHARED_DATA_HASHTABLE = uhash_openSize(uhash_hashChars, uhash_compareChars, NULL, |
michael@0 | 426 | ucnv_io_countKnownConverters(&err)*UCNV_CACHE_LOAD_FACTOR, |
michael@0 | 427 | &err); |
michael@0 | 428 | ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); |
michael@0 | 429 | |
michael@0 | 430 | if (U_FAILURE(err)) |
michael@0 | 431 | return; |
michael@0 | 432 | } |
michael@0 | 433 | |
michael@0 | 434 | /* ### check to see if the element is not already there! */ |
michael@0 | 435 | |
michael@0 | 436 | /* |
michael@0 | 437 | sanity = ucnv_getSharedConverterData (data->staticData->name); |
michael@0 | 438 | if(sanity != NULL) |
michael@0 | 439 | { |
michael@0 | 440 | UCNV_DEBUG_LOG("put:overwrite!",data->staticData->name,sanity); |
michael@0 | 441 | } |
michael@0 | 442 | UCNV_DEBUG_LOG("put:chk",data->staticData->name,sanity); |
michael@0 | 443 | */ |
michael@0 | 444 | |
michael@0 | 445 | /* Mark it shared */ |
michael@0 | 446 | data->sharedDataCached = TRUE; |
michael@0 | 447 | |
michael@0 | 448 | uhash_put(SHARED_DATA_HASHTABLE, |
michael@0 | 449 | (void*) data->staticData->name, /* Okay to cast away const as long as |
michael@0 | 450 | keyDeleter == NULL */ |
michael@0 | 451 | data, |
michael@0 | 452 | &err); |
michael@0 | 453 | UCNV_DEBUG_LOG("put", data->staticData->name,data); |
michael@0 | 454 | |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | /* Look up a converter name in the shared data cache. */ |
michael@0 | 458 | /* cnvCacheMutex must be held by the caller to protect the hash table. */ |
michael@0 | 459 | /* gets the shared data from the SHARED_DATA_HASHTABLE (might return NULL if it isn't there) |
michael@0 | 460 | * @param name The name of the shared data |
michael@0 | 461 | * @return the shared data from the SHARED_DATA_HASHTABLE |
michael@0 | 462 | */ |
michael@0 | 463 | static UConverterSharedData * |
michael@0 | 464 | ucnv_getSharedConverterData(const char *name) |
michael@0 | 465 | { |
michael@0 | 466 | /*special case when no Table has yet been created we return NULL */ |
michael@0 | 467 | if (SHARED_DATA_HASHTABLE == NULL) |
michael@0 | 468 | { |
michael@0 | 469 | return NULL; |
michael@0 | 470 | } |
michael@0 | 471 | else |
michael@0 | 472 | { |
michael@0 | 473 | UConverterSharedData *rc; |
michael@0 | 474 | |
michael@0 | 475 | rc = (UConverterSharedData*)uhash_get(SHARED_DATA_HASHTABLE, name); |
michael@0 | 476 | UCNV_DEBUG_LOG("get",name,rc); |
michael@0 | 477 | return rc; |
michael@0 | 478 | } |
michael@0 | 479 | } |
michael@0 | 480 | |
michael@0 | 481 | /*frees the string of memory blocks associates with a sharedConverter |
michael@0 | 482 | *if and only if the referenceCounter == 0 |
michael@0 | 483 | */ |
michael@0 | 484 | /* Deletes (frees) the Shared data it's passed. first it checks the referenceCounter to |
michael@0 | 485 | * see if anyone is using it, if not it frees all the memory stemming from sharedConverterData and |
michael@0 | 486 | * returns TRUE, |
michael@0 | 487 | * otherwise returns FALSE |
michael@0 | 488 | * @param sharedConverterData The shared data |
michael@0 | 489 | * @return if not it frees all the memory stemming from sharedConverterData and |
michael@0 | 490 | * returns TRUE, otherwise returns FALSE |
michael@0 | 491 | */ |
michael@0 | 492 | static UBool |
michael@0 | 493 | ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData) |
michael@0 | 494 | { |
michael@0 | 495 | UTRACE_ENTRY_OC(UTRACE_UCNV_UNLOAD); |
michael@0 | 496 | UTRACE_DATA2(UTRACE_OPEN_CLOSE, "unload converter %s shared data %p", deadSharedData->staticData->name, deadSharedData); |
michael@0 | 497 | |
michael@0 | 498 | if (deadSharedData->referenceCounter > 0) { |
michael@0 | 499 | UTRACE_EXIT_VALUE((int32_t)FALSE); |
michael@0 | 500 | return FALSE; |
michael@0 | 501 | } |
michael@0 | 502 | |
michael@0 | 503 | if (deadSharedData->impl->unload != NULL) { |
michael@0 | 504 | deadSharedData->impl->unload(deadSharedData); |
michael@0 | 505 | } |
michael@0 | 506 | |
michael@0 | 507 | if(deadSharedData->dataMemory != NULL) |
michael@0 | 508 | { |
michael@0 | 509 | UDataMemory *data = (UDataMemory*)deadSharedData->dataMemory; |
michael@0 | 510 | udata_close(data); |
michael@0 | 511 | } |
michael@0 | 512 | |
michael@0 | 513 | if(deadSharedData->table != NULL) |
michael@0 | 514 | { |
michael@0 | 515 | uprv_free(deadSharedData->table); |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | #if 0 |
michael@0 | 519 | /* if the static data is actually owned by the shared data */ |
michael@0 | 520 | /* enable if we ever have this situation. */ |
michael@0 | 521 | if(deadSharedData->staticDataOwned == TRUE) /* see ucnv_bld.h */ |
michael@0 | 522 | { |
michael@0 | 523 | uprv_free((void*)deadSharedData->staticData); |
michael@0 | 524 | } |
michael@0 | 525 | #endif |
michael@0 | 526 | |
michael@0 | 527 | #if 0 |
michael@0 | 528 | /* Zap it ! */ |
michael@0 | 529 | uprv_memset(deadSharedData->0, sizeof(*deadSharedData)); |
michael@0 | 530 | #endif |
michael@0 | 531 | |
michael@0 | 532 | uprv_free(deadSharedData); |
michael@0 | 533 | |
michael@0 | 534 | UTRACE_EXIT_VALUE((int32_t)TRUE); |
michael@0 | 535 | return TRUE; |
michael@0 | 536 | } |
michael@0 | 537 | |
michael@0 | 538 | /** |
michael@0 | 539 | * Load a non-algorithmic converter. |
michael@0 | 540 | * If pkg==NULL, then this function must be called inside umtx_lock(&cnvCacheMutex). |
michael@0 | 541 | */ |
michael@0 | 542 | UConverterSharedData * |
michael@0 | 543 | ucnv_load(UConverterLoadArgs *pArgs, UErrorCode *err) { |
michael@0 | 544 | UConverterSharedData *mySharedConverterData; |
michael@0 | 545 | |
michael@0 | 546 | if(err == NULL || U_FAILURE(*err)) { |
michael@0 | 547 | return NULL; |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | if(pArgs->pkg != NULL && *pArgs->pkg != 0) { |
michael@0 | 551 | /* application-provided converters are not currently cached */ |
michael@0 | 552 | return createConverterFromFile(pArgs, err); |
michael@0 | 553 | } |
michael@0 | 554 | |
michael@0 | 555 | mySharedConverterData = ucnv_getSharedConverterData(pArgs->name); |
michael@0 | 556 | if (mySharedConverterData == NULL) |
michael@0 | 557 | { |
michael@0 | 558 | /*Not cached, we need to stream it in from file */ |
michael@0 | 559 | mySharedConverterData = createConverterFromFile(pArgs, err); |
michael@0 | 560 | if (U_FAILURE (*err) || (mySharedConverterData == NULL)) |
michael@0 | 561 | { |
michael@0 | 562 | return NULL; |
michael@0 | 563 | } |
michael@0 | 564 | else if (!pArgs->onlyTestIsLoadable) |
michael@0 | 565 | { |
michael@0 | 566 | /* share it with other library clients */ |
michael@0 | 567 | ucnv_shareConverterData(mySharedConverterData); |
michael@0 | 568 | } |
michael@0 | 569 | } |
michael@0 | 570 | else |
michael@0 | 571 | { |
michael@0 | 572 | /* The data for this converter was already in the cache. */ |
michael@0 | 573 | /* Update the reference counter on the shared data: one more client */ |
michael@0 | 574 | mySharedConverterData->referenceCounter++; |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | return mySharedConverterData; |
michael@0 | 578 | } |
michael@0 | 579 | |
michael@0 | 580 | /** |
michael@0 | 581 | * Unload a non-algorithmic converter. |
michael@0 | 582 | * It must be sharedData->referenceCounter != ~0 |
michael@0 | 583 | * and this function must be called inside umtx_lock(&cnvCacheMutex). |
michael@0 | 584 | */ |
michael@0 | 585 | U_CAPI void |
michael@0 | 586 | ucnv_unload(UConverterSharedData *sharedData) { |
michael@0 | 587 | if(sharedData != NULL) { |
michael@0 | 588 | if (sharedData->referenceCounter > 0) { |
michael@0 | 589 | sharedData->referenceCounter--; |
michael@0 | 590 | } |
michael@0 | 591 | |
michael@0 | 592 | if((sharedData->referenceCounter <= 0)&&(sharedData->sharedDataCached == FALSE)) { |
michael@0 | 593 | ucnv_deleteSharedConverterData(sharedData); |
michael@0 | 594 | } |
michael@0 | 595 | } |
michael@0 | 596 | } |
michael@0 | 597 | |
michael@0 | 598 | U_CFUNC void |
michael@0 | 599 | ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData) |
michael@0 | 600 | { |
michael@0 | 601 | /* |
michael@0 | 602 | Checking whether it's an algorithic converter is okay |
michael@0 | 603 | in multithreaded applications because the value never changes. |
michael@0 | 604 | Don't check referenceCounter for any other value. |
michael@0 | 605 | */ |
michael@0 | 606 | if(sharedData != NULL && sharedData->referenceCounter != (uint32_t)~0) { |
michael@0 | 607 | umtx_lock(&cnvCacheMutex); |
michael@0 | 608 | ucnv_unload(sharedData); |
michael@0 | 609 | umtx_unlock(&cnvCacheMutex); |
michael@0 | 610 | } |
michael@0 | 611 | } |
michael@0 | 612 | |
michael@0 | 613 | U_CFUNC void |
michael@0 | 614 | ucnv_incrementRefCount(UConverterSharedData *sharedData) |
michael@0 | 615 | { |
michael@0 | 616 | /* |
michael@0 | 617 | Checking whether it's an algorithic converter is okay |
michael@0 | 618 | in multithreaded applications because the value never changes. |
michael@0 | 619 | Don't check referenceCounter for any other value. |
michael@0 | 620 | */ |
michael@0 | 621 | if(sharedData != NULL && sharedData->referenceCounter != (uint32_t)~0) { |
michael@0 | 622 | umtx_lock(&cnvCacheMutex); |
michael@0 | 623 | sharedData->referenceCounter++; |
michael@0 | 624 | umtx_unlock(&cnvCacheMutex); |
michael@0 | 625 | } |
michael@0 | 626 | } |
michael@0 | 627 | |
michael@0 | 628 | /* |
michael@0 | 629 | * *pPieces must be initialized. |
michael@0 | 630 | * The name without options will be copied to pPieces->cnvName. |
michael@0 | 631 | * The locale and options will be copied to pPieces only if present in inName, |
michael@0 | 632 | * otherwise the existing values in pPieces remain. |
michael@0 | 633 | * *pArgs will be set to the pPieces values. |
michael@0 | 634 | */ |
michael@0 | 635 | static void |
michael@0 | 636 | parseConverterOptions(const char *inName, |
michael@0 | 637 | UConverterNamePieces *pPieces, |
michael@0 | 638 | UConverterLoadArgs *pArgs, |
michael@0 | 639 | UErrorCode *err) |
michael@0 | 640 | { |
michael@0 | 641 | char *cnvName = pPieces->cnvName; |
michael@0 | 642 | char c; |
michael@0 | 643 | int32_t len = 0; |
michael@0 | 644 | |
michael@0 | 645 | pArgs->name=inName; |
michael@0 | 646 | pArgs->locale=pPieces->locale; |
michael@0 | 647 | pArgs->options=pPieces->options; |
michael@0 | 648 | |
michael@0 | 649 | /* copy the converter name itself to cnvName */ |
michael@0 | 650 | while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { |
michael@0 | 651 | if (++len>=UCNV_MAX_CONVERTER_NAME_LENGTH) { |
michael@0 | 652 | *err = U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ |
michael@0 | 653 | pPieces->cnvName[0]=0; |
michael@0 | 654 | return; |
michael@0 | 655 | } |
michael@0 | 656 | *cnvName++=c; |
michael@0 | 657 | inName++; |
michael@0 | 658 | } |
michael@0 | 659 | *cnvName=0; |
michael@0 | 660 | pArgs->name=pPieces->cnvName; |
michael@0 | 661 | |
michael@0 | 662 | /* parse options. No more name copying should occur. */ |
michael@0 | 663 | while((c=*inName)!=0) { |
michael@0 | 664 | if(c==UCNV_OPTION_SEP_CHAR) { |
michael@0 | 665 | ++inName; |
michael@0 | 666 | } |
michael@0 | 667 | |
michael@0 | 668 | /* inName is behind an option separator */ |
michael@0 | 669 | if(uprv_strncmp(inName, "locale=", 7)==0) { |
michael@0 | 670 | /* do not modify locale itself in case we have multiple locale options */ |
michael@0 | 671 | char *dest=pPieces->locale; |
michael@0 | 672 | |
michael@0 | 673 | /* copy the locale option value */ |
michael@0 | 674 | inName+=7; |
michael@0 | 675 | len=0; |
michael@0 | 676 | while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { |
michael@0 | 677 | ++inName; |
michael@0 | 678 | |
michael@0 | 679 | if(++len>=ULOC_FULLNAME_CAPACITY) { |
michael@0 | 680 | *err=U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ |
michael@0 | 681 | pPieces->locale[0]=0; |
michael@0 | 682 | return; |
michael@0 | 683 | } |
michael@0 | 684 | |
michael@0 | 685 | *dest++=c; |
michael@0 | 686 | } |
michael@0 | 687 | *dest=0; |
michael@0 | 688 | } else if(uprv_strncmp(inName, "version=", 8)==0) { |
michael@0 | 689 | /* copy the version option value into bits 3..0 of pPieces->options */ |
michael@0 | 690 | inName+=8; |
michael@0 | 691 | c=*inName; |
michael@0 | 692 | if(c==0) { |
michael@0 | 693 | pArgs->options=(pPieces->options&=~UCNV_OPTION_VERSION); |
michael@0 | 694 | return; |
michael@0 | 695 | } else if((uint8_t)(c-'0')<10) { |
michael@0 | 696 | pArgs->options=pPieces->options=(pPieces->options&~UCNV_OPTION_VERSION)|(uint32_t)(c-'0'); |
michael@0 | 697 | ++inName; |
michael@0 | 698 | } |
michael@0 | 699 | } else if(uprv_strncmp(inName, "swaplfnl", 8)==0) { |
michael@0 | 700 | inName+=8; |
michael@0 | 701 | pArgs->options=(pPieces->options|=UCNV_OPTION_SWAP_LFNL); |
michael@0 | 702 | /* add processing for new options here with another } else if(uprv_strncmp(inName, "option-name=", XX)==0) { */ |
michael@0 | 703 | } else { |
michael@0 | 704 | /* ignore any other options until we define some */ |
michael@0 | 705 | while(((c = *inName++) != 0) && (c != UCNV_OPTION_SEP_CHAR)) { |
michael@0 | 706 | } |
michael@0 | 707 | if(c==0) { |
michael@0 | 708 | return; |
michael@0 | 709 | } |
michael@0 | 710 | } |
michael@0 | 711 | } |
michael@0 | 712 | } |
michael@0 | 713 | |
michael@0 | 714 | /*Logic determines if the converter is Algorithmic AND/OR cached |
michael@0 | 715 | *depending on that: |
michael@0 | 716 | * -we either go to get data from disk and cache it (Data=TRUE, Cached=False) |
michael@0 | 717 | * -Get it from a Hashtable (Data=X, Cached=TRUE) |
michael@0 | 718 | * -Call dataConverter initializer (Data=TRUE, Cached=TRUE) |
michael@0 | 719 | * -Call AlgorithmicConverter initializer (Data=FALSE, Cached=TRUE) |
michael@0 | 720 | */ |
michael@0 | 721 | U_CFUNC UConverterSharedData * |
michael@0 | 722 | ucnv_loadSharedData(const char *converterName, |
michael@0 | 723 | UConverterNamePieces *pPieces, |
michael@0 | 724 | UConverterLoadArgs *pArgs, |
michael@0 | 725 | UErrorCode * err) { |
michael@0 | 726 | UConverterNamePieces stackPieces; |
michael@0 | 727 | UConverterLoadArgs stackArgs; |
michael@0 | 728 | UConverterSharedData *mySharedConverterData = NULL; |
michael@0 | 729 | UErrorCode internalErrorCode = U_ZERO_ERROR; |
michael@0 | 730 | UBool mayContainOption = TRUE; |
michael@0 | 731 | UBool checkForAlgorithmic = TRUE; |
michael@0 | 732 | |
michael@0 | 733 | if (U_FAILURE (*err)) { |
michael@0 | 734 | return NULL; |
michael@0 | 735 | } |
michael@0 | 736 | |
michael@0 | 737 | if(pPieces == NULL) { |
michael@0 | 738 | if(pArgs != NULL) { |
michael@0 | 739 | /* |
michael@0 | 740 | * Bad: We may set pArgs pointers to stackPieces fields |
michael@0 | 741 | * which will be invalid after this function returns. |
michael@0 | 742 | */ |
michael@0 | 743 | *err = U_INTERNAL_PROGRAM_ERROR; |
michael@0 | 744 | return NULL; |
michael@0 | 745 | } |
michael@0 | 746 | pPieces = &stackPieces; |
michael@0 | 747 | } |
michael@0 | 748 | if(pArgs == NULL) { |
michael@0 | 749 | uprv_memset(&stackArgs, 0, sizeof(stackArgs)); |
michael@0 | 750 | stackArgs.size = (int32_t)sizeof(stackArgs); |
michael@0 | 751 | pArgs = &stackArgs; |
michael@0 | 752 | } |
michael@0 | 753 | |
michael@0 | 754 | pPieces->cnvName[0] = 0; |
michael@0 | 755 | pPieces->locale[0] = 0; |
michael@0 | 756 | pPieces->options = 0; |
michael@0 | 757 | |
michael@0 | 758 | pArgs->name = converterName; |
michael@0 | 759 | pArgs->locale = pPieces->locale; |
michael@0 | 760 | pArgs->options = pPieces->options; |
michael@0 | 761 | |
michael@0 | 762 | /* In case "name" is NULL we want to open the default converter. */ |
michael@0 | 763 | if (converterName == NULL) { |
michael@0 | 764 | #if U_CHARSET_IS_UTF8 |
michael@0 | 765 | pArgs->name = "UTF-8"; |
michael@0 | 766 | return (UConverterSharedData *)converterData[UCNV_UTF8]; |
michael@0 | 767 | #else |
michael@0 | 768 | /* Call ucnv_getDefaultName first to query the name from the OS. */ |
michael@0 | 769 | pArgs->name = ucnv_getDefaultName(); |
michael@0 | 770 | if (pArgs->name == NULL) { |
michael@0 | 771 | *err = U_MISSING_RESOURCE_ERROR; |
michael@0 | 772 | return NULL; |
michael@0 | 773 | } |
michael@0 | 774 | mySharedConverterData = (UConverterSharedData *)gDefaultAlgorithmicSharedData; |
michael@0 | 775 | checkForAlgorithmic = FALSE; |
michael@0 | 776 | mayContainOption = gDefaultConverterContainsOption; |
michael@0 | 777 | /* the default converter name is already canonical */ |
michael@0 | 778 | #endif |
michael@0 | 779 | } |
michael@0 | 780 | else if(UCNV_FAST_IS_UTF8(converterName)) { |
michael@0 | 781 | /* fastpath for UTF-8 */ |
michael@0 | 782 | pArgs->name = "UTF-8"; |
michael@0 | 783 | return (UConverterSharedData *)converterData[UCNV_UTF8]; |
michael@0 | 784 | } |
michael@0 | 785 | else { |
michael@0 | 786 | /* separate the converter name from the options */ |
michael@0 | 787 | parseConverterOptions(converterName, pPieces, pArgs, err); |
michael@0 | 788 | if (U_FAILURE(*err)) { |
michael@0 | 789 | /* Very bad name used. */ |
michael@0 | 790 | return NULL; |
michael@0 | 791 | } |
michael@0 | 792 | |
michael@0 | 793 | /* get the canonical converter name */ |
michael@0 | 794 | pArgs->name = ucnv_io_getConverterName(pArgs->name, &mayContainOption, &internalErrorCode); |
michael@0 | 795 | if (U_FAILURE(internalErrorCode) || pArgs->name == NULL) { |
michael@0 | 796 | /* |
michael@0 | 797 | * set the input name in case the converter was added |
michael@0 | 798 | * without updating the alias table, or when there is no alias table |
michael@0 | 799 | */ |
michael@0 | 800 | pArgs->name = pPieces->cnvName; |
michael@0 | 801 | } else if (internalErrorCode == U_AMBIGUOUS_ALIAS_WARNING) { |
michael@0 | 802 | *err = U_AMBIGUOUS_ALIAS_WARNING; |
michael@0 | 803 | } |
michael@0 | 804 | } |
michael@0 | 805 | |
michael@0 | 806 | /* separate the converter name from the options */ |
michael@0 | 807 | if(mayContainOption && pArgs->name != pPieces->cnvName) { |
michael@0 | 808 | parseConverterOptions(pArgs->name, pPieces, pArgs, err); |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | /* get the shared data for an algorithmic converter, if it is one */ |
michael@0 | 812 | if (checkForAlgorithmic) { |
michael@0 | 813 | mySharedConverterData = (UConverterSharedData *)getAlgorithmicTypeFromName(pArgs->name); |
michael@0 | 814 | } |
michael@0 | 815 | if (mySharedConverterData == NULL) |
michael@0 | 816 | { |
michael@0 | 817 | /* it is a data-based converter, get its shared data. */ |
michael@0 | 818 | /* Hold the cnvCacheMutex through the whole process of checking the */ |
michael@0 | 819 | /* converter data cache, and adding new entries to the cache */ |
michael@0 | 820 | /* to prevent other threads from modifying the cache during the */ |
michael@0 | 821 | /* process. */ |
michael@0 | 822 | pArgs->nestedLoads=1; |
michael@0 | 823 | pArgs->pkg=NULL; |
michael@0 | 824 | |
michael@0 | 825 | umtx_lock(&cnvCacheMutex); |
michael@0 | 826 | mySharedConverterData = ucnv_load(pArgs, err); |
michael@0 | 827 | umtx_unlock(&cnvCacheMutex); |
michael@0 | 828 | if (U_FAILURE (*err) || (mySharedConverterData == NULL)) |
michael@0 | 829 | { |
michael@0 | 830 | return NULL; |
michael@0 | 831 | } |
michael@0 | 832 | } |
michael@0 | 833 | |
michael@0 | 834 | return mySharedConverterData; |
michael@0 | 835 | } |
michael@0 | 836 | |
michael@0 | 837 | U_CAPI UConverter * |
michael@0 | 838 | ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err) |
michael@0 | 839 | { |
michael@0 | 840 | UConverterNamePieces stackPieces; |
michael@0 | 841 | UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; |
michael@0 | 842 | UConverterSharedData *mySharedConverterData; |
michael@0 | 843 | |
michael@0 | 844 | UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); |
michael@0 | 845 | |
michael@0 | 846 | if(U_SUCCESS(*err)) { |
michael@0 | 847 | UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open converter %s", converterName); |
michael@0 | 848 | |
michael@0 | 849 | mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); |
michael@0 | 850 | |
michael@0 | 851 | myUConverter = ucnv_createConverterFromSharedData( |
michael@0 | 852 | myUConverter, mySharedConverterData, |
michael@0 | 853 | &stackArgs, |
michael@0 | 854 | err); |
michael@0 | 855 | |
michael@0 | 856 | if(U_SUCCESS(*err)) { |
michael@0 | 857 | UTRACE_EXIT_PTR_STATUS(myUConverter, *err); |
michael@0 | 858 | return myUConverter; |
michael@0 | 859 | } |
michael@0 | 860 | } |
michael@0 | 861 | |
michael@0 | 862 | /* exit with error */ |
michael@0 | 863 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 864 | return NULL; |
michael@0 | 865 | } |
michael@0 | 866 | |
michael@0 | 867 | U_CFUNC UBool |
michael@0 | 868 | ucnv_canCreateConverter(const char *converterName, UErrorCode *err) { |
michael@0 | 869 | UConverter myUConverter; |
michael@0 | 870 | UConverterNamePieces stackPieces; |
michael@0 | 871 | UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; |
michael@0 | 872 | UConverterSharedData *mySharedConverterData; |
michael@0 | 873 | |
michael@0 | 874 | UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); |
michael@0 | 875 | |
michael@0 | 876 | if(U_SUCCESS(*err)) { |
michael@0 | 877 | UTRACE_DATA1(UTRACE_OPEN_CLOSE, "test if can open converter %s", converterName); |
michael@0 | 878 | |
michael@0 | 879 | stackArgs.onlyTestIsLoadable=TRUE; |
michael@0 | 880 | mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); |
michael@0 | 881 | ucnv_createConverterFromSharedData( |
michael@0 | 882 | &myUConverter, mySharedConverterData, |
michael@0 | 883 | &stackArgs, |
michael@0 | 884 | err); |
michael@0 | 885 | ucnv_unloadSharedDataIfReady(mySharedConverterData); |
michael@0 | 886 | } |
michael@0 | 887 | |
michael@0 | 888 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 889 | return U_SUCCESS(*err); |
michael@0 | 890 | } |
michael@0 | 891 | |
michael@0 | 892 | UConverter * |
michael@0 | 893 | ucnv_createAlgorithmicConverter(UConverter *myUConverter, |
michael@0 | 894 | UConverterType type, |
michael@0 | 895 | const char *locale, uint32_t options, |
michael@0 | 896 | UErrorCode *err) { |
michael@0 | 897 | UConverter *cnv; |
michael@0 | 898 | const UConverterSharedData *sharedData; |
michael@0 | 899 | UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; |
michael@0 | 900 | |
michael@0 | 901 | UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_ALGORITHMIC); |
michael@0 | 902 | UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open algorithmic converter type %d", (int32_t)type); |
michael@0 | 903 | |
michael@0 | 904 | if(type<0 || UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES<=type) { |
michael@0 | 905 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 906 | UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); |
michael@0 | 907 | return NULL; |
michael@0 | 908 | } |
michael@0 | 909 | |
michael@0 | 910 | sharedData = converterData[type]; |
michael@0 | 911 | /* |
michael@0 | 912 | Checking whether it's an algorithic converter is okay |
michael@0 | 913 | in multithreaded applications because the value never changes. |
michael@0 | 914 | Don't check referenceCounter for any other value. |
michael@0 | 915 | */ |
michael@0 | 916 | if(sharedData == NULL || sharedData->referenceCounter != (uint32_t)~0) { |
michael@0 | 917 | /* not a valid type, or not an algorithmic converter */ |
michael@0 | 918 | *err = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 919 | UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); |
michael@0 | 920 | return NULL; |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | stackArgs.name = ""; |
michael@0 | 924 | stackArgs.options = options; |
michael@0 | 925 | stackArgs.locale=locale; |
michael@0 | 926 | cnv = ucnv_createConverterFromSharedData( |
michael@0 | 927 | myUConverter, (UConverterSharedData *)sharedData, |
michael@0 | 928 | &stackArgs, err); |
michael@0 | 929 | |
michael@0 | 930 | UTRACE_EXIT_PTR_STATUS(cnv, *err); |
michael@0 | 931 | return cnv; |
michael@0 | 932 | } |
michael@0 | 933 | |
michael@0 | 934 | U_CFUNC UConverter* |
michael@0 | 935 | ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode * err) |
michael@0 | 936 | { |
michael@0 | 937 | UConverter *myUConverter; |
michael@0 | 938 | UConverterSharedData *mySharedConverterData; |
michael@0 | 939 | UConverterNamePieces stackPieces; |
michael@0 | 940 | UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; |
michael@0 | 941 | |
michael@0 | 942 | UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_PACKAGE); |
michael@0 | 943 | |
michael@0 | 944 | if(U_FAILURE(*err)) { |
michael@0 | 945 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 946 | return NULL; |
michael@0 | 947 | } |
michael@0 | 948 | |
michael@0 | 949 | UTRACE_DATA2(UTRACE_OPEN_CLOSE, "open converter %s from package %s", converterName, packageName); |
michael@0 | 950 | |
michael@0 | 951 | /* first, get the options out of the converterName string */ |
michael@0 | 952 | stackPieces.cnvName[0] = 0; |
michael@0 | 953 | stackPieces.locale[0] = 0; |
michael@0 | 954 | stackPieces.options = 0; |
michael@0 | 955 | parseConverterOptions(converterName, &stackPieces, &stackArgs, err); |
michael@0 | 956 | if (U_FAILURE(*err)) { |
michael@0 | 957 | /* Very bad name used. */ |
michael@0 | 958 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 959 | return NULL; |
michael@0 | 960 | } |
michael@0 | 961 | stackArgs.nestedLoads=1; |
michael@0 | 962 | stackArgs.pkg=packageName; |
michael@0 | 963 | |
michael@0 | 964 | /* open the data, unflatten the shared structure */ |
michael@0 | 965 | mySharedConverterData = createConverterFromFile(&stackArgs, err); |
michael@0 | 966 | |
michael@0 | 967 | if (U_FAILURE(*err)) { |
michael@0 | 968 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 969 | return NULL; |
michael@0 | 970 | } |
michael@0 | 971 | |
michael@0 | 972 | /* create the actual converter */ |
michael@0 | 973 | myUConverter = ucnv_createConverterFromSharedData(NULL, mySharedConverterData, &stackArgs, err); |
michael@0 | 974 | |
michael@0 | 975 | if (U_FAILURE(*err)) { |
michael@0 | 976 | ucnv_close(myUConverter); |
michael@0 | 977 | UTRACE_EXIT_STATUS(*err); |
michael@0 | 978 | return NULL; |
michael@0 | 979 | } |
michael@0 | 980 | |
michael@0 | 981 | UTRACE_EXIT_PTR_STATUS(myUConverter, *err); |
michael@0 | 982 | return myUConverter; |
michael@0 | 983 | } |
michael@0 | 984 | |
michael@0 | 985 | |
michael@0 | 986 | U_CFUNC UConverter* |
michael@0 | 987 | ucnv_createConverterFromSharedData(UConverter *myUConverter, |
michael@0 | 988 | UConverterSharedData *mySharedConverterData, |
michael@0 | 989 | UConverterLoadArgs *pArgs, |
michael@0 | 990 | UErrorCode *err) |
michael@0 | 991 | { |
michael@0 | 992 | UBool isCopyLocal; |
michael@0 | 993 | |
michael@0 | 994 | if(U_FAILURE(*err)) { |
michael@0 | 995 | ucnv_unloadSharedDataIfReady(mySharedConverterData); |
michael@0 | 996 | return myUConverter; |
michael@0 | 997 | } |
michael@0 | 998 | if(myUConverter == NULL) |
michael@0 | 999 | { |
michael@0 | 1000 | myUConverter = (UConverter *) uprv_malloc (sizeof (UConverter)); |
michael@0 | 1001 | if(myUConverter == NULL) |
michael@0 | 1002 | { |
michael@0 | 1003 | *err = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1004 | ucnv_unloadSharedDataIfReady(mySharedConverterData); |
michael@0 | 1005 | return NULL; |
michael@0 | 1006 | } |
michael@0 | 1007 | isCopyLocal = FALSE; |
michael@0 | 1008 | } else { |
michael@0 | 1009 | isCopyLocal = TRUE; |
michael@0 | 1010 | } |
michael@0 | 1011 | |
michael@0 | 1012 | /* initialize the converter */ |
michael@0 | 1013 | uprv_memset(myUConverter, 0, sizeof(UConverter)); |
michael@0 | 1014 | myUConverter->isCopyLocal = isCopyLocal; |
michael@0 | 1015 | /*myUConverter->isExtraLocal = FALSE;*/ /* Set by the memset call */ |
michael@0 | 1016 | myUConverter->sharedData = mySharedConverterData; |
michael@0 | 1017 | myUConverter->options = pArgs->options; |
michael@0 | 1018 | if(!pArgs->onlyTestIsLoadable) { |
michael@0 | 1019 | myUConverter->preFromUFirstCP = U_SENTINEL; |
michael@0 | 1020 | myUConverter->fromCharErrorBehaviour = UCNV_TO_U_DEFAULT_CALLBACK; |
michael@0 | 1021 | myUConverter->fromUCharErrorBehaviour = UCNV_FROM_U_DEFAULT_CALLBACK; |
michael@0 | 1022 | myUConverter->toUnicodeStatus = mySharedConverterData->toUnicodeStatus; |
michael@0 | 1023 | myUConverter->maxBytesPerUChar = mySharedConverterData->staticData->maxBytesPerChar; |
michael@0 | 1024 | myUConverter->subChar1 = mySharedConverterData->staticData->subChar1; |
michael@0 | 1025 | myUConverter->subCharLen = mySharedConverterData->staticData->subCharLen; |
michael@0 | 1026 | myUConverter->subChars = (uint8_t *)myUConverter->subUChars; |
michael@0 | 1027 | uprv_memcpy(myUConverter->subChars, mySharedConverterData->staticData->subChar, myUConverter->subCharLen); |
michael@0 | 1028 | myUConverter->toUCallbackReason = UCNV_ILLEGAL; /* default reason to invoke (*fromCharErrorBehaviour) */ |
michael@0 | 1029 | } |
michael@0 | 1030 | |
michael@0 | 1031 | if(mySharedConverterData->impl->open != NULL) { |
michael@0 | 1032 | mySharedConverterData->impl->open(myUConverter, pArgs, err); |
michael@0 | 1033 | if(U_FAILURE(*err) && !pArgs->onlyTestIsLoadable) { |
michael@0 | 1034 | /* don't ucnv_close() if onlyTestIsLoadable because not fully initialized */ |
michael@0 | 1035 | ucnv_close(myUConverter); |
michael@0 | 1036 | return NULL; |
michael@0 | 1037 | } |
michael@0 | 1038 | } |
michael@0 | 1039 | |
michael@0 | 1040 | return myUConverter; |
michael@0 | 1041 | } |
michael@0 | 1042 | |
michael@0 | 1043 | /*Frees all shared immutable objects that aren't referred to (reference count = 0) |
michael@0 | 1044 | */ |
michael@0 | 1045 | U_CAPI int32_t U_EXPORT2 |
michael@0 | 1046 | ucnv_flushCache () |
michael@0 | 1047 | { |
michael@0 | 1048 | UConverterSharedData *mySharedData = NULL; |
michael@0 | 1049 | int32_t pos; |
michael@0 | 1050 | int32_t tableDeletedNum = 0; |
michael@0 | 1051 | const UHashElement *e; |
michael@0 | 1052 | /*UErrorCode status = U_ILLEGAL_ARGUMENT_ERROR;*/ |
michael@0 | 1053 | int32_t i, remaining; |
michael@0 | 1054 | |
michael@0 | 1055 | UTRACE_ENTRY_OC(UTRACE_UCNV_FLUSH_CACHE); |
michael@0 | 1056 | |
michael@0 | 1057 | /* Close the default converter without creating a new one so that everything will be flushed. */ |
michael@0 | 1058 | u_flushDefaultConverter(); |
michael@0 | 1059 | |
michael@0 | 1060 | /*if shared data hasn't even been lazy evaluated yet |
michael@0 | 1061 | * return 0 |
michael@0 | 1062 | */ |
michael@0 | 1063 | if (SHARED_DATA_HASHTABLE == NULL) { |
michael@0 | 1064 | UTRACE_EXIT_VALUE((int32_t)0); |
michael@0 | 1065 | return 0; |
michael@0 | 1066 | } |
michael@0 | 1067 | |
michael@0 | 1068 | /*creates an enumeration to iterate through every element in the |
michael@0 | 1069 | * table |
michael@0 | 1070 | * |
michael@0 | 1071 | * Synchronization: holding cnvCacheMutex will prevent any other thread from |
michael@0 | 1072 | * accessing or modifying the hash table during the iteration. |
michael@0 | 1073 | * The reference count of an entry may be decremented by |
michael@0 | 1074 | * ucnv_close while the iteration is in process, but this is |
michael@0 | 1075 | * benign. It can't be incremented (in ucnv_createConverter()) |
michael@0 | 1076 | * because the sequence of looking up in the cache + incrementing |
michael@0 | 1077 | * is protected by cnvCacheMutex. |
michael@0 | 1078 | */ |
michael@0 | 1079 | umtx_lock(&cnvCacheMutex); |
michael@0 | 1080 | /* |
michael@0 | 1081 | * double loop: A delta/extension-only converter has a pointer to its base table's |
michael@0 | 1082 | * shared data; the first iteration of the outer loop may see the delta converter |
michael@0 | 1083 | * before the base converter, and unloading the delta converter may get the base |
michael@0 | 1084 | * converter's reference counter down to 0. |
michael@0 | 1085 | */ |
michael@0 | 1086 | i = 0; |
michael@0 | 1087 | do { |
michael@0 | 1088 | remaining = 0; |
michael@0 | 1089 | pos = -1; |
michael@0 | 1090 | while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL) |
michael@0 | 1091 | { |
michael@0 | 1092 | mySharedData = (UConverterSharedData *) e->value.pointer; |
michael@0 | 1093 | /*deletes only if reference counter == 0 */ |
michael@0 | 1094 | if (mySharedData->referenceCounter == 0) |
michael@0 | 1095 | { |
michael@0 | 1096 | tableDeletedNum++; |
michael@0 | 1097 | |
michael@0 | 1098 | UCNV_DEBUG_LOG("del",mySharedData->staticData->name,mySharedData); |
michael@0 | 1099 | |
michael@0 | 1100 | uhash_removeElement(SHARED_DATA_HASHTABLE, e); |
michael@0 | 1101 | mySharedData->sharedDataCached = FALSE; |
michael@0 | 1102 | ucnv_deleteSharedConverterData (mySharedData); |
michael@0 | 1103 | } else { |
michael@0 | 1104 | ++remaining; |
michael@0 | 1105 | } |
michael@0 | 1106 | } |
michael@0 | 1107 | } while(++i == 1 && remaining > 0); |
michael@0 | 1108 | umtx_unlock(&cnvCacheMutex); |
michael@0 | 1109 | |
michael@0 | 1110 | UTRACE_DATA1(UTRACE_INFO, "ucnv_flushCache() exits with %d converters remaining", remaining); |
michael@0 | 1111 | |
michael@0 | 1112 | UTRACE_EXIT_VALUE(tableDeletedNum); |
michael@0 | 1113 | return tableDeletedNum; |
michael@0 | 1114 | } |
michael@0 | 1115 | |
michael@0 | 1116 | /* available converters list --------------------------------------------------- */ |
michael@0 | 1117 | |
michael@0 | 1118 | static void U_CALLCONV initAvailableConvertersList(UErrorCode &errCode) { |
michael@0 | 1119 | U_ASSERT(gAvailableConverterCount == 0); |
michael@0 | 1120 | U_ASSERT(gAvailableConverters == NULL); |
michael@0 | 1121 | |
michael@0 | 1122 | ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); |
michael@0 | 1123 | UEnumeration *allConvEnum = ucnv_openAllNames(&errCode); |
michael@0 | 1124 | int32_t allConverterCount = uenum_count(allConvEnum, &errCode); |
michael@0 | 1125 | if (U_FAILURE(errCode)) { |
michael@0 | 1126 | return; |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | /* We can't have more than "*converterTable" converters to open */ |
michael@0 | 1130 | gAvailableConverters = (const char **) uprv_malloc(allConverterCount * sizeof(char*)); |
michael@0 | 1131 | if (!gAvailableConverters) { |
michael@0 | 1132 | errCode = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1133 | return; |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | /* Open the default converter to make sure that it has first dibs in the hash table. */ |
michael@0 | 1137 | UErrorCode localStatus = U_ZERO_ERROR; |
michael@0 | 1138 | UConverter tempConverter; |
michael@0 | 1139 | ucnv_close(ucnv_createConverter(&tempConverter, NULL, &localStatus)); |
michael@0 | 1140 | |
michael@0 | 1141 | gAvailableConverterCount = 0; |
michael@0 | 1142 | |
michael@0 | 1143 | for (int32_t idx = 0; idx < allConverterCount; idx++) { |
michael@0 | 1144 | localStatus = U_ZERO_ERROR; |
michael@0 | 1145 | const char *converterName = uenum_next(allConvEnum, NULL, &localStatus); |
michael@0 | 1146 | if (ucnv_canCreateConverter(converterName, &localStatus)) { |
michael@0 | 1147 | gAvailableConverters[gAvailableConverterCount++] = converterName; |
michael@0 | 1148 | } |
michael@0 | 1149 | } |
michael@0 | 1150 | |
michael@0 | 1151 | uenum_close(allConvEnum); |
michael@0 | 1152 | } |
michael@0 | 1153 | |
michael@0 | 1154 | |
michael@0 | 1155 | static UBool haveAvailableConverterList(UErrorCode *pErrorCode) { |
michael@0 | 1156 | umtx_initOnce(gAvailableConvertersInitOnce, &initAvailableConvertersList, *pErrorCode); |
michael@0 | 1157 | return U_SUCCESS(*pErrorCode); |
michael@0 | 1158 | } |
michael@0 | 1159 | |
michael@0 | 1160 | U_CFUNC uint16_t |
michael@0 | 1161 | ucnv_bld_countAvailableConverters(UErrorCode *pErrorCode) { |
michael@0 | 1162 | if (haveAvailableConverterList(pErrorCode)) { |
michael@0 | 1163 | return gAvailableConverterCount; |
michael@0 | 1164 | } |
michael@0 | 1165 | return 0; |
michael@0 | 1166 | } |
michael@0 | 1167 | |
michael@0 | 1168 | U_CFUNC const char * |
michael@0 | 1169 | ucnv_bld_getAvailableConverter(uint16_t n, UErrorCode *pErrorCode) { |
michael@0 | 1170 | if (haveAvailableConverterList(pErrorCode)) { |
michael@0 | 1171 | if (n < gAvailableConverterCount) { |
michael@0 | 1172 | return gAvailableConverters[n]; |
michael@0 | 1173 | } |
michael@0 | 1174 | *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 1175 | } |
michael@0 | 1176 | return NULL; |
michael@0 | 1177 | } |
michael@0 | 1178 | |
michael@0 | 1179 | /* default converter name --------------------------------------------------- */ |
michael@0 | 1180 | |
michael@0 | 1181 | #if !U_CHARSET_IS_UTF8 |
michael@0 | 1182 | /* |
michael@0 | 1183 | Copy the canonical converter name. |
michael@0 | 1184 | ucnv_getDefaultName must be thread safe, which can call this function. |
michael@0 | 1185 | |
michael@0 | 1186 | ucnv_setDefaultName calls this function and it doesn't have to be |
michael@0 | 1187 | thread safe because there is no reliable/safe way to reset the |
michael@0 | 1188 | converter in use in all threads. If you did reset the converter, you |
michael@0 | 1189 | would not be sure that retrieving a default converter for one string |
michael@0 | 1190 | would be the same type of default converter for a successive string. |
michael@0 | 1191 | Since the name is a returned via ucnv_getDefaultName without copying, |
michael@0 | 1192 | you shouldn't be modifying or deleting the string from a separate thread. |
michael@0 | 1193 | */ |
michael@0 | 1194 | static inline void |
michael@0 | 1195 | internalSetName(const char *name, UErrorCode *status) { |
michael@0 | 1196 | UConverterNamePieces stackPieces; |
michael@0 | 1197 | UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; |
michael@0 | 1198 | int32_t length=(int32_t)(uprv_strlen(name)); |
michael@0 | 1199 | UBool containsOption = (UBool)(uprv_strchr(name, UCNV_OPTION_SEP_CHAR) != NULL); |
michael@0 | 1200 | const UConverterSharedData *algorithmicSharedData; |
michael@0 | 1201 | |
michael@0 | 1202 | stackArgs.name = name; |
michael@0 | 1203 | if(containsOption) { |
michael@0 | 1204 | stackPieces.cnvName[0] = 0; |
michael@0 | 1205 | stackPieces.locale[0] = 0; |
michael@0 | 1206 | stackPieces.options = 0; |
michael@0 | 1207 | parseConverterOptions(name, &stackPieces, &stackArgs, status); |
michael@0 | 1208 | if(U_FAILURE(*status)) { |
michael@0 | 1209 | return; |
michael@0 | 1210 | } |
michael@0 | 1211 | } |
michael@0 | 1212 | algorithmicSharedData = getAlgorithmicTypeFromName(stackArgs.name); |
michael@0 | 1213 | |
michael@0 | 1214 | umtx_lock(&cnvCacheMutex); |
michael@0 | 1215 | |
michael@0 | 1216 | gDefaultAlgorithmicSharedData = algorithmicSharedData; |
michael@0 | 1217 | gDefaultConverterContainsOption = containsOption; |
michael@0 | 1218 | uprv_memcpy(gDefaultConverterNameBuffer, name, length); |
michael@0 | 1219 | gDefaultConverterNameBuffer[length]=0; |
michael@0 | 1220 | |
michael@0 | 1221 | /* gDefaultConverterName MUST be the last global var set by this function. */ |
michael@0 | 1222 | /* It is the variable checked in ucnv_getDefaultName() to see if initialization is required. */ |
michael@0 | 1223 | // But there is nothing here preventing that from being reordered, either by the compiler |
michael@0 | 1224 | // or hardware. I'm adding the mutex to ucnv_getDefaultName for now. UMTX_CHECK is not enough. |
michael@0 | 1225 | // -- Andy |
michael@0 | 1226 | gDefaultConverterName = gDefaultConverterNameBuffer; |
michael@0 | 1227 | |
michael@0 | 1228 | ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); |
michael@0 | 1229 | |
michael@0 | 1230 | umtx_unlock(&cnvCacheMutex); |
michael@0 | 1231 | } |
michael@0 | 1232 | #endif |
michael@0 | 1233 | |
michael@0 | 1234 | /* |
michael@0 | 1235 | * In order to be really thread-safe, the get function would have to take |
michael@0 | 1236 | * a buffer parameter and copy the current string inside a mutex block. |
michael@0 | 1237 | * This implementation only tries to be really thread-safe while |
michael@0 | 1238 | * setting the name. |
michael@0 | 1239 | * It assumes that setting a pointer is atomic. |
michael@0 | 1240 | */ |
michael@0 | 1241 | |
michael@0 | 1242 | U_CAPI const char* U_EXPORT2 |
michael@0 | 1243 | ucnv_getDefaultName() { |
michael@0 | 1244 | #if U_CHARSET_IS_UTF8 |
michael@0 | 1245 | return "UTF-8"; |
michael@0 | 1246 | #else |
michael@0 | 1247 | /* local variable to be thread-safe */ |
michael@0 | 1248 | const char *name; |
michael@0 | 1249 | |
michael@0 | 1250 | /* |
michael@0 | 1251 | Concurrent calls to ucnv_getDefaultName must be thread safe, |
michael@0 | 1252 | but ucnv_setDefaultName is not thread safe. |
michael@0 | 1253 | */ |
michael@0 | 1254 | { |
michael@0 | 1255 | icu::Mutex lock(&cnvCacheMutex); |
michael@0 | 1256 | name = gDefaultConverterName; |
michael@0 | 1257 | } |
michael@0 | 1258 | if(name==NULL) { |
michael@0 | 1259 | UErrorCode errorCode = U_ZERO_ERROR; |
michael@0 | 1260 | UConverter *cnv = NULL; |
michael@0 | 1261 | |
michael@0 | 1262 | name = uprv_getDefaultCodepage(); |
michael@0 | 1263 | |
michael@0 | 1264 | /* if the name is there, test it out and get the canonical name with options */ |
michael@0 | 1265 | if(name != NULL) { |
michael@0 | 1266 | cnv = ucnv_open(name, &errorCode); |
michael@0 | 1267 | if(U_SUCCESS(errorCode) && cnv != NULL) { |
michael@0 | 1268 | name = ucnv_getName(cnv, &errorCode); |
michael@0 | 1269 | } |
michael@0 | 1270 | } |
michael@0 | 1271 | |
michael@0 | 1272 | if(name == NULL || name[0] == 0 |
michael@0 | 1273 | || U_FAILURE(errorCode) || cnv == NULL |
michael@0 | 1274 | || uprv_strlen(name)>=sizeof(gDefaultConverterNameBuffer)) |
michael@0 | 1275 | { |
michael@0 | 1276 | /* Panic time, let's use a fallback. */ |
michael@0 | 1277 | #if (U_CHARSET_FAMILY == U_ASCII_FAMILY) |
michael@0 | 1278 | name = "US-ASCII"; |
michael@0 | 1279 | /* there is no 'algorithmic' converter for EBCDIC */ |
michael@0 | 1280 | #elif U_PLATFORM == U_PF_OS390 |
michael@0 | 1281 | name = "ibm-1047_P100-1995" UCNV_SWAP_LFNL_OPTION_STRING; |
michael@0 | 1282 | #else |
michael@0 | 1283 | name = "ibm-37_P100-1995"; |
michael@0 | 1284 | #endif |
michael@0 | 1285 | } |
michael@0 | 1286 | |
michael@0 | 1287 | internalSetName(name, &errorCode); |
michael@0 | 1288 | |
michael@0 | 1289 | /* The close may make the current name go away. */ |
michael@0 | 1290 | ucnv_close(cnv); |
michael@0 | 1291 | } |
michael@0 | 1292 | |
michael@0 | 1293 | return name; |
michael@0 | 1294 | #endif |
michael@0 | 1295 | } |
michael@0 | 1296 | |
michael@0 | 1297 | #if U_CHARSET_IS_UTF8 |
michael@0 | 1298 | U_CAPI void U_EXPORT2 ucnv_setDefaultName(const char *) {} |
michael@0 | 1299 | #else |
michael@0 | 1300 | /* |
michael@0 | 1301 | This function is not thread safe, and it can't be thread safe. |
michael@0 | 1302 | See internalSetName or the API reference for details. |
michael@0 | 1303 | */ |
michael@0 | 1304 | U_CAPI void U_EXPORT2 |
michael@0 | 1305 | ucnv_setDefaultName(const char *converterName) { |
michael@0 | 1306 | if(converterName==NULL) { |
michael@0 | 1307 | /* reset to the default codepage */ |
michael@0 | 1308 | gDefaultConverterName=NULL; |
michael@0 | 1309 | } else { |
michael@0 | 1310 | UErrorCode errorCode = U_ZERO_ERROR; |
michael@0 | 1311 | UConverter *cnv = NULL; |
michael@0 | 1312 | const char *name = NULL; |
michael@0 | 1313 | |
michael@0 | 1314 | /* if the name is there, test it out and get the canonical name with options */ |
michael@0 | 1315 | cnv = ucnv_open(converterName, &errorCode); |
michael@0 | 1316 | if(U_SUCCESS(errorCode) && cnv != NULL) { |
michael@0 | 1317 | name = ucnv_getName(cnv, &errorCode); |
michael@0 | 1318 | } |
michael@0 | 1319 | |
michael@0 | 1320 | if(U_SUCCESS(errorCode) && name!=NULL) { |
michael@0 | 1321 | internalSetName(name, &errorCode); |
michael@0 | 1322 | } |
michael@0 | 1323 | /* else this converter is bad to use. Don't change it to a bad value. */ |
michael@0 | 1324 | |
michael@0 | 1325 | /* The close may make the current name go away. */ |
michael@0 | 1326 | ucnv_close(cnv); |
michael@0 | 1327 | |
michael@0 | 1328 | /* reset the converter cache */ |
michael@0 | 1329 | u_flushDefaultConverter(); |
michael@0 | 1330 | } |
michael@0 | 1331 | } |
michael@0 | 1332 | #endif |
michael@0 | 1333 | |
michael@0 | 1334 | /* data swapping ------------------------------------------------------------ */ |
michael@0 | 1335 | |
michael@0 | 1336 | /* most of this might belong more properly into ucnvmbcs.c, but that is so large */ |
michael@0 | 1337 | |
michael@0 | 1338 | #if !UCONFIG_NO_LEGACY_CONVERSION |
michael@0 | 1339 | |
michael@0 | 1340 | U_CAPI int32_t U_EXPORT2 |
michael@0 | 1341 | ucnv_swap(const UDataSwapper *ds, |
michael@0 | 1342 | const void *inData, int32_t length, void *outData, |
michael@0 | 1343 | UErrorCode *pErrorCode) { |
michael@0 | 1344 | const UDataInfo *pInfo; |
michael@0 | 1345 | int32_t headerSize; |
michael@0 | 1346 | |
michael@0 | 1347 | const uint8_t *inBytes; |
michael@0 | 1348 | uint8_t *outBytes; |
michael@0 | 1349 | |
michael@0 | 1350 | uint32_t offset, count, staticDataSize; |
michael@0 | 1351 | int32_t size; |
michael@0 | 1352 | |
michael@0 | 1353 | const UConverterStaticData *inStaticData; |
michael@0 | 1354 | UConverterStaticData *outStaticData; |
michael@0 | 1355 | |
michael@0 | 1356 | const _MBCSHeader *inMBCSHeader; |
michael@0 | 1357 | _MBCSHeader *outMBCSHeader; |
michael@0 | 1358 | _MBCSHeader mbcsHeader; |
michael@0 | 1359 | uint32_t mbcsHeaderLength; |
michael@0 | 1360 | UBool noFromU=FALSE; |
michael@0 | 1361 | |
michael@0 | 1362 | uint8_t outputType; |
michael@0 | 1363 | |
michael@0 | 1364 | int32_t maxFastUChar, mbcsIndexLength; |
michael@0 | 1365 | |
michael@0 | 1366 | const int32_t *inExtIndexes; |
michael@0 | 1367 | int32_t extOffset; |
michael@0 | 1368 | |
michael@0 | 1369 | /* udata_swapDataHeader checks the arguments */ |
michael@0 | 1370 | headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); |
michael@0 | 1371 | if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { |
michael@0 | 1372 | return 0; |
michael@0 | 1373 | } |
michael@0 | 1374 | |
michael@0 | 1375 | /* check data format and format version */ |
michael@0 | 1376 | pInfo=(const UDataInfo *)((const char *)inData+4); |
michael@0 | 1377 | if(!( |
michael@0 | 1378 | pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ |
michael@0 | 1379 | pInfo->dataFormat[1]==0x6e && |
michael@0 | 1380 | pInfo->dataFormat[2]==0x76 && |
michael@0 | 1381 | pInfo->dataFormat[3]==0x74 && |
michael@0 | 1382 | pInfo->formatVersion[0]==6 && |
michael@0 | 1383 | pInfo->formatVersion[1]>=2 |
michael@0 | 1384 | )) { |
michael@0 | 1385 | 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", |
michael@0 | 1386 | pInfo->dataFormat[0], pInfo->dataFormat[1], |
michael@0 | 1387 | pInfo->dataFormat[2], pInfo->dataFormat[3], |
michael@0 | 1388 | pInfo->formatVersion[0], pInfo->formatVersion[1]); |
michael@0 | 1389 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 1390 | return 0; |
michael@0 | 1391 | } |
michael@0 | 1392 | |
michael@0 | 1393 | inBytes=(const uint8_t *)inData+headerSize; |
michael@0 | 1394 | outBytes=(uint8_t *)outData+headerSize; |
michael@0 | 1395 | |
michael@0 | 1396 | /* read the initial UConverterStaticData structure after the UDataInfo header */ |
michael@0 | 1397 | inStaticData=(const UConverterStaticData *)inBytes; |
michael@0 | 1398 | outStaticData=(UConverterStaticData *)outBytes; |
michael@0 | 1399 | |
michael@0 | 1400 | if(length<0) { |
michael@0 | 1401 | staticDataSize=ds->readUInt32(inStaticData->structSize); |
michael@0 | 1402 | } else { |
michael@0 | 1403 | length-=headerSize; |
michael@0 | 1404 | if( length<(int32_t)sizeof(UConverterStaticData) || |
michael@0 | 1405 | (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize)) |
michael@0 | 1406 | ) { |
michael@0 | 1407 | udata_printError(ds, "ucnv_swap(): too few bytes (%d after header) for an ICU .cnv conversion table\n", |
michael@0 | 1408 | length); |
michael@0 | 1409 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 1410 | return 0; |
michael@0 | 1411 | } |
michael@0 | 1412 | } |
michael@0 | 1413 | |
michael@0 | 1414 | if(length>=0) { |
michael@0 | 1415 | /* swap the static data */ |
michael@0 | 1416 | if(inStaticData!=outStaticData) { |
michael@0 | 1417 | uprv_memcpy(outStaticData, inStaticData, staticDataSize); |
michael@0 | 1418 | } |
michael@0 | 1419 | |
michael@0 | 1420 | ds->swapArray32(ds, &inStaticData->structSize, 4, |
michael@0 | 1421 | &outStaticData->structSize, pErrorCode); |
michael@0 | 1422 | ds->swapArray32(ds, &inStaticData->codepage, 4, |
michael@0 | 1423 | &outStaticData->codepage, pErrorCode); |
michael@0 | 1424 | |
michael@0 | 1425 | ds->swapInvChars(ds, inStaticData->name, (int32_t)uprv_strlen(inStaticData->name), |
michael@0 | 1426 | outStaticData->name, pErrorCode); |
michael@0 | 1427 | if(U_FAILURE(*pErrorCode)) { |
michael@0 | 1428 | udata_printError(ds, "ucnv_swap(): error swapping converter name\n"); |
michael@0 | 1429 | return 0; |
michael@0 | 1430 | } |
michael@0 | 1431 | } |
michael@0 | 1432 | |
michael@0 | 1433 | inBytes+=staticDataSize; |
michael@0 | 1434 | outBytes+=staticDataSize; |
michael@0 | 1435 | if(length>=0) { |
michael@0 | 1436 | length-=(int32_t)staticDataSize; |
michael@0 | 1437 | } |
michael@0 | 1438 | |
michael@0 | 1439 | /* check for supported conversionType values */ |
michael@0 | 1440 | if(inStaticData->conversionType==UCNV_MBCS) { |
michael@0 | 1441 | /* swap MBCS data */ |
michael@0 | 1442 | inMBCSHeader=(const _MBCSHeader *)inBytes; |
michael@0 | 1443 | outMBCSHeader=(_MBCSHeader *)outBytes; |
michael@0 | 1444 | |
michael@0 | 1445 | if(0<=length && length<(int32_t)sizeof(_MBCSHeader)) { |
michael@0 | 1446 | udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", |
michael@0 | 1447 | length); |
michael@0 | 1448 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 1449 | return 0; |
michael@0 | 1450 | } |
michael@0 | 1451 | if(inMBCSHeader->version[0]==4 && inMBCSHeader->version[1]>=1) { |
michael@0 | 1452 | mbcsHeaderLength=MBCS_HEADER_V4_LENGTH; |
michael@0 | 1453 | } else if(inMBCSHeader->version[0]==5 && inMBCSHeader->version[1]>=3 && |
michael@0 | 1454 | ((mbcsHeader.options=ds->readUInt32(inMBCSHeader->options))& |
michael@0 | 1455 | MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0 |
michael@0 | 1456 | ) { |
michael@0 | 1457 | mbcsHeaderLength=mbcsHeader.options&MBCS_OPT_LENGTH_MASK; |
michael@0 | 1458 | noFromU=(UBool)((mbcsHeader.options&MBCS_OPT_NO_FROM_U)!=0); |
michael@0 | 1459 | } else { |
michael@0 | 1460 | udata_printError(ds, "ucnv_swap(): unsupported _MBCSHeader.version %d.%d\n", |
michael@0 | 1461 | inMBCSHeader->version[0], inMBCSHeader->version[1]); |
michael@0 | 1462 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 1463 | return 0; |
michael@0 | 1464 | } |
michael@0 | 1465 | |
michael@0 | 1466 | uprv_memcpy(mbcsHeader.version, inMBCSHeader->version, 4); |
michael@0 | 1467 | mbcsHeader.countStates= ds->readUInt32(inMBCSHeader->countStates); |
michael@0 | 1468 | mbcsHeader.countToUFallbacks= ds->readUInt32(inMBCSHeader->countToUFallbacks); |
michael@0 | 1469 | mbcsHeader.offsetToUCodeUnits= ds->readUInt32(inMBCSHeader->offsetToUCodeUnits); |
michael@0 | 1470 | mbcsHeader.offsetFromUTable= ds->readUInt32(inMBCSHeader->offsetFromUTable); |
michael@0 | 1471 | mbcsHeader.offsetFromUBytes= ds->readUInt32(inMBCSHeader->offsetFromUBytes); |
michael@0 | 1472 | mbcsHeader.flags= ds->readUInt32(inMBCSHeader->flags); |
michael@0 | 1473 | mbcsHeader.fromUBytesLength= ds->readUInt32(inMBCSHeader->fromUBytesLength); |
michael@0 | 1474 | /* mbcsHeader.options have been read above */ |
michael@0 | 1475 | |
michael@0 | 1476 | extOffset=(int32_t)(mbcsHeader.flags>>8); |
michael@0 | 1477 | outputType=(uint8_t)mbcsHeader.flags; |
michael@0 | 1478 | if(noFromU && outputType==MBCS_OUTPUT_1) { |
michael@0 | 1479 | udata_printError(ds, "ucnv_swap(): unsupported combination of makeconv --small with SBCS\n"); |
michael@0 | 1480 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 1481 | return 0; |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | /* make sure that the output type is known */ |
michael@0 | 1485 | switch(outputType) { |
michael@0 | 1486 | case MBCS_OUTPUT_1: |
michael@0 | 1487 | case MBCS_OUTPUT_2: |
michael@0 | 1488 | case MBCS_OUTPUT_3: |
michael@0 | 1489 | case MBCS_OUTPUT_4: |
michael@0 | 1490 | case MBCS_OUTPUT_3_EUC: |
michael@0 | 1491 | case MBCS_OUTPUT_4_EUC: |
michael@0 | 1492 | case MBCS_OUTPUT_2_SISO: |
michael@0 | 1493 | case MBCS_OUTPUT_EXT_ONLY: |
michael@0 | 1494 | /* OK */ |
michael@0 | 1495 | break; |
michael@0 | 1496 | default: |
michael@0 | 1497 | udata_printError(ds, "ucnv_swap(): unsupported MBCS output type 0x%x\n", |
michael@0 | 1498 | outputType); |
michael@0 | 1499 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 1500 | return 0; |
michael@0 | 1501 | } |
michael@0 | 1502 | |
michael@0 | 1503 | /* calculate the length of the MBCS data */ |
michael@0 | 1504 | |
michael@0 | 1505 | /* |
michael@0 | 1506 | * utf8Friendly MBCS files (mbcsHeader.version 4.3) |
michael@0 | 1507 | * contain an additional mbcsIndex table: |
michael@0 | 1508 | * uint16_t[(maxFastUChar+1)>>6]; |
michael@0 | 1509 | * where maxFastUChar=((mbcsHeader.version[2]<<8)|0xff). |
michael@0 | 1510 | */ |
michael@0 | 1511 | maxFastUChar=0; |
michael@0 | 1512 | mbcsIndexLength=0; |
michael@0 | 1513 | if( outputType!=MBCS_OUTPUT_EXT_ONLY && outputType!=MBCS_OUTPUT_1 && |
michael@0 | 1514 | mbcsHeader.version[1]>=3 && (maxFastUChar=mbcsHeader.version[2])!=0 |
michael@0 | 1515 | ) { |
michael@0 | 1516 | maxFastUChar=(maxFastUChar<<8)|0xff; |
michael@0 | 1517 | mbcsIndexLength=((maxFastUChar+1)>>6)*2; /* number of bytes */ |
michael@0 | 1518 | } |
michael@0 | 1519 | |
michael@0 | 1520 | if(extOffset==0) { |
michael@0 | 1521 | size=(int32_t)(mbcsHeader.offsetFromUBytes+mbcsIndexLength); |
michael@0 | 1522 | if(!noFromU) { |
michael@0 | 1523 | size+=(int32_t)mbcsHeader.fromUBytesLength; |
michael@0 | 1524 | } |
michael@0 | 1525 | |
michael@0 | 1526 | /* avoid compiler warnings - not otherwise necessary, and the value does not matter */ |
michael@0 | 1527 | inExtIndexes=NULL; |
michael@0 | 1528 | } else { |
michael@0 | 1529 | /* there is extension data after the base data, see ucnv_ext.h */ |
michael@0 | 1530 | if(length>=0 && length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) { |
michael@0 | 1531 | udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n", |
michael@0 | 1532 | length); |
michael@0 | 1533 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 1534 | return 0; |
michael@0 | 1535 | } |
michael@0 | 1536 | |
michael@0 | 1537 | inExtIndexes=(const int32_t *)(inBytes+extOffset); |
michael@0 | 1538 | size=extOffset+udata_readInt32(ds, inExtIndexes[UCNV_EXT_SIZE]); |
michael@0 | 1539 | } |
michael@0 | 1540 | |
michael@0 | 1541 | if(length>=0) { |
michael@0 | 1542 | if(length<size) { |
michael@0 | 1543 | udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", |
michael@0 | 1544 | length); |
michael@0 | 1545 | *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; |
michael@0 | 1546 | return 0; |
michael@0 | 1547 | } |
michael@0 | 1548 | |
michael@0 | 1549 | /* copy the data for inaccessible bytes */ |
michael@0 | 1550 | if(inBytes!=outBytes) { |
michael@0 | 1551 | uprv_memcpy(outBytes, inBytes, size); |
michael@0 | 1552 | } |
michael@0 | 1553 | |
michael@0 | 1554 | /* swap the MBCSHeader, except for the version field */ |
michael@0 | 1555 | count=mbcsHeaderLength*4; |
michael@0 | 1556 | ds->swapArray32(ds, &inMBCSHeader->countStates, count-4, |
michael@0 | 1557 | &outMBCSHeader->countStates, pErrorCode); |
michael@0 | 1558 | |
michael@0 | 1559 | if(outputType==MBCS_OUTPUT_EXT_ONLY) { |
michael@0 | 1560 | /* |
michael@0 | 1561 | * extension-only file, |
michael@0 | 1562 | * contains a base name instead of normal base table data |
michael@0 | 1563 | */ |
michael@0 | 1564 | |
michael@0 | 1565 | /* swap the base name, between the header and the extension data */ |
michael@0 | 1566 | const char *inBaseName=(const char *)inBytes+count; |
michael@0 | 1567 | char *outBaseName=(char *)outBytes+count; |
michael@0 | 1568 | ds->swapInvChars(ds, inBaseName, (int32_t)uprv_strlen(inBaseName), |
michael@0 | 1569 | outBaseName, pErrorCode); |
michael@0 | 1570 | } else { |
michael@0 | 1571 | /* normal file with base table data */ |
michael@0 | 1572 | |
michael@0 | 1573 | /* swap the state table, 1kB per state */ |
michael@0 | 1574 | offset=count; |
michael@0 | 1575 | count=mbcsHeader.countStates*1024; |
michael@0 | 1576 | ds->swapArray32(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1577 | outBytes+offset, pErrorCode); |
michael@0 | 1578 | |
michael@0 | 1579 | /* swap the toUFallbacks[] */ |
michael@0 | 1580 | offset+=count; |
michael@0 | 1581 | count=mbcsHeader.countToUFallbacks*8; |
michael@0 | 1582 | ds->swapArray32(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1583 | outBytes+offset, pErrorCode); |
michael@0 | 1584 | |
michael@0 | 1585 | /* swap the unicodeCodeUnits[] */ |
michael@0 | 1586 | offset=mbcsHeader.offsetToUCodeUnits; |
michael@0 | 1587 | count=mbcsHeader.offsetFromUTable-offset; |
michael@0 | 1588 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1589 | outBytes+offset, pErrorCode); |
michael@0 | 1590 | |
michael@0 | 1591 | /* offset to the stage 1 table, independent of the outputType */ |
michael@0 | 1592 | offset=mbcsHeader.offsetFromUTable; |
michael@0 | 1593 | |
michael@0 | 1594 | if(outputType==MBCS_OUTPUT_1) { |
michael@0 | 1595 | /* SBCS: swap the fromU tables, all 16 bits wide */ |
michael@0 | 1596 | count=(mbcsHeader.offsetFromUBytes-offset)+mbcsHeader.fromUBytesLength; |
michael@0 | 1597 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1598 | outBytes+offset, pErrorCode); |
michael@0 | 1599 | } else { |
michael@0 | 1600 | /* otherwise: swap the stage tables separately */ |
michael@0 | 1601 | |
michael@0 | 1602 | /* stage 1 table: uint16_t[0x440 or 0x40] */ |
michael@0 | 1603 | if(inStaticData->unicodeMask&UCNV_HAS_SUPPLEMENTARY) { |
michael@0 | 1604 | count=0x440*2; /* for all of Unicode */ |
michael@0 | 1605 | } else { |
michael@0 | 1606 | count=0x40*2; /* only BMP */ |
michael@0 | 1607 | } |
michael@0 | 1608 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1609 | outBytes+offset, pErrorCode); |
michael@0 | 1610 | |
michael@0 | 1611 | /* stage 2 table: uint32_t[] */ |
michael@0 | 1612 | offset+=count; |
michael@0 | 1613 | count=mbcsHeader.offsetFromUBytes-offset; |
michael@0 | 1614 | ds->swapArray32(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1615 | outBytes+offset, pErrorCode); |
michael@0 | 1616 | |
michael@0 | 1617 | /* stage 3/result bytes: sometimes uint16_t[] or uint32_t[] */ |
michael@0 | 1618 | offset=mbcsHeader.offsetFromUBytes; |
michael@0 | 1619 | count= noFromU ? 0 : mbcsHeader.fromUBytesLength; |
michael@0 | 1620 | switch(outputType) { |
michael@0 | 1621 | case MBCS_OUTPUT_2: |
michael@0 | 1622 | case MBCS_OUTPUT_3_EUC: |
michael@0 | 1623 | case MBCS_OUTPUT_2_SISO: |
michael@0 | 1624 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1625 | outBytes+offset, pErrorCode); |
michael@0 | 1626 | break; |
michael@0 | 1627 | case MBCS_OUTPUT_4: |
michael@0 | 1628 | ds->swapArray32(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1629 | outBytes+offset, pErrorCode); |
michael@0 | 1630 | break; |
michael@0 | 1631 | default: |
michael@0 | 1632 | /* just uint8_t[], nothing to swap */ |
michael@0 | 1633 | break; |
michael@0 | 1634 | } |
michael@0 | 1635 | |
michael@0 | 1636 | if(mbcsIndexLength!=0) { |
michael@0 | 1637 | offset+=count; |
michael@0 | 1638 | count=mbcsIndexLength; |
michael@0 | 1639 | ds->swapArray16(ds, inBytes+offset, (int32_t)count, |
michael@0 | 1640 | outBytes+offset, pErrorCode); |
michael@0 | 1641 | } |
michael@0 | 1642 | } |
michael@0 | 1643 | } |
michael@0 | 1644 | |
michael@0 | 1645 | if(extOffset!=0) { |
michael@0 | 1646 | /* swap the extension data */ |
michael@0 | 1647 | inBytes+=extOffset; |
michael@0 | 1648 | outBytes+=extOffset; |
michael@0 | 1649 | |
michael@0 | 1650 | /* swap toUTable[] */ |
michael@0 | 1651 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_INDEX]); |
michael@0 | 1652 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_LENGTH]); |
michael@0 | 1653 | ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); |
michael@0 | 1654 | |
michael@0 | 1655 | /* swap toUUChars[] */ |
michael@0 | 1656 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_INDEX]); |
michael@0 | 1657 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_LENGTH]); |
michael@0 | 1658 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); |
michael@0 | 1659 | |
michael@0 | 1660 | /* swap fromUTableUChars[] */ |
michael@0 | 1661 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_UCHARS_INDEX]); |
michael@0 | 1662 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_LENGTH]); |
michael@0 | 1663 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); |
michael@0 | 1664 | |
michael@0 | 1665 | /* swap fromUTableValues[] */ |
michael@0 | 1666 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_VALUES_INDEX]); |
michael@0 | 1667 | /* same length as for fromUTableUChars[] */ |
michael@0 | 1668 | ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); |
michael@0 | 1669 | |
michael@0 | 1670 | /* no need to swap fromUBytes[] */ |
michael@0 | 1671 | |
michael@0 | 1672 | /* swap fromUStage12[] */ |
michael@0 | 1673 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_INDEX]); |
michael@0 | 1674 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_LENGTH]); |
michael@0 | 1675 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); |
michael@0 | 1676 | |
michael@0 | 1677 | /* swap fromUStage3[] */ |
michael@0 | 1678 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_INDEX]); |
michael@0 | 1679 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_LENGTH]); |
michael@0 | 1680 | ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); |
michael@0 | 1681 | |
michael@0 | 1682 | /* swap fromUStage3b[] */ |
michael@0 | 1683 | offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_INDEX]); |
michael@0 | 1684 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_LENGTH]); |
michael@0 | 1685 | ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); |
michael@0 | 1686 | |
michael@0 | 1687 | /* swap indexes[] */ |
michael@0 | 1688 | length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_INDEXES_LENGTH]); |
michael@0 | 1689 | ds->swapArray32(ds, inBytes, length*4, outBytes, pErrorCode); |
michael@0 | 1690 | } |
michael@0 | 1691 | } |
michael@0 | 1692 | } else { |
michael@0 | 1693 | udata_printError(ds, "ucnv_swap(): unknown conversionType=%d!=UCNV_MBCS\n", |
michael@0 | 1694 | inStaticData->conversionType); |
michael@0 | 1695 | *pErrorCode=U_UNSUPPORTED_ERROR; |
michael@0 | 1696 | return 0; |
michael@0 | 1697 | } |
michael@0 | 1698 | |
michael@0 | 1699 | return headerSize+(int32_t)staticDataSize+size; |
michael@0 | 1700 | } |
michael@0 | 1701 | |
michael@0 | 1702 | #endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ |
michael@0 | 1703 | |
michael@0 | 1704 | #endif |