]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/ucurr.cpp
ICU-64232.0.1.tar.gz
[apple/icu.git] / icuSources / common / ucurr.cpp
index eada053d53be2d327e418340d78a15f916d551f4..d3cac894b5ddf3255aa7775fa0873d0c71d6a23a 100644 (file)
@@ -1,3 +1,5 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 **********************************************************************
 * Copyright (c) 2002-2016, International Business Machines
 #include "unicode/ures.h"
 #include "unicode/ustring.h"
 #include "unicode/parsepos.h"
+#include "unicode/uniset.h"
+#include "unicode/usetiter.h"
+#include "unicode/utf16.h"
 #include "ustr_imp.h"
+#include "charstr.h"
 #include "cmemory.h"
 #include "cstring.h"
+#include "static_unicode_sets.h"
 #include "uassert.h"
 #include "umutex.h"
 #include "ucln_cmn.h"
 #include "uenumimp.h"
 #include "uhash.h"
 #include "hash.h"
+#include "uinvchar.h"
 #include "uresimp.h"
 #include "ulist.h"
+#include "uresimp.h"
 #include "ureslocs.h"
 #include "ulocimp.h"
 
+using namespace icu;
+
 //#define UCURR_DEBUG_EQUIV 1
 #ifdef UCURR_DEBUG_EQUIV
 #include "stdio.h"
@@ -58,14 +69,6 @@ static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
 
 static const int32_t MAX_POW10 = UPRV_LENGTHOF(POW10) - 1;
 
-// Defines equivalent currency symbols.
-static const char *EQUIV_CURRENCY_SYMBOLS[][2] = {
-    {"\\u00a5", "\\uffe5"},
-    {"$", "\\ufe69"},
-    {"$", "\\uff04"},
-    {"\\u20a8", "\\u20b9"},
-    {"\\u00a3", "\\u20a4"}};
-
 #define ISO_CURRENCY_CODE_LENGTH 3
 
 //------------------------------------------------------------
@@ -82,29 +85,14 @@ static const char CURRENCY_MAP[] = "CurrencyMap";
 // Tag for default meta-data, in CURRENCY_META
 static const char DEFAULT_META[] = "DEFAULT";
 
-// Variant for legacy pre-euro mapping in CurrencyMap
-static const char VAR_PRE_EURO[] = "PREEURO";
-
-// Variant for legacy euro mapping in CurrencyMap
-static const char VAR_EURO[] = "EURO";
-
 // Variant delimiter
 static const char VAR_DELIM = '_';
-static const char VAR_DELIM_STR[] = "_";
-
-// Variant for legacy euro mapping in CurrencyMap
-//static const char VAR_DELIM_EURO[] = "_EURO";
-
-#define VARIANT_IS_EMPTY    0
-#define VARIANT_IS_EURO     0x1
-#define VARIANT_IS_PREEURO  0x2
 
 // Tag for localized display names (symbols) of currencies
 static const char CURRENCIES[] = "Currencies";
+static const char CURRENCIES_NARROW[] = "Currencies%narrow";
 static const char CURRENCYPLURALS[] = "CurrencyPlurals";
 
-static const UChar EUR_STR[] = {0x0045,0x0055,0x0052,0};
-
 // ISO codes mapping table
 static const UHashtable* gIsoCodes = NULL;
 static icu::UInitOnce gIsoCodesInitOnce = U_INITONCE_INITIALIZER;
@@ -117,7 +105,7 @@ U_NAMESPACE_BEGIN
 
 // EquivIterator iterates over all strings that are equivalent to a given
 // string, s. Note that EquivIterator will never yield s itself.
