diff --git a/icu4c/source/i18n/numsys.cpp b/icu4c/source/i18n/numsys.cpp index 162c50a5fe9..50be63e7b93 100644 --- a/icu4c/source/i18n/numsys.cpp +++ b/icu4c/source/i18n/numsys.cpp @@ -26,6 +26,8 @@ #include "unicode/numsys.h" #include "cstring.h" #include "uassert.h" +#include "ucln_in.h" +#include "umutex.h" #include "uresimp.h" #include "numsys_impl.h" @@ -266,75 +268,82 @@ UBool NumberingSystem::isAlgorithmic() const { return ( algorithmic ); } -StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) { - // TODO(ticket #11908): Init-once static cache, with u_cleanup() callback. - static StringEnumeration* availableNames = nullptr; +namespace { - if (U_FAILURE(status)) { - return nullptr; - } +UVector* gNumsysNames = nullptr; +UInitOnce gNumSysInitOnce = U_INITONCE_INITIALIZER; - if ( availableNames == nullptr ) { - // TODO: Simple array of UnicodeString objects, based on length of table resource? - LocalPointer numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status); - if (U_FAILURE(status)) { - return nullptr; - } - - UErrorCode rbstatus = U_ZERO_ERROR; - UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus); - numberingSystemsInfo = ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus); - if (U_FAILURE(rbstatus)) { - // Don't stomp on the catastrophic failure of OOM. - if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { - status = rbstatus; - } else { - status = U_MISSING_RESOURCE_ERROR; - } - ures_close(numberingSystemsInfo); - return nullptr; - } - - while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) { - LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus)); - if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { - status = rbstatus; // we want to report OOM failure back to the caller. - break; - } - const char *nsName = ures_getKey(nsCurrent.getAlias()); - LocalPointer newElem(new UnicodeString(nsName, -1, US_INV), status); - if (U_SUCCESS(status)) { - numsysNames->addElement(newElem.getAlias(), status); - if (U_SUCCESS(status)) { - newElem.orphan(); // on success, the numsysNames vector owns newElem. - } - } - } - - ures_close(numberingSystemsInfo); - if (U_FAILURE(status)) { - return nullptr; - } - availableNames = new NumsysNameEnumeration(numsysNames.getAlias(), status); - if (availableNames == nullptr) { - status = U_MEMORY_ALLOCATION_ERROR; - return nullptr; - } - numsysNames.orphan(); // The names got adopted. - } - - return availableNames; +U_CFUNC UBool U_CALLCONV numSysCleanup() { + delete gNumsysNames; + gNumsysNames = nullptr; + gNumSysInitOnce.reset(); + return true; } -NumsysNameEnumeration::NumsysNameEnumeration(UVector *numsysNames, UErrorCode& /*status*/) { - pos=0; - fNumsysNames = numsysNames; +U_CFUNC void initNumsysNames(UErrorCode &status) { + U_ASSERT(gNumsysNames == nullptr); + ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS, numSysCleanup); + + // TODO: Simple array of UnicodeString objects, based on length of table resource? + LocalPointer numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status); + if (U_FAILURE(status)) { + return; + } + + UErrorCode rbstatus = U_ZERO_ERROR; + UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus); + numberingSystemsInfo = + ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus); + if (U_FAILURE(rbstatus)) { + // Don't stomp on the catastrophic failure of OOM. + if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { + status = rbstatus; + } else { + status = U_MISSING_RESOURCE_ERROR; + } + ures_close(numberingSystemsInfo); + return; + } + + while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) { + LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus)); + if (rbstatus == U_MEMORY_ALLOCATION_ERROR) { + status = rbstatus; // we want to report OOM failure back to the caller. + break; + } + const char *nsName = ures_getKey(nsCurrent.getAlias()); + LocalPointer newElem(new UnicodeString(nsName, -1, US_INV), status); + if (U_SUCCESS(status)) { + numsysNames->addElement(newElem.getAlias(), status); + if (U_SUCCESS(status)) { + newElem.orphan(); // on success, the numsysNames vector owns newElem. + } + } + } + + ures_close(numberingSystemsInfo); + if (U_SUCCESS(status)) { + gNumsysNames = numsysNames.orphan(); + } + return; +} + +} // end anonymous namespace + +StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) { + umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status); + LocalPointer result(new NumsysNameEnumeration(status), status); + return result.orphan(); +} + +NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) { + (void)status; } const UnicodeString* NumsysNameEnumeration::snext(UErrorCode& status) { - if (U_SUCCESS(status) && (fNumsysNames != nullptr) && (pos < fNumsysNames->size())) { - return (const UnicodeString*)fNumsysNames->elementAt(pos++); + if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) { + return (const UnicodeString*)gNumsysNames->elementAt(pos++); } return nullptr; } @@ -346,11 +355,10 @@ NumsysNameEnumeration::reset(UErrorCode& /*status*/) { int32_t NumsysNameEnumeration::count(UErrorCode& /*status*/) const { - return (fNumsysNames==nullptr) ? 0 : fNumsysNames->size(); + return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size(); } NumsysNameEnumeration::~NumsysNameEnumeration() { - delete fNumsysNames; } U_NAMESPACE_END diff --git a/icu4c/source/i18n/numsys_impl.h b/icu4c/source/i18n/numsys_impl.h index b798286bf1d..e76e634dd23 100644 --- a/icu4c/source/i18n/numsys_impl.h +++ b/icu4c/source/i18n/numsys_impl.h @@ -26,18 +26,16 @@ U_NAMESPACE_BEGIN class NumsysNameEnumeration : public StringEnumeration { public: - // NumsysNameEnumeration instance adopts numsysNames - NumsysNameEnumeration(UVector *numsysNames, UErrorCode& status); + NumsysNameEnumeration(UErrorCode& status); virtual ~NumsysNameEnumeration(); static UClassID U_EXPORT2 getStaticClassID(void); - virtual UClassID getDynamicClassID(void) const; - virtual const UnicodeString* snext(UErrorCode& status); - virtual void reset(UErrorCode& status); - virtual int32_t count(UErrorCode& status) const; + virtual UClassID getDynamicClassID(void) const override; + virtual const UnicodeString* snext(UErrorCode& status) override; + virtual void reset(UErrorCode& status) override; + virtual int32_t count(UErrorCode& status) const override; private: int32_t pos; - UVector *fNumsysNames = nullptr; }; U_NAMESPACE_END diff --git a/icu4c/source/i18n/ucln_in.h b/icu4c/source/i18n/ucln_in.h index 4c13b9ffcb5..2f70a8500e1 100644 --- a/icu4c/source/i18n/ucln_in.h +++ b/icu4c/source/i18n/ucln_in.h @@ -60,6 +60,7 @@ typedef enum ECleanupI18NType { UCLN_I18N_CDFINFO, UCLN_I18N_REGION, UCLN_I18N_LIST_FORMATTER, + UCLN_I18N_NUMSYS, UCLN_I18N_COUNT /* This must be last */ } ECleanupI18NType; diff --git a/icu4c/source/i18n/unicode/numsys.h b/icu4c/source/i18n/unicode/numsys.h index f1c1090279d..5e14ea5c311 100644 --- a/icu4c/source/i18n/unicode/numsys.h +++ b/icu4c/source/i18n/unicode/numsys.h @@ -109,6 +109,10 @@ public: /** * Return a StringEnumeration over all the names of numbering systems known to ICU. * The numbering system names will be in alphabetical (invariant) order. + * + * The returned StringEnumeration is owned by the caller, who must delete it when + * finished with it. + * * @stable ICU 4.2 */ static StringEnumeration * U_EXPORT2 getAvailableNames(UErrorCode& status); diff --git a/icu4c/source/test/cintltst/cnumtst.c b/icu4c/source/test/cintltst/cnumtst.c index 0abdd67423f..b4d3a581399 100644 --- a/icu4c/source/test/cintltst/cnumtst.c +++ b/icu4c/source/test/cintltst/cnumtst.c @@ -2500,33 +2500,37 @@ static void TestUNumberingSystem(void) { } } - status = U_ZERO_ERROR; - uenum = unumsys_openAvailableNames(&status); - if ( U_SUCCESS(status) ) { - int32_t numsysCount = 0; - // sanity check for a couple of number systems that must be in the enumeration - UBool foundLatn = FALSE; - UBool foundArab = FALSE; - while ( (numsys = uenum_next(uenum, NULL, &status)) != NULL && U_SUCCESS(status) ) { - status = U_ZERO_ERROR; - unumsys = unumsys_openByName(numsys, &status); - if ( U_SUCCESS(status) ) { - numsysCount++; - if ( uprv_strcmp(numsys, "latn") ) foundLatn = TRUE; - if ( uprv_strcmp(numsys, "arab") ) foundArab = TRUE; - unumsys_close(unumsys); - } else { - log_err("unumsys_openAvailableNames includes %s but unumsys_openByName on it fails with status %s\n", - numsys, myErrorName(status)); + for (int i=0; i<3; ++i) { + // Run the test of unumsys_openAvailableNames() multiple times. + // Helps verify the management of the internal cache of the names. + status = U_ZERO_ERROR; + uenum = unumsys_openAvailableNames(&status); + if ( U_SUCCESS(status) ) { + int32_t numsysCount = 0; + // sanity check for a couple of number systems that must be in the enumeration + UBool foundLatn = FALSE; + UBool foundArab = FALSE; + while ( (numsys = uenum_next(uenum, NULL, &status)) != NULL && U_SUCCESS(status) ) { + status = U_ZERO_ERROR; + unumsys = unumsys_openByName(numsys, &status); + if ( U_SUCCESS(status) ) { + numsysCount++; + if ( uprv_strcmp(numsys, "latn") ) foundLatn = TRUE; + if ( uprv_strcmp(numsys, "arab") ) foundArab = TRUE; + unumsys_close(unumsys); + } else { + log_err("unumsys_openAvailableNames includes %s but unumsys_openByName on it fails with status %s\n", + numsys, myErrorName(status)); + } } + uenum_close(uenum); + if ( numsysCount < 40 || !foundLatn || !foundArab ) { + log_err("unumsys_openAvailableNames results incomplete: numsysCount %d, foundLatn %d, foundArab %d\n", + numsysCount, foundLatn, foundArab); + } + } else { + log_data_err("unumsys_openAvailableNames fails with status %s\n", myErrorName(status)); } - uenum_close(uenum); - if ( numsysCount < 40 || !foundLatn || !foundArab ) { - log_err("unumsys_openAvailableNames results incomplete: numsysCount %d, foundLatn %d, foundArab %d\n", - numsysCount, foundLatn, foundArab); - } - } else { - log_data_err("unumsys_openAvailableNames fails with status %s\n", myErrorName(status)); } }