]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/zonemeta.cpp
ICU-57131.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / zonemeta.cpp
index 522c3f04990e3a07371f48de77bea442988db839..e2c75e5577b503499895a78c50e0e2c079f01970 100644 (file)
@@ -1,7 +1,7 @@
 /*
 *******************************************************************************
-* Copyright (C) 2007-2013, International Business Machines Corporation and    *
-* others. All Rights Reserved.                                                *
+* Copyright (C) 2007-2014, International Business Machines Corporation and
+* others. All Rights Reserved.
 *******************************************************************************
 */
 
@@ -31,21 +31,21 @@ static UMutex gZoneMetaLock = U_MUTEX_INITIALIZER;
 
 // CLDR Canonical ID mapping table
 static UHashtable *gCanonicalIDCache = NULL;
-static UBool gCanonicalIDCacheInitialized = FALSE;
+static icu::UInitOnce gCanonicalIDCacheInitOnce = U_INITONCE_INITIALIZER;
 
 // Metazone mapping table
 static UHashtable *gOlsonToMeta = NULL;
-static UBool gOlsonToMetaInitialized = FALSE;
+static icu::UInitOnce gOlsonToMetaInitOnce = U_INITONCE_INITIALIZER;
 
 // Available metazone IDs vector and table
 static icu::UVector *gMetaZoneIDs = NULL;
 static UHashtable *gMetaZoneIDTable = NULL;
-static UBool gMetaZoneIDsInitialized = FALSE;
+static icu::UInitOnce gMetaZoneIDsInitOnce = U_INITONCE_INITIALIZER;
 
 // Country info vectors
 static icu::UVector *gSingleZoneCountries = NULL;
 static icu::UVector *gMultiZonesCountries = NULL;
-static UBool gCountryInfoVectorsInitialized = FALSE;
+static icu::UInitOnce gCountryInfoVectorsInitOnce = U_INITONCE_INITIALIZER;
 
 U_CDECL_BEGIN
 
@@ -58,25 +58,29 @@ static UBool U_CALLCONV zoneMeta_cleanup(void)
         uhash_close(gCanonicalIDCache);
         gCanonicalIDCache = NULL;
     }
-    gCanonicalIDCacheInitialized = FALSE;
+    gCanonicalIDCacheInitOnce.reset();
 
     if (gOlsonToMeta != NULL) {
         uhash_close(gOlsonToMeta);
         gOlsonToMeta = NULL;
     }
-    gOlsonToMetaInitialized = FALSE;
+    gOlsonToMetaInitOnce.reset();
 
     if (gMetaZoneIDTable != NULL) {
         uhash_close(gMetaZoneIDTable);
+        gMetaZoneIDTable = NULL;
     }
     // delete after closing gMetaZoneIDTable, because it holds
     // value objects held by the hashtable
     delete gMetaZoneIDs;
-    gMetaZoneIDsInitialized = FALSE;
+    gMetaZoneIDs = NULL;
+    gMetaZoneIDsInitOnce.reset();
 
     delete gSingleZoneCountries;
+    gSingleZoneCountries = NULL;
     delete gMultiZonesCountries;
-    gCountryInfoVectorsInitialized = FALSE;
+    gMultiZonesCountries = NULL;
+    gCountryInfoVectorsInitOnce.reset();
 
     return TRUE;
 }
@@ -212,40 +216,34 @@ parseDate (const UChar *text, UErrorCode &status) {
     return 0;
 }
 