-class EquivIterator : icu::UMemory {
+class EquivIterator : public icu::UMemory {
 public:
     // Constructor. hash stores the equivalence relationships; s is the string
     // for which we find equivalent strings.
@@ -356,30 +344,10 @@ _findMetaData(const UChar* currency, UErrorCode& ec) {
 
 // -------------------------------------
 
-/**
- * @see VARIANT_IS_EURO
- * @see VARIANT_IS_PREEURO
- */
-static uint32_t
+static void
 idForLocale(const char* locale, char* countryAndVariant, int capacity, UErrorCode* ec)
 {
-    uint32_t variantType = 0;
-    // !!! this is internal only, assumes buffer is not null and capacity is sufficient
-    // Extract the country name and variant name.  We only
-    // recognize two variant names, EURO and PREEURO.
-    char variant[ULOC_FULLNAME_CAPACITY];
     ulocimp_getRegionForSupplementalData(locale, FALSE, countryAndVariant, capacity, ec);
-    uloc_getVariant(locale, variant, sizeof(variant), ec);
-    if (variant[0] != 0) {
-        variantType = (uint32_t)(0 == uprv_strcmp(variant, VAR_EURO))
-                   | ((uint32_t)(0 == uprv_strcmp(variant, VAR_PRE_EURO)) << 1);
-        if (variantType)
-        {
-            uprv_strcat(countryAndVariant, VAR_DELIM_STR);
-            uprv_strcat(countryAndVariant, variant);
-        }
-    }
-    return variantType;
 }
 
 // ------------------------------------------
@@ -397,7 +365,10 @@ U_CDECL_END
 #if !UCONFIG_NO_SERVICE
 struct CReg;
 
-static UMutex gCRegLock = U_MUTEX_INITIALIZER;
+static UMutex *gCRegLock() {
+    static UMutex *m = STATIC_NEW(UMutex);
+    return m;
+}
 static CReg* gCRegHead = 0;
 
 struct CReg : public icu::UMemory {
@@ -423,14 +394,14 @@ struct CReg : public icu::UMemory {
         if (status && U_SUCCESS(*status) && _iso && _id) {
             CReg* n = new CReg(_iso, _id);
             if (n) {
-                umtx_lock(&gCRegLock);
+                umtx_lock(gCRegLock());
                 if (!gCRegHead) {
                     /* register for the first time */
                     ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
                 }
                 n->next = gCRegHead;
                 gCRegHead = n;
-                umtx_unlock(&gCRegLock);
+                umtx_unlock(gCRegLock());
                 return n;
             }
             *status = U_MEMORY_ALLOCATION_ERROR;
@@ -440,7 +411,7 @@ struct CReg : public icu::UMemory {
 
     static UBool unreg(UCurrRegistryKey key) {
         UBool found = FALSE;
-        umtx_lock(&gCRegLock);
+        umtx_lock(gCRegLock());
 
         CReg** p = &gCRegHead;
         while (*p) {
@@ -453,13 +424,13 @@ struct CReg : public icu::UMemory {
             p = &((*p)->next);
         }
 
-        umtx_unlock(&gCRegLock);
+        umtx_unlock(gCRegLock());
         return found;
     }
 
     static const UChar* get(const char* id) {
         const UChar* result = NULL;
-        umtx_lock(&gCRegLock);
+        umtx_lock(gCRegLock());
         CReg* p = gCRegHead;
 
         /* register cleanup of the mutex */
@@ -471,7 +442,7 @@ struct CReg : public icu::UMemory {
             }
             p = p->next;
         }
-        umtx_unlock(&gCRegLock);
+        umtx_unlock(gCRegLock());
         return result;
     }
 
@@ -543,93 +514,83 @@ U_CAPI int32_t U_EXPORT2
 ucurr_forLocale(const char* locale,
                 UChar* buff,
                 int32_t buffCapacity,
-                UErrorCode* ec)
-{
-    int32_t resLen = 0;
-    const UChar* s = NULL;
-    if (ec != NULL && U_SUCCESS(*ec)) {
-        if ((buff && buffCapacity) || !buffCapacity) {
-            UErrorCode localStatus = U_ZERO_ERROR;
-            char id[ULOC_FULLNAME_CAPACITY];
-            if ((resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus))) {
-                // there is a currency keyword. Try to see if it's valid
-                if(buffCapacity > resLen) {
-                    /* Normalize the currency keyword value to upper case. */
-                    T_CString_toUpperCase(id);
-                    u_charsToUChars(id, buff, resLen);
-                }
-            } else {
-                // get country or country_variant in `id'
-                uint32_t variantType = idForLocale(locale, id, sizeof(id), ec);
+                UErrorCode* ec) {
+    if (U_FAILURE(*ec)) { return 0; }
+    if (buffCapacity < 0 || (buff == nullptr && buffCapacity > 0)) {
+        *ec = U_ILLEGAL_ARGUMENT_ERROR;
+        return 0;
+    }
 
-                if (U_FAILURE(*ec)) {
-                    return 0;
-                }
+    char currency[4];  // ISO currency codes are alpha3 codes.
+    UErrorCode localStatus = U_ZERO_ERROR;
+    int32_t resLen = uloc_getKeywordValue(locale, "currency",
+                                          currency, UPRV_LENGTHOF(currency), &localStatus);
+    if (U_SUCCESS(localStatus) && resLen == 3 && uprv_isInvariantString(currency, resLen)) {
+        if (resLen < buffCapacity) {
+            T_CString_toUpperCase(currency);
+            u_charsToUChars(currency, buff, resLen);
+        }
+        return u_terminateUChars(buff, buffCapacity, resLen, ec);
+    }
+
+    // get country or country_variant in `id'
+    char id[ULOC_FULLNAME_CAPACITY];
+    idForLocale(locale, id, UPRV_LENGTHOF(id), ec);
+    if (U_FAILURE(*ec)) {
+        return 0;
+    }
 
 #if !UCONFIG_NO_SERVICE
-                const UChar* result = CReg::get(id);
-                if (result) {
-                    if(buffCapacity > u_strlen(result)) {
-                        u_strcpy(buff, result);
-                    }
-                    return u_strlen(result);
-                }
+    const UChar* result = CReg::get(id);
+    if (result) {
+        if(buffCapacity > u_strlen(result)) {
+            u_strcpy(buff, result);
+        }
+        resLen = u_strlen(result);
+        return u_terminateUChars(buff, buffCapacity, resLen, ec);
+    }
 #endif
-                // Remove variants, which is only needed for registration.
-                char *idDelim = strchr(id, VAR_DELIM);
-                if (idDelim) {
-                    idDelim[0] = 0;
-                }
+    // Remove variants, which is only needed for registration.
+    char *idDelim = uprv_strchr(id, VAR_DELIM);
+    if (idDelim) {
+        idDelim[0] = 0;
+    }
 
-                // Look up the CurrencyMap element in the root bundle.
-                UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
-                UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
-                UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
-                UResourceBundle *currencyReq = ures_getByIndex(countryArray, 0, NULL, &localStatus);
-                s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus);
-
-                /*
-                Get the second item when PREEURO is requested, and this is a known Euro country.
-                If the requested variant is PREEURO, and this isn't a Euro country, assume
-                that the country changed over to the Euro in the future. This is probably
-                an old version of ICU that hasn't been updated yet. The latest currency is
-                probably correct.
-                */
-                if (U_SUCCESS(localStatus)) {
-                    if ((variantType & VARIANT_IS_PREEURO) && u_strcmp(s, EUR_STR) == 0) {
-                        currencyReq = ures_getByIndex(countryArray, 1, currencyReq, &localStatus);
-                        s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus);
-                    }
-                    else if ((variantType & VARIANT_IS_EURO)) {
-                        s = EUR_STR;
-                    }
-                }
-                ures_close(countryArray);
-                ures_close(currencyReq);
+    const UChar* s = NULL;  // Currency code from data file.
+    if (id[0] == 0) {
+        // No point looking in the data for an empty string.
+        // This is what we would get.
+        localStatus = U_MISSING_RESOURCE_ERROR;
+    } else {
+        // Look up the CurrencyMap element in the root bundle.
+        localStatus = U_ZERO_ERROR;
+        UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
+        UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
+        UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
+        UResourceBundle *currencyReq = ures_getByIndex(countryArray, 0, NULL, &localStatus);
+        s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus);
+        ures_close(currencyReq);
+        ures_close(countryArray);
+    }
 
-                if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0)
-                {
-                    // We don't know about it.  Check to see if we support the variant.
-                    uloc_getParent(locale, id, sizeof(id), ec);
-                    *ec = U_USING_FALLBACK_WARNING;
-                    return ucurr_forLocale(id, buff, buffCapacity, ec);
-                }
-                else if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) {
-                    // There is nothing to fallback to. Report the failure/warning if possible.
-                    *ec = localStatus;
-                }
-                if (U_SUCCESS(*ec)) {
-                    if(buffCapacity > resLen) {
-                        u_strcpy(buff, s);
-                    }
-                }
-            }
-            return u_terminateUChars(buff, buffCapacity, resLen, ec);
-        } else {
-            *ec = U_ILLEGAL_ARGUMENT_ERROR;
+    if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0) {
+        // We don't know about it.  Check to see if we support the variant.
+        uloc_getParent(locale, id, UPRV_LENGTHOF(id), ec);
+        *ec = U_USING_FALLBACK_WARNING;
+        // TODO: Loop over the shortened id rather than recursing and
+        // looking again for a currency keyword.
+        return ucurr_forLocale(id, buff, buffCapacity, ec);
+    }
+    if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) {
+        // There is nothing to fallback to. Report the failure/warning if possible.
+        *ec = localStatus;
+    }
+    if (U_SUCCESS(*ec)) {
+        if(buffCapacity > resLen) {
+            u_strcpy(buff, s);
         }
     }
-    return resLen;
+    return u_terminateUChars(buff, buffCapacity, resLen, ec);
 }
 
 // end registration
@@ -646,7 +607,16 @@ static UBool fallback(char *loc) {
         return FALSE;
     }
     UErrorCode status = U_ZERO_ERROR;
-    uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status);
+    if (uprv_strcmp(loc, "en_GB") == 0) {
+        // HACK: See #13368.  We need "en_GB" to fall back to "en_001" instead of "en"
+        // in order to consume the correct data strings.  This hack will be removed
+        // when proper data sink loading is implemented here.
+        // NOTE: "001" adds 1 char over "GB".  However, both call sites allocate
+        // arrays with length ULOC_FULLNAME_CAPACITY (plenty of room for en_001).
+        uprv_strcpy(loc + 3, "001");
+    } else {
+        uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status);
+    }
  /*
     char *i = uprv_strrchr(loc, '_');
     if (i == NULL) {
@@ -682,7 +652,7 @@ ucurr_getName(const UChar* currency,
     }
 
     int32_t choice = (int32_t) nameStyle;
-    if (choice < 0 || choice > 1) {
+    if (choice < 0 || choice > 2) {
         *ec = U_ILLEGAL_ARGUMENT_ERROR;
         return 0;
     }
@@ -715,15 +685,25 @@ ucurr_getName(const UChar* currency,
     
     const UChar* s = NULL;
     ec2 = U_ZERO_ERROR;
-    UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
-
-    rb = ures_getByKey(rb, CURRENCIES, rb, &ec2);
-
-    // Fetch resource with multi-level resource inheritance fallback
-    rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2);
-
-    s = ures_getStringByIndex(rb, choice, len, &ec2);
-    ures_close(rb);
+    LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc, &ec2));
+
+    if (nameStyle == UCURR_NARROW_SYMBOL_NAME) {
+        CharString key;
+        key.append(CURRENCIES_NARROW, ec2);
+        key.append("/", ec2);
+        key.append(buf, ec2);
+        s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2);
+        if (ec2 == U_MISSING_RESOURCE_ERROR) {
+            *ec = U_USING_FALLBACK_WARNING;
+            ec2 = U_ZERO_ERROR;
+            choice = UCURR_SYMBOL_NAME;
+        }
+    }
+    if (s == NULL) {
+        ures_getByKey(rb.getAlias(), CURRENCIES, rb.getAlias(), &ec2);
+        ures_getByKeyWithFallback(rb.getAlias(), buf, rb.getAlias(), &ec2);
+        s = ures_getStringByIndex(rb.getAlias(), choice, len, &ec2);
+    }
 
     // If we've succeeded we're done.  Otherwise, try to fallback.
     // If that fails (because we are already at root) then exit.
@@ -1023,11 +1003,13 @@ collectCurrencyNames(const char* locale,
             (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = len;
             // Add equivalent symbols
             if (currencySymbolsEquiv != NULL) {
-                icu::EquivIterator iter(*currencySymbolsEquiv, UnicodeString(TRUE, s, len));
+                UnicodeString str(TRUE, s, len);
+                icu::EquivIterator iter(*currencySymbolsEquiv, str);
                 const UnicodeString *symbol;
                 while ((symbol = iter.next()) != NULL) {
                     (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
-                    (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*) symbol->getBuffer();
+                    (*currencySymbols)[*total_currency_symbol_count].currencyName =
+                        const_cast<UChar*>(symbol->getBuffer());
                     (*currencySymbols)[*total_currency_symbol_count].flag = 0;
                     (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = symbol->length();
                 }
@@ -1054,11 +1036,11 @@ collectCurrencyNames(const char* locale,
         }
 
         // currency plurals
-        UErrorCode ec3 = U_ZERO_ERROR;
-        UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec3);
+        UErrorCode ec5 = U_ZERO_ERROR;
+        UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec5);
         n = ures_getSize(curr_p);
         for (int32_t i=0; i<n; ++i) {
-            UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec3);
+            UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec5);
             iso = (char*)ures_getKey(names);
             // Using hash to remove duplicated ISO codes in fallback chain.
             if (localeLevel == 0) {
@@ -1076,7 +1058,7 @@ collectCurrencyNames(const char* locale,
             for (int32_t j = 0; j < num; ++j) {
                 // TODO: remove duplicates between singular name and 
                 // currency long name?
-                s = ures_getStringByIndex(names, j, &len, &ec3);
+                s = ures_getStringByIndex(names, j, &len, &ec5);
                 (*currencyNames)[*total_currency_name_count].IsoCode = iso;
                 UChar* upperName = toUpperCase(s, len, locale);
                 (*currencyNames)[*total_currency_name_count].currencyName = upperName;
@@ -1260,17 +1242,28 @@ static void
 linearSearch(const CurrencyNameStruct* currencyNames, 
              int32_t begin, int32_t end,
              const UChar* text, int32_t textLen,
+             int32_t *partialMatchLen,
              int32_t *maxMatchLen, int32_t* maxMatchIndex) {
+    int32_t initialPartialMatchLen = *partialMatchLen;
     for (int32_t index = begin; index <= end; ++index) {
         int32_t len = currencyNames[index].currencyNameLen;
         if (len > *maxMatchLen && len <= textLen &&
             uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(UChar)) == 0) {
+            *partialMatchLen = MAX(*partialMatchLen, len);
             *maxMatchIndex = index;
             *maxMatchLen = len;
 #ifdef UCURR_DEBUG
             printf("maxMatchIndex = %d, maxMatchLen = %d\n",
                    *maxMatchIndex, *maxMatchLen);
 #endif
+        } else {
+            // Check for partial matches.
+            for (int32_t i=initialPartialMatchLen; i<MIN(len, textLen); i++) {
+                if (currencyNames[index].currencyName[i] != text[i]) {
+                    break;
+                }
+                *partialMatchLen = MAX(*partialMatchLen, i + 1);
+            }
         }
     }
 }
@@ -1287,7 +1280,8 @@ linearSearch(const CurrencyNameStruct* currencyNames,
 static void
 searchCurrencyName(const CurrencyNameStruct* currencyNames, 
                    int32_t total_currency_count,
-                   const UChar* text, int32_t textLen, 
+                   const UChar* text, int32_t textLen,
+                   int32_t *partialMatchLen,
                    int32_t* maxMatchLen, int32_t* maxMatchIndex) {
     *maxMatchIndex = -1;
     *maxMatchLen = 0;
@@ -1317,6 +1311,7 @@ searchCurrencyName(const CurrencyNameStruct* currencyNames,
         if (binarySearchBegin == -1) { // did not find the range
             break;
         }
+        *partialMatchLen = MAX(*partialMatchLen, index + 1);
         if (matchIndex != -1) { 
             // find an exact match for text from text[0] to text[index] 
             // in currencyNames array.
@@ -1327,6 +1322,7 @@ searchCurrencyName(const CurrencyNameStruct* currencyNames,
             // linear search if within threshold.
             linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
                          text, textLen,
+                         partialMatchLen,
                          maxMatchLen, maxMatchIndex);
             break;
         }
@@ -1360,7 +1356,10 @@ static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {NULL};
 // It is a simple round-robin replacement strategy.
 static int8_t currentCacheEntryIndex = 0;
 
-static UMutex gCurrencyCacheMutex = U_MUTEX_INITIALIZER;
+static UMutex *gCurrencyCacheMutex() {
+    static UMutex *m = STATIC_NEW(UMutex);
+    return m;
+}
 
 // Cache deletion
 static void
@@ -1395,19 +1394,13 @@ currency_cache_cleanup(void) {
 }
 
 
-U_CAPI void
-uprv_parseCurrency(const char* locale,
-                   const icu::UnicodeString& text,
-                   icu::ParsePosition& pos,
-                   int8_t type,
-                   UChar* result,
-                   UErrorCode& ec)
-{
-    U_NAMESPACE_USE
-
-    if (U_FAILURE(ec)) {
-        return;
-    }
+/**
+ * Loads the currency name data from the cache, or from resource bundles if necessary.
+ * The refCount is automatically incremented.  It is the caller's responsibility
+ * to decrement it when done!
+ */
+static CurrencyNameCacheEntry*
+getCacheEntry(const char* locale, UErrorCode& ec) {
 
     int32_t total_currency_name_count = 0;
     CurrencyNameStruct* currencyNames = NULL;
@@ -1415,10 +1408,10 @@ uprv_parseCurrency(const char* locale,
     CurrencyNameStruct* currencySymbols = NULL;
     CurrencyNameCacheEntry* cacheEntry = NULL;
 
-    umtx_lock(&gCurrencyCacheMutex);
+    umtx_lock(gCurrencyCacheMutex());
     // in order to handle racing correctly,
     // not putting 'search' in a separate function.
-    int8_t  found = -1;
+    int8_t found = -1;
     for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
         if (currCache[i]!= NULL &&
             uprv_strcmp(locale, currCache[i]->locale) == 0) {
@@ -1428,21 +1421,16 @@ uprv_parseCurrency(const char* locale,
     }
     if (found != -1) {
         cacheEntry = currCache[found];
-        currencyNames = cacheEntry->currencyNames;
-        total_currency_name_count = cacheEntry->totalCurrencyNameCount;
-        currencySymbols = cacheEntry->currencySymbols;
-        total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
         ++(cacheEntry->refCount);
     }
-    umtx_unlock(&gCurrencyCacheMutex);
+    umtx_unlock(gCurrencyCacheMutex());
     if (found == -1) {
         collectCurrencyNames(locale, &currencyNames, &total_currency_name_count, &currencySymbols, &total_currency_symbol_count, ec);
         if (U_FAILURE(ec)) {
-            return;
+            return NULL;
         }
-        umtx_lock(&gCurrencyCacheMutex);
+        umtx_lock(gCurrencyCacheMutex());
         // check again.
-        int8_t  found = -1;
         for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
             if (currCache[i]!= NULL &&
                 uprv_strcmp(locale, currCache[i]->locale) == 0) {
@@ -1473,20 +1461,50 @@ uprv_parseCurrency(const char* locale,
             cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count;
             cacheEntry->refCount = 2; // one for cache, one for reference
             currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM;
-            ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cache_cleanup);
+            ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
         } else {
             deleteCurrencyNames(currencyNames, total_currency_name_count);
             deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
             cacheEntry = currCache[found];
-            currencyNames = cacheEntry->currencyNames;
-            total_currency_name_count = cacheEntry->totalCurrencyNameCount;
-            currencySymbols = cacheEntry->currencySymbols;
-            total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
             ++(cacheEntry->refCount);
         }
-        umtx_unlock(&gCurrencyCacheMutex);
+        umtx_unlock(gCurrencyCacheMutex());
+    }
+
+    return cacheEntry;
+}
+
+static void releaseCacheEntry(CurrencyNameCacheEntry* cacheEntry) {
+    umtx_lock(gCurrencyCacheMutex());
+    --(cacheEntry->refCount);
+    if (cacheEntry->refCount == 0) {  // remove
+        deleteCacheEntry(cacheEntry);
+    }
+    umtx_unlock(gCurrencyCacheMutex());
+}
+
+U_CAPI void
+uprv_parseCurrency(const char* locale,
+                   const icu::UnicodeString& text,
+                   icu::ParsePosition& pos,
+                   int8_t type,
+                   int32_t* partialMatchLen,
+                   UChar* result,
+                   UErrorCode& ec) {
+    U_NAMESPACE_USE
+    if (U_FAILURE(ec)) {
+        return;
+    }
+    CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
+    if (U_FAILURE(ec)) {
+        return;
     }
 
+    int32_t total_currency_name_count = cacheEntry->totalCurrencyNameCount;
+    CurrencyNameStruct* currencyNames = cacheEntry->currencyNames;
+    int32_t total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
+    CurrencyNameStruct* currencySymbols = cacheEntry->currencySymbols;
+
     int32_t start = pos.getIndex();
 
     UChar inputText[MAX_CURRENCY_NAME_LEN];  
@@ -1496,11 +1514,14 @@ uprv_parseCurrency(const char* locale,
     UErrorCode ec1 = U_ZERO_ERROR;
     textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1);
 
+    // Make sure partialMatchLen is initialized
+    *partialMatchLen = 0;
+
     int32_t max = 0;
     int32_t matchIndex = -1;
     // case in-sensitive comparision against currency names
     searchCurrencyName(currencyNames, total_currency_name_count, 
-                       upperText, textLen, &max, &matchIndex);
+                       upperText, textLen, partialMatchLen, &max, &matchIndex);
 
 #ifdef UCURR_DEBUG
     printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex);
@@ -1511,7 +1532,8 @@ uprv_parseCurrency(const char* locale,
     if (type != UCURR_LONG_NAME) {  // not name only
         // case sensitive comparison against currency symbols and ISO code.
         searchCurrencyName(currencySymbols, total_currency_symbol_count, 
-                           inputText, textLen, 
+                           inputText, textLen,
+                           partialMatchLen,
                            &maxInSymbol, &matchIndexInSymbol);
     }
 
@@ -1528,15 +1550,38 @@ uprv_parseCurrency(const char* locale,
     } else if (maxInSymbol >= max && matchIndexInSymbol != -1) {
         u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4);
         pos.setIndex(start + maxInSymbol);
-    } 
+    }
 
     // decrease reference count
-    umtx_lock(&gCurrencyCacheMutex);
-    --(cacheEntry->refCount);
-    if (cacheEntry->refCount == 0) {  // remove 
-        deleteCacheEntry(cacheEntry);
+    releaseCacheEntry(cacheEntry);
+}
+
+void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) {
+    U_NAMESPACE_USE
+    if (U_FAILURE(ec)) {
+        return;
     }
-    umtx_unlock(&gCurrencyCacheMutex);
+    CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
+    if (U_FAILURE(ec)) {
+        return;
+    }
+
+    for (int32_t i=0; i<cacheEntry->totalCurrencySymbolCount; i++) {
+        const CurrencyNameStruct& info = cacheEntry->currencySymbols[i];
+        UChar32 cp;
+        U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
+        result.add(cp);
+    }
+
+    for (int32_t i=0; i<cacheEntry->totalCurrencyNameCount; i++) {
+        const CurrencyNameStruct& info = cacheEntry->currencyNames[i];
+        UChar32 cp;
+        U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
+        result.add(cp);
+    }
+
+    // decrease reference count
+    releaseCacheEntry(cacheEntry);
 }
 
 
@@ -1642,7 +1687,7 @@ typedef struct UCurrencyContext {
 Please keep this list in alphabetical order.
 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
 of these items.
-ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html
+ISO-4217: https://www.iso.org/iso-4217-currency-codes.html
 */
 static const struct CurrencyList {
     const char *currency;
@@ -1702,7 +1747,8 @@ static const struct CurrencyList {
     {"BUK", UCURR_COMMON|UCURR_DEPRECATED},
     {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"BYB", UCURR_COMMON|UCURR_DEPRECATED},
-    {"BYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"BYN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"BYR", UCURR_COMMON|UCURR_DEPRECATED},
     {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED},
@@ -1712,6 +1758,7 @@ static const struct CurrencyList {
     {"CLE", UCURR_COMMON|UCURR_DEPRECATED},
     {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
     {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"CNH", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
     {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED},
     {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED},
@@ -1734,7 +1781,7 @@ static const struct CurrencyList {
     {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED},
     {"EEK", UCURR_COMMON|UCURR_DEPRECATED},
     {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED},
-    {"EQE", UCURR_COMMON|UCURR_DEPRECATED},
+    {"EQE", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove?
     {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED},
     {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED},
@@ -1758,7 +1805,7 @@ static const struct CurrencyList {
     {"GRD", UCURR_COMMON|UCURR_DEPRECATED},
     {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"GWE", UCURR_COMMON|UCURR_DEPRECATED},
-    {"GWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"GWP", UCURR_COMMON|UCURR_DEPRECATED},
     {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED},
@@ -1796,13 +1843,13 @@ static const struct CurrencyList {
     {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED},
-    {"LSM", UCURR_COMMON|UCURR_DEPRECATED},
-    {"LTL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"LSM", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove?
+    {"LTL", UCURR_COMMON|UCURR_DEPRECATED},
     {"LTT", UCURR_COMMON|UCURR_DEPRECATED},
     {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED},
     {"LUF", UCURR_COMMON|UCURR_DEPRECATED},
     {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED},
-    {"LVL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"LVL", UCURR_COMMON|UCURR_DEPRECATED},
     {"LVR", UCURR_COMMON|UCURR_DEPRECATED},
     {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
@@ -1818,18 +1865,19 @@ static const struct CurrencyList {
     {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
-    {"MRO", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"MRO", UCURR_COMMON|UCURR_DEPRECATED},
+    {"MRU", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"MTL", UCURR_COMMON|UCURR_DEPRECATED},
     {"MTP", UCURR_COMMON|UCURR_DEPRECATED},
     {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
-    {"MVP", UCURR_COMMON|UCURR_DEPRECATED},
+    {"MVP", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove?
     {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"MXP", UCURR_COMMON|UCURR_DEPRECATED},
     {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
     {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
-    {"MZE", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"MZE", UCURR_COMMON|UCURR_DEPRECATED},
     {"MZM", UCURR_COMMON|UCURR_DEPRECATED},
     {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
@@ -1870,15 +1918,16 @@ static const struct CurrencyList {
     {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"SIT", UCURR_COMMON|UCURR_DEPRECATED},
-    {"SKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"SKK", UCURR_COMMON|UCURR_DEPRECATED},
     {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"SRG", UCURR_COMMON|UCURR_DEPRECATED},
     {"SSP", UCURR_COMMON|UCURR_NON_DEPRECATED},
-    {"STD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"STD", UCURR_COMMON|UCURR_DEPRECATED},
+    {"STN", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"SUR", UCURR_COMMON|UCURR_DEPRECATED},
-    {"SVC", UCURR_COMMON|UCURR_NON_DEPRECATED},
+    {"SVC", UCURR_COMMON|UCURR_DEPRECATED},
     {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED},
@@ -1927,7 +1976,7 @@ static const struct CurrencyList {
     {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
     {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
-    {"XRE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+    {"XRE", UCURR_UNCOMMON|UCURR_DEPRECATED},
     {"XSU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
     {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
     {"XUA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
@@ -1938,15 +1987,15 @@ static const struct CurrencyList {
     {"YUM", UCURR_COMMON|UCURR_DEPRECATED},
     {"YUN", UCURR_COMMON|UCURR_DEPRECATED},
     {"YUR", UCURR_COMMON|UCURR_DEPRECATED},
-    {"ZAL", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+    {"ZAL", UCURR_UNCOMMON|UCURR_DEPRECATED},
     {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"ZMK", UCURR_COMMON|UCURR_DEPRECATED},
     {"ZMW", UCURR_COMMON|UCURR_NON_DEPRECATED},
     {"ZRN", UCURR_COMMON|UCURR_DEPRECATED},
     {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED},
+    {"ZWD", UCURR_COMMON|UCURR_DEPRECATED},
     {"ZWL", UCURR_COMMON|UCURR_DEPRECATED},
     {"ZWR", UCURR_COMMON|UCURR_DEPRECATED},
-    {"ZWD", UCURR_COMMON|UCURR_DEPRECATED},
     { NULL, 0 } // Leave here to denote the end of the list.
 };
 
@@ -2117,16 +2166,20 @@ static void U_CALLCONV initIsoCodes(UErrorCode &status) {
 }
 
 static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) {
-    if (U_FAILURE(status)) {
-        return;
-    }
-    int32_t length = UPRV_LENGTHOF(EQUIV_CURRENCY_SYMBOLS);
-    for (int32_t i = 0; i < length; ++i) {
-        icu::UnicodeString lhs(EQUIV_CURRENCY_SYMBOLS[i][0], -1, US_INV);
-        icu::UnicodeString rhs(EQUIV_CURRENCY_SYMBOLS[i][1], -1, US_INV);
-        makeEquivalent(lhs.unescape(), rhs.unescape(), hash, status);
-        if (U_FAILURE(status)) {
-            return;
+    if (U_FAILURE(status)) { return; }
+    for (auto& entry : unisets::kCurrencyEntries) {
+        UnicodeString exemplar(entry.exemplar);
+        const UnicodeSet* set = unisets::get(entry.key);
+        if (set == nullptr) { return; }
+        UnicodeSetIterator it(*set);
+        while (it.next()) {
+            UnicodeString value = it.getString();
+            if (value == exemplar) {
+                // No need to mark the exemplar character as an equivalent
+                continue;
+            }
+            makeEquivalent(exemplar, value, hash, status);
+            if (U_FAILURE(status)) { return; }
         }
     }
 }
@@ -2212,8 +2265,9 @@ ucurr_countCurrencies(const char* locale,
         UErrorCode localStatus = U_ZERO_ERROR;
         char id[ULOC_FULLNAME_CAPACITY];
         uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
+
         // get country or country_variant in `id'
-        /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
+        idForLocale(locale, id, sizeof(id), ec);
 
         if (U_FAILURE(*ec))
         {
@@ -2329,7 +2383,7 @@ ucurr_forLocaleAndDate(const char* locale,
             resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
 
             // get country or country_variant in `id'
-            /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
+            idForLocale(locale, id, sizeof(id), ec);
             if (U_FAILURE(*ec))
             {
                 return 0;