]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/ucnv_bld.cpp
ICU-59180.0.1.tar.gz
[apple/icu.git] / icuSources / common / ucnv_bld.cpp
index 25e853dd17da5a8392212e059164b82267b4b681..14de21bd7391390415e42cd888d44a632718d607 100644 (file)
@@ -1,11 +1,13 @@
+// © 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.
@@ -27,7 +29,9 @@
 #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"
@@ -43,7 +47,6 @@
 #include "ustr_cnv.h"
 
 
-
 #if 0
 #include <stdio.h>
 extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l);
@@ -63,33 +66,51 @@ converterData[UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES]={
 #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
@@ -104,18 +125,24 @@ static struct {
   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 },
@@ -129,7 +156,9 @@ static struct {
   { "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 },
@@ -141,6 +170,7 @@ static struct {
   { "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 },
@@ -151,20 +181,26 @@ static struct {
   { "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
 
@@ -187,15 +223,18 @@ static UBool gDefaultConverterContainsOption;
 
 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  */
@@ -219,9 +258,6 @@ static UBool U_CALLCONV ucnv_cleanup(void) {
     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);
 }
 
@@ -258,6 +294,7 @@ ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCo
 
     if( (uint16_t)type >= UCNV_NUMBER_OF_SUPPORTED_CONVERTER_TYPES ||
         converterData[type] == NULL ||
+        !converterData[type]->isReferenceCounted ||
         converterData[type]->referenceCounter != 1 ||
         source->structSize != sizeof(UConverterStaticData))
     {
@@ -274,26 +311,6 @@ ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCo
     /* 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;
@@ -304,7 +321,6 @@ ucnv_data_unFlattenClone(UConverterLoadArgs *pArgs, UDataMemory *pData, UErrorCo
     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;
         }
@@ -371,7 +387,7 @@ getAlgorithmicTypeFromName(const char *realName)
 
     /* do a binary search for the alias */
     start = 0;
-    limit = sizeof(cnvNameType)/sizeof(cnvNameType[0]);
+    limit = UPRV_LENGTHOF(cnvNameType);
     mid = limit;
     lastMid = UINT32_MAX;
 
@@ -508,25 +524,6 @@ ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData)
         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);
@@ -577,7 +574,7 @@ ucnv_load(UConverterLoadArgs *pArgs, UErrorCode *err) {
 
 /**
  * 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
@@ -596,12 +593,7 @@ ucnv_unload(UConverterSharedData *sharedData) {
 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);
@@ -611,12 +603,7 @@ ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData)
 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);
@@ -796,6 +783,8 @@ ucnv_loadSharedData(const char *converterName,
             * 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;
         }
     }
 
@@ -904,12 +893,7 @@ ucnv_createAlgorithmicConverter(UConverter *myUConverter,
     }
 
     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);
@@ -1082,7 +1066,7 @@ ucnv_flushCache ()
     i = 0;
     do {
         remaining = 0;
-        pos = -1;
+        pos = UHASH_FIRST;
         while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL)
         {
             mySharedData = (UConverterSharedData *) e->value.pointer;
@@ -1111,59 +1095,46 @@ ucnv_flushCache ()
 
 /* 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
@@ -1229,6 +1200,9 @@ internalSetName(const char *name, UErrorCode *status) {
 
     /* 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);
@@ -1254,10 +1228,13 @@ ucnv_getDefaultName() {
     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;
@@ -1297,13 +1274,15 @@ ucnv_getDefaultName() {
 #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;
@@ -1329,8 +1308,8 @@ ucnv_setDefaultName(const char *converterName) {
         /* reset the converter cache */
         u_flushDefaultConverter();
     }
-#endif
 }
+#endif
 
 /* data swapping ------------------------------------------------------------ */