+static void U_CALLCONV initCanonicalIDCache(UErrorCode &status) {
+    gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+    if (gCanonicalIDCache == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
+    if (U_FAILURE(status)) {
+        gCanonicalIDCache = NULL;
+    }
+    // No key/value deleters - keys/values are from a resource bundle
+    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+}
+
+
 const UChar* U_EXPORT2
 ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
     if (U_FAILURE(status)) {
         return NULL;
     }
 
-    int32_t len = tzid.length();
-    if (len > ZID_KEY_MAX) {
+    if (tzid.isBogus() || tzid.length() > ZID_KEY_MAX) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
         return NULL;
     }
 
     // Checking the cached results
-    UBool initialized;
-    UMTX_CHECK(&gZoneMetaLock, gCanonicalIDCacheInitialized, initialized);
-    if (!initialized) {
-        // Create empty hashtable
-        umtx_lock(&gZoneMetaLock);
-        {
-            if (!gCanonicalIDCacheInitialized) {
-                gCanonicalIDCache = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
-                if (gCanonicalIDCache == NULL) {
-                    status = U_MEMORY_ALLOCATION_ERROR;
-                }
-                if (U_FAILURE(status)) {
-                    gCanonicalIDCache = NULL;
-                    return NULL;
-                }
-                // No key/value deleters - keys/values are from a resource bundle
-                gCanonicalIDCacheInitialized = TRUE;
-                ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
-            }
-        }
-        umtx_unlock(&gZoneMetaLock);
+    umtx_initOnce(gCanonicalIDCacheInitOnce, &initCanonicalIDCache, status);
+    if (U_FAILURE(status)) {
+        return NULL;
     }
 
     const UChar *canonicalID = NULL;
@@ -269,10 +267,7 @@ ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
     // If not, resolve CLDR canonical ID with resource data
     UBool isInputCanonical = FALSE;
     char id[ZID_KEY_MAX + 1];
-    const UChar* idChars = tzid.getBuffer();
-
-    u_UCharsToChars(idChars,id,len);
-    id[len] = (char) 0; // Make sure it is null terminated.
+    tzid.extract(0, 0x7fffffff, id, UPRV_LENGTHOF(id), US_INV);
 
     // replace '/' with ':'
     char *p = id;
@@ -310,7 +305,7 @@ ZoneMeta::getCanonicalCLDRID(const UnicodeString &tzid, UErrorCode& status) {
             if (derefer == NULL) {
                 status = U_ILLEGAL_ARGUMENT_ERROR;
             } else {
-                len = u_strlen(derefer);
+                int32_t len = u_strlen(derefer);
                 u_UCharsToChars(derefer,id,len);
                 id[len] = (char) 0; // Make sure it is null terminated.
 
@@ -393,6 +388,28 @@ ZoneMeta::getCanonicalCLDRID(const TimeZone& tz) {
     return getCanonicalCLDRID(tz.getID(tzID), status);
 }
 
+static void U_CALLCONV countryInfoVectorsInit(UErrorCode &status) {
+    // Create empty vectors
+    // No deleters for these UVectors, it's a reference to a resource bundle string.
+    gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
+    if (gSingleZoneCountries == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
+    gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
+    if (gMultiZonesCountries == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
+
+    if (U_FAILURE(status)) {
+        delete gSingleZoneCountries;
+        delete gMultiZonesCountries;
+        gSingleZoneCountries = NULL;
+        gMultiZonesCountries  = NULL;
+    }
+    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+}
+
+
 UnicodeString& U_EXPORT2
 ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country, UBool *isPrimary /* = NULL */) {
     if (isPrimary != NULL) {
@@ -412,39 +429,9 @@ ZoneMeta::getCanonicalCountry(const UnicodeString &tzid, UnicodeString &country,
 
         // Checking the cached results
         UErrorCode status = U_ZERO_ERROR;
-        UBool initialized;
-        UMTX_CHECK(&gZoneMetaLock, gCountryInfoVectorsInitialized, initialized);
-        if (!initialized) {
-            // Create empty vectors
-            umtx_lock(&gZoneMetaLock);
-            {
-                if (!gCountryInfoVectorsInitialized) {
-                    // No deleters for these UVectors, it's a reference to a resource bundle string.
-                    gSingleZoneCountries = new UVector(NULL, uhash_compareUChars, status);
-                    if (gSingleZoneCountries == NULL) {
-                        status = U_MEMORY_ALLOCATION_ERROR;
-                    }
-                    gMultiZonesCountries = new UVector(NULL, uhash_compareUChars, status);
-                    if (gMultiZonesCountries == NULL) {
-                        status = U_MEMORY_ALLOCATION_ERROR;
-                    }
-
-                    if (U_SUCCESS(status)) {
-                        gCountryInfoVectorsInitialized = TRUE;
-                        ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
-                    } else {
-                        delete gSingleZoneCountries;
-                        delete gMultiZonesCountries;
-                    }
-                }
-            }
-            umtx_unlock(&gZoneMetaLock);
-
-            if (U_FAILURE(status)) {
-                return country;
-            }
-            U_ASSERT(gSingleZoneCountries != NULL);
-            U_ASSERT(gMultiZonesCountries != NULL);
+        umtx_initOnce(gCountryInfoVectorsInitOnce, &countryInfoVectorsInit, status);
+        if (U_FAILURE(status)) {
+            return country;
         }
 
         // Check if it was already cached
@@ -546,6 +533,19 @@ ZoneMeta::getMetazoneID(const UnicodeString &tzid, UDate date, UnicodeString &re
     return result;
 }
 
+static void U_CALLCONV olsonToMetaInit(UErrorCode &status) {
+    U_ASSERT(gOlsonToMeta == NULL);
+    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+    gOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+    if (U_FAILURE(status)) {
+        gOlsonToMeta = NULL;
+    } else {
+        uhash_setKeyDeleter(gOlsonToMeta, deleteUCharString);
+        uhash_setValueDeleter(gOlsonToMeta, deleteUVector);
+    }
+}
+
+
 const UVector* U_EXPORT2
 ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
     UErrorCode status = U_ZERO_ERROR;
@@ -555,31 +555,9 @@ ZoneMeta::getMetazoneMappings(const UnicodeString &tzid) {
         return NULL;
     }
 
-    UBool initialized;
-    UMTX_CHECK(&gZoneMetaLock, gOlsonToMetaInitialized, initialized);
-    if (!initialized) {
-        UHashtable *tmpOlsonToMeta = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
-        if (U_FAILURE(status)) {
-            return NULL;
-        }
-        uhash_setKeyDeleter(tmpOlsonToMeta, deleteUCharString);
-        uhash_setValueDeleter(tmpOlsonToMeta, deleteUVector);
-
-        umtx_lock(&gZoneMetaLock);
-        {
-            if (!gOlsonToMetaInitialized) {
-                gOlsonToMeta = tmpOlsonToMeta;
-                tmpOlsonToMeta = NULL;
-                gOlsonToMetaInitialized = TRUE;
-            }
-        }
-        umtx_unlock(&gZoneMetaLock);
-
-        // OK to call the following multiple times with the same function
-        ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
-        if (tmpOlsonToMeta != NULL) {
-            uhash_close(tmpOlsonToMeta);
-        }
+    umtx_initOnce(gOlsonToMetaInitOnce, &olsonToMetaInit, status);
+    if (U_FAILURE(status)) {
+        return NULL;
     }
 
     // get the mapping from cache
@@ -735,7 +713,7 @@ ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &re
     char keyBuf[ZID_KEY_MAX + 1];
     int32_t keyLen = 0;
 
-    if (mzid.length() > ZID_KEY_MAX) {
+    if (mzid.isBogus() || mzid.length() > ZID_KEY_MAX) {
         result.setToBogus();
         return result;
     }
@@ -773,86 +751,79 @@ ZoneMeta::getZoneIdByMetazone(const UnicodeString &mzid, const UnicodeString &re
     return result;
 }
 
-void
-ZoneMeta::initAvailableMetaZoneIDs () {
-    UBool initialized;
-    UMTX_CHECK(&gZoneMetaLock, gMetaZoneIDsInitialized, initialized);
-    if (!initialized) {
-        umtx_lock(&gZoneMetaLock);
-        {
-            if (!gMetaZoneIDsInitialized) {
-                UErrorCode status = U_ZERO_ERROR;
-                UHashtable *metaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
-                uhash_setKeyDeleter(metaZoneIDTable, uprv_deleteUObject);
-                // No valueDeleter, because the vector maintain the value objects
-                UVector *metaZoneIDs = NULL;
-                if (U_SUCCESS(status)) {
-                    metaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
-                    if (metaZoneIDs == NULL) {
-                        status = U_MEMORY_ALLOCATION_ERROR;
-                    }
-                } else {
-                    uhash_close(metaZoneIDTable);
-                }
-                if (U_SUCCESS(status)) {
-                    U_ASSERT(metaZoneIDs != NULL);
-                    metaZoneIDs->setDeleter(uprv_free);
-
-                    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
-                    UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
-                    UResourceBundle res;
-                    ures_initStackObject(&res);
-                    while (U_SUCCESS(status) && ures_hasNext(bundle)) {
-                        ures_getNextResource(bundle, &res, &status);
-                        if (U_FAILURE(status)) {
-                            break;
-                        }
-                        const char *mzID = ures_getKey(&res);
-                        int32_t len = uprv_strlen(mzID);
-                        UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
-                        if (uMzID == NULL) {
-                            status = U_MEMORY_ALLOCATION_ERROR;
-                            break;
-                        }
-                        u_charsToUChars(mzID, uMzID, len);
-                        uMzID[len] = 0;
-                        UnicodeString *usMzID = new UnicodeString(uMzID);
-                        if (uhash_get(metaZoneIDTable, usMzID) == NULL) {
-                            metaZoneIDs->addElement((void *)uMzID, status);
-                            uhash_put(metaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
-                        } else {
-                            uprv_free(uMzID);
-                            delete usMzID;
-                        }
-                    }
-                    if (U_SUCCESS(status)) {
-                        gMetaZoneIDs = metaZoneIDs;
-                        gMetaZoneIDTable = metaZoneIDTable;
-                        gMetaZoneIDsInitialized = TRUE;
-                        ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
-                    } else {
-                        uhash_close(metaZoneIDTable);
-                        delete metaZoneIDs;
-                    }
-                    ures_close(&res);
-                    ures_close(bundle);
-                    ures_close(rb);
-                }
-            }
+static void U_CALLCONV initAvailableMetaZoneIDs () {
+    U_ASSERT(gMetaZoneIDs == NULL);
+    U_ASSERT(gMetaZoneIDTable == NULL);
+    ucln_i18n_registerCleanup(UCLN_I18N_ZONEMETA, zoneMeta_cleanup);
+
+    UErrorCode status = U_ZERO_ERROR;
+    gMetaZoneIDTable = uhash_open(uhash_hashUnicodeString, uhash_compareUnicodeString, NULL, &status);
+    if (U_FAILURE(status) || gMetaZoneIDTable == NULL) {
+        gMetaZoneIDTable = NULL;
+        return;
+    }
+    uhash_setKeyDeleter(gMetaZoneIDTable, uprv_deleteUObject);
+    // No valueDeleter, because the vector maintain the value objects
+    gMetaZoneIDs = new UVector(NULL, uhash_compareUChars, status);
+    if (U_FAILURE(status) || gMetaZoneIDs == NULL) {
+        gMetaZoneIDs = NULL;
+        uhash_close(gMetaZoneIDTable);
+        gMetaZoneIDTable = NULL;
+        return;
+    }
+    gMetaZoneIDs->setDeleter(uprv_free);
+
+    UResourceBundle *rb = ures_openDirect(NULL, gMetaZones, &status);
+    UResourceBundle *bundle = ures_getByKey(rb, gMapTimezonesTag, NULL, &status);
+    UResourceBundle res;
+    ures_initStackObject(&res);
+    while (U_SUCCESS(status) && ures_hasNext(bundle)) {
+        ures_getNextResource(bundle, &res, &status);
+        if (U_FAILURE(status)) {
+            break;
         }
-        umtx_unlock(&gZoneMetaLock);
+        const char *mzID = ures_getKey(&res);
+        int32_t len = uprv_strlen(mzID);
+        UChar *uMzID = (UChar*)uprv_malloc(sizeof(UChar) * (len + 1));
+        if (uMzID == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            break;
+        }
+        u_charsToUChars(mzID, uMzID, len);
+        uMzID[len] = 0;
+        UnicodeString *usMzID = new UnicodeString(uMzID);
+        if (uhash_get(gMetaZoneIDTable, usMzID) == NULL) {
+            gMetaZoneIDs->addElement((void *)uMzID, status);
+            uhash_put(gMetaZoneIDTable, (void *)usMzID, (void *)uMzID, &status);
+        } else {
+            uprv_free(uMzID);
+            delete usMzID;
+        }
+    }
+    ures_close(&res);
+    ures_close(bundle);
+    ures_close(rb);
+
+    if (U_FAILURE(status)) {
+        uhash_close(gMetaZoneIDTable);
+        delete gMetaZoneIDs;
+        gMetaZoneIDTable = NULL;
+        gMetaZoneIDs = NULL;
     }
 }
 
 const UVector*
 ZoneMeta::getAvailableMetazoneIDs() {
-    initAvailableMetaZoneIDs();
+    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
     return gMetaZoneIDs;
 }
 
 const UChar*
 ZoneMeta::findMetaZoneID(const UnicodeString& mzid) {
-    initAvailableMetaZoneIDs();
+    umtx_initOnce(gMetaZoneIDsInitOnce, &initAvailableMetaZoneIDs);
+    if (gMetaZoneIDTable == NULL) {
+        return NULL;
+    }
     return (const UChar*)uhash_get(gMetaZoneIDTable, &mzid);
 }