michael@0: /* michael@0: ******************************************************************** michael@0: * COPYRIGHT: michael@0: * Copyright (c) 1996-2013, International Business Machines Corporation and michael@0: * others. All Rights Reserved. michael@0: ******************************************************************** michael@0: * michael@0: * uconv_bld.cpp: michael@0: * michael@0: * Defines functions that are used in the creation/initialization/deletion michael@0: * of converters and related structures. michael@0: * uses uconv_io.h routines to access disk information michael@0: * is used by ucnv.h to implement public API create/delete/flushCache routines michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * michael@0: * 06/20/2000 helena OS/400 port changes; mostly typecast. michael@0: * 06/29/2000 helena Major rewrite of the callback interface. michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_CONVERSION michael@0: michael@0: #include "unicode/putil.h" michael@0: #include "unicode/udata.h" michael@0: #include "unicode/ucnv.h" michael@0: #include "unicode/uloc.h" michael@0: #include "mutex.h" michael@0: #include "putilimp.h" michael@0: #include "uassert.h" michael@0: #include "utracimp.h" michael@0: #include "ucnv_io.h" michael@0: #include "ucnv_bld.h" michael@0: #include "ucnvmbcs.h" michael@0: #include "ucnv_ext.h" michael@0: #include "ucnv_cnv.h" michael@0: #include "ucnv_imp.h" michael@0: #include "uhash.h" michael@0: #include "umutex.h" michael@0: #include "cstring.h" michael@0: #include "cmemory.h" michael@0: #include "ucln_cmn.h" michael@0: #include "ustr_cnv.h" michael@0: michael@0: michael@0: #if 0 michael@0: #include michael@0: extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l); michael@0: #define UCNV_DEBUG_LOG(x,y,z) UCNV_DEBUG_LOG(x,y,z,__LINE__) michael@0: #else michael@0: # define UCNV_DEBUG_LOG(x,y,z) michael@0: #endif michael@0: michael@0: static const UConverterSharedData * const michael@0: converterData[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES]={ michael@0: NULL, NULL, michael@0: michael@0: #if UCONFIG_NO_LEGACY_CONVERSION michael@0: NULL, michael@0: #else michael@0: &_MBCSData, michael@0: #endif michael@0: michael@0: &_Latin1Data, michael@0: &_UTF8Data, &_UTF16BEData, &_UTF16LEData, &_UTF32BEData, &_UTF32LEData, michael@0: NULL, michael@0: michael@0: #if UCONFIG_NO_LEGACY_CONVERSION michael@0: NULL, michael@0: NULL, NULL, NULL, NULL, NULL, NULL, michael@0: NULL, NULL, NULL, NULL, NULL, NULL, michael@0: NULL, michael@0: #else michael@0: &_ISO2022Data, michael@0: &_LMBCSData1,&_LMBCSData2, &_LMBCSData3, &_LMBCSData4, &_LMBCSData5, &_LMBCSData6, michael@0: &_LMBCSData8,&_LMBCSData11,&_LMBCSData16,&_LMBCSData17,&_LMBCSData18,&_LMBCSData19, michael@0: &_HZData, michael@0: #endif michael@0: michael@0: &_SCSUData, michael@0: michael@0: #if UCONFIG_NO_LEGACY_CONVERSION michael@0: NULL, michael@0: #else michael@0: &_ISCIIData, michael@0: #endif michael@0: michael@0: &_ASCIIData, michael@0: &_UTF7Data, &_Bocu1Data, &_UTF16Data, &_UTF32Data, &_CESU8Data, &_IMAPData, michael@0: michael@0: #if UCONFIG_NO_LEGACY_CONVERSION michael@0: NULL, michael@0: #else michael@0: &_CompoundTextData michael@0: #endif michael@0: }; michael@0: michael@0: /* Please keep this in binary sorted order for getAlgorithmicTypeFromName. michael@0: Also the name should be in lower case and all spaces, dashes and underscores michael@0: removed michael@0: */ michael@0: static struct { michael@0: const char *name; michael@0: const UConverterType type; michael@0: } const cnvNameType[] = { michael@0: { "bocu1", UCNV_BOCU1 }, michael@0: { "cesu8", UCNV_CESU8 }, michael@0: #if !UCONFIG_NO_LEGACY_CONVERSION michael@0: { "hz",UCNV_HZ }, michael@0: #endif michael@0: { "imapmailboxname", UCNV_IMAP_MAILBOX }, michael@0: #if !UCONFIG_NO_LEGACY_CONVERSION michael@0: { "iscii", UCNV_ISCII }, michael@0: { "iso2022", UCNV_ISO_2022 }, michael@0: #endif michael@0: { "iso88591", UCNV_LATIN_1 }, michael@0: #if !UCONFIG_NO_LEGACY_CONVERSION michael@0: { "lmbcs1", UCNV_LMBCS_1 }, michael@0: { "lmbcs11",UCNV_LMBCS_11 }, michael@0: { "lmbcs16",UCNV_LMBCS_16 }, michael@0: { "lmbcs17",UCNV_LMBCS_17 }, michael@0: { "lmbcs18",UCNV_LMBCS_18 }, michael@0: { "lmbcs19",UCNV_LMBCS_19 }, michael@0: { "lmbcs2", UCNV_LMBCS_2 }, michael@0: { "lmbcs3", UCNV_LMBCS_3 }, michael@0: { "lmbcs4", UCNV_LMBCS_4 }, michael@0: { "lmbcs5", UCNV_LMBCS_5 }, michael@0: { "lmbcs6", UCNV_LMBCS_6 }, michael@0: { "lmbcs8", UCNV_LMBCS_8 }, michael@0: #endif michael@0: { "scsu", UCNV_SCSU }, michael@0: { "usascii", UCNV_US_ASCII }, michael@0: { "utf16", UCNV_UTF16 }, michael@0: { "utf16be", UCNV_UTF16_BigEndian }, michael@0: { "utf16le", UCNV_UTF16_LittleEndian }, michael@0: #if U_IS_BIG_ENDIAN michael@0: { "utf16oppositeendian", UCNV_UTF16_LittleEndian }, michael@0: { "utf16platformendian", UCNV_UTF16_BigEndian }, michael@0: #else michael@0: { "utf16oppositeendian", UCNV_UTF16_BigEndian}, michael@0: { "utf16platformendian", UCNV_UTF16_LittleEndian }, michael@0: #endif michael@0: { "utf32", UCNV_UTF32 }, michael@0: { "utf32be", UCNV_UTF32_BigEndian }, michael@0: { "utf32le", UCNV_UTF32_LittleEndian }, michael@0: #if U_IS_BIG_ENDIAN michael@0: { "utf32oppositeendian", UCNV_UTF32_LittleEndian }, michael@0: { "utf32platformendian", UCNV_UTF32_BigEndian }, michael@0: #else michael@0: { "utf32oppositeendian", UCNV_UTF32_BigEndian }, michael@0: { "utf32platformendian", UCNV_UTF32_LittleEndian }, michael@0: #endif michael@0: { "utf7", UCNV_UTF7 }, michael@0: { "utf8", UCNV_UTF8 }, michael@0: { "x11compoundtext", UCNV_COMPOUND_TEXT} michael@0: }; michael@0: michael@0: michael@0: /*initializes some global variables */ michael@0: static UHashtable *SHARED_DATA_HASHTABLE = NULL; michael@0: static UMutex cnvCacheMutex = U_MUTEX_INITIALIZER; /* Mutex for synchronizing cnv cache access. */ michael@0: /* Note: the global mutex is used for */ michael@0: /* reference count updates. */ michael@0: michael@0: static const char **gAvailableConverters = NULL; michael@0: static uint16_t gAvailableConverterCount = 0; michael@0: static icu::UInitOnce gAvailableConvertersInitOnce = U_INITONCE_INITIALIZER; michael@0: michael@0: #if !U_CHARSET_IS_UTF8 michael@0: michael@0: /* This contains the resolved converter name. So no further alias lookup is needed again. */ michael@0: static char gDefaultConverterNameBuffer[UCNV_MAX_CONVERTER_NAME_LENGTH + 1]; /* +1 for NULL */ michael@0: static const char *gDefaultConverterName = NULL; michael@0: michael@0: /* michael@0: If the default converter is an algorithmic converter, this is the cached value. michael@0: We don't cache a full UConverter and clone it because ucnv_clone doesn't have michael@0: less overhead than an algorithmic open. We don't cache non-algorithmic converters michael@0: because ucnv_flushCache must be able to unload the default converter and its table. michael@0: */ michael@0: static const UConverterSharedData *gDefaultAlgorithmicSharedData = NULL; michael@0: michael@0: /* Does gDefaultConverterName have a converter option and require extra parsing? */ michael@0: static UBool gDefaultConverterContainsOption; michael@0: michael@0: #endif /* !U_CHARSET_IS_UTF8 */ michael@0: michael@0: static const char DATA_TYPE[] = "cnv"; michael@0: michael@0: /* ucnv_flushAvailableConverterCache. This is only called from ucnv_cleanup(). michael@0: * If it is ever to be called from elsewhere, synchronization michael@0: * will need to be considered. michael@0: */ michael@0: static void michael@0: ucnv_flushAvailableConverterCache() { michael@0: gAvailableConverterCount = 0; michael@0: if (gAvailableConverters) { michael@0: uprv_free((char **)gAvailableConverters); michael@0: gAvailableConverters = NULL; michael@0: } michael@0: gAvailableConvertersInitOnce.reset(); michael@0: } michael@0: michael@0: /* ucnv_cleanup - delete all storage held by the converter cache, except any */ michael@0: /* in use by open converters. */ michael@0: /* Not thread safe. */ michael@0: /* Not supported API. */ michael@0: static UBool U_CALLCONV ucnv_cleanup(void) { michael@0: ucnv_flushCache(); michael@0: if (SHARED_DATA_HASHTABLE != NULL && uhash_count(SHARED_DATA_HASHTABLE) == 0) { michael@0: uhash_close(SHARED_DATA_HASHTABLE); michael@0: SHARED_DATA_HASHTABLE = NULL; michael@0: } michael@0: michael@0: /* Isn't called from flushCache because other threads may have preexisting references to the table. */ michael@0: ucnv_flushAvailableConverterCache(); michael@0: michael@0: #if !U_CHARSET_IS_UTF8 michael@0: gDefaultConverterName = NULL; michael@0: gDefaultConverterNameBuffer[0] = 0; michael@0: gDefaultConverterContainsOption = FALSE; michael@0: gDefaultAlgorithmicSharedData = NULL; michael@0: #endif michael@0: michael@0: return (SHARED_DATA_HASHTABLE == NULL); michael@0: } michael@0: michael@0: static UBool U_CALLCONV michael@0: isCnvAcceptable(void * /*context*/, michael@0: const char * /*type*/, const char * /*name*/, michael@0: const UDataInfo *pInfo) { michael@0: return (UBool)( michael@0: pInfo->size>=20 && michael@0: pInfo->isBigEndian==U_IS_BIG_ENDIAN && michael@0: pInfo->charsetFamily==U_CHARSET_FAMILY && michael@0: pInfo->sizeofUChar==U_SIZEOF_UCHAR && michael@0: pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ michael@0: pInfo->dataFormat[1]==0x6e && michael@0: pInfo->dataFormat[2]==0x76 && michael@0: pInfo->dataFormat[3]==0x74 && michael@0: pInfo->formatVersion[0]==6); /* Everything will be version 6 */ michael@0: } michael@0: michael@0: /** michael@0: * Un flatten shared data from a UDATA.. michael@0: */ michael@0: static UConverterSharedData* michael@0: ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCode *status) michael@0: { michael@0: /* UDataInfo info; -- necessary only if some converters have different formatVersion */ michael@0: const uint8_t *raw = (const uint8_t *)udata_getMemory(pData); michael@0: const UConverterStaticData *source = (const UConverterStaticData *) raw; michael@0: UConverterSharedData *data; michael@0: UConverterType type = (UConverterType)source->conversionType; michael@0: michael@0: if(U_FAILURE(*status)) michael@0: return NULL; michael@0: michael@0: if( (uint16_t)type >= UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES || michael@0: converterData[type] == NULL || michael@0: converterData[type]->referenceCounter != 1 || michael@0: source->structSize != sizeof(UConverterStaticData)) michael@0: { michael@0: *status = U_INVALID_TABLE_FORMAT; michael@0: return NULL; michael@0: } michael@0: michael@0: data = (UConverterSharedData *)uprv_malloc(sizeof(UConverterSharedData)); michael@0: if(data == NULL) { michael@0: *status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: /* copy initial values from the static structure for this type */ michael@0: uprv_memcpy(data, converterData[type], sizeof(UConverterSharedData)); michael@0: michael@0: #if 0 /* made UConverterMBCSTable part of UConverterSharedData -- markus 20031107 */ michael@0: /* michael@0: * It would be much more efficient if the table were a direct member, not a pointer. michael@0: * However, that would add to the size of all UConverterSharedData objects michael@0: * even if they do not use this table (especially algorithmic ones). michael@0: * If this changes, then the static templates from converterData[type] michael@0: * need more entries. michael@0: * michael@0: * In principle, it would be cleaner if the load() function below michael@0: * allocated the table. michael@0: */ michael@0: data->table = (UConverterTable *)uprv_malloc(sizeof(UConverterTable)); michael@0: if(data->table == NULL) { michael@0: uprv_free(data); michael@0: *status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: uprv_memset(data->table, 0, sizeof(UConverterTable)); michael@0: #endif michael@0: michael@0: data->staticData = source; michael@0: michael@0: data->sharedDataCached = FALSE; michael@0: michael@0: /* fill in fields from the loaded data */ michael@0: data->dataMemory = (void*)pData; /* for future use */ michael@0: michael@0: if(data->impl->load != NULL) { michael@0: data->impl->load(data, pArgs, raw + source->structSize, status); michael@0: if(U_FAILURE(*status)) { michael@0: uprv_free(data->table); michael@0: uprv_free(data); michael@0: return NULL; michael@0: } michael@0: } michael@0: return data; michael@0: } michael@0: michael@0: /*Takes an alias name gets an actual converter file name michael@0: *goes to disk and opens it. michael@0: *allocates the memory and returns a new UConverter object michael@0: */ michael@0: static UConverterSharedData *createConverterFromFile(UConverterLoadArgs *pArgs, UErrorCode * err) michael@0: { michael@0: UDataMemory *data; michael@0: UConverterSharedData *sharedData; michael@0: michael@0: UTRACE_ENTRY_OC(UTRACE_UCNV_LOAD); michael@0: michael@0: if (U_FAILURE (*err)) { michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: michael@0: UTRACE_DATA2(UTRACE_OPEN_CLOSE, "load converter %s from package %s", pArgs->name, pArgs->pkg); michael@0: michael@0: data = udata_openChoice(pArgs->pkg, DATA_TYPE, pArgs->name, isCnvAcceptable, NULL, err); michael@0: if(U_FAILURE(*err)) michael@0: { michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: michael@0: sharedData = ucnv_data_unFlattenClone(pArgs, data, err); michael@0: if(U_FAILURE(*err)) michael@0: { michael@0: udata_close(data); michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: michael@0: /* michael@0: * TODO Store pkg in a field in the shared data so that delta-only converters michael@0: * can load base converters from the same package. michael@0: * If the pkg name is longer than the field, then either do not load the converter michael@0: * in the first place, or just set the pkg field to "". michael@0: */ michael@0: michael@0: UTRACE_EXIT_PTR_STATUS(sharedData, *err); michael@0: return sharedData; michael@0: } michael@0: michael@0: /*returns a converter type from a string michael@0: */ michael@0: static const UConverterSharedData * michael@0: getAlgorithmicTypeFromName(const char *realName) michael@0: { michael@0: uint32_t mid, start, limit; michael@0: uint32_t lastMid; michael@0: int result; michael@0: char strippedName[UCNV_MAX_CONVERTER_NAME_LENGTH]; michael@0: michael@0: /* Lower case and remove ignoreable characters. */ michael@0: ucnv_io_stripForCompare(strippedName, realName); michael@0: michael@0: /* do a binary search for the alias */ michael@0: start = 0; michael@0: limit = sizeof(cnvNameType)/sizeof(cnvNameType[0]); michael@0: mid = limit; michael@0: lastMid = UINT32_MAX; michael@0: michael@0: for (;;) { michael@0: mid = (uint32_t)((start + limit) / 2); michael@0: if (lastMid == mid) { /* Have we moved? */ michael@0: break; /* We haven't moved, and it wasn't found. */ michael@0: } michael@0: lastMid = mid; michael@0: result = uprv_strcmp(strippedName, cnvNameType[mid].name); michael@0: michael@0: if (result < 0) { michael@0: limit = mid; michael@0: } else if (result > 0) { michael@0: start = mid; michael@0: } else { michael@0: return converterData[cnvNameType[mid].type]; michael@0: } michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: /* michael@0: * Based on the number of known converters, this determines how many times larger michael@0: * the shared data hash table should be. When on small platforms, or just a couple michael@0: * of converters are used, this number should be 2. When memory is plentiful, or michael@0: * when ucnv_countAvailable is ever used with a lot of available converters, michael@0: * this should be 4. michael@0: * Larger numbers reduce the number of hash collisions, but use more memory. michael@0: */ michael@0: #define UCNV_CACHE_LOAD_FACTOR 2 michael@0: michael@0: /* Puts the shared data in the static hashtable SHARED_DATA_HASHTABLE */ michael@0: /* Will always be called with the cnvCacheMutex alrady being held */ michael@0: /* by the calling function. */ michael@0: /* Stores the shared data in the SHARED_DATA_HASHTABLE michael@0: * @param data The shared data michael@0: */ michael@0: static void michael@0: ucnv_shareConverterData(UConverterSharedData * data) michael@0: { michael@0: UErrorCode err = U_ZERO_ERROR; michael@0: /*Lazy evaluates the Hashtable itself */ michael@0: /*void *sanity = NULL;*/ michael@0: michael@0: if (SHARED_DATA_HASHTABLE == NULL) michael@0: { michael@0: SHARED_DATA_HASHTABLE = uhash_openSize(uhash_hashChars, uhash_compareChars, NULL, michael@0: ucnv_io_countKnownConverters(&err)*UCNV_CACHE_LOAD_FACTOR, michael@0: &err); michael@0: ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); michael@0: michael@0: if (U_FAILURE(err)) michael@0: return; michael@0: } michael@0: michael@0: /* ### check to see if the element is not already there! */ michael@0: michael@0: /* michael@0: sanity = ucnv_getSharedConverterData (data->staticData->name); michael@0: if(sanity != NULL) michael@0: { michael@0: UCNV_DEBUG_LOG("put:overwrite!",data->staticData->name,sanity); michael@0: } michael@0: UCNV_DEBUG_LOG("put:chk",data->staticData->name,sanity); michael@0: */ michael@0: michael@0: /* Mark it shared */ michael@0: data->sharedDataCached = TRUE; michael@0: michael@0: uhash_put(SHARED_DATA_HASHTABLE, michael@0: (void*) data->staticData->name, /* Okay to cast away const as long as michael@0: keyDeleter == NULL */ michael@0: data, michael@0: &err); michael@0: UCNV_DEBUG_LOG("put", data->staticData->name,data); michael@0: michael@0: } michael@0: michael@0: /* Look up a converter name in the shared data cache. */ michael@0: /* cnvCacheMutex must be held by the caller to protect the hash table. */ michael@0: /* gets the shared data from the SHARED_DATA_HASHTABLE (might return NULL if it isn't there) michael@0: * @param name The name of the shared data michael@0: * @return the shared data from the SHARED_DATA_HASHTABLE michael@0: */ michael@0: static UConverterSharedData * michael@0: ucnv_getSharedConverterData(const char *name) michael@0: { michael@0: /*special case when no Table has yet been created we return NULL */ michael@0: if (SHARED_DATA_HASHTABLE == NULL) michael@0: { michael@0: return NULL; michael@0: } michael@0: else michael@0: { michael@0: UConverterSharedData *rc; michael@0: michael@0: rc = (UConverterSharedData*)uhash_get(SHARED_DATA_HASHTABLE, name); michael@0: UCNV_DEBUG_LOG("get",name,rc); michael@0: return rc; michael@0: } michael@0: } michael@0: michael@0: /*frees the string of memory blocks associates with a sharedConverter michael@0: *if and only if the referenceCounter == 0 michael@0: */ michael@0: /* Deletes (frees) the Shared data it's passed. first it checks the referenceCounter to michael@0: * see if anyone is using it, if not it frees all the memory stemming from sharedConverterData and michael@0: * returns TRUE, michael@0: * otherwise returns FALSE michael@0: * @param sharedConverterData The shared data michael@0: * @return if not it frees all the memory stemming from sharedConverterData and michael@0: * returns TRUE, otherwise returns FALSE michael@0: */ michael@0: static UBool michael@0: ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData) michael@0: { michael@0: UTRACE_ENTRY_OC(UTRACE_UCNV_UNLOAD); michael@0: UTRACE_DATA2(UTRACE_OPEN_CLOSE, "unload converter %s shared data %p", deadSharedData->staticData->name, deadSharedData); michael@0: michael@0: if (deadSharedData->referenceCounter > 0) { michael@0: UTRACE_EXIT_VALUE((int32_t)FALSE); michael@0: return FALSE; michael@0: } michael@0: michael@0: if (deadSharedData->impl->unload != NULL) { michael@0: deadSharedData->impl->unload(deadSharedData); michael@0: } michael@0: michael@0: if(deadSharedData->dataMemory != NULL) michael@0: { michael@0: UDataMemory *data = (UDataMemory*)deadSharedData->dataMemory; michael@0: udata_close(data); michael@0: } michael@0: michael@0: if(deadSharedData->table != NULL) michael@0: { michael@0: uprv_free(deadSharedData->table); michael@0: } michael@0: michael@0: #if 0 michael@0: /* if the static data is actually owned by the shared data */ michael@0: /* enable if we ever have this situation. */ michael@0: if(deadSharedData->staticDataOwned == TRUE) /* see ucnv_bld.h */ michael@0: { michael@0: uprv_free((void*)deadSharedData->staticData); michael@0: } michael@0: #endif michael@0: michael@0: #if 0 michael@0: /* Zap it ! */ michael@0: uprv_memset(deadSharedData->0, sizeof(*deadSharedData)); michael@0: #endif michael@0: michael@0: uprv_free(deadSharedData); michael@0: michael@0: UTRACE_EXIT_VALUE((int32_t)TRUE); michael@0: return TRUE; michael@0: } michael@0: michael@0: /** michael@0: * Load a non-algorithmic converter. michael@0: * If pkg==NULL, then this function must be called inside umtx_lock(&cnvCacheMutex). michael@0: */ michael@0: UConverterSharedData * michael@0: ucnv_load(UConverterLoadArgs *pArgs, UErrorCode *err) { michael@0: UConverterSharedData *mySharedConverterData; michael@0: michael@0: if(err == NULL || U_FAILURE(*err)) { michael@0: return NULL; michael@0: } michael@0: michael@0: if(pArgs->pkg != NULL && *pArgs->pkg != 0) { michael@0: /* application-provided converters are not currently cached */ michael@0: return createConverterFromFile(pArgs, err); michael@0: } michael@0: michael@0: mySharedConverterData = ucnv_getSharedConverterData(pArgs->name); michael@0: if (mySharedConverterData == NULL) michael@0: { michael@0: /*Not cached, we need to stream it in from file */ michael@0: mySharedConverterData = createConverterFromFile(pArgs, err); michael@0: if (U_FAILURE (*err) || (mySharedConverterData == NULL)) michael@0: { michael@0: return NULL; michael@0: } michael@0: else if (!pArgs->onlyTestIsLoadable) michael@0: { michael@0: /* share it with other library clients */ michael@0: ucnv_shareConverterData(mySharedConverterData); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: /* The data for this converter was already in the cache. */ michael@0: /* Update the reference counter on the shared data: one more client */ michael@0: mySharedConverterData->referenceCounter++; michael@0: } michael@0: michael@0: return mySharedConverterData; michael@0: } michael@0: michael@0: /** michael@0: * Unload a non-algorithmic converter. michael@0: * It must be sharedData->referenceCounter != ~0 michael@0: * and this function must be called inside umtx_lock(&cnvCacheMutex). michael@0: */ michael@0: U_CAPI void michael@0: ucnv_unload(UConverterSharedData *sharedData) { michael@0: if(sharedData != NULL) { michael@0: if (sharedData->referenceCounter > 0) { michael@0: sharedData->referenceCounter--; michael@0: } michael@0: michael@0: if((sharedData->referenceCounter <= 0)&&(sharedData->sharedDataCached == FALSE)) { michael@0: ucnv_deleteSharedConverterData(sharedData); michael@0: } michael@0: } michael@0: } michael@0: michael@0: U_CFUNC void michael@0: ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData) michael@0: { michael@0: /* michael@0: Checking whether it's an algorithic converter is okay michael@0: in multithreaded applications because the value never changes. michael@0: Don't check referenceCounter for any other value. michael@0: */ michael@0: if(sharedData != NULL && sharedData->referenceCounter != (uint32_t)~0) { michael@0: umtx_lock(&cnvCacheMutex); michael@0: ucnv_unload(sharedData); michael@0: umtx_unlock(&cnvCacheMutex); michael@0: } michael@0: } michael@0: michael@0: U_CFUNC void michael@0: ucnv_incrementRefCount(UConverterSharedData *sharedData) michael@0: { michael@0: /* michael@0: Checking whether it's an algorithic converter is okay michael@0: in multithreaded applications because the value never changes. michael@0: Don't check referenceCounter for any other value. michael@0: */ michael@0: if(sharedData != NULL && sharedData->referenceCounter != (uint32_t)~0) { michael@0: umtx_lock(&cnvCacheMutex); michael@0: sharedData->referenceCounter++; michael@0: umtx_unlock(&cnvCacheMutex); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * *pPieces must be initialized. michael@0: * The name without options will be copied to pPieces->cnvName. michael@0: * The locale and options will be copied to pPieces only if present in inName, michael@0: * otherwise the existing values in pPieces remain. michael@0: * *pArgs will be set to the pPieces values. michael@0: */ michael@0: static void michael@0: parseConverterOptions(const char *inName, michael@0: UConverterNamePieces *pPieces, michael@0: UConverterLoadArgs *pArgs, michael@0: UErrorCode *err) michael@0: { michael@0: char *cnvName = pPieces->cnvName; michael@0: char c; michael@0: int32_t len = 0; michael@0: michael@0: pArgs->name=inName; michael@0: pArgs->locale=pPieces->locale; michael@0: pArgs->options=pPieces->options; michael@0: michael@0: /* copy the converter name itself to cnvName */ michael@0: while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { michael@0: if (++len>=UCNV_MAX_CONVERTER_NAME_LENGTH) { michael@0: *err = U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ michael@0: pPieces->cnvName[0]=0; michael@0: return; michael@0: } michael@0: *cnvName++=c; michael@0: inName++; michael@0: } michael@0: *cnvName=0; michael@0: pArgs->name=pPieces->cnvName; michael@0: michael@0: /* parse options. No more name copying should occur. */ michael@0: while((c=*inName)!=0) { michael@0: if(c==UCNV_OPTION_SEP_CHAR) { michael@0: ++inName; michael@0: } michael@0: michael@0: /* inName is behind an option separator */ michael@0: if(uprv_strncmp(inName, "locale=", 7)==0) { michael@0: /* do not modify locale itself in case we have multiple locale options */ michael@0: char *dest=pPieces->locale; michael@0: michael@0: /* copy the locale option value */ michael@0: inName+=7; michael@0: len=0; michael@0: while((c=*inName)!=0 && c!=UCNV_OPTION_SEP_CHAR) { michael@0: ++inName; michael@0: michael@0: if(++len>=ULOC_FULLNAME_CAPACITY) { michael@0: *err=U_ILLEGAL_ARGUMENT_ERROR; /* bad name */ michael@0: pPieces->locale[0]=0; michael@0: return; michael@0: } michael@0: michael@0: *dest++=c; michael@0: } michael@0: *dest=0; michael@0: } else if(uprv_strncmp(inName, "version=", 8)==0) { michael@0: /* copy the version option value into bits 3..0 of pPieces->options */ michael@0: inName+=8; michael@0: c=*inName; michael@0: if(c==0) { michael@0: pArgs->options=(pPieces->options&=~UCNV_OPTION_VERSION); michael@0: return; michael@0: } else if((uint8_t)(c-'0')<10) { michael@0: pArgs->options=pPieces->options=(pPieces->options&~UCNV_OPTION_VERSION)|(uint32_t)(c-'0'); michael@0: ++inName; michael@0: } michael@0: } else if(uprv_strncmp(inName, "swaplfnl", 8)==0) { michael@0: inName+=8; michael@0: pArgs->options=(pPieces->options|=UCNV_OPTION_SWAP_LFNL); michael@0: /* add processing for new options here with another } else if(uprv_strncmp(inName, "option-name=", XX)==0) { */ michael@0: } else { michael@0: /* ignore any other options until we define some */ michael@0: while(((c = *inName++) != 0) && (c != UCNV_OPTION_SEP_CHAR)) { michael@0: } michael@0: if(c==0) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /*Logic determines if the converter is Algorithmic AND/OR cached michael@0: *depending on that: michael@0: * -we either go to get data from disk and cache it (Data=TRUE, Cached=False) michael@0: * -Get it from a Hashtable (Data=X, Cached=TRUE) michael@0: * -Call dataConverter initializer (Data=TRUE, Cached=TRUE) michael@0: * -Call AlgorithmicConverter initializer (Data=FALSE, Cached=TRUE) michael@0: */ michael@0: U_CFUNC UConverterSharedData * michael@0: ucnv_loadSharedData(const char *converterName, michael@0: UConverterNamePieces *pPieces, michael@0: UConverterLoadArgs *pArgs, michael@0: UErrorCode * err) { michael@0: UConverterNamePieces stackPieces; michael@0: UConverterLoadArgs stackArgs; michael@0: UConverterSharedData *mySharedConverterData = NULL; michael@0: UErrorCode internalErrorCode = U_ZERO_ERROR; michael@0: UBool mayContainOption = TRUE; michael@0: UBool checkForAlgorithmic = TRUE; michael@0: michael@0: if (U_FAILURE (*err)) { michael@0: return NULL; michael@0: } michael@0: michael@0: if(pPieces == NULL) { michael@0: if(pArgs != NULL) { michael@0: /* michael@0: * Bad: We may set pArgs pointers to stackPieces fields michael@0: * which will be invalid after this function returns. michael@0: */ michael@0: *err = U_INTERNAL_PROGRAM_ERROR; michael@0: return NULL; michael@0: } michael@0: pPieces = &stackPieces; michael@0: } michael@0: if(pArgs == NULL) { michael@0: uprv_memset(&stackArgs, 0, sizeof(stackArgs)); michael@0: stackArgs.size = (int32_t)sizeof(stackArgs); michael@0: pArgs = &stackArgs; michael@0: } michael@0: michael@0: pPieces->cnvName[0] = 0; michael@0: pPieces->locale[0] = 0; michael@0: pPieces->options = 0; michael@0: michael@0: pArgs->name = converterName; michael@0: pArgs->locale = pPieces->locale; michael@0: pArgs->options = pPieces->options; michael@0: michael@0: /* In case "name" is NULL we want to open the default converter. */ michael@0: if (converterName == NULL) { michael@0: #if U_CHARSET_IS_UTF8 michael@0: pArgs->name = "UTF-8"; michael@0: return (UConverterSharedData *)converterData[UCNV_UTF8]; michael@0: #else michael@0: /* Call ucnv_getDefaultName first to query the name from the OS. */ michael@0: pArgs->name = ucnv_getDefaultName(); michael@0: if (pArgs->name == NULL) { michael@0: *err = U_MISSING_RESOURCE_ERROR; michael@0: return NULL; michael@0: } michael@0: mySharedConverterData = (UConverterSharedData *)gDefaultAlgorithmicSharedData; michael@0: checkForAlgorithmic = FALSE; michael@0: mayContainOption = gDefaultConverterContainsOption; michael@0: /* the default converter name is already canonical */ michael@0: #endif michael@0: } michael@0: else if(UCNV_FAST_IS_UTF8(converterName)) { michael@0: /* fastpath for UTF-8 */ michael@0: pArgs->name = "UTF-8"; michael@0: return (UConverterSharedData *)converterData[UCNV_UTF8]; michael@0: } michael@0: else { michael@0: /* separate the converter name from the options */ michael@0: parseConverterOptions(converterName, pPieces, pArgs, err); michael@0: if (U_FAILURE(*err)) { michael@0: /* Very bad name used. */ michael@0: return NULL; michael@0: } michael@0: michael@0: /* get the canonical converter name */ michael@0: pArgs->name = ucnv_io_getConverterName(pArgs->name, &mayContainOption, &internalErrorCode); michael@0: if (U_FAILURE(internalErrorCode) || pArgs->name == NULL) { michael@0: /* michael@0: * set the input name in case the converter was added michael@0: * without updating the alias table, or when there is no alias table michael@0: */ michael@0: pArgs->name = pPieces->cnvName; michael@0: } else if (internalErrorCode == U_AMBIGUOUS_ALIAS_WARNING) { michael@0: *err = U_AMBIGUOUS_ALIAS_WARNING; michael@0: } michael@0: } michael@0: michael@0: /* separate the converter name from the options */ michael@0: if(mayContainOption && pArgs->name != pPieces->cnvName) { michael@0: parseConverterOptions(pArgs->name, pPieces, pArgs, err); michael@0: } michael@0: michael@0: /* get the shared data for an algorithmic converter, if it is one */ michael@0: if (checkForAlgorithmic) { michael@0: mySharedConverterData = (UConverterSharedData *)getAlgorithmicTypeFromName(pArgs->name); michael@0: } michael@0: if (mySharedConverterData == NULL) michael@0: { michael@0: /* it is a data-based converter, get its shared data. */ michael@0: /* Hold the cnvCacheMutex through the whole process of checking the */ michael@0: /* converter data cache, and adding new entries to the cache */ michael@0: /* to prevent other threads from modifying the cache during the */ michael@0: /* process. */ michael@0: pArgs->nestedLoads=1; michael@0: pArgs->pkg=NULL; michael@0: michael@0: umtx_lock(&cnvCacheMutex); michael@0: mySharedConverterData = ucnv_load(pArgs, err); michael@0: umtx_unlock(&cnvCacheMutex); michael@0: if (U_FAILURE (*err) || (mySharedConverterData == NULL)) michael@0: { michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: return mySharedConverterData; michael@0: } michael@0: michael@0: U_CAPI UConverter * michael@0: ucnv_createConverter(UConverter *myUConverter, const char *converterName, UErrorCode * err) michael@0: { michael@0: UConverterNamePieces stackPieces; michael@0: UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; michael@0: UConverterSharedData *mySharedConverterData; michael@0: michael@0: UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); michael@0: michael@0: if(U_SUCCESS(*err)) { michael@0: UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open converter %s", converterName); michael@0: michael@0: mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); michael@0: michael@0: myUConverter = ucnv_createConverterFromSharedData( michael@0: myUConverter, mySharedConverterData, michael@0: &stackArgs, michael@0: err); michael@0: michael@0: if(U_SUCCESS(*err)) { michael@0: UTRACE_EXIT_PTR_STATUS(myUConverter, *err); michael@0: return myUConverter; michael@0: } michael@0: } michael@0: michael@0: /* exit with error */ michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: michael@0: U_CFUNC UBool michael@0: ucnv_canCreateConverter(const char *converterName, UErrorCode *err) { michael@0: UConverter myUConverter; michael@0: UConverterNamePieces stackPieces; michael@0: UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; michael@0: UConverterSharedData *mySharedConverterData; michael@0: michael@0: UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN); michael@0: michael@0: if(U_SUCCESS(*err)) { michael@0: UTRACE_DATA1(UTRACE_OPEN_CLOSE, "test if can open converter %s", converterName); michael@0: michael@0: stackArgs.onlyTestIsLoadable=TRUE; michael@0: mySharedConverterData = ucnv_loadSharedData(converterName, &stackPieces, &stackArgs, err); michael@0: ucnv_createConverterFromSharedData( michael@0: &myUConverter, mySharedConverterData, michael@0: &stackArgs, michael@0: err); michael@0: ucnv_unloadSharedDataIfReady(mySharedConverterData); michael@0: } michael@0: michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return U_SUCCESS(*err); michael@0: } michael@0: michael@0: UConverter * michael@0: ucnv_createAlgorithmicConverter(UConverter *myUConverter, michael@0: UConverterType type, michael@0: const char *locale, uint32_t options, michael@0: UErrorCode *err) { michael@0: UConverter *cnv; michael@0: const UConverterSharedData *sharedData; michael@0: UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; michael@0: michael@0: UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_ALGORITHMIC); michael@0: UTRACE_DATA1(UTRACE_OPEN_CLOSE, "open algorithmic converter type %d", (int32_t)type); michael@0: michael@0: if(type<0 || UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES<=type) { michael@0: *err = U_ILLEGAL_ARGUMENT_ERROR; michael@0: UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); michael@0: return NULL; michael@0: } michael@0: michael@0: sharedData = converterData[type]; michael@0: /* michael@0: Checking whether it's an algorithic converter is okay michael@0: in multithreaded applications because the value never changes. michael@0: Don't check referenceCounter for any other value. michael@0: */ michael@0: if(sharedData == NULL || sharedData->referenceCounter != (uint32_t)~0) { michael@0: /* not a valid type, or not an algorithmic converter */ michael@0: *err = U_ILLEGAL_ARGUMENT_ERROR; michael@0: UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR); michael@0: return NULL; michael@0: } michael@0: michael@0: stackArgs.name = ""; michael@0: stackArgs.options = options; michael@0: stackArgs.locale=locale; michael@0: cnv = ucnv_createConverterFromSharedData( michael@0: myUConverter, (UConverterSharedData *)sharedData, michael@0: &stackArgs, err); michael@0: michael@0: UTRACE_EXIT_PTR_STATUS(cnv, *err); michael@0: return cnv; michael@0: } michael@0: michael@0: U_CFUNC UConverter* michael@0: ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode * err) michael@0: { michael@0: UConverter *myUConverter; michael@0: UConverterSharedData *mySharedConverterData; michael@0: UConverterNamePieces stackPieces; michael@0: UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; michael@0: michael@0: UTRACE_ENTRY_OC(UTRACE_UCNV_OPEN_PACKAGE); michael@0: michael@0: if(U_FAILURE(*err)) { michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: michael@0: UTRACE_DATA2(UTRACE_OPEN_CLOSE, "open converter %s from package %s", converterName, packageName); michael@0: michael@0: /* first, get the options out of the converterName string */ michael@0: stackPieces.cnvName[0] = 0; michael@0: stackPieces.locale[0] = 0; michael@0: stackPieces.options = 0; michael@0: parseConverterOptions(converterName, &stackPieces, &stackArgs, err); michael@0: if (U_FAILURE(*err)) { michael@0: /* Very bad name used. */ michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: stackArgs.nestedLoads=1; michael@0: stackArgs.pkg=packageName; michael@0: michael@0: /* open the data, unflatten the shared structure */ michael@0: mySharedConverterData = createConverterFromFile(&stackArgs, err); michael@0: michael@0: if (U_FAILURE(*err)) { michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: michael@0: /* create the actual converter */ michael@0: myUConverter = ucnv_createConverterFromSharedData(NULL, mySharedConverterData, &stackArgs, err); michael@0: michael@0: if (U_FAILURE(*err)) { michael@0: ucnv_close(myUConverter); michael@0: UTRACE_EXIT_STATUS(*err); michael@0: return NULL; michael@0: } michael@0: michael@0: UTRACE_EXIT_PTR_STATUS(myUConverter, *err); michael@0: return myUConverter; michael@0: } michael@0: michael@0: michael@0: U_CFUNC UConverter* michael@0: ucnv_createConverterFromSharedData(UConverter *myUConverter, michael@0: UConverterSharedData *mySharedConverterData, michael@0: UConverterLoadArgs *pArgs, michael@0: UErrorCode *err) michael@0: { michael@0: UBool isCopyLocal; michael@0: michael@0: if(U_FAILURE(*err)) { michael@0: ucnv_unloadSharedDataIfReady(mySharedConverterData); michael@0: return myUConverter; michael@0: } michael@0: if(myUConverter == NULL) michael@0: { michael@0: myUConverter = (UConverter *) uprv_malloc (sizeof (UConverter)); michael@0: if(myUConverter == NULL) michael@0: { michael@0: *err = U_MEMORY_ALLOCATION_ERROR; michael@0: ucnv_unloadSharedDataIfReady(mySharedConverterData); michael@0: return NULL; michael@0: } michael@0: isCopyLocal = FALSE; michael@0: } else { michael@0: isCopyLocal = TRUE; michael@0: } michael@0: michael@0: /* initialize the converter */ michael@0: uprv_memset(myUConverter, 0, sizeof(UConverter)); michael@0: myUConverter->isCopyLocal = isCopyLocal; michael@0: /*myUConverter->isExtraLocal = FALSE;*/ /* Set by the memset call */ michael@0: myUConverter->sharedData = mySharedConverterData; michael@0: myUConverter->options = pArgs->options; michael@0: if(!pArgs->onlyTestIsLoadable) { michael@0: myUConverter->preFromUFirstCP = U_SENTINEL; michael@0: myUConverter->fromCharErrorBehaviour = UCNV_TO_U_DEFAULT_CALLBACK; michael@0: myUConverter->fromUCharErrorBehaviour = UCNV_FROM_U_DEFAULT_CALLBACK; michael@0: myUConverter->toUnicodeStatus = mySharedConverterData->toUnicodeStatus; michael@0: myUConverter->maxBytesPerUChar = mySharedConverterData->staticData->maxBytesPerChar; michael@0: myUConverter->subChar1 = mySharedConverterData->staticData->subChar1; michael@0: myUConverter->subCharLen = mySharedConverterData->staticData->subCharLen; michael@0: myUConverter->subChars = (uint8_t *)myUConverter->subUChars; michael@0: uprv_memcpy(myUConverter->subChars, mySharedConverterData->staticData->subChar, myUConverter->subCharLen); michael@0: myUConverter->toUCallbackReason = UCNV_ILLEGAL; /* default reason to invoke (*fromCharErrorBehaviour) */ michael@0: } michael@0: michael@0: if(mySharedConverterData->impl->open != NULL) { michael@0: mySharedConverterData->impl->open(myUConverter, pArgs, err); michael@0: if(U_FAILURE(*err) && !pArgs->onlyTestIsLoadable) { michael@0: /* don't ucnv_close() if onlyTestIsLoadable because not fully initialized */ michael@0: ucnv_close(myUConverter); michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: return myUConverter; michael@0: } michael@0: michael@0: /*Frees all shared immutable objects that aren't referred to (reference count = 0) michael@0: */ michael@0: U_CAPI int32_t U_EXPORT2 michael@0: ucnv_flushCache () michael@0: { michael@0: UConverterSharedData *mySharedData = NULL; michael@0: int32_t pos; michael@0: int32_t tableDeletedNum = 0; michael@0: const UHashElement *e; michael@0: /*UErrorCode status = U_ILLEGAL_ARGUMENT_ERROR;*/ michael@0: int32_t i, remaining; michael@0: michael@0: UTRACE_ENTRY_OC(UTRACE_UCNV_FLUSH_CACHE); michael@0: michael@0: /* Close the default converter without creating a new one so that everything will be flushed. */ michael@0: u_flushDefaultConverter(); michael@0: michael@0: /*if shared data hasn't even been lazy evaluated yet michael@0: * return 0 michael@0: */ michael@0: if (SHARED_DATA_HASHTABLE == NULL) { michael@0: UTRACE_EXIT_VALUE((int32_t)0); michael@0: return 0; michael@0: } michael@0: michael@0: /*creates an enumeration to iterate through every element in the michael@0: * table michael@0: * michael@0: * Synchronization: holding cnvCacheMutex will prevent any other thread from michael@0: * accessing or modifying the hash table during the iteration. michael@0: * The reference count of an entry may be decremented by michael@0: * ucnv_close while the iteration is in process, but this is michael@0: * benign. It can't be incremented (in ucnv_createConverter()) michael@0: * because the sequence of looking up in the cache + incrementing michael@0: * is protected by cnvCacheMutex. michael@0: */ michael@0: umtx_lock(&cnvCacheMutex); michael@0: /* michael@0: * double loop: A delta/extension-only converter has a pointer to its base table's michael@0: * shared data; the first iteration of the outer loop may see the delta converter michael@0: * before the base converter, and unloading the delta converter may get the base michael@0: * converter's reference counter down to 0. michael@0: */ michael@0: i = 0; michael@0: do { michael@0: remaining = 0; michael@0: pos = -1; michael@0: while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL) michael@0: { michael@0: mySharedData = (UConverterSharedData *) e->value.pointer; michael@0: /*deletes only if reference counter == 0 */ michael@0: if (mySharedData->referenceCounter == 0) michael@0: { michael@0: tableDeletedNum++; michael@0: michael@0: UCNV_DEBUG_LOG("del",mySharedData->staticData->name,mySharedData); michael@0: michael@0: uhash_removeElement(SHARED_DATA_HASHTABLE, e); michael@0: mySharedData->sharedDataCached = FALSE; michael@0: ucnv_deleteSharedConverterData (mySharedData); michael@0: } else { michael@0: ++remaining; michael@0: } michael@0: } michael@0: } while(++i == 1 && remaining > 0); michael@0: umtx_unlock(&cnvCacheMutex); michael@0: michael@0: UTRACE_DATA1(UTRACE_INFO, "ucnv_flushCache() exits with %d converters remaining", remaining); michael@0: michael@0: UTRACE_EXIT_VALUE(tableDeletedNum); michael@0: return tableDeletedNum; michael@0: } michael@0: michael@0: /* available converters list --------------------------------------------------- */ michael@0: michael@0: static void U_CALLCONV initAvailableConvertersList(UErrorCode &errCode) { michael@0: U_ASSERT(gAvailableConverterCount == 0); michael@0: U_ASSERT(gAvailableConverters == NULL); michael@0: michael@0: ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); michael@0: UEnumeration *allConvEnum = ucnv_openAllNames(&errCode); michael@0: int32_t allConverterCount = uenum_count(allConvEnum, &errCode); michael@0: if (U_FAILURE(errCode)) { michael@0: return; michael@0: } michael@0: michael@0: /* We can't have more than "*converterTable" converters to open */ michael@0: gAvailableConverters = (const char **) uprv_malloc(allConverterCount * sizeof(char*)); michael@0: if (!gAvailableConverters) { michael@0: errCode = U_MEMORY_ALLOCATION_ERROR; michael@0: return; michael@0: } michael@0: michael@0: /* Open the default converter to make sure that it has first dibs in the hash table. */ michael@0: UErrorCode localStatus = U_ZERO_ERROR; michael@0: UConverter tempConverter; michael@0: ucnv_close(ucnv_createConverter(&tempConverter, NULL, &localStatus)); michael@0: michael@0: gAvailableConverterCount = 0; michael@0: michael@0: for (int32_t idx = 0; idx < allConverterCount; idx++) { michael@0: localStatus = U_ZERO_ERROR; michael@0: const char *converterName = uenum_next(allConvEnum, NULL, &localStatus); michael@0: if (ucnv_canCreateConverter(converterName, &localStatus)) { michael@0: gAvailableConverters[gAvailableConverterCount++] = converterName; michael@0: } michael@0: } michael@0: michael@0: uenum_close(allConvEnum); michael@0: } michael@0: michael@0: michael@0: static UBool haveAvailableConverterList(UErrorCode *pErrorCode) { michael@0: umtx_initOnce(gAvailableConvertersInitOnce, &initAvailableConvertersList, *pErrorCode); michael@0: return U_SUCCESS(*pErrorCode); michael@0: } michael@0: michael@0: U_CFUNC uint16_t michael@0: ucnv_bld_countAvailableConverters(UErrorCode *pErrorCode) { michael@0: if (haveAvailableConverterList(pErrorCode)) { michael@0: return gAvailableConverterCount; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: U_CFUNC const char * michael@0: ucnv_bld_getAvailableConverter(uint16_t n, UErrorCode *pErrorCode) { michael@0: if (haveAvailableConverterList(pErrorCode)) { michael@0: if (n < gAvailableConverterCount) { michael@0: return gAvailableConverters[n]; michael@0: } michael@0: *pErrorCode = U_INDEX_OUTOFBOUNDS_ERROR; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: /* default converter name --------------------------------------------------- */ michael@0: michael@0: #if !U_CHARSET_IS_UTF8 michael@0: /* michael@0: Copy the canonical converter name. michael@0: ucnv_getDefaultName must be thread safe, which can call this function. michael@0: michael@0: ucnv_setDefaultName calls this function and it doesn't have to be michael@0: thread safe because there is no reliable/safe way to reset the michael@0: converter in use in all threads. If you did reset the converter, you michael@0: would not be sure that retrieving a default converter for one string michael@0: would be the same type of default converter for a successive string. michael@0: Since the name is a returned via ucnv_getDefaultName without copying, michael@0: you shouldn't be modifying or deleting the string from a separate thread. michael@0: */ michael@0: static inline void michael@0: internalSetName(const char *name, UErrorCode *status) { michael@0: UConverterNamePieces stackPieces; michael@0: UConverterLoadArgs stackArgs=UCNV_LOAD_ARGS_INITIALIZER; michael@0: int32_t length=(int32_t)(uprv_strlen(name)); michael@0: UBool containsOption = (UBool)(uprv_strchr(name, UCNV_OPTION_SEP_CHAR) != NULL); michael@0: const UConverterSharedData *algorithmicSharedData; michael@0: michael@0: stackArgs.name = name; michael@0: if(containsOption) { michael@0: stackPieces.cnvName[0] = 0; michael@0: stackPieces.locale[0] = 0; michael@0: stackPieces.options = 0; michael@0: parseConverterOptions(name, &stackPieces, &stackArgs, status); michael@0: if(U_FAILURE(*status)) { michael@0: return; michael@0: } michael@0: } michael@0: algorithmicSharedData = getAlgorithmicTypeFromName(stackArgs.name); michael@0: michael@0: umtx_lock(&cnvCacheMutex); michael@0: michael@0: gDefaultAlgorithmicSharedData = algorithmicSharedData; michael@0: gDefaultConverterContainsOption = containsOption; michael@0: uprv_memcpy(gDefaultConverterNameBuffer, name, length); michael@0: gDefaultConverterNameBuffer[length]=0; michael@0: michael@0: /* gDefaultConverterName MUST be the last global var set by this function. */ michael@0: /* It is the variable checked in ucnv_getDefaultName() to see if initialization is required. */ michael@0: // But there is nothing here preventing that from being reordered, either by the compiler michael@0: // or hardware. I'm adding the mutex to ucnv_getDefaultName for now. UMTX_CHECK is not enough. michael@0: // -- Andy michael@0: gDefaultConverterName = gDefaultConverterNameBuffer; michael@0: michael@0: ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup); michael@0: michael@0: umtx_unlock(&cnvCacheMutex); michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * In order to be really thread-safe, the get function would have to take michael@0: * a buffer parameter and copy the current string inside a mutex block. michael@0: * This implementation only tries to be really thread-safe while michael@0: * setting the name. michael@0: * It assumes that setting a pointer is atomic. michael@0: */ michael@0: michael@0: U_CAPI const char* U_EXPORT2 michael@0: ucnv_getDefaultName() { michael@0: #if U_CHARSET_IS_UTF8 michael@0: return "UTF-8"; michael@0: #else michael@0: /* local variable to be thread-safe */ michael@0: const char *name; michael@0: michael@0: /* michael@0: Concurrent calls to ucnv_getDefaultName must be thread safe, michael@0: but ucnv_setDefaultName is not thread safe. michael@0: */ michael@0: { michael@0: icu::Mutex lock(&cnvCacheMutex); michael@0: name = gDefaultConverterName; michael@0: } michael@0: if(name==NULL) { michael@0: UErrorCode errorCode = U_ZERO_ERROR; michael@0: UConverter *cnv = NULL; michael@0: michael@0: name = uprv_getDefaultCodepage(); michael@0: michael@0: /* if the name is there, test it out and get the canonical name with options */ michael@0: if(name != NULL) { michael@0: cnv = ucnv_open(name, &errorCode); michael@0: if(U_SUCCESS(errorCode) && cnv != NULL) { michael@0: name = ucnv_getName(cnv, &errorCode); michael@0: } michael@0: } michael@0: michael@0: if(name == NULL || name[0] == 0 michael@0: || U_FAILURE(errorCode) || cnv == NULL michael@0: || uprv_strlen(name)>=sizeof(gDefaultConverterNameBuffer)) michael@0: { michael@0: /* Panic time, let's use a fallback. */ michael@0: #if (U_CHARSET_FAMILY == U_ASCII_FAMILY) michael@0: name = "US-ASCII"; michael@0: /* there is no 'algorithmic' converter for EBCDIC */ michael@0: #elif U_PLATFORM == U_PF_OS390 michael@0: name = "ibm-1047_P100-1995" UCNV_SWAP_LFNL_OPTION_STRING; michael@0: #else michael@0: name = "ibm-37_P100-1995"; michael@0: #endif michael@0: } michael@0: michael@0: internalSetName(name, &errorCode); michael@0: michael@0: /* The close may make the current name go away. */ michael@0: ucnv_close(cnv); michael@0: } michael@0: michael@0: return name; michael@0: #endif michael@0: } michael@0: michael@0: #if U_CHARSET_IS_UTF8 michael@0: U_CAPI void U_EXPORT2 ucnv_setDefaultName(const char *) {} michael@0: #else michael@0: /* michael@0: This function is not thread safe, and it can't be thread safe. michael@0: See internalSetName or the API reference for details. michael@0: */ michael@0: U_CAPI void U_EXPORT2 michael@0: ucnv_setDefaultName(const char *converterName) { michael@0: if(converterName==NULL) { michael@0: /* reset to the default codepage */ michael@0: gDefaultConverterName=NULL; michael@0: } else { michael@0: UErrorCode errorCode = U_ZERO_ERROR; michael@0: UConverter *cnv = NULL; michael@0: const char *name = NULL; michael@0: michael@0: /* if the name is there, test it out and get the canonical name with options */ michael@0: cnv = ucnv_open(converterName, &errorCode); michael@0: if(U_SUCCESS(errorCode) && cnv != NULL) { michael@0: name = ucnv_getName(cnv, &errorCode); michael@0: } michael@0: michael@0: if(U_SUCCESS(errorCode) && name!=NULL) { michael@0: internalSetName(name, &errorCode); michael@0: } michael@0: /* else this converter is bad to use. Don't change it to a bad value. */ michael@0: michael@0: /* The close may make the current name go away. */ michael@0: ucnv_close(cnv); michael@0: michael@0: /* reset the converter cache */ michael@0: u_flushDefaultConverter(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: /* data swapping ------------------------------------------------------------ */ michael@0: michael@0: /* most of this might belong more properly into ucnvmbcs.c, but that is so large */ michael@0: michael@0: #if !UCONFIG_NO_LEGACY_CONVERSION michael@0: michael@0: U_CAPI int32_t U_EXPORT2 michael@0: ucnv_swap(const UDataSwapper *ds, michael@0: const void *inData, int32_t length, void *outData, michael@0: UErrorCode *pErrorCode) { michael@0: const UDataInfo *pInfo; michael@0: int32_t headerSize; michael@0: michael@0: const uint8_t *inBytes; michael@0: uint8_t *outBytes; michael@0: michael@0: uint32_t offset, count, staticDataSize; michael@0: int32_t size; michael@0: michael@0: const UConverterStaticData *inStaticData; michael@0: UConverterStaticData *outStaticData; michael@0: michael@0: const _MBCSHeader *inMBCSHeader; michael@0: _MBCSHeader *outMBCSHeader; michael@0: _MBCSHeader mbcsHeader; michael@0: uint32_t mbcsHeaderLength; michael@0: UBool noFromU=FALSE; michael@0: michael@0: uint8_t outputType; michael@0: michael@0: int32_t maxFastUChar, mbcsIndexLength; michael@0: michael@0: const int32_t *inExtIndexes; michael@0: int32_t extOffset; michael@0: michael@0: /* udata_swapDataHeader checks the arguments */ michael@0: headerSize=udata_swapDataHeader(ds, inData, length, outData, pErrorCode); michael@0: if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { michael@0: return 0; michael@0: } michael@0: michael@0: /* check data format and format version */ michael@0: pInfo=(const UDataInfo *)((const char *)inData+4); michael@0: if(!( michael@0: pInfo->dataFormat[0]==0x63 && /* dataFormat="cnvt" */ michael@0: pInfo->dataFormat[1]==0x6e && michael@0: pInfo->dataFormat[2]==0x76 && michael@0: pInfo->dataFormat[3]==0x74 && michael@0: pInfo->formatVersion[0]==6 && michael@0: pInfo->formatVersion[1]>=2 michael@0: )) { michael@0: 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: pInfo->dataFormat[0], pInfo->dataFormat[1], michael@0: pInfo->dataFormat[2], pInfo->dataFormat[3], michael@0: pInfo->formatVersion[0], pInfo->formatVersion[1]); michael@0: *pErrorCode=U_UNSUPPORTED_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: inBytes=(const uint8_t *)inData+headerSize; michael@0: outBytes=(uint8_t *)outData+headerSize; michael@0: michael@0: /* read the initial UConverterStaticData structure after the UDataInfo header */ michael@0: inStaticData=(const UConverterStaticData *)inBytes; michael@0: outStaticData=(UConverterStaticData *)outBytes; michael@0: michael@0: if(length<0) { michael@0: staticDataSize=ds->readUInt32(inStaticData->structSize); michael@0: } else { michael@0: length-=headerSize; michael@0: if( length<(int32_t)sizeof(UConverterStaticData) || michael@0: (uint32_t)length<(staticDataSize=ds->readUInt32(inStaticData->structSize)) michael@0: ) { michael@0: udata_printError(ds, "ucnv_swap(): too few bytes (%d after header) for an ICU .cnv conversion table\n", michael@0: length); michael@0: *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: if(length>=0) { michael@0: /* swap the static data */ michael@0: if(inStaticData!=outStaticData) { michael@0: uprv_memcpy(outStaticData, inStaticData, staticDataSize); michael@0: } michael@0: michael@0: ds->swapArray32(ds, &inStaticData->structSize, 4, michael@0: &outStaticData->structSize, pErrorCode); michael@0: ds->swapArray32(ds, &inStaticData->codepage, 4, michael@0: &outStaticData->codepage, pErrorCode); michael@0: michael@0: ds->swapInvChars(ds, inStaticData->name, (int32_t)uprv_strlen(inStaticData->name), michael@0: outStaticData->name, pErrorCode); michael@0: if(U_FAILURE(*pErrorCode)) { michael@0: udata_printError(ds, "ucnv_swap(): error swapping converter name\n"); michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: inBytes+=staticDataSize; michael@0: outBytes+=staticDataSize; michael@0: if(length>=0) { michael@0: length-=(int32_t)staticDataSize; michael@0: } michael@0: michael@0: /* check for supported conversionType values */ michael@0: if(inStaticData->conversionType==UCNV_MBCS) { michael@0: /* swap MBCS data */ michael@0: inMBCSHeader=(const _MBCSHeader *)inBytes; michael@0: outMBCSHeader=(_MBCSHeader *)outBytes; michael@0: michael@0: if(0<=length && length<(int32_t)sizeof(_MBCSHeader)) { michael@0: udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table\n", michael@0: length); michael@0: *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; michael@0: return 0; michael@0: } michael@0: if(inMBCSHeader->version[0]==4 && inMBCSHeader->version[1]>=1) { michael@0: mbcsHeaderLength=MBCS_HEADER_V4_LENGTH; michael@0: } else if(inMBCSHeader->version[0]==5 && inMBCSHeader->version[1]>=3 && michael@0: ((mbcsHeader.options=ds->readUInt32(inMBCSHeader->options))& michael@0: MBCS_OPT_UNKNOWN_INCOMPATIBLE_MASK)==0 michael@0: ) { michael@0: mbcsHeaderLength=mbcsHeader.options&MBCS_OPT_LENGTH_MASK; michael@0: noFromU=(UBool)((mbcsHeader.options&MBCS_OPT_NO_FROM_U)!=0); michael@0: } else { michael@0: udata_printError(ds, "ucnv_swap(): unsupported _MBCSHeader.version %d.%d\n", michael@0: inMBCSHeader->version[0], inMBCSHeader->version[1]); michael@0: *pErrorCode=U_UNSUPPORTED_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: uprv_memcpy(mbcsHeader.version, inMBCSHeader->version, 4); michael@0: mbcsHeader.countStates= ds->readUInt32(inMBCSHeader->countStates); michael@0: mbcsHeader.countToUFallbacks= ds->readUInt32(inMBCSHeader->countToUFallbacks); michael@0: mbcsHeader.offsetToUCodeUnits= ds->readUInt32(inMBCSHeader->offsetToUCodeUnits); michael@0: mbcsHeader.offsetFromUTable= ds->readUInt32(inMBCSHeader->offsetFromUTable); michael@0: mbcsHeader.offsetFromUBytes= ds->readUInt32(inMBCSHeader->offsetFromUBytes); michael@0: mbcsHeader.flags= ds->readUInt32(inMBCSHeader->flags); michael@0: mbcsHeader.fromUBytesLength= ds->readUInt32(inMBCSHeader->fromUBytesLength); michael@0: /* mbcsHeader.options have been read above */ michael@0: michael@0: extOffset=(int32_t)(mbcsHeader.flags>>8); michael@0: outputType=(uint8_t)mbcsHeader.flags; michael@0: if(noFromU && outputType==MBCS_OUTPUT_1) { michael@0: udata_printError(ds, "ucnv_swap(): unsupported combination of makeconv --small with SBCS\n"); michael@0: *pErrorCode=U_UNSUPPORTED_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: /* make sure that the output type is known */ michael@0: switch(outputType) { michael@0: case MBCS_OUTPUT_1: michael@0: case MBCS_OUTPUT_2: michael@0: case MBCS_OUTPUT_3: michael@0: case MBCS_OUTPUT_4: michael@0: case MBCS_OUTPUT_3_EUC: michael@0: case MBCS_OUTPUT_4_EUC: michael@0: case MBCS_OUTPUT_2_SISO: michael@0: case MBCS_OUTPUT_EXT_ONLY: michael@0: /* OK */ michael@0: break; michael@0: default: michael@0: udata_printError(ds, "ucnv_swap(): unsupported MBCS output type 0x%x\n", michael@0: outputType); michael@0: *pErrorCode=U_UNSUPPORTED_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: /* calculate the length of the MBCS data */ michael@0: michael@0: /* michael@0: * utf8Friendly MBCS files (mbcsHeader.version 4.3) michael@0: * contain an additional mbcsIndex table: michael@0: * uint16_t[(maxFastUChar+1)>>6]; michael@0: * where maxFastUChar=((mbcsHeader.version[2]<<8)|0xff). michael@0: */ michael@0: maxFastUChar=0; michael@0: mbcsIndexLength=0; michael@0: if( outputType!=MBCS_OUTPUT_EXT_ONLY && outputType!=MBCS_OUTPUT_1 && michael@0: mbcsHeader.version[1]>=3 && (maxFastUChar=mbcsHeader.version[2])!=0 michael@0: ) { michael@0: maxFastUChar=(maxFastUChar<<8)|0xff; michael@0: mbcsIndexLength=((maxFastUChar+1)>>6)*2; /* number of bytes */ michael@0: } michael@0: michael@0: if(extOffset==0) { michael@0: size=(int32_t)(mbcsHeader.offsetFromUBytes+mbcsIndexLength); michael@0: if(!noFromU) { michael@0: size+=(int32_t)mbcsHeader.fromUBytesLength; michael@0: } michael@0: michael@0: /* avoid compiler warnings - not otherwise necessary, and the value does not matter */ michael@0: inExtIndexes=NULL; michael@0: } else { michael@0: /* there is extension data after the base data, see ucnv_ext.h */ michael@0: if(length>=0 && length<(extOffset+UCNV_EXT_INDEXES_MIN_LENGTH*4)) { michael@0: udata_printError(ds, "ucnv_swap(): too few bytes (%d after headers) for an ICU MBCS .cnv conversion table with extension data\n", michael@0: length); michael@0: *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: inExtIndexes=(const int32_t *)(inBytes+extOffset); michael@0: size=extOffset+udata_readInt32(ds, inExtIndexes[UCNV_EXT_SIZE]); michael@0: } michael@0: michael@0: if(length>=0) { michael@0: if(lengthswapArray32(ds, &inMBCSHeader->countStates, count-4, michael@0: &outMBCSHeader->countStates, pErrorCode); michael@0: michael@0: if(outputType==MBCS_OUTPUT_EXT_ONLY) { michael@0: /* michael@0: * extension-only file, michael@0: * contains a base name instead of normal base table data michael@0: */ michael@0: michael@0: /* swap the base name, between the header and the extension data */ michael@0: const char *inBaseName=(const char *)inBytes+count; michael@0: char *outBaseName=(char *)outBytes+count; michael@0: ds->swapInvChars(ds, inBaseName, (int32_t)uprv_strlen(inBaseName), michael@0: outBaseName, pErrorCode); michael@0: } else { michael@0: /* normal file with base table data */ michael@0: michael@0: /* swap the state table, 1kB per state */ michael@0: offset=count; michael@0: count=mbcsHeader.countStates*1024; michael@0: ds->swapArray32(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: michael@0: /* swap the toUFallbacks[] */ michael@0: offset+=count; michael@0: count=mbcsHeader.countToUFallbacks*8; michael@0: ds->swapArray32(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: michael@0: /* swap the unicodeCodeUnits[] */ michael@0: offset=mbcsHeader.offsetToUCodeUnits; michael@0: count=mbcsHeader.offsetFromUTable-offset; michael@0: ds->swapArray16(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: michael@0: /* offset to the stage 1 table, independent of the outputType */ michael@0: offset=mbcsHeader.offsetFromUTable; michael@0: michael@0: if(outputType==MBCS_OUTPUT_1) { michael@0: /* SBCS: swap the fromU tables, all 16 bits wide */ michael@0: count=(mbcsHeader.offsetFromUBytes-offset)+mbcsHeader.fromUBytesLength; michael@0: ds->swapArray16(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: } else { michael@0: /* otherwise: swap the stage tables separately */ michael@0: michael@0: /* stage 1 table: uint16_t[0x440 or 0x40] */ michael@0: if(inStaticData->unicodeMask&UCNV_HAS_SUPPLEMENTARY) { michael@0: count=0x440*2; /* for all of Unicode */ michael@0: } else { michael@0: count=0x40*2; /* only BMP */ michael@0: } michael@0: ds->swapArray16(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: michael@0: /* stage 2 table: uint32_t[] */ michael@0: offset+=count; michael@0: count=mbcsHeader.offsetFromUBytes-offset; michael@0: ds->swapArray32(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: michael@0: /* stage 3/result bytes: sometimes uint16_t[] or uint32_t[] */ michael@0: offset=mbcsHeader.offsetFromUBytes; michael@0: count= noFromU ? 0 : mbcsHeader.fromUBytesLength; michael@0: switch(outputType) { michael@0: case MBCS_OUTPUT_2: michael@0: case MBCS_OUTPUT_3_EUC: michael@0: case MBCS_OUTPUT_2_SISO: michael@0: ds->swapArray16(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: break; michael@0: case MBCS_OUTPUT_4: michael@0: ds->swapArray32(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: break; michael@0: default: michael@0: /* just uint8_t[], nothing to swap */ michael@0: break; michael@0: } michael@0: michael@0: if(mbcsIndexLength!=0) { michael@0: offset+=count; michael@0: count=mbcsIndexLength; michael@0: ds->swapArray16(ds, inBytes+offset, (int32_t)count, michael@0: outBytes+offset, pErrorCode); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if(extOffset!=0) { michael@0: /* swap the extension data */ michael@0: inBytes+=extOffset; michael@0: outBytes+=extOffset; michael@0: michael@0: /* swap toUTable[] */ michael@0: offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_INDEX]); michael@0: length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_LENGTH]); michael@0: ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); michael@0: michael@0: /* swap toUUChars[] */ michael@0: offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_INDEX]); michael@0: length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_TO_U_UCHARS_LENGTH]); michael@0: ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); michael@0: michael@0: /* swap fromUTableUChars[] */ michael@0: offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_UCHARS_INDEX]); michael@0: length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_LENGTH]); michael@0: ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); michael@0: michael@0: /* swap fromUTableValues[] */ michael@0: offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_VALUES_INDEX]); michael@0: /* same length as for fromUTableUChars[] */ michael@0: ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); michael@0: michael@0: /* no need to swap fromUBytes[] */ michael@0: michael@0: /* swap fromUStage12[] */ michael@0: offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_INDEX]); michael@0: length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_12_LENGTH]); michael@0: ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); michael@0: michael@0: /* swap fromUStage3[] */ michael@0: offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_INDEX]); michael@0: length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3_LENGTH]); michael@0: ds->swapArray16(ds, inBytes+offset, length*2, outBytes+offset, pErrorCode); michael@0: michael@0: /* swap fromUStage3b[] */ michael@0: offset=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_INDEX]); michael@0: length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_FROM_U_STAGE_3B_LENGTH]); michael@0: ds->swapArray32(ds, inBytes+offset, length*4, outBytes+offset, pErrorCode); michael@0: michael@0: /* swap indexes[] */ michael@0: length=udata_readInt32(ds, inExtIndexes[UCNV_EXT_INDEXES_LENGTH]); michael@0: ds->swapArray32(ds, inBytes, length*4, outBytes, pErrorCode); michael@0: } michael@0: } michael@0: } else { michael@0: udata_printError(ds, "ucnv_swap(): unknown conversionType=%d!=UCNV_MBCS\n", michael@0: inStaticData->conversionType); michael@0: *pErrorCode=U_UNSUPPORTED_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: return headerSize+(int32_t)staticDataSize+size; michael@0: } michael@0: michael@0: #endif /* #if !UCONFIG_NO_LEGACY_CONVERSION */ michael@0: michael@0: #endif