+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
********************************************************************
* COPYRIGHT:
- * Copyright (c) 1996-2011, International Business Machines Corporation and
+ * Copyright (c) 1996-2016, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************
*
- * uconv_bld.cpp:
+ * ucnv_bld.cpp:
*
* Defines functions that are used in the creation/initialization/deletion
* of converters and related structures.
#include "unicode/udata.h"
#include "unicode/ucnv.h"
#include "unicode/uloc.h"
+#include "mutex.h"
#include "putilimp.h"
+#include "uassert.h"
#include "utracimp.h"
#include "ucnv_io.h"
#include "ucnv_bld.h"
#include "ustr_cnv.h"
-
#if 0
#include <stdio.h>
extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l);
#endif
&_Latin1Data,
- &_UTF8Data, &_UTF16BEData, &_UTF16LEData, &_UTF32BEData, &_UTF32LEData,
+ &_UTF8Data, &_UTF16BEData, &_UTF16LEData,
+#if UCONFIG_ONLY_HTML_CONVERSION
+ NULL, NULL,
+#else
+ &_UTF32BEData, &_UTF32LEData,
+#endif
NULL,
#if UCONFIG_NO_LEGACY_CONVERSION
NULL,
+#else
+ &_ISO2022Data,
+#endif
+
+#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL,
#else
- &_ISO2022Data,
&_LMBCSData1,&_LMBCSData2, &_LMBCSData3, &_LMBCSData4, &_LMBCSData5, &_LMBCSData6,
&_LMBCSData8,&_LMBCSData11,&_LMBCSData16,&_LMBCSData17,&_LMBCSData18,&_LMBCSData19,
&_HZData,
#endif
+#if UCONFIG_ONLY_HTML_CONVERSION
+ NULL,
+#else
&_SCSUData,
+#endif
-#if UCONFIG_NO_LEGACY_CONVERSION
+
+#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION
NULL,
#else
&_ISCIIData,
#endif
&_ASCIIData,
+#if UCONFIG_ONLY_HTML_CONVERSION
+ NULL, NULL, &_UTF16Data, NULL, NULL, NULL,
+#else
&_UTF7Data, &_Bocu1Data, &_UTF16Data, &_UTF32Data, &_CESU8Data, &_IMAPData,
+#endif
-#if UCONFIG_NO_LEGACY_CONVERSION
+#if UCONFIG_NO_LEGACY_CONVERSION || UCONFIG_ONLY_HTML_CONVERSION
NULL,
#else
&_CompoundTextData
const char *name;
const UConverterType type;
} const cnvNameType[] = {
+#if !UCONFIG_ONLY_HTML_CONVERSION
{ "bocu1", UCNV_BOCU1 },
{ "cesu8", UCNV_CESU8 },
-#if !UCONFIG_NO_LEGACY_CONVERSION
+#endif
+#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION
{ "hz",UCNV_HZ },
#endif
+#if !UCONFIG_ONLY_HTML_CONVERSION
{ "imapmailboxname", UCNV_IMAP_MAILBOX },
-#if !UCONFIG_NO_LEGACY_CONVERSION
+#endif
+#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION
{ "iscii", UCNV_ISCII },
+#endif
+#if !UCONFIG_NO_LEGACY_CONVERSION
{ "iso2022", UCNV_ISO_2022 },
#endif
{ "iso88591", UCNV_LATIN_1 },
-#if !UCONFIG_NO_LEGACY_CONVERSION
+#if !UCONFIG_NO_LEGACY_CONVERSION && !UCONFIG_ONLY_HTML_CONVERSION
{ "lmbcs1", UCNV_LMBCS_1 },
{ "lmbcs11",UCNV_LMBCS_11 },
{ "lmbcs16",UCNV_LMBCS_16 },
{ "lmbcs6", UCNV_LMBCS_6 },
{ "lmbcs8", UCNV_LMBCS_8 },
#endif
+#if !UCONFIG_ONLY_HTML_CONVERSION
{ "scsu", UCNV_SCSU },
+#endif
{ "usascii", UCNV_US_ASCII },
{ "utf16", UCNV_UTF16 },
{ "utf16be", UCNV_UTF16_BigEndian },
{ "utf16oppositeendian", UCNV_UTF16_BigEndian},
{ "utf16platformendian", UCNV_UTF16_LittleEndian },
#endif
+#if !UCONFIG_ONLY_HTML_CONVERSION
{ "utf32", UCNV_UTF32 },
{ "utf32be", UCNV_UTF32_BigEndian },
{ "utf32le", UCNV_UTF32_LittleEndian },
{ "utf32oppositeendian", UCNV_UTF32_BigEndian },
{ "utf32platformendian", UCNV_UTF32_LittleEndian },
#endif
+#endif
+#if !UCONFIG_ONLY_HTML_CONVERSION
{ "utf7", UCNV_UTF7 },
+#endif
{ "utf8", UCNV_UTF8 },
+#if !UCONFIG_ONLY_HTML_CONVERSION
{ "x11compoundtext", UCNV_COMPOUND_TEXT}
+#endif
};
/*initializes some global variables */
static UHashtable *SHARED_DATA_HASHTABLE = NULL;
-static UMTX cnvCacheMutex = NULL; /* Mutex for synchronizing cnv cache access. */
- /* Note: the global mutex is used for */
- /* reference count updates. */
+static UMutex cnvCacheMutex = U_MUTEX_INITIALIZER; /* Mutex for synchronizing cnv cache access. */
+ /* Note: the global mutex is used for */
+ /* reference count updates. */
static const char **gAvailableConverters = NULL;
static uint16_t gAvailableConverterCount = 0;
+static icu::UInitOnce gAvailableConvertersInitOnce = U_INITONCE_INITIALIZER;
#if !U_CHARSET_IS_UTF8
static const char DATA_TYPE[] = "cnv";
+/* ucnv_flushAvailableConverterCache. This is only called from ucnv_cleanup().
+ * If it is ever to be called from elsewhere, synchronization
+ * will need to be considered.
+ */
static void
ucnv_flushAvailableConverterCache() {
+ gAvailableConverterCount = 0;
if (gAvailableConverters) {
- umtx_lock(&cnvCacheMutex);
- gAvailableConverterCount = 0;
uprv_free((char **)gAvailableConverters);
gAvailableConverters = NULL;
- umtx_unlock(&cnvCacheMutex);
}
+ gAvailableConvertersInitOnce.reset();
}
/* ucnv_cleanup - delete all storage held by the converter cache, except any */
gDefaultAlgorithmicSharedData = NULL;
#endif
- umtx_destroy(&cnvCacheMutex); /* Don't worry about destroying the mutex even */
- /* if the hash table still exists. The mutex */
- /* will lazily re-init itself if needed. */
return (SHARED_DATA_HASHTABLE == NULL);
}
if( (uint16_t)type >= UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES ||
converterData[type] == NULL ||
+ !converterData[type]->isReferenceCounted ||
converterData[type]->referenceCounter != 1 ||
source->structSize != sizeof(UConverterStaticData))
{
/* copy initial values from the static structure for this type */
uprv_memcpy(data, converterData[type], sizeof(UConverterSharedData));
-#if 0 /* made UConverterMBCSTable part of UConverterSharedData -- markus 20031107 */
- /*
- * It would be much more efficient if the table were a direct member, not a pointer.
- * However, that would add to the size of all UConverterSharedData objects
- * even if they do not use this table (especially algorithmic ones).
- * If this changes, then the static templates from converterData[type]
- * need more entries.
- *
- * In principle, it would be cleaner if the load() function below
- * allocated the table.
- */
- data->table = (UConverterTable *)uprv_malloc(sizeof(UConverterTable));
- if(data->table == NULL) {
- uprv_free(data);
- *status = U_MEMORY_ALLOCATION_ERROR;
- return NULL;
- }
- uprv_memset(data->table, 0, sizeof(UConverterTable));
-#endif
-
data->staticData = source;
data->sharedDataCached = FALSE;
if(data->impl->load != NULL) {
data->impl->load(data, pArgs, raw + source->structSize, status);
if(U_FAILURE(*status)) {
- uprv_free(data->table);
uprv_free(data);
return NULL;
}
/* do a binary search for the alias */
start = 0;
- limit = sizeof(cnvNameType)/sizeof(cnvNameType[0]);
+ limit = UPRV_LENGTHOF(cnvNameType);
mid = limit;
lastMid = UINT32_MAX;
udata_close(data);
}
- if(deadSharedData->table != NULL)
- {
- uprv_free(deadSharedData->table);
- }
-
-#if 0
- /* if the static data is actually owned by the shared data */
- /* enable if we ever have this situation. */
- if(deadSharedData->staticDataOwned == TRUE) /* see ucnv_bld.h */
- {
- uprv_free((void*)deadSharedData->staticData);
- }
-#endif
-
-#if 0
- /* Zap it ! */
- uprv_memset(deadSharedData->0, sizeof(*deadSharedData));
-#endif
-
uprv_free(deadSharedData);
UTRACE_EXIT_VALUE((int32_t)TRUE);
/**
* Unload a non-algorithmic converter.
- * It must be sharedData->referenceCounter != ~0
+ * It must be sharedData->isReferenceCounted
* and this function must be called inside umtx_lock(&cnvCacheMutex).
*/
U_CAPI void
U_CFUNC void
ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData)
{
- /*
- Checking whether it's an algorithic converter is okay
- in multithreaded applications because the value never changes.
- Don't check referenceCounter for any other value.
- */
- if(sharedData != NULL && sharedData->referenceCounter != (uint32_t)~0) {
+ if(sharedData != NULL && sharedData->isReferenceCounted) {
umtx_lock(&cnvCacheMutex);
ucnv_unload(sharedData);
umtx_unlock(&cnvCacheMutex);
U_CFUNC void
ucnv_incrementRefCount(UConverterSharedData *sharedData)
{
- /*
- Checking whether it's an algorithic converter is okay
- in multithreaded applications because the value never changes.
- Don't check referenceCounter for any other value.
- */
- if(sharedData != NULL && sharedData->referenceCounter != (uint32_t)~0) {
+ if(sharedData != NULL && sharedData->isReferenceCounted) {
umtx_lock(&cnvCacheMutex);
sharedData->referenceCounter++;
umtx_unlock(&cnvCacheMutex);
* without updating the alias table, or when there is no alias table
*/
pArgs->name = pPieces->cnvName;
+ } else if (internalErrorCode == U_AMBIGUOUS_ALIAS_WARNING) {
+ *err = U_AMBIGUOUS_ALIAS_WARNING;
}
}
}
sharedData = converterData[type];
- /*
- Checking whether it's an algorithic converter is okay
- in multithreaded applications because the value never changes.
- Don't check referenceCounter for any other value.
- */
- if(sharedData == NULL || sharedData->referenceCounter != (uint32_t)~0) {
+ if(sharedData == NULL || sharedData->isReferenceCounted) {
/* not a valid type, or not an algorithmic converter */
*err = U_ILLEGAL_ARGUMENT_ERROR;
UTRACE_EXIT_STATUS(U_ILLEGAL_ARGUMENT_ERROR);
i = 0;
do {
remaining = 0;
- pos = -1;
+ pos = UHASH_FIRST;
while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL)
{
mySharedData = (UConverterSharedData *) e->value.pointer;
/* available converters list --------------------------------------------------- */
-static UBool haveAvailableConverterList(UErrorCode *pErrorCode) {
- int needInit;
- UMTX_CHECK(&cnvCacheMutex, (gAvailableConverters == NULL), needInit);
- if (needInit) {
- UConverter tempConverter;
- UEnumeration *allConvEnum = NULL;
- uint16_t idx;
- uint16_t localConverterCount;
- uint16_t allConverterCount;
- UErrorCode localStatus;
- const char *converterName;
- const char **localConverterList;
-
- allConvEnum = ucnv_openAllNames(pErrorCode);
- allConverterCount = uenum_count(allConvEnum, pErrorCode);
- if (U_FAILURE(*pErrorCode)) {
- return FALSE;
- }
+static void U_CALLCONV initAvailableConvertersList(UErrorCode &errCode) {
+ U_ASSERT(gAvailableConverterCount == 0);
+ U_ASSERT(gAvailableConverters == NULL);
- /* We can't have more than "*converterTable" converters to open */
- localConverterList = (const char **) uprv_malloc(allConverterCount * sizeof(char*));
- if (!localConverterList) {
- *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
- return FALSE;
- }
+ ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup);
+ UEnumeration *allConvEnum = ucnv_openAllNames(&errCode);
+ int32_t allConverterCount = uenum_count(allConvEnum, &errCode);
+ if (U_FAILURE(errCode)) {
+ return;
+ }
- /* Open the default converter to make sure that it has first dibs in the hash table. */
- localStatus = U_ZERO_ERROR;
- ucnv_close(ucnv_createConverter(&tempConverter, NULL, &localStatus));
+ /* We can't have more than "*converterTable" converters to open */
+ gAvailableConverters = (const char **) uprv_malloc(allConverterCount * sizeof(char*));
+ if (!gAvailableConverters) {
+ errCode = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
- localConverterCount = 0;
+ /* Open the default converter to make sure that it has first dibs in the hash table. */
+ UErrorCode localStatus = U_ZERO_ERROR;
+ UConverter tempConverter;
+ ucnv_close(ucnv_createConverter(&tempConverter, NULL, &localStatus));
- for (idx = 0; idx < allConverterCount; idx++) {
- localStatus = U_ZERO_ERROR;
- converterName = uenum_next(allConvEnum, NULL, &localStatus);
- if (ucnv_canCreateConverter(converterName, &localStatus)) {
- localConverterList[localConverterCount++] = converterName;
- }
- }
- uenum_close(allConvEnum);
+ gAvailableConverterCount = 0;
- umtx_lock(&cnvCacheMutex);
- if (gAvailableConverters == NULL) {
- gAvailableConverterCount = localConverterCount;
- gAvailableConverters = localConverterList;
- ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup);
- }
- else {
- uprv_free((char **)localConverterList);
+ for (int32_t idx = 0; idx < allConverterCount; idx++) {
+ localStatus = U_ZERO_ERROR;
+ const char *converterName = uenum_next(allConvEnum, NULL, &localStatus);
+ if (ucnv_canCreateConverter(converterName, &localStatus)) {
+ gAvailableConverters[gAvailableConverterCount++] = converterName;
}
- umtx_unlock(&cnvCacheMutex);
}
- return TRUE;
+
+ uenum_close(allConvEnum);
+}
+
+
+static UBool haveAvailableConverterList(UErrorCode *pErrorCode) {
+ umtx_initOnce(gAvailableConvertersInitOnce, &initAvailableConvertersList, *pErrorCode);
+ return U_SUCCESS(*pErrorCode);
}
U_CFUNC uint16_t
/* gDefaultConverterName MUST be the last global var set by this function. */
/* It is the variable checked in ucnv_getDefaultName() to see if initialization is required. */
+ // But there is nothing here preventing that from being reordered, either by the compiler
+ // or hardware. I'm adding the mutex to ucnv_getDefaultName for now. UMTX_CHECK is not enough.
+ // -- Andy
gDefaultConverterName = gDefaultConverterNameBuffer;
ucln_common_registerCleanup(UCLN_COMMON_UCNV, ucnv_cleanup);
const char *name;
/*
- Multiple calls to ucnv_getDefaultName must be thread safe,
+ Concurrent calls to ucnv_getDefaultName must be thread safe,
but ucnv_setDefaultName is not thread safe.
*/
- UMTX_CHECK(&cnvCacheMutex, gDefaultConverterName, name);
+ {
+ icu::Mutex lock(&cnvCacheMutex);
+ name = gDefaultConverterName;
+ }
if(name==NULL) {
UErrorCode errorCode = U_ZERO_ERROR;
UConverter *cnv = NULL;
#endif
}
+#if U_CHARSET_IS_UTF8
+U_CAPI void U_EXPORT2 ucnv_setDefaultName(const char *) {}
+#else
/*
This function is not thread safe, and it can't be thread safe.
See internalSetName or the API reference for details.
*/
U_CAPI void U_EXPORT2
ucnv_setDefaultName(const char *converterName) {
-#if !U_CHARSET_IS_UTF8
if(converterName==NULL) {
/* reset to the default codepage */
gDefaultConverterName=NULL;
/* reset the converter cache */
u_flushDefaultConverter();
}
-#endif
}
+#endif
/* data swapping ------------------------------------------------------------ */