X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/729e4ab9bc6618bc3d8a898e575df7f4019e29ca..b331163bffd790ced0e88b73f44f86d49ccc48a5:/icuSources/common/locid.cpp?ds=sidebyside diff --git a/icuSources/common/locid.cpp b/icuSources/common/locid.cpp index f975d8fb..27e40306 100644 --- a/icuSources/common/locid.cpp +++ b/icuSources/common/locid.cpp @@ -1,6 +1,6 @@ /* ********************************************************************** - * Copyright (C) 1997-2010, International Business Machines + * Copyright (C) 1997-2015, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -32,14 +32,32 @@ #include "unicode/locid.h" #include "unicode/uloc.h" +#include "putilimp.h" +#include "mutex.h" #include "umutex.h" #include "uassert.h" #include "cmemory.h" #include "cstring.h" +#include "uassert.h" #include "uhash.h" #include "ucln_cmn.h" +#include "ustr_imp.h" + +U_CDECL_BEGIN +static UBool U_CALLCONV locale_cleanup(void); +U_CDECL_END + +U_NAMESPACE_BEGIN + +static Locale *gLocaleCache = NULL; +static UInitOnce gLocaleCacheInitOnce = U_INITONCE_INITIALIZER; + +// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale. +static UMutex gDefaultLocaleMutex = U_MUTEX_INITIALIZER; +static UHashtable *gDefaultLocalesHashT = NULL; +static Locale *gDefaultLocale = NULL; -#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) +U_NAMESPACE_END typedef enum ELocalePos { eENGLISH, @@ -75,49 +93,71 @@ U_CFUNC int32_t locale_getKeywords(const char *localeID, UBool valuesToo, UErrorCode *status); -static U_NAMESPACE_QUALIFIER Locale *gLocaleCache = NULL; -static U_NAMESPACE_QUALIFIER Locale *gDefaultLocale = NULL; -static UHashtable *gDefaultLocalesHashT = NULL; - U_CDECL_BEGIN // // Deleter function for Locales owned by the default Locale hash table/ // static void U_CALLCONV deleteLocale(void *obj) { - delete (U_NAMESPACE_QUALIFIER Locale *) obj; + delete (icu::Locale *) obj; } static UBool U_CALLCONV locale_cleanup(void) { U_NAMESPACE_USE - if (gLocaleCache) { - delete [] gLocaleCache; - gLocaleCache = NULL; - } + delete [] gLocaleCache; + gLocaleCache = NULL; + gLocaleCacheInitOnce.reset(); if (gDefaultLocalesHashT) { uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. gDefaultLocalesHashT = NULL; } - else if (gDefaultLocale) { - // The cache wasn't created, and only one default locale was created. - delete gDefaultLocale; - } gDefaultLocale = NULL; - return TRUE; } + + +static void U_CALLCONV locale_init(UErrorCode &status) { + U_NAMESPACE_USE + + U_ASSERT(gLocaleCache == NULL); + gLocaleCache = new Locale[(int)eMAX_LOCALES]; + if (gLocaleCache == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); + gLocaleCache[eROOT] = Locale(""); + gLocaleCache[eENGLISH] = Locale("en"); + gLocaleCache[eFRENCH] = Locale("fr"); + gLocaleCache[eGERMAN] = Locale("de"); + gLocaleCache[eITALIAN] = Locale("it"); + gLocaleCache[eJAPANESE] = Locale("ja"); + gLocaleCache[eKOREAN] = Locale("ko"); + gLocaleCache[eCHINESE] = Locale("zh"); + gLocaleCache[eFRANCE] = Locale("fr", "FR"); + gLocaleCache[eGERMANY] = Locale("de", "DE"); + gLocaleCache[eITALY] = Locale("it", "IT"); + gLocaleCache[eJAPAN] = Locale("ja", "JP"); + gLocaleCache[eKOREA] = Locale("ko", "KR"); + gLocaleCache[eCHINA] = Locale("zh", "CN"); + gLocaleCache[eTAIWAN] = Locale("zh", "TW"); + gLocaleCache[eUK] = Locale("en", "GB"); + gLocaleCache[eUS] = Locale("en", "US"); + gLocaleCache[eCANADA] = Locale("en", "CA"); + gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); +} + U_CDECL_END U_NAMESPACE_BEGIN -// -// locale_set_default_internal. -// -void locale_set_default_internal(const char *id) -{ - UErrorCode status = U_ZERO_ERROR; + +Locale *locale_set_default_internal(const char *id, UErrorCode& status) { + // Synchronize this entire function. + Mutex lock(&gDefaultLocaleMutex); + UBool canonicalize = FALSE; // If given a NULL string for the locale id, grab the default @@ -125,17 +165,10 @@ void locale_set_default_internal(const char *id) // (Different from most other locale APIs, where a null name means use // the current ICU default locale.) if (id == NULL) { - umtx_lock(NULL); - id = uprv_getDefaultLocaleID(); - umtx_unlock(NULL); + id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify. canonicalize = TRUE; // always canonicalize host ID } - // put the locale id into a canonical form, - // in preparation for looking up this locale in the hash table of - // already-created locale objects. - // - status = U_ZERO_ERROR; char localeNameBuf[512]; if (canonicalize) { @@ -146,110 +179,37 @@ void locale_set_default_internal(const char *id) localeNameBuf[sizeof(localeNameBuf)-1] = 0; // Force null termination in event of // a long name filling the buffer. // (long names are truncated.) - - // Lazy creation of the hash table itself, if needed. - UBool isOnlyLocale; - UMTX_CHECK(NULL, (gDefaultLocale == NULL), isOnlyLocale); - if (isOnlyLocale) { - // We haven't seen this locale id before. - // Create a new Locale object for it. - Locale *newFirstDefault = new Locale(Locale::eBOGUS); - if (newFirstDefault == NULL) { - // No way to report errors from here. - return; - } - newFirstDefault->init(localeNameBuf, FALSE); - umtx_lock(NULL); - if (gDefaultLocale == NULL) { - gDefaultLocale = newFirstDefault; // Assignment to gDefaultLocale must happen inside mutex - newFirstDefault = NULL; - ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); - - // We were successful in setting the locale, and we were the first one to set it. - umtx_unlock(NULL); - return; - } - // Else some other thread raced us through here, and set the new Locale. - else { - delete newFirstDefault; - } - umtx_unlock(NULL); - } - - // Create the hash table next, unless the racing thread is setting the same locale as default. - // The new locale might have been set while a racing thread was stuck at the lock - // earlier in this function. (For example, see uprv_getDefaultLocaleID above). - umtx_lock(NULL); - // Only create the hash table if we need to. - if (uprv_strcmp(gDefaultLocale->getName(), localeNameBuf) == 0) { - umtx_unlock(NULL); - return; + // + if (U_FAILURE(status)) { + return gDefaultLocale; } - umtx_unlock(NULL); - // start using the hash table. - - // Lazy creation of the hash table itself, if needed. - UBool hashTableNeedsInit; - UMTX_CHECK(NULL, (gDefaultLocalesHashT == NULL), hashTableNeedsInit); - if (hashTableNeedsInit) { - status = U_ZERO_ERROR; - UHashtable *tHashTable = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); + if (gDefaultLocalesHashT == NULL) { + gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); if (U_FAILURE(status)) { - return; + return gDefaultLocale; } - uhash_setValueDeleter(tHashTable, deleteLocale); - umtx_lock(NULL); - if (gDefaultLocalesHashT == NULL) { - gDefaultLocalesHashT = tHashTable; - ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); - - uhash_put(gDefaultLocalesHashT, (void *)gDefaultLocale->getName(), gDefaultLocale, &status); - } else { - uhash_close(tHashTable); - } - umtx_unlock(NULL); + uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); + ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); } - // Hash table lookup, key is the locale full name - umtx_lock(NULL); Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); - if (newDefault != NULL) { - // We have the requested locale in the hash table already. - // Just set it as default. Inside the mutex lock, for those troublesome processors. - gDefaultLocale = newDefault; - umtx_unlock(NULL); - } else { - umtx_unlock(NULL); - // We haven't seen this locale id before. - // Create a new Locale object for it. + if (newDefault == NULL) { newDefault = new Locale(Locale::eBOGUS); if (newDefault == NULL) { - // No way to report errors from here. - return; + status = U_MEMORY_ALLOCATION_ERROR; + return gDefaultLocale; } newDefault->init(localeNameBuf, FALSE); - - // Add newly created Locale to the hash table of default Locales - const char *key = newDefault->getName(); - U_ASSERT(uprv_strcmp(key, localeNameBuf) == 0); - umtx_lock(NULL); - Locale *hashTableVal = (Locale *)uhash_get(gDefaultLocalesHashT, key); - if (hashTableVal == NULL) { - uhash_put(gDefaultLocalesHashT, (void *)key, newDefault, &status); - gDefaultLocale = newDefault; - // ignore errors from hash table insert. (Couldn't do anything anyway) - // We can still set the default Locale, - // it just wont be cached, and will eventually leak. - } else { - // Some other thread raced us through here, and got the new Locale - // into the hash table before us. Use that one. - gDefaultLocale = hashTableVal; // Assignment to gDefaultLocale must happen inside mutex - delete newDefault; + uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); + if (U_FAILURE(status)) { + return gDefaultLocale; } - umtx_unlock(NULL); } + gDefaultLocale = newDefault; + return gDefaultLocale; } + U_NAMESPACE_END /* sfb 07/21/99 */ @@ -257,7 +217,8 @@ U_CFUNC void locale_set_default(const char *id) { U_NAMESPACE_USE - locale_set_default_internal(id); + UErrorCode status = U_ZERO_ERROR; + locale_set_default_internal(id, status); } /* end */ @@ -265,7 +226,6 @@ U_CFUNC const char * locale_get_default(void) { U_NAMESPACE_USE - return Locale::getDefault().getName(); } @@ -281,16 +241,16 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale) Locale::~Locale() { + if (baseName != fullName) { + uprv_free(baseName); + } + baseName = NULL; /*if fullName is on the heap, we free it*/ if (fullName != fullNameBuffer) { uprv_free(fullName); fullName = NULL; } - if (baseName && baseName != baseNameBuffer) { - uprv_free(baseName); - baseName = NULL; - } } Locale::Locale() @@ -461,12 +421,11 @@ Locale &Locale::operator=(const Locale &other) return *this; } - if (&other == NULL) { - this->setToBogus(); - return *this; - } - /* Free our current storage */ + if (baseName != fullName) { + uprv_free(baseName); + } + baseName = NULL; if(fullName != fullNameBuffer) { uprv_free(fullName); fullName = fullNameBuffer; @@ -482,18 +441,13 @@ Locale &Locale::operator=(const Locale &other) /* Copy the full name */ uprv_strcpy(fullName, other.fullName); - /* baseName is the cached result of getBaseName. if 'other' has a - baseName and it fits in baseNameBuffer, then copy it. otherwise set - it to NULL, and let the user lazy-create it (in getBaseName) if they - want it. */ - if(baseName && baseName != baseNameBuffer) { - uprv_free(baseName); - } - baseName = NULL; - - if(other.baseName == other.baseNameBuffer) { - uprv_strcpy(baseNameBuffer, other.baseNameBuffer); - baseName = baseNameBuffer; + /* Copy the baseName if it differs from fullName. */ + if (other.baseName == other.fullName) { + baseName = fullName; + } else { + if (other.baseName) { + baseName = uprv_strdup(other.baseName); + } } /* Copy the language and country fields */ @@ -518,21 +472,22 @@ Locale::operator==( const Locale& other) const return (uprv_strcmp(other.fullName, fullName) == 0); } +#define ISASCIIALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) + /*This function initializes a Locale from a C locale ID*/ Locale& Locale::init(const char* localeID, UBool canonicalize) { fIsBogus = FALSE; /* Free our current storage */ + if (baseName != fullName) { + uprv_free(baseName); + } + baseName = NULL; if(fullName != fullNameBuffer) { uprv_free(fullName); fullName = fullNameBuffer; } - if(baseName && baseName != baseNameBuffer) { - uprv_free(baseName); - baseName = NULL; - } - // not a loop: // just an easy way to have a common error-exit // without goto and without another function @@ -598,40 +553,46 @@ Locale& Locale::init(const char* localeID, UBool canonicalize) fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName); } - if (fieldLen[0] >= (int32_t)(sizeof(language)) - || (fieldLen[1] == 4 && fieldLen[2] >= (int32_t)(sizeof(country))) - || (fieldLen[1] != 4 && fieldLen[1] >= (int32_t)(sizeof(country)))) + if (fieldLen[0] >= (int32_t)(sizeof(language))) { - break; // error: one of the fields is too long + break; // error: the language field is too long } - variantField = 2; /* Usually the 2nd one, except when a script is used. */ + variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */ if (fieldLen[0] > 0) { /* We have a language */ uprv_memcpy(language, fullName, fieldLen[0]); language[fieldLen[0]] = 0; } - if (fieldLen[1] == 4) { + if (fieldLen[1] == 4 && ISASCIIALPHA(field[1][0]) && + ISASCIIALPHA(field[1][1]) && ISASCIIALPHA(field[1][2]) && + ISASCIIALPHA(field[1][3])) { /* We have at least a script */ uprv_memcpy(script, field[1], fieldLen[1]); script[fieldLen[1]] = 0; - variantField = 3; - if (fieldLen[2] > 0) { - /* We have a country */ - uprv_memcpy(country, field[2], fieldLen[2]); - country[fieldLen[2]] = 0; - } + variantField++; } - else if (fieldLen[1] > 0) { - /* We have a country and no script */ - uprv_memcpy(country, field[1], fieldLen[1]); - country[fieldLen[1]] = 0; + + if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) { + /* We have a country */ + uprv_memcpy(country, field[variantField], fieldLen[variantField]); + country[fieldLen[variantField]] = 0; + variantField++; + } else if (fieldLen[variantField] == 0) { + variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */ } - if (variantField > 0 && fieldLen[variantField] > 0) { + + if (fieldLen[variantField] > 0) { /* We have a variant */ variantBegin = (int32_t)(field[variantField] - fullName); } + err = U_ZERO_ERROR; + initBaseName(err); + if (U_FAILURE(err)) { + break; + } + // successful end of init() return *this; } while(0); /*loop doesn't iterate*/ @@ -642,25 +603,60 @@ Locale& Locale::init(const char* localeID, UBool canonicalize) return *this; } +/* + * Set up the base name. + * If there are no key words, it's exactly the full name. + * If key words exist, it's the full name truncated at the '@' character. + * Need to set up both at init() and after setting a keyword. + */ +void +Locale::initBaseName(UErrorCode &status) { + if (U_FAILURE(status)) { + return; + } + U_ASSERT(baseName==NULL || baseName==fullName); + const char *atPtr = uprv_strchr(fullName, '@'); + const char *eqPtr = uprv_strchr(fullName, '='); + if (atPtr && eqPtr && atPtr < eqPtr) { + // Key words exist. + int32_t baseNameLength = (int32_t)(atPtr - fullName); + baseName = (char *)uprv_malloc(baseNameLength + 1); + if (baseName == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + uprv_strncpy(baseName, fullName, baseNameLength); + baseName[baseNameLength] = 0; + + // The original computation of variantBegin leaves it equal to the length + // of fullName if there is no variant. It should instead be + // the length of the baseName. + if (variantBegin > baseNameLength) { + variantBegin = baseNameLength; + } + } else { + baseName = fullName; + } +} + + int32_t Locale::hashCode() const { - UHashTok hashKey; - hashKey.pointer = fullName; - return uhash_hashChars(hashKey); + return ustr_hashCharsN(fullName, uprv_strlen(fullName)); } void Locale::setToBogus() { /* Free our current storage */ + if(baseName != fullName) { + uprv_free(baseName); + } + baseName = NULL; if(fullName != fullNameBuffer) { uprv_free(fullName); fullName = fullNameBuffer; } - if(baseName && baseName != baseNameBuffer) { - uprv_free(baseName); - baseName = NULL; - } *fullNameBuffer = 0; *language = 0; *script = 0; @@ -671,19 +667,14 @@ Locale::setToBogus() { const Locale& U_EXPORT2 Locale::getDefault() { - const Locale *retLocale; - UMTX_CHECK(NULL, gDefaultLocale, retLocale); - if (retLocale == NULL) { - locale_set_default_internal(NULL); - umtx_lock(NULL); - // Need a mutex in case some other thread set a new - // default inbetween when we set and when we get the new default. For - // processors with weak memory coherency, we might not otherwise see all - // of the newly created new default locale. - retLocale = gDefaultLocale; - umtx_unlock(NULL); + { + Mutex lock(&gDefaultLocaleMutex); + if (gDefaultLocale != NULL) { + return *gDefaultLocale; + } } - return *retLocale; + UErrorCode status = U_ZERO_ERROR; + return *locale_set_default_internal(NULL, status); } @@ -700,7 +691,7 @@ Locale::setDefault( const Locale& newLocale, * This is a convenient way to access the default locale caching mechanisms. */ const char *localeID = newLocale.getName(); - locale_set_default_internal(localeID); + locale_set_default_internal(localeID, status); } Locale U_EXPORT2 @@ -918,46 +909,8 @@ initialization and static destruction. Locale * Locale::getLocaleCache(void) { - umtx_lock(NULL); - UBool needInit = (gLocaleCache == NULL); - umtx_unlock(NULL); - - if (needInit) { - Locale *tLocaleCache = new Locale[(int)eMAX_LOCALES]; - if (tLocaleCache == NULL) { - return NULL; - } - tLocaleCache[eROOT] = Locale(""); - tLocaleCache[eENGLISH] = Locale("en"); - tLocaleCache[eFRENCH] = Locale("fr"); - tLocaleCache[eGERMAN] = Locale("de"); - tLocaleCache[eITALIAN] = Locale("it"); - tLocaleCache[eJAPANESE] = Locale("ja"); - tLocaleCache[eKOREAN] = Locale("ko"); - tLocaleCache[eCHINESE] = Locale("zh"); - tLocaleCache[eFRANCE] = Locale("fr", "FR"); - tLocaleCache[eGERMANY] = Locale("de", "DE"); - tLocaleCache[eITALY] = Locale("it", "IT"); - tLocaleCache[eJAPAN] = Locale("ja", "JP"); - tLocaleCache[eKOREA] = Locale("ko", "KR"); - tLocaleCache[eCHINA] = Locale("zh", "CN"); - tLocaleCache[eTAIWAN] = Locale("zh", "TW"); - tLocaleCache[eUK] = Locale("en", "GB"); - tLocaleCache[eUS] = Locale("en", "US"); - tLocaleCache[eCANADA] = Locale("en", "CA"); - tLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); - - umtx_lock(NULL); - if (gLocaleCache == NULL) { - gLocaleCache = tLocaleCache; - tLocaleCache = NULL; - ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); - } - umtx_unlock(NULL); - if (tLocaleCache) { - delete [] tLocaleCache; // Fancy array delete will destruct each member. - } - } + UErrorCode status = U_ZERO_ERROR; + umtx_initOnce(gLocaleCacheInitOnce, locale_init, status); return gLocaleCache; } @@ -993,9 +946,7 @@ public: } } - virtual ~KeywordEnumeration() { - uprv_free(keywords); - } + virtual ~KeywordEnumeration(); virtual StringEnumeration * clone() const { @@ -1045,6 +996,10 @@ public: const char KeywordEnumeration::fgClassID = '\0'; +KeywordEnumeration::~KeywordEnumeration() { + uprv_free(keywords); +} + StringEnumeration * Locale::createKeywords(UErrorCode &status) const { @@ -1077,33 +1032,14 @@ void Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status) { uloc_setKeywordValue(keywordName, keywordValue, fullName, ULOC_FULLNAME_CAPACITY, &status); + if (U_SUCCESS(status) && baseName == fullName) { + // May have added the first keyword, meaning that the fullName is no longer also the baseName. + initBaseName(status); + } } const char * -Locale::getBaseName() const -{ - // lazy init - UErrorCode status = U_ZERO_ERROR; - // semantically const - if(baseName == 0) { - ((Locale *)this)->baseName = ((Locale *)this)->baseNameBuffer; - int32_t baseNameSize = uloc_getBaseName(fullName, baseName, ULOC_FULLNAME_CAPACITY, &status); - if(baseNameSize >= ULOC_FULLNAME_CAPACITY) { - ((Locale *)this)->baseName = (char *)uprv_malloc(sizeof(char) * baseNameSize + 1); - if (baseName == NULL) { - return baseName; - } - uloc_getBaseName(fullName, baseName, baseNameSize+1, &status); - } - baseName[baseNameSize] = 0; - - // the computation of variantBegin leaves it equal to the length - // of fullName if there is no variant. It should instead be - // the length of the baseName. Patch around this for now. - if (variantBegin == (int32_t)uprv_strlen(fullName)) { - ((Locale*)this)->variantBegin = baseNameSize; - } - } +Locale::getBaseName() const { return baseName; }