tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ucurr.cpp (99437B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 **********************************************************************
      5 * Copyright (c) 2002-2016, International Business Machines
      6 * Corporation and others.  All Rights Reserved.
      7 **********************************************************************
      8 */
      9 
     10 #include "unicode/utypes.h"
     11 
     12 #if !UCONFIG_NO_FORMATTING
     13 
     14 #include <utility>
     15 
     16 #include "unicode/ucurr.h"
     17 #include "unicode/locid.h"
     18 #include "unicode/ures.h"
     19 #include "unicode/ustring.h"
     20 #include "unicode/parsepos.h"
     21 #include "unicode/uniset.h"
     22 #include "unicode/usetiter.h"
     23 #include "unicode/utf16.h"
     24 #include "ustr_imp.h"
     25 #include "charstr.h"
     26 #include "cmemory.h"
     27 #include "cstring.h"
     28 #include "static_unicode_sets.h"
     29 #include "uassert.h"
     30 #include "umutex.h"
     31 #include "ucln_cmn.h"
     32 #include "uenumimp.h"
     33 #include "uhash.h"
     34 #include "hash.h"
     35 #include "uinvchar.h"
     36 #include "uresimp.h"
     37 #include "ulist.h"
     38 #include "uresimp.h"
     39 #include "ureslocs.h"
     40 #include "ulocimp.h"
     41 
     42 using namespace icu;
     43 
     44 //#define UCURR_DEBUG_EQUIV 1
     45 #ifdef UCURR_DEBUG_EQUIV
     46 #include "stdio.h"
     47 #endif
     48 //#define UCURR_DEBUG 1
     49 #ifdef UCURR_DEBUG
     50 #include "stdio.h"
     51 #endif
     52 
     53 typedef struct IsoCodeEntry {
     54    const char16_t *isoCode; /* const because it's a reference to a resource bundle string. */
     55    UDate from;
     56    UDate to;
     57 } IsoCodeEntry;
     58 
     59 //------------------------------------------------------------
     60 // Constants
     61 
     62 // Default currency meta data of last resort.  We try to use the
     63 // defaults encoded in the meta data resource bundle.  If there is a
     64 // configuration/build error and these are not available, we use these
     65 // hard-coded defaults (which should be identical).
     66 static const int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 };
     67 
     68 // POW10[i] = 10^i, i=0..MAX_POW10
     69 static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
     70                                 1000000, 10000000, 100000000, 1000000000 };
     71 
     72 static const int32_t MAX_POW10 = UPRV_LENGTHOF(POW10) - 1;
     73 
     74 #define ISO_CURRENCY_CODE_LENGTH 3
     75 
     76 //------------------------------------------------------------
     77 // Resource tags
     78 //
     79 
     80 static const char CURRENCY_DATA[] = "supplementalData";
     81 // Tag for meta-data, in root.
     82 static const char CURRENCY_META[] = "CurrencyMeta";
     83 
     84 // Tag for map from countries to currencies, in root.
     85 static const char CURRENCY_MAP[] = "CurrencyMap";
     86 
     87 // Tag for default meta-data, in CURRENCY_META
     88 static const char DEFAULT_META[] = "DEFAULT";
     89 
     90 // Variant delimiter
     91 static const char VAR_DELIM = '_';
     92 
     93 // Tag for localized display names (symbols) of currencies
     94 static const char CURRENCIES[] = "Currencies";
     95 static const char CURRENCIES_NARROW[] = "Currencies%narrow";
     96 static const char CURRENCIES_FORMAL[] = "Currencies%formal";
     97 static const char CURRENCIES_VARIANT[] = "Currencies%variant";
     98 static const char CURRENCYPLURALS[] = "CurrencyPlurals";
     99 
    100 // ISO codes mapping table
    101 static const UHashtable* gIsoCodes = nullptr;
    102 static icu::UInitOnce gIsoCodesInitOnce {};
    103 
    104 // Currency symbol equivalances
    105 static const icu::Hashtable* gCurrSymbolsEquiv = nullptr;
    106 static icu::UInitOnce gCurrSymbolsEquivInitOnce {};
    107 
    108 U_NAMESPACE_BEGIN
    109 
    110 // EquivIterator iterates over all strings that are equivalent to a given
    111 // string, s. Note that EquivIterator will never yield s itself.
    112 class EquivIterator : public icu::UMemory {
    113 public:
    114    // Constructor. hash stores the equivalence relationships; s is the string
    115    // for which we find equivalent strings.
    116    inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s)
    117        : _hash(hash) { 
    118        _start = _current = &s;
    119    }
    120    inline ~EquivIterator() { }
    121 
    122    // next returns the next equivalent string or nullptr if there are no more.
    123    // If s has no equivalent strings, next returns nullptr on the first call.
    124    const icu::UnicodeString *next();
    125 private:
    126    const icu::Hashtable& _hash;
    127    const icu::UnicodeString* _start;
    128    const icu::UnicodeString* _current;
    129 };
    130 
    131 const icu::UnicodeString *
    132 EquivIterator::next() {
    133    const icu::UnicodeString* _next = static_cast<const icu::UnicodeString*>(_hash.get(*_current));
    134    if (_next == nullptr) {
    135        U_ASSERT(_current == _start);
    136        return nullptr;
    137    }
    138    if (*_next == *_start) {
    139        return nullptr;
    140    }
    141    _current = _next;
    142    return _next;
    143 }
    144 
    145 U_NAMESPACE_END
    146 
    147 // makeEquivalent makes lhs and rhs equivalent by updating the equivalence
    148 // relations in hash accordingly.
    149 static void makeEquivalent(
    150    const icu::UnicodeString &lhs,
    151    const icu::UnicodeString &rhs,
    152    icu::Hashtable* hash, UErrorCode &status) {
    153    if (U_FAILURE(status)) {
    154        return;
    155    }
    156    if (lhs == rhs) {
    157        // already equivalent
    158        return;
    159    }
    160    icu::EquivIterator leftIter(*hash, lhs);
    161    icu::EquivIterator rightIter(*hash, rhs);
    162    const icu::UnicodeString *firstLeft = leftIter.next();
    163    const icu::UnicodeString *firstRight = rightIter.next();
    164    const icu::UnicodeString *nextLeft = firstLeft;
    165    const icu::UnicodeString *nextRight = firstRight;
    166    while (nextLeft != nullptr && nextRight != nullptr) {
    167        if (*nextLeft == rhs || *nextRight == lhs) {
    168            // Already equivalent
    169            return;
    170        }
    171        nextLeft = leftIter.next();
    172        nextRight = rightIter.next();
    173    }
    174    // Not equivalent. Must join.
    175    icu::UnicodeString *newFirstLeft;
    176    icu::UnicodeString *newFirstRight;
    177    if (firstRight == nullptr && firstLeft == nullptr) {
    178        // Neither lhs or rhs belong to an equivalence circle, so we form
    179        // a new equivalnce circle of just lhs and rhs.
    180        newFirstLeft = new icu::UnicodeString(rhs);
    181        newFirstRight = new icu::UnicodeString(lhs);
    182    } else if (firstRight == nullptr) {
    183        // lhs belongs to an equivalence circle, but rhs does not, so we link
    184        // rhs into lhs' circle.
    185        newFirstLeft = new icu::UnicodeString(rhs);
    186        newFirstRight = new icu::UnicodeString(*firstLeft);
    187    } else if (firstLeft == nullptr) {
    188        // rhs belongs to an equivlance circle, but lhs does not, so we link
    189        // lhs into rhs' circle.
    190        newFirstLeft = new icu::UnicodeString(*firstRight);
    191        newFirstRight = new icu::UnicodeString(lhs);
    192    } else {
    193        // Both lhs and rhs belong to different equivalnce circles. We link
    194        // them together to form one single, larger equivalnce circle.
    195        newFirstLeft = new icu::UnicodeString(*firstRight);
    196        newFirstRight = new icu::UnicodeString(*firstLeft);
    197    }
    198    if (newFirstLeft == nullptr || newFirstRight == nullptr) {
    199        delete newFirstLeft;
    200        delete newFirstRight;
    201        status = U_MEMORY_ALLOCATION_ERROR;
    202        return;
    203    }
    204    hash->put(lhs, (void *) newFirstLeft, status);
    205    hash->put(rhs, (void *) newFirstRight, status);
    206 }
    207 
    208 // countEquivalent counts how many strings are equivalent to s.
    209 // hash stores all the equivalnce relations.
    210 // countEquivalent does not include s itself in the count.
    211 static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) {
    212    int32_t result = 0;
    213    icu::EquivIterator iter(hash, s);
    214    while (iter.next() != nullptr) {
    215        ++result;
    216    }
    217 #ifdef UCURR_DEBUG_EQUIV
    218 {
    219   char tmp[200];
    220   s.extract(0,s.length(),tmp, "UTF-8");
    221   printf("CountEquivalent('%s') = %d\n", tmp, result);
    222 }
    223 #endif
    224    return result;
    225 }
    226 
    227 static const icu::Hashtable* getCurrSymbolsEquiv();
    228 
    229 //------------------------------------------------------------
    230 // Code
    231 
    232 /**
    233 * Cleanup callback func
    234 */
    235 static UBool U_CALLCONV 
    236 isoCodes_cleanup()
    237 {
    238    if (gIsoCodes != nullptr) {
    239        uhash_close(const_cast<UHashtable *>(gIsoCodes));
    240        gIsoCodes = nullptr;
    241    }
    242    gIsoCodesInitOnce.reset();
    243    return true;
    244 }
    245 
    246 /**
    247 * Cleanup callback func
    248 */
    249 static UBool U_CALLCONV 
    250 currSymbolsEquiv_cleanup()
    251 {
    252    delete const_cast<icu::Hashtable *>(gCurrSymbolsEquiv);
    253    gCurrSymbolsEquiv = nullptr;
    254    gCurrSymbolsEquivInitOnce.reset();
    255    return true;
    256 }
    257 
    258 /**
    259 * Deleter for IsoCodeEntry
    260 */
    261 static void U_CALLCONV
    262 deleteIsoCodeEntry(void *obj) {
    263    IsoCodeEntry* entry = static_cast<IsoCodeEntry*>(obj);
    264    uprv_free(entry);
    265 }
    266 
    267 /**
    268 * Deleter for gCurrSymbolsEquiv.
    269 */
    270 static void U_CALLCONV
    271 deleteUnicode(void *obj) {
    272    icu::UnicodeString* entry = static_cast<icu::UnicodeString*>(obj);
    273    delete entry;
    274 }
    275 
    276 /**
    277 * Unfortunately, we have to convert the char16_t* currency code to char*
    278 * to use it as a resource key.
    279 */
    280 static inline char*
    281 myUCharsToChars(char* resultOfLen4, const char16_t* currency) {
    282    u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH);
    283    resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0;
    284    return resultOfLen4;
    285 }
    286 
    287 /**
    288 * Internal function to look up currency data.  Result is an array of
    289 * four integers.  The first is the fraction digits.  The second is the
    290 * rounding increment, or 0 if none.  The rounding increment is in
    291 * units of 10^(-fraction_digits).  The third and fourth are the same
    292 * except that they are those used in cash transactions ( cashDigits
    293 * and cashRounding ).
    294 */
    295 static const int32_t*
    296 _findMetaData(const char16_t* currency, UErrorCode& ec) {
    297 
    298    if (currency == nullptr || *currency == 0) {
    299        if (U_SUCCESS(ec)) {
    300            ec = U_ILLEGAL_ARGUMENT_ERROR;
    301        }
    302        return LAST_RESORT_DATA;
    303    }
    304 
    305    // Get CurrencyMeta resource out of root locale file.  [This may
    306    // move out of the root locale file later; if it does, update this
    307    // code.]
    308    UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec);
    309    LocalUResourceBundlePointer currencyMeta(ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec));
    310 
    311    if (U_FAILURE(ec)) {
    312        // Config/build error; return hard-coded defaults
    313        return LAST_RESORT_DATA;
    314    }
    315 
    316    // Look up our currency, or if that's not available, then DEFAULT
    317    char buf[ISO_CURRENCY_CODE_LENGTH+1];
    318    UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure
    319    LocalUResourceBundlePointer rb(ures_getByKey(currencyMeta.getAlias(), myUCharsToChars(buf, currency), nullptr, &ec2));
    320      if (U_FAILURE(ec2)) {
    321        rb.adoptInstead(ures_getByKey(currencyMeta.getAlias(),DEFAULT_META, nullptr, &ec));
    322        if (U_FAILURE(ec)) {
    323            // Config/build error; return hard-coded defaults
    324            return LAST_RESORT_DATA;
    325        }
    326    }
    327 
    328    int32_t len;
    329    const int32_t *data = ures_getIntVector(rb.getAlias(), &len, &ec);
    330    if (U_FAILURE(ec) || len != 4) {
    331        // Config/build error; return hard-coded defaults
    332        if (U_SUCCESS(ec)) {
    333            ec = U_INVALID_FORMAT_ERROR;
    334        }
    335        return LAST_RESORT_DATA;
    336    }
    337 
    338    return data;
    339 }
    340 
    341 // -------------------------------------
    342 
    343 static CharString
    344 idForLocale(const char* locale, UErrorCode* ec)
    345 {
    346    return ulocimp_getRegionForSupplementalData(locale, false, *ec);
    347 }
    348 
    349 // ------------------------------------------
    350 //
    351 // Registration
    352 //
    353 //-------------------------------------------
    354 
    355 // don't use ICUService since we don't need fallback
    356 
    357 U_CDECL_BEGIN
    358 static UBool U_CALLCONV currency_cleanup();
    359 U_CDECL_END
    360 
    361 #if !UCONFIG_NO_SERVICE
    362 struct CReg;
    363 
    364 static UMutex gCRegLock;
    365 static CReg* gCRegHead = nullptr;
    366 
    367 struct CReg : public icu::UMemory {
    368    CReg *next;
    369    char16_t iso[ISO_CURRENCY_CODE_LENGTH+1];
    370    char  id[ULOC_FULLNAME_CAPACITY];
    371 
    372    CReg(const char16_t* _iso, const char* _id)
    373        : next(nullptr)
    374    {
    375        uprv_strncpy(id, _id, sizeof(id)-1);
    376        id[sizeof(id)-1] = 0;
    377        u_memcpy(iso, _iso, ISO_CURRENCY_CODE_LENGTH);
    378        iso[ISO_CURRENCY_CODE_LENGTH] = 0;
    379    }
    380 
    381    static UCurrRegistryKey reg(const char16_t* _iso, const char* _id, UErrorCode* status)
    382    {
    383        if (status && U_SUCCESS(*status) && _iso && _id) {
    384            CReg* n = new CReg(_iso, _id);
    385            if (n) {
    386                umtx_lock(&gCRegLock);
    387                if (!gCRegHead) {
    388                    /* register for the first time */
    389                    ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
    390                }
    391                n->next = gCRegHead;
    392                gCRegHead = n;
    393                umtx_unlock(&gCRegLock);
    394                return n;
    395            }
    396            *status = U_MEMORY_ALLOCATION_ERROR;
    397        }
    398        return nullptr;
    399    }
    400 
    401    static UBool unreg(UCurrRegistryKey key) {
    402        UBool found = false;
    403        umtx_lock(&gCRegLock);
    404 
    405        CReg** p = &gCRegHead;
    406        while (*p) {
    407            if (*p == key) {
    408                *p = ((CReg*)key)->next;
    409                delete (CReg*)key;
    410                found = true;
    411                break;
    412            }
    413            p = &((*p)->next);
    414        }
    415 
    416        umtx_unlock(&gCRegLock);
    417        return found;
    418    }
    419 
    420    static const char16_t* get(const char* id) {
    421        const char16_t* result = nullptr;
    422        umtx_lock(&gCRegLock);
    423        CReg* p = gCRegHead;
    424 
    425        /* register cleanup of the mutex */
    426        ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
    427        while (p) {
    428            if (uprv_strcmp(id, p->id) == 0) {
    429                result = p->iso;
    430                break;
    431            }
    432            p = p->next;
    433        }
    434        umtx_unlock(&gCRegLock);
    435        return result;
    436    }
    437 
    438    /* This doesn't need to be thread safe. It's for u_cleanup only. */
    439    static void cleanup() {
    440        while (gCRegHead) {
    441            CReg* n = gCRegHead;
    442            gCRegHead = gCRegHead->next;
    443            delete n;
    444        }
    445    }
    446 };
    447 
    448 // -------------------------------------
    449 
    450 U_CAPI UCurrRegistryKey U_EXPORT2
    451 ucurr_register(const char16_t* isoCode, const char* locale, UErrorCode *status)
    452 {
    453    if (status && U_SUCCESS(*status)) {
    454        CharString id = idForLocale(locale, status);
    455        return CReg::reg(isoCode, id.data(), status);
    456    }
    457    return nullptr;
    458 }
    459 
    460 // -------------------------------------
    461 
    462 U_CAPI UBool U_EXPORT2
    463 ucurr_unregister(UCurrRegistryKey key, UErrorCode* status)
    464 {
    465    if (status && U_SUCCESS(*status)) {
    466        return CReg::unreg(key);
    467    }
    468    return false;
    469 }
    470 #endif /* UCONFIG_NO_SERVICE */
    471 
    472 // -------------------------------------
    473 
    474 /**
    475 * Release all static memory held by currency.
    476 */
    477 /*The declaration here is needed so currency_cleanup()
    478 * can call this function.
    479 */
    480 static UBool U_CALLCONV
    481 currency_cache_cleanup();
    482 
    483 U_CDECL_BEGIN
    484 static UBool U_CALLCONV currency_cleanup() {
    485 #if !UCONFIG_NO_SERVICE
    486    CReg::cleanup();
    487 #endif
    488    /*
    489     * There might be some cached currency data or isoCodes data.
    490     */
    491    currency_cache_cleanup();
    492    isoCodes_cleanup();
    493    currSymbolsEquiv_cleanup();
    494 
    495    return true;
    496 }
    497 U_CDECL_END
    498 
    499 // -------------------------------------
    500 
    501 U_CAPI int32_t U_EXPORT2
    502 ucurr_forLocale(const char* locale,
    503                char16_t* buff,
    504                int32_t buffCapacity,
    505                UErrorCode* ec) {
    506    if (U_FAILURE(*ec)) { return 0; }
    507    if (buffCapacity < 0 || (buff == nullptr && buffCapacity > 0)) {
    508        *ec = U_ILLEGAL_ARGUMENT_ERROR;
    509        return 0;
    510    }
    511 
    512    UErrorCode localStatus = U_ZERO_ERROR;
    513    CharString currency = ulocimp_getKeywordValue(locale, "currency", localStatus);
    514    int32_t resLen = currency.length();
    515 
    516    if (U_SUCCESS(localStatus) && resLen == 3 && uprv_isInvariantString(currency.data(), resLen)) {
    517        if (resLen < buffCapacity) {
    518            T_CString_toUpperCase(currency.data());
    519            u_charsToUChars(currency.data(), buff, resLen);
    520        }
    521        return u_terminateUChars(buff, buffCapacity, resLen, ec);
    522    }
    523 
    524    // get country or country_variant in `id'
    525    CharString id = idForLocale(locale, ec);
    526    if (U_FAILURE(*ec)) {
    527        return 0;
    528    }
    529 
    530 #if !UCONFIG_NO_SERVICE
    531    const char16_t* result = CReg::get(id.data());
    532    if (result) {
    533        if(buffCapacity > u_strlen(result)) {
    534            u_strcpy(buff, result);
    535        }
    536        resLen = u_strlen(result);
    537        return u_terminateUChars(buff, buffCapacity, resLen, ec);
    538    }
    539 #endif
    540    // Remove variants, which is only needed for registration.
    541    char *idDelim = uprv_strchr(id.data(), VAR_DELIM);
    542    if (idDelim) {
    543        id.truncate(idDelim - id.data());
    544    }
    545 
    546    const char16_t* s = nullptr;  // Currency code from data file.
    547    if (id.isEmpty()) {
    548        // No point looking in the data for an empty string.
    549        // This is what we would get.
    550        localStatus = U_MISSING_RESOURCE_ERROR;
    551    } else {
    552        // Look up the CurrencyMap element in the root bundle.
    553        localStatus = U_ZERO_ERROR;
    554        UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
    555        UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
    556        LocalUResourceBundlePointer countryArray(ures_getByKey(rb, id.data(), cm, &localStatus));
    557        // https://unicode-org.atlassian.net/browse/ICU-21997
    558        // Prefer to use currencies that are legal tender.
    559        if (U_SUCCESS(localStatus)) {
    560            int32_t arrayLength = ures_getSize(countryArray.getAlias());
    561            for (int32_t i = 0; i < arrayLength; ++i) {
    562                LocalUResourceBundlePointer currencyReq(
    563                    ures_getByIndex(countryArray.getAlias(), i, nullptr, &localStatus));
    564                // The currency is legal tender if it is *not* marked with tender{"false"}.
    565                UErrorCode tenderStatus = localStatus;
    566                const char16_t *tender =
    567                    ures_getStringByKey(currencyReq.getAlias(), "tender", nullptr, &tenderStatus);
    568                bool isTender = U_FAILURE(tenderStatus) || u_strcmp(tender, u"false") != 0;
    569                if (!isTender && s != nullptr) {
    570                    // We already have a non-tender currency. Ignore all following non-tender ones.
    571                    continue;
    572                }
    573                // Fetch the currency code.
    574                s = ures_getStringByKey(currencyReq.getAlias(), "id", &resLen, &localStatus);
    575                if (isTender) {
    576                    break;
    577                }
    578            }
    579            if (U_SUCCESS(localStatus) && s == nullptr) {
    580                localStatus = U_MISSING_RESOURCE_ERROR;
    581            }
    582        }
    583    }
    584 
    585    if ((U_FAILURE(localStatus)) && strchr(id.data(), '_') != nullptr) {
    586        // We don't know about it.  Check to see if we support the variant.
    587        CharString parent = ulocimp_getParent(locale, *ec);
    588        *ec = U_USING_FALLBACK_WARNING;
    589        // TODO: Loop over the parent rather than recursing and
    590        // looking again for a currency keyword.
    591        return ucurr_forLocale(parent.data(), buff, buffCapacity, ec);
    592    }
    593    if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) {
    594        // There is nothing to fallback to. Report the failure/warning if possible.
    595        *ec = localStatus;
    596    }
    597    if (U_SUCCESS(*ec)) {
    598        if(buffCapacity > resLen) {
    599            u_strcpy(buff, s);
    600        }
    601    }
    602    return u_terminateUChars(buff, buffCapacity, resLen, ec);
    603 }
    604 
    605 // end registration
    606 
    607 /**
    608 * Modify the given locale name by removing the rightmost _-delimited
    609 * element.  If there is none, empty the string ("" == root).
    610 * NOTE: The string "root" is not recognized; do not use it.
    611 * @return true if the fallback happened; false if locale is already
    612 * root ("").
    613 */
    614 static UBool fallback(CharString& loc) {
    615    if (loc.isEmpty()) {
    616        return false;
    617    }
    618    UErrorCode status = U_ZERO_ERROR;
    619    if (loc == "en_GB") {
    620        // HACK: See #13368.  We need "en_GB" to fall back to "en_001" instead of "en"
    621        // in order to consume the correct data strings.  This hack will be removed
    622        // when proper data sink loading is implemented here.
    623        loc.truncate(3);
    624        loc.append("001", status);
    625    } else {
    626        loc = ulocimp_getParent(loc.data(), status);
    627    }
    628 /*
    629    char *i = uprv_strrchr(loc, '_');
    630    if (i == nullptr) {
    631        i = loc;
    632    }
    633    *i = 0;
    634 */
    635    return true;
    636 }
    637 
    638 
    639 U_CAPI const char16_t* U_EXPORT2
    640 ucurr_getName(const char16_t* currency,
    641              const char* locale,
    642              UCurrNameStyle nameStyle,
    643              UBool* isChoiceFormat, // fillin
    644              int32_t* len, // fillin
    645              UErrorCode* ec) {
    646 
    647    // Look up the Currencies resource for the given locale.  The
    648    // Currencies locale data looks like this:
    649    //|en {
    650    //|  Currencies {
    651    //|    USD { "US$", "US Dollar" }
    652    //|    CHF { "Sw F", "Swiss Franc" }
    653    //|    INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
    654    //|    //...
    655    //|  }
    656    //|}
    657 
    658    if (U_FAILURE(*ec)) {
    659        return nullptr;
    660    }
    661 
    662    int32_t choice = (int32_t) nameStyle;
    663    if (choice < 0 || choice > 4) {
    664        *ec = U_ILLEGAL_ARGUMENT_ERROR;
    665        return nullptr;
    666    }
    667 
    668    // In the future, resource bundles may implement multi-level
    669    // fallback.  That is, if a currency is not found in the en_US
    670    // Currencies data, then the en Currencies data will be searched.
    671    // Currently, if a Currencies datum exists in en_US and en, the
    672    // en_US entry hides that in en.
    673 
    674    // We want multi-level fallback for this resource, so we implement
    675    // it manually.
    676 
    677    // Use a separate UErrorCode here that does not propagate out of
    678    // this function.
    679    UErrorCode ec2 = U_ZERO_ERROR;
    680 
    681    if (locale == nullptr) {
    682        locale = uloc_getDefault();
    683    }
    684    CharString loc = ulocimp_getName(locale, ec2);
    685    if (U_FAILURE(ec2)) {
    686        *ec = U_ILLEGAL_ARGUMENT_ERROR;
    687        return nullptr;
    688    }
    689 
    690    char buf[ISO_CURRENCY_CODE_LENGTH+1];
    691    myUCharsToChars(buf, currency);
    692    
    693    /* Normalize the keyword value to uppercase */
    694    T_CString_toUpperCase(buf);
    695    
    696    const char16_t* s = nullptr;
    697    ec2 = U_ZERO_ERROR;
    698    LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc.data(), &ec2));
    699 
    700    if (nameStyle == UCURR_NARROW_SYMBOL_NAME || nameStyle == UCURR_FORMAL_SYMBOL_NAME || nameStyle == UCURR_VARIANT_SYMBOL_NAME) {
    701        CharString key;
    702        switch (nameStyle) {
    703        case UCURR_NARROW_SYMBOL_NAME:
    704            key.append(CURRENCIES_NARROW, ec2);
    705            break;
    706        case UCURR_FORMAL_SYMBOL_NAME:
    707            key.append(CURRENCIES_FORMAL, ec2);
    708            break;
    709        case UCURR_VARIANT_SYMBOL_NAME:
    710            key.append(CURRENCIES_VARIANT, ec2);
    711            break;
    712        default:
    713            *ec = U_UNSUPPORTED_ERROR;
    714            return nullptr;
    715        }
    716        key.append("/", ec2);
    717        key.append(buf, ec2);
    718        s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2);
    719        if (ec2 == U_MISSING_RESOURCE_ERROR) {
    720            *ec = U_USING_FALLBACK_WARNING;
    721            ec2 = U_ZERO_ERROR;
    722            choice = UCURR_SYMBOL_NAME;
    723        }
    724    }
    725    if (s == nullptr) {
    726        ures_getByKey(rb.getAlias(), CURRENCIES, rb.getAlias(), &ec2);
    727        ures_getByKeyWithFallback(rb.getAlias(), buf, rb.getAlias(), &ec2);
    728        s = ures_getStringByIndex(rb.getAlias(), choice, len, &ec2);
    729    }
    730 
    731    // If we've succeeded we're done.  Otherwise, try to fallback.
    732    // If that fails (because we are already at root) then exit.
    733    if (U_SUCCESS(ec2)) {
    734        if (ec2 == U_USING_DEFAULT_WARNING
    735            || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
    736            *ec = ec2;
    737        }
    738    }
    739 
    740    // We no longer support choice format data in names.  Data should not contain
    741    // choice patterns.
    742    if (isChoiceFormat != nullptr) {
    743        *isChoiceFormat = false;
    744    }
    745    if (U_SUCCESS(ec2)) {
    746        U_ASSERT(s != nullptr);
    747        return s;
    748    }
    749 
    750    // If we fail to find a match, use the ISO 4217 code
    751    *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
    752    *ec = U_USING_DEFAULT_WARNING;
    753    return currency;
    754 }
    755 
    756 U_CAPI const char16_t* U_EXPORT2
    757 ucurr_getPluralName(const char16_t* currency,
    758                    const char* locale,
    759                    UBool* isChoiceFormat,
    760                    const char* pluralCount,
    761                    int32_t* len, // fillin
    762                    UErrorCode* ec) {
    763    // Look up the Currencies resource for the given locale.  The
    764    // Currencies locale data looks like this:
    765    //|en {
    766    //|  CurrencyPlurals {
    767    //|    USD{
    768    //|      one{"US dollar"}
    769    //|      other{"US dollars"}
    770    //|    }
    771    //|  }
    772    //|}
    773 
    774    if (U_FAILURE(*ec)) {
    775        return nullptr;
    776    }
    777 
    778    // Use a separate UErrorCode here that does not propagate out of
    779    // this function.
    780    UErrorCode ec2 = U_ZERO_ERROR;
    781 
    782    if (locale == nullptr) {
    783        locale = uloc_getDefault();
    784    }
    785    CharString loc = ulocimp_getName(locale, ec2);
    786    if (U_FAILURE(ec2)) {
    787        *ec = U_ILLEGAL_ARGUMENT_ERROR;
    788        return nullptr;
    789    }
    790 
    791    char buf[ISO_CURRENCY_CODE_LENGTH+1];
    792    myUCharsToChars(buf, currency);
    793 
    794    const char16_t* s = nullptr;
    795    ec2 = U_ZERO_ERROR;
    796    UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc.data(), &ec2);
    797 
    798    rb = ures_getByKey(rb, CURRENCYPLURALS, rb, &ec2);
    799 
    800    // Fetch resource with multi-level resource inheritance fallback
    801    LocalUResourceBundlePointer curr(ures_getByKeyWithFallback(rb, buf, rb, &ec2));
    802 
    803    s = ures_getStringByKeyWithFallback(curr.getAlias(), pluralCount, len, &ec2);
    804    if (U_FAILURE(ec2)) {
    805        //  fall back to "other"
    806        ec2 = U_ZERO_ERROR;
    807        s = ures_getStringByKeyWithFallback(curr.getAlias(), "other", len, &ec2);     
    808        if (U_FAILURE(ec2)) {
    809            // fall back to long name in Currencies
    810            return ucurr_getName(currency, locale, UCURR_LONG_NAME, 
    811                                 isChoiceFormat, len, ec);
    812        }
    813    }
    814 
    815    // If we've succeeded we're done.  Otherwise, try to fallback.
    816    // If that fails (because we are already at root) then exit.
    817    if (U_SUCCESS(ec2)) {
    818        if (ec2 == U_USING_DEFAULT_WARNING
    819            || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
    820            *ec = ec2;
    821        }
    822        U_ASSERT(s != nullptr);
    823        return s;
    824    }
    825 
    826    // If we fail to find a match, use the ISO 4217 code
    827    *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
    828    *ec = U_USING_DEFAULT_WARNING;
    829    return currency;
    830 }
    831 
    832 
    833 //========================================================================
    834 // Following are structure and function for parsing currency names
    835 
    836 #define NEED_TO_BE_DELETED 0x1
    837 
    838 // TODO: a better way to define this?
    839 #define MAX_CURRENCY_NAME_LEN 100
    840 
    841 typedef struct {
    842    const char* IsoCode;  // key
    843    char16_t* currencyName;  // value
    844    int32_t currencyNameLen;  // value length
    845    int32_t flag;  // flags
    846 } CurrencyNameStruct;
    847 
    848 
    849 #ifndef MIN
    850 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
    851 #endif
    852 
    853 #ifndef MAX
    854 #define MAX(a,b) (((a)<(b)) ? (b) : (a))
    855 #endif
    856 
    857 
    858 // Comparison function used in quick sort.
    859 static int U_CALLCONV currencyNameComparator(const void* a, const void* b) {
    860    const CurrencyNameStruct* currName_1 = static_cast<const CurrencyNameStruct*>(a);
    861    const CurrencyNameStruct* currName_2 = static_cast<const CurrencyNameStruct*>(b);
    862    for (int32_t i = 0; 
    863         i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen);
    864         ++i) {
    865        if (currName_1->currencyName[i] < currName_2->currencyName[i]) {
    866            return -1;
    867        }
    868        if (currName_1->currencyName[i] > currName_2->currencyName[i]) {
    869            return 1;
    870        }
    871    }
    872    if (currName_1->currencyNameLen < currName_2->currencyNameLen) {
    873        return -1;
    874    } else if (currName_1->currencyNameLen > currName_2->currencyNameLen) {
    875        return 1;
    876    }
    877    return 0;
    878 }
    879 
    880 
    881 // Give a locale, return the maximum number of currency names associated with
    882 // this locale.
    883 // It gets currency names from resource bundles using fallback.
    884 // It is the maximum number because in the fallback chain, some of the 
    885 // currency names are duplicated.
    886 // For example, given locale as "en_US", the currency names get from resource
    887 // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
    888 // all currency names in "en_US" and "en".
    889 static void
    890 getCurrencyNameCount(const char* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) {
    891    U_NAMESPACE_USE
    892    *total_currency_name_count = 0;
    893    *total_currency_symbol_count = 0;
    894    const char16_t* s = nullptr;
    895    CharString locale;
    896    {
    897        UErrorCode status = U_ZERO_ERROR;
    898        locale.append(loc, status);
    899        if (U_FAILURE(status)) { return; }
    900    }
    901    const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
    902    for (;;) {
    903        UErrorCode ec2 = U_ZERO_ERROR;
    904        // TODO: ures_openDirect?
    905        LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, locale.data(), &ec2));
    906        LocalUResourceBundlePointer curr(ures_getByKey(rb.getAlias(), CURRENCIES, nullptr, &ec2));
    907        int32_t n = ures_getSize(curr.getAlias());
    908        for (int32_t i=0; i<n; ++i) {
    909            LocalUResourceBundlePointer names(ures_getByIndex(curr.getAlias(), i, nullptr, &ec2));
    910            int32_t len;
    911            s = ures_getStringByIndex(names.getAlias(), UCURR_SYMBOL_NAME, &len, &ec2);
    912            ++(*total_currency_symbol_count);  // currency symbol
    913            if (currencySymbolsEquiv != nullptr) {
    914                *total_currency_symbol_count += countEquivalent(*currencySymbolsEquiv, UnicodeString(true, s, len));
    915            }
    916            ++(*total_currency_symbol_count); // iso code
    917            ++(*total_currency_name_count); // long name
    918        }
    919 
    920        // currency plurals
    921        UErrorCode ec3 = U_ZERO_ERROR;
    922        LocalUResourceBundlePointer curr_p(ures_getByKey(rb.getAlias(), CURRENCYPLURALS, nullptr, &ec3));
    923        n = ures_getSize(curr_p.getAlias());
    924        for (int32_t i=0; i<n; ++i) {
    925            LocalUResourceBundlePointer names(ures_getByIndex(curr_p.getAlias(), i, nullptr, &ec3));
    926            *total_currency_name_count += ures_getSize(names.getAlias());
    927        }
    928 
    929        if (!fallback(locale)) {
    930            break;
    931        }
    932    }
    933 }
    934 
    935 static char16_t*
    936 toUpperCase(const char16_t* source, int32_t len, const char* locale) {
    937    char16_t* dest = nullptr;
    938    UErrorCode ec = U_ZERO_ERROR;
    939    int32_t destLen = u_strToUpper(dest, 0, source, len, locale, &ec);
    940 
    941    ec = U_ZERO_ERROR;
    942    dest = static_cast<char16_t*>(uprv_malloc(sizeof(char16_t) * MAX(destLen, len)));
    943    if (dest == nullptr) {
    944        return nullptr;
    945    }
    946    u_strToUpper(dest, destLen, source, len, locale, &ec);
    947    if (U_FAILURE(ec)) {
    948        u_memcpy(dest, source, len);
    949    } 
    950    return dest;
    951 }
    952 
    953 
    954 static void deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count);
    955 // Collect all available currency names associated with the given locale
    956 // (enable fallback chain).
    957 // Read currenc names defined in resource bundle "Currencies" and
    958 // "CurrencyPlural", enable fallback chain.
    959 // return the malloc-ed currency name arrays and the total number of currency
    960 // names in the array.
    961 static void
    962 collectCurrencyNames(const char* locale, 
    963                     CurrencyNameStruct** currencyNames, 
    964                     int32_t* total_currency_name_count, 
    965                     CurrencyNameStruct** currencySymbols, 
    966                     int32_t* total_currency_symbol_count, 
    967                     UErrorCode& ec) {
    968    if (U_FAILURE(ec)) {
    969        *currencyNames = *currencySymbols = nullptr;
    970        *total_currency_name_count = *total_currency_symbol_count = 0;
    971        return;
    972    }
    973    U_NAMESPACE_USE
    974    const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
    975    // Look up the Currencies resource for the given locale.
    976    UErrorCode ec2 = U_ZERO_ERROR;
    977 
    978    if (locale == nullptr) {
    979        locale = uloc_getDefault();
    980    }
    981    CharString loc = ulocimp_getName(locale, ec2);
    982    if (U_FAILURE(ec2)) {
    983        ec = U_ILLEGAL_ARGUMENT_ERROR;
    984        *currencyNames = *currencySymbols = nullptr;
    985        *total_currency_name_count = *total_currency_symbol_count = 0;
    986        return;
    987    }
    988 
    989    // Get maximum currency name count first.
    990    getCurrencyNameCount(loc.data(), total_currency_name_count, total_currency_symbol_count);
    991 
    992    *currencyNames = static_cast<CurrencyNameStruct*>(
    993        uprv_malloc(sizeof(CurrencyNameStruct) * (*total_currency_name_count)));
    994    if(*currencyNames == nullptr) {
    995        *currencySymbols = nullptr;
    996        *total_currency_name_count = *total_currency_symbol_count = 0;
    997        ec = U_MEMORY_ALLOCATION_ERROR;
    998        return;
    999    }
   1000    *currencySymbols = static_cast<CurrencyNameStruct*>(
   1001        uprv_malloc(sizeof(CurrencyNameStruct) * (*total_currency_symbol_count)));
   1002 
   1003    if(*currencySymbols == nullptr) {
   1004        uprv_free(*currencyNames);
   1005        *currencyNames = nullptr;
   1006        *total_currency_name_count = *total_currency_symbol_count = 0;
   1007        ec = U_MEMORY_ALLOCATION_ERROR;
   1008        return;
   1009    }
   1010 
   1011    const char16_t* s = nullptr;  // currency name
   1012    char* iso = nullptr;  // currency ISO code
   1013 
   1014    *total_currency_name_count = 0;
   1015    *total_currency_symbol_count = 0;
   1016 
   1017    UErrorCode ec3 = U_ZERO_ERROR;
   1018    UErrorCode ec4 = U_ZERO_ERROR;
   1019 
   1020    // Using hash to remove duplicates caused by locale fallback
   1021    LocalUHashtablePointer currencyIsoCodes(uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &ec3));
   1022    LocalUHashtablePointer currencyPluralIsoCodes(uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &ec4));
   1023    for (int32_t localeLevel = 0; ; ++localeLevel) {
   1024        ec2 = U_ZERO_ERROR;
   1025        // TODO: ures_openDirect
   1026        LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc.data(), &ec2));
   1027        LocalUResourceBundlePointer curr(ures_getByKey(rb.getAlias(), CURRENCIES, nullptr, &ec2));
   1028        int32_t n = ures_getSize(curr.getAlias());
   1029        for (int32_t i=0; i<n; ++i) {
   1030            LocalUResourceBundlePointer names(ures_getByIndex(curr.getAlias(), i, nullptr, &ec2));
   1031            int32_t len;
   1032            s = ures_getStringByIndex(names.getAlias(), UCURR_SYMBOL_NAME, &len, &ec2);
   1033            // TODO: uhash_put wont change key/value?
   1034            iso = const_cast<char*>(ures_getKey(names.getAlias()));
   1035            if (localeLevel != 0 && uhash_get(currencyIsoCodes.getAlias(), iso) != nullptr) {
   1036                continue;
   1037            }
   1038            uhash_put(currencyIsoCodes.getAlias(), iso, iso, &ec3); 
   1039            // Add currency symbol.
   1040            (*currencySymbols)[(*total_currency_symbol_count)++] = {iso, const_cast<char16_t*>(s), len, 0};
   1041 
   1042            // Add equivalent symbols
   1043            if (currencySymbolsEquiv != nullptr) {
   1044                UnicodeString str(true, s, len);
   1045                icu::EquivIterator iter(*currencySymbolsEquiv, str);
   1046                const UnicodeString *symbol;
   1047                while ((symbol = iter.next()) != nullptr) {
   1048                    (*currencySymbols)[(*total_currency_symbol_count)++]
   1049                        = {iso, const_cast<char16_t*>(symbol->getBuffer()), symbol->length(), 0};
   1050                }
   1051            }
   1052 
   1053            // Add currency long name.
   1054            s = ures_getStringByIndex(names.getAlias(), UCURR_LONG_NAME, &len, &ec2);
   1055            char16_t* upperName = toUpperCase(s, len, locale);
   1056            if (upperName == nullptr) {
   1057                ec = U_MEMORY_ALLOCATION_ERROR;
   1058                goto error;
   1059            }
   1060            (*currencyNames)[(*total_currency_name_count)++] = {iso, upperName, len, NEED_TO_BE_DELETED};
   1061 
   1062            // put (iso, 3, and iso) in to array
   1063            // Add currency ISO code.
   1064            char16_t* isoCode = static_cast<char16_t*>(uprv_malloc(sizeof(char16_t) * 3));
   1065            if (isoCode == nullptr) {
   1066                ec = U_MEMORY_ALLOCATION_ERROR;
   1067                goto error;
   1068            }
   1069            // Must convert iso[] into Unicode
   1070            u_charsToUChars(iso, isoCode, 3);
   1071            (*currencySymbols)[(*total_currency_symbol_count)++] = {iso, isoCode, 3, NEED_TO_BE_DELETED};
   1072        }
   1073 
   1074        // currency plurals
   1075        UErrorCode ec5 = U_ZERO_ERROR;
   1076        LocalUResourceBundlePointer curr_p(ures_getByKey(rb.getAlias(), CURRENCYPLURALS, nullptr, &ec5));
   1077        n = ures_getSize(curr_p.getAlias());
   1078        for (int32_t i=0; i<n; ++i) {
   1079            LocalUResourceBundlePointer names(ures_getByIndex(curr_p.getAlias(), i, nullptr, &ec5));
   1080            iso = const_cast<char*>(ures_getKey(names.getAlias()));
   1081            // Using hash to remove duplicated ISO codes in fallback chain.
   1082            if (localeLevel != 0 && uhash_get(currencyPluralIsoCodes.getAlias(), iso) != nullptr) {
   1083                continue;
   1084            }
   1085            uhash_put(currencyPluralIsoCodes.getAlias(), iso, iso, &ec4);
   1086            int32_t num = ures_getSize(names.getAlias());
   1087            int32_t len;
   1088            for (int32_t j = 0; j < num; ++j) {
   1089                // TODO: remove duplicates between singular name and 
   1090                // currency long name?
   1091                s = ures_getStringByIndex(names.getAlias(), j, &len, &ec5);
   1092                char16_t* upperName = toUpperCase(s, len, locale);
   1093                if (upperName == nullptr) {
   1094                    ec = U_MEMORY_ALLOCATION_ERROR;
   1095                    goto error;
   1096                }
   1097                (*currencyNames)[(*total_currency_name_count)++] = {iso, upperName, len, NEED_TO_BE_DELETED};
   1098            }
   1099        }
   1100 
   1101        if (!fallback(loc)) {
   1102            break;
   1103        }
   1104    }
   1105 
   1106    // quick sort the struct
   1107    qsort(*currencyNames, *total_currency_name_count, 
   1108          sizeof(CurrencyNameStruct), currencyNameComparator);
   1109    qsort(*currencySymbols, *total_currency_symbol_count, 
   1110          sizeof(CurrencyNameStruct), currencyNameComparator);
   1111 
   1112 #ifdef UCURR_DEBUG
   1113    printf("currency name count: %d\n", *total_currency_name_count);
   1114    for (int32_t index = 0; index < *total_currency_name_count; ++index) {
   1115        printf("index: %d\n", index);
   1116        printf("iso: %s\n", (*currencyNames)[index].IsoCode);
   1117        char curNameBuf[1024];
   1118        memset(curNameBuf, 0, 1024);
   1119        u_austrncpy(curNameBuf, (*currencyNames)[index].currencyName, (*currencyNames)[index].currencyNameLen);
   1120        printf("currencyName: %s\n", curNameBuf);
   1121        printf("len: %d\n", (*currencyNames)[index].currencyNameLen);
   1122    }
   1123    printf("currency symbol count: %d\n", *total_currency_symbol_count);
   1124    for (int32_t index = 0; index < *total_currency_symbol_count; ++index) {
   1125        printf("index: %d\n", index);
   1126        printf("iso: %s\n", (*currencySymbols)[index].IsoCode);
   1127        char curNameBuf[1024];
   1128        memset(curNameBuf, 0, 1024);
   1129        u_austrncpy(curNameBuf, (*currencySymbols)[index].currencyName, (*currencySymbols)[index].currencyNameLen);
   1130        printf("currencySymbol: %s\n", curNameBuf);
   1131        printf("len: %d\n", (*currencySymbols)[index].currencyNameLen);
   1132    }
   1133 #endif
   1134    // fail on hashtable errors
   1135    if (U_FAILURE(ec3)) {
   1136      ec = ec3;
   1137    } else if (U_FAILURE(ec4)) {
   1138      ec = ec4;
   1139    }
   1140 
   1141  error:
   1142    // clean up if we got error
   1143    if (U_FAILURE(ec)) {
   1144        deleteCurrencyNames(*currencyNames, *total_currency_name_count);
   1145        deleteCurrencyNames(*currencySymbols, *total_currency_symbol_count);
   1146        *currencyNames = *currencySymbols = nullptr;
   1147        *total_currency_name_count = *total_currency_symbol_count = 0;
   1148    }
   1149 }
   1150 
   1151 // @param  currencyNames: currency names array
   1152 // @param  indexInCurrencyNames: the index of the character in currency names 
   1153 //         array against which the comparison is done
   1154 // @param  key: input text char to compare against
   1155 // @param  begin(IN/OUT): the begin index of matching range in currency names array
   1156 // @param  end(IN/OUT): the end index of matching range in currency names array.
   1157 static int32_t
   1158 binarySearch(const CurrencyNameStruct* currencyNames, 
   1159             int32_t indexInCurrencyNames,
   1160             const char16_t key,
   1161             int32_t* begin, int32_t* end) {
   1162 #ifdef UCURR_DEBUG
   1163    printf("key = %x\n", key);
   1164 #endif
   1165   int32_t first = *begin;
   1166   int32_t last = *end;
   1167   while (first <= last) {
   1168       int32_t mid = (first + last) / 2;  // compute mid point.
   1169       if (indexInCurrencyNames >= currencyNames[mid].currencyNameLen) {
   1170           first = mid + 1;
   1171       } else {
   1172           if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) {
   1173               first = mid + 1;
   1174           }
   1175           else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) {
   1176               last = mid - 1;
   1177           }
   1178           else {
   1179                // Find a match, and looking for ranges
   1180                // Now do two more binary searches. First, on the left side for
   1181                // the greatest L such that CurrencyNameStruct[L] < key.
   1182                int32_t L = *begin;
   1183                int32_t R = mid;
   1184 
   1185 #ifdef UCURR_DEBUG
   1186                printf("mid = %d\n", mid);
   1187 #endif
   1188                while (L < R) {
   1189                    int32_t M = (L + R) / 2;
   1190 #ifdef UCURR_DEBUG
   1191                    printf("L = %d, R = %d, M = %d\n", L, R, M);
   1192 #endif
   1193                    if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) {
   1194                        L = M + 1;
   1195                    } else {
   1196                        if (currencyNames[M].currencyName[indexInCurrencyNames] < key) {
   1197                            L = M + 1;
   1198                        } else {
   1199 #ifdef UCURR_DEBUG
   1200                            U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
   1201 #endif
   1202                            R = M;
   1203                        }
   1204                    }
   1205                }
   1206 #ifdef UCURR_DEBUG
   1207                U_ASSERT(L == R);
   1208 #endif
   1209                *begin = L;
   1210 #ifdef UCURR_DEBUG
   1211                printf("begin = %d\n", *begin);
   1212                U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key);
   1213 #endif
   1214 
   1215                // Now for the second search, finding the least R such that
   1216                // key < CurrencyNameStruct[R].
   1217                L = mid;
   1218                R = *end;
   1219                while (L < R) {
   1220                    int32_t M = (L + R) / 2;
   1221 #ifdef UCURR_DEBUG
   1222                    printf("L = %d, R = %d, M = %d\n", L, R, M);
   1223 #endif
   1224                    if (currencyNames[M].currencyNameLen < indexInCurrencyNames) {
   1225                        L = M + 1;
   1226                    } else {
   1227                        if (currencyNames[M].currencyName[indexInCurrencyNames] > key) {
   1228                            R = M;
   1229                        } else {
   1230 #ifdef UCURR_DEBUG
   1231                            U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
   1232 #endif
   1233                            L = M + 1;
   1234                        }
   1235                    }
   1236                }
   1237 #ifdef UCURR_DEBUG
   1238                U_ASSERT(L == R);
   1239 #endif
   1240                if (currencyNames[R].currencyName[indexInCurrencyNames] > key) {
   1241                    *end = R - 1;
   1242                } else {
   1243                    *end = R;
   1244                }
   1245 #ifdef UCURR_DEBUG
   1246                printf("end = %d\n", *end);
   1247 #endif
   1248 
   1249                // now, found the range. check whether there is exact match
   1250                if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) {
   1251                    return *begin;  // find range and exact match.
   1252                }
   1253                return -1;  // find range, but no exact match.
   1254           }
   1255       }
   1256   }
   1257   *begin = -1;
   1258   *end = -1;
   1259   return -1;    // failed to find range.
   1260 }
   1261 
   1262 
   1263 // Linear search "text" in "currencyNames".
   1264 // @param  begin, end: the begin and end index in currencyNames, within which
   1265 //         range should the search be performed.
   1266 // @param  textLen: the length of the text to be compared
   1267 // @param  maxMatchLen(IN/OUT): passing in the computed max matching length
   1268 //                              pass out the new max  matching length
   1269 // @param  maxMatchIndex: the index in currencyName which has the longest
   1270 //                        match with input text.
   1271 static void
   1272 linearSearch(const CurrencyNameStruct* currencyNames, 
   1273             int32_t begin, int32_t end,
   1274             const char16_t* text, int32_t textLen,
   1275             int32_t *partialMatchLen,
   1276             int32_t *maxMatchLen, int32_t* maxMatchIndex) {
   1277    int32_t initialPartialMatchLen = *partialMatchLen;
   1278    for (int32_t index = begin; index <= end; ++index) {
   1279        int32_t len = currencyNames[index].currencyNameLen;
   1280        if (len > *maxMatchLen && len <= textLen &&
   1281            uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(char16_t)) == 0) {
   1282            *partialMatchLen = MAX(*partialMatchLen, len);
   1283            *maxMatchIndex = index;
   1284            *maxMatchLen = len;
   1285 #ifdef UCURR_DEBUG
   1286            printf("maxMatchIndex = %d, maxMatchLen = %d\n",
   1287                   *maxMatchIndex, *maxMatchLen);
   1288 #endif
   1289        } else {
   1290            // Check for partial matches.
   1291            for (int32_t i=initialPartialMatchLen; i<MIN(len, textLen); i++) {
   1292                if (currencyNames[index].currencyName[i] != text[i]) {
   1293                    break;
   1294                }
   1295                *partialMatchLen = MAX(*partialMatchLen, i + 1);
   1296            }
   1297        }
   1298    }
   1299 }
   1300 
   1301 #define LINEAR_SEARCH_THRESHOLD 10
   1302 
   1303 // Find longest match between "text" and currency names in "currencyNames".
   1304 // @param  total_currency_count: total number of currency names in CurrencyNames.
   1305 // @param  textLen: the length of the text to be compared
   1306 // @param  maxMatchLen: passing in the computed max matching length
   1307 //                              pass out the new max  matching length
   1308 // @param  maxMatchIndex: the index in currencyName which has the longest
   1309 //                        match with input text.
   1310 static void
   1311 searchCurrencyName(const CurrencyNameStruct* currencyNames, 
   1312                   int32_t total_currency_count,
   1313                   const char16_t* text, int32_t textLen,
   1314                   int32_t *partialMatchLen,
   1315                   int32_t* maxMatchLen, int32_t* maxMatchIndex) {
   1316    *maxMatchIndex = -1;
   1317    *maxMatchLen = 0;
   1318    int32_t matchIndex = -1;
   1319    int32_t binarySearchBegin = 0;
   1320    int32_t binarySearchEnd = total_currency_count - 1;
   1321    // It is a variant of binary search.
   1322    // For example, given the currency names in currencyNames array are:
   1323    // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
   1324    // and the input text is BBEXST
   1325    // The first round binary search search "B" in the text against
   1326    // the first char in currency names, and find the first char matching range
   1327    // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
   1328    // The 2nd round binary search search the second "B" in the text against
   1329    // the 2nd char in currency names, and narrow the matching range to
   1330    // "BB BBEX BBEXYZ" (and the maximum matching "BB").
   1331    // The 3rd round returns the range as "BBEX BBEXYZ" (without changing
   1332    // maximum matching).
   1333    // The 4th round returns the same range (the maximum matching is "BBEX").
   1334    // The 5th round returns no matching range.
   1335    for (int32_t index = 0; index < textLen; ++index) {
   1336        // matchIndex saves the one with exact match till the current point.
   1337        // [binarySearchBegin, binarySearchEnd] saves the matching range.
   1338        matchIndex = binarySearch(currencyNames, index,
   1339                                  text[index],
   1340                                  &binarySearchBegin, &binarySearchEnd);
   1341        if (binarySearchBegin == -1) { // did not find the range
   1342            break;
   1343        }
   1344        *partialMatchLen = MAX(*partialMatchLen, index + 1);
   1345        if (matchIndex != -1) { 
   1346            // find an exact match for text from text[0] to text[index] 
   1347            // in currencyNames array.
   1348            *maxMatchLen = index + 1;
   1349            *maxMatchIndex = matchIndex;
   1350        }
   1351        if (binarySearchEnd - binarySearchBegin < LINEAR_SEARCH_THRESHOLD) {
   1352            // linear search if within threshold.
   1353            linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
   1354                         text, textLen,
   1355                         partialMatchLen,
   1356                         maxMatchLen, maxMatchIndex);
   1357            break;
   1358        }
   1359    }
   1360 }
   1361 
   1362 //========================= currency name cache =====================
   1363 typedef struct {
   1364    char locale[ULOC_FULLNAME_CAPACITY];  //key
   1365    // currency names, case insensitive
   1366    CurrencyNameStruct* currencyNames;  // value
   1367    int32_t totalCurrencyNameCount;  // currency name count
   1368    // currency symbols and ISO code, case sensitive
   1369    CurrencyNameStruct* currencySymbols; // value
   1370    int32_t totalCurrencySymbolCount;  // count
   1371    // reference count.
   1372    // reference count is set to 1 when an entry is put to cache.
   1373    // it increases by 1 before accessing, and decreased by 1 after accessing.
   1374    // The entry is deleted when ref count is zero, which means 
   1375    // the entry is replaced out of cache and no process is accessing it.
   1376    int32_t refCount;
   1377 } CurrencyNameCacheEntry;
   1378 
   1379 
   1380 #define CURRENCY_NAME_CACHE_NUM 10
   1381 
   1382 // Reserve 10 cache entries.
   1383 static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {nullptr};
   1384 // Using an index to indicate which entry to be replaced when cache is full.
   1385 // It is a simple round-robin replacement strategy.
   1386 static int8_t currentCacheEntryIndex = 0;
   1387 
   1388 static UMutex gCurrencyCacheMutex;
   1389 
   1390 // Cache deletion
   1391 static void
   1392 deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) {
   1393    for (int32_t index = 0; index < count; ++index) {
   1394        if ( (currencyNames[index].flag & NEED_TO_BE_DELETED) ) {
   1395            uprv_free(currencyNames[index].currencyName);
   1396        }
   1397    }
   1398    uprv_free(currencyNames);
   1399 }
   1400 
   1401 
   1402 static void
   1403 deleteCacheEntry(CurrencyNameCacheEntry* entry) {
   1404    deleteCurrencyNames(entry->currencyNames, entry->totalCurrencyNameCount);
   1405    deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount);
   1406    uprv_free(entry);
   1407 }
   1408 
   1409 
   1410 // Cache clean up
   1411 static UBool U_CALLCONV
   1412 currency_cache_cleanup() {
   1413    for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
   1414        if (currCache[i]) {
   1415            deleteCacheEntry(currCache[i]);
   1416            currCache[i] = nullptr;
   1417        }
   1418    }
   1419    return true;
   1420 }
   1421 
   1422 
   1423 /**
   1424 * Loads the currency name data from the cache, or from resource bundles if necessary.
   1425 * The refCount is automatically incremented.  It is the caller's responsibility
   1426 * to decrement it when done!
   1427 */
   1428 static CurrencyNameCacheEntry*
   1429 getCacheEntry(const char* locale, UErrorCode& ec) {
   1430 
   1431    int32_t total_currency_name_count = 0;
   1432    CurrencyNameStruct* currencyNames = nullptr;
   1433    int32_t total_currency_symbol_count = 0;
   1434    CurrencyNameStruct* currencySymbols = nullptr;
   1435    CurrencyNameCacheEntry* cacheEntry = nullptr;
   1436 
   1437    umtx_lock(&gCurrencyCacheMutex);
   1438    // in order to handle racing correctly,
   1439    // not putting 'search' in a separate function.
   1440    int8_t found = -1;
   1441    for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
   1442        if (currCache[i]!= nullptr &&
   1443            uprv_strcmp(locale, currCache[i]->locale) == 0) {
   1444            found = i;
   1445            break;
   1446        }
   1447    }
   1448    if (found != -1) {
   1449        cacheEntry = currCache[found];
   1450        ++(cacheEntry->refCount);
   1451    }
   1452    umtx_unlock(&gCurrencyCacheMutex);
   1453    if (found == -1) {
   1454        collectCurrencyNames(locale, &currencyNames, &total_currency_name_count, &currencySymbols, &total_currency_symbol_count, ec);
   1455        if (U_FAILURE(ec)) {
   1456            return nullptr;
   1457        }
   1458        umtx_lock(&gCurrencyCacheMutex);
   1459        // check again.
   1460        for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
   1461            if (currCache[i]!= nullptr &&
   1462                uprv_strcmp(locale, currCache[i]->locale) == 0) {
   1463                found = i;
   1464                break;
   1465            }
   1466        }
   1467        if (found == -1) {
   1468            // insert new entry to 
   1469            // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
   1470            // and remove the existing entry 
   1471            // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
   1472            // from cache.
   1473            cacheEntry = currCache[currentCacheEntryIndex];
   1474            if (cacheEntry) {
   1475                --(cacheEntry->refCount);
   1476                // delete if the ref count is zero
   1477                if (cacheEntry->refCount == 0) {
   1478                    deleteCacheEntry(cacheEntry);
   1479                }
   1480            }
   1481            cacheEntry = static_cast<CurrencyNameCacheEntry*>(uprv_malloc(sizeof(CurrencyNameCacheEntry)));
   1482            if (cacheEntry == nullptr) {
   1483                deleteCurrencyNames(currencyNames, total_currency_name_count);
   1484                deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
   1485                ec = U_MEMORY_ALLOCATION_ERROR;
   1486                return nullptr;
   1487            }
   1488            currCache[currentCacheEntryIndex] = cacheEntry;
   1489            uprv_strcpy(cacheEntry->locale, locale);
   1490            cacheEntry->currencyNames = currencyNames;
   1491            cacheEntry->totalCurrencyNameCount = total_currency_name_count;
   1492            cacheEntry->currencySymbols = currencySymbols;
   1493            cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count;
   1494            cacheEntry->refCount = 2; // one for cache, one for reference
   1495            currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM;
   1496            ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
   1497        } else {
   1498            deleteCurrencyNames(currencyNames, total_currency_name_count);
   1499            deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
   1500            cacheEntry = currCache[found];
   1501            ++(cacheEntry->refCount);
   1502        }
   1503        umtx_unlock(&gCurrencyCacheMutex);
   1504    }
   1505 
   1506    return cacheEntry;
   1507 }
   1508 
   1509 static void releaseCacheEntry(CurrencyNameCacheEntry* cacheEntry) {
   1510    umtx_lock(&gCurrencyCacheMutex);
   1511    --(cacheEntry->refCount);
   1512    if (cacheEntry->refCount == 0) {  // remove
   1513        deleteCacheEntry(cacheEntry);
   1514    }
   1515    umtx_unlock(&gCurrencyCacheMutex);
   1516 }
   1517 
   1518 U_CAPI void
   1519 uprv_parseCurrency(const char* locale,
   1520                   const icu::UnicodeString& text,
   1521                   icu::ParsePosition& pos,
   1522                   int8_t type,
   1523                   int32_t* partialMatchLen,
   1524                   char16_t* result,
   1525                   UErrorCode& ec) {
   1526    U_NAMESPACE_USE
   1527    if (U_FAILURE(ec)) {
   1528        return;
   1529    }
   1530    CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
   1531    if (U_FAILURE(ec)) {
   1532        return;
   1533    }
   1534 
   1535    int32_t total_currency_name_count = cacheEntry->totalCurrencyNameCount;
   1536    CurrencyNameStruct* currencyNames = cacheEntry->currencyNames;
   1537    int32_t total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
   1538    CurrencyNameStruct* currencySymbols = cacheEntry->currencySymbols;
   1539 
   1540    int32_t start = pos.getIndex();
   1541 
   1542    char16_t inputText[MAX_CURRENCY_NAME_LEN];
   1543    char16_t upperText[MAX_CURRENCY_NAME_LEN];
   1544    int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start);
   1545    text.extract(start, textLen, inputText);
   1546    UErrorCode ec1 = U_ZERO_ERROR;
   1547    textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1);
   1548 
   1549    // Make sure partialMatchLen is initialized
   1550    *partialMatchLen = 0;
   1551 
   1552    int32_t max = 0;
   1553    int32_t matchIndex = -1;
   1554    // case in-sensitive comparison against currency names
   1555    searchCurrencyName(currencyNames, total_currency_name_count, 
   1556                       upperText, textLen, partialMatchLen, &max, &matchIndex);
   1557 
   1558 #ifdef UCURR_DEBUG
   1559    printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex);
   1560 #endif
   1561 
   1562    int32_t maxInSymbol = 0;
   1563    int32_t matchIndexInSymbol = -1;
   1564    if (type != UCURR_LONG_NAME) {  // not name only
   1565        // case sensitive comparison against currency symbols and ISO code.
   1566        searchCurrencyName(currencySymbols, total_currency_symbol_count, 
   1567                           inputText, textLen,
   1568                           partialMatchLen,
   1569                           &maxInSymbol, &matchIndexInSymbol);
   1570    }
   1571 
   1572 #ifdef UCURR_DEBUG
   1573    printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol);
   1574    if(matchIndexInSymbol != -1) {
   1575      printf("== ISO=%s\n", currencySymbols[matchIndexInSymbol].IsoCode);
   1576    }
   1577 #endif
   1578 
   1579    if (max >= maxInSymbol && matchIndex != -1) {
   1580        u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4);
   1581        pos.setIndex(start + max);
   1582    } else if (maxInSymbol >= max && matchIndexInSymbol != -1) {
   1583        u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4);
   1584        pos.setIndex(start + maxInSymbol);
   1585    }
   1586 
   1587    // decrease reference count
   1588    releaseCacheEntry(cacheEntry);
   1589 }
   1590 
   1591 void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) {
   1592    U_NAMESPACE_USE
   1593    if (U_FAILURE(ec)) {
   1594        return;
   1595    }
   1596    CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
   1597    if (U_FAILURE(ec)) {
   1598        return;
   1599    }
   1600 
   1601    for (int32_t i=0; i<cacheEntry->totalCurrencySymbolCount; i++) {
   1602        const CurrencyNameStruct& info = cacheEntry->currencySymbols[i];
   1603        UChar32 cp;
   1604        U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
   1605        result.add(cp);
   1606    }
   1607 
   1608    for (int32_t i=0; i<cacheEntry->totalCurrencyNameCount; i++) {
   1609        const CurrencyNameStruct& info = cacheEntry->currencyNames[i];
   1610        UChar32 cp;
   1611        U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
   1612        result.add(cp);
   1613    }
   1614 
   1615    // decrease reference count
   1616    releaseCacheEntry(cacheEntry);
   1617 }
   1618 
   1619 
   1620 /**
   1621 * Internal method.  Given a currency ISO code and a locale, return
   1622 * the "static" currency name.  This is usually the same as the
   1623 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
   1624 * format is applied to the number 2.0 (to yield the more common
   1625 * plural) to return a static name.
   1626 *
   1627 * This is used for backward compatibility with old currency logic in
   1628 * DecimalFormat and DecimalFormatSymbols.
   1629 */
   1630 U_CAPI void
   1631 uprv_getStaticCurrencyName(const char16_t* iso, const char* loc,
   1632                           icu::UnicodeString& result, UErrorCode& ec)
   1633 {
   1634    U_NAMESPACE_USE
   1635 
   1636    int32_t len;
   1637    const char16_t* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME,
   1638                                          nullptr /* isChoiceFormat */, &len, &ec);
   1639    if (U_SUCCESS(ec)) {
   1640        result.setTo(currname, len);
   1641    }
   1642 }
   1643 
   1644 U_CAPI int32_t U_EXPORT2
   1645 ucurr_getDefaultFractionDigits(const char16_t* currency, UErrorCode* ec) {
   1646    return ucurr_getDefaultFractionDigitsForUsage(currency,UCURR_USAGE_STANDARD,ec);
   1647 }
   1648 
   1649 U_CAPI int32_t U_EXPORT2
   1650 ucurr_getDefaultFractionDigitsForUsage(const char16_t* currency, const UCurrencyUsage usage, UErrorCode* ec) {
   1651    int32_t fracDigits = 0;
   1652    if (U_SUCCESS(*ec)) {
   1653        switch (usage) {
   1654            case UCURR_USAGE_STANDARD:
   1655                fracDigits = (_findMetaData(currency, *ec))[0];
   1656                break;
   1657            case UCURR_USAGE_CASH:
   1658                fracDigits = (_findMetaData(currency, *ec))[2];
   1659                break;
   1660            default:
   1661                *ec = U_UNSUPPORTED_ERROR;
   1662        }
   1663    }
   1664    return fracDigits;
   1665 }
   1666 
   1667 U_CAPI double U_EXPORT2
   1668 ucurr_getRoundingIncrement(const char16_t* currency, UErrorCode* ec) {
   1669    return ucurr_getRoundingIncrementForUsage(currency, UCURR_USAGE_STANDARD, ec);
   1670 }
   1671 
   1672 U_CAPI double U_EXPORT2
   1673 ucurr_getRoundingIncrementForUsage(const char16_t* currency, const UCurrencyUsage usage, UErrorCode* ec) {
   1674    double result = 0.0;
   1675 
   1676    const int32_t *data = _findMetaData(currency, *ec);
   1677    if (U_SUCCESS(*ec)) {
   1678        int32_t fracDigits;
   1679        int32_t increment;
   1680        switch (usage) {
   1681            case UCURR_USAGE_STANDARD:
   1682                fracDigits = data[0];
   1683                increment = data[1];
   1684                break;
   1685            case UCURR_USAGE_CASH:
   1686                fracDigits = data[2];
   1687                increment = data[3];
   1688                break;
   1689            default:
   1690                *ec = U_UNSUPPORTED_ERROR;
   1691                return result;
   1692        }
   1693 
   1694        // If the meta data is invalid, return 0.0
   1695        if (fracDigits < 0 || fracDigits > MAX_POW10) {
   1696            *ec = U_INVALID_FORMAT_ERROR;
   1697        } else {
   1698            // A rounding value of 0 or 1 indicates no rounding.
   1699            if (increment >= 2) {
   1700                // Return (increment) / 10^(fracDigits).  The only actual rounding data,
   1701                // as of this writing, is CHF { 2, 5 }.
   1702                result = double(increment) / POW10[fracDigits];
   1703            }
   1704        }
   1705    }
   1706 
   1707    return result;
   1708 }
   1709 
   1710 U_CDECL_BEGIN
   1711 
   1712 typedef struct UCurrencyContext {
   1713    uint32_t currType; /* UCurrCurrencyType */
   1714    uint32_t listIdx;
   1715 } UCurrencyContext;
   1716 
   1717 /*
   1718 Please keep this list in alphabetical order.
   1719 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
   1720 of these items.
   1721 ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html
   1722 */
   1723 static const struct CurrencyList {
   1724    const char *currency;
   1725    uint32_t currType;
   1726 } gCurrencyList[] = {
   1727    {"ADP", UCURR_COMMON|UCURR_DEPRECATED},
   1728    {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1729    {"AFA", UCURR_COMMON|UCURR_DEPRECATED},
   1730    {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1731    {"ALK", UCURR_COMMON|UCURR_DEPRECATED},
   1732    {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1733    {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1734    {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1735    {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1736    {"AOK", UCURR_COMMON|UCURR_DEPRECATED},
   1737    {"AON", UCURR_COMMON|UCURR_DEPRECATED},
   1738    {"AOR", UCURR_COMMON|UCURR_DEPRECATED},
   1739    {"ARA", UCURR_COMMON|UCURR_DEPRECATED},
   1740    {"ARL", UCURR_COMMON|UCURR_DEPRECATED},
   1741    {"ARM", UCURR_COMMON|UCURR_DEPRECATED},
   1742    {"ARP", UCURR_COMMON|UCURR_DEPRECATED},
   1743    {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1744    {"ATS", UCURR_COMMON|UCURR_DEPRECATED},
   1745    {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1746    {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1747    {"AZM", UCURR_COMMON|UCURR_DEPRECATED},
   1748    {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1749    {"BAD", UCURR_COMMON|UCURR_DEPRECATED},
   1750    {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1751    {"BAN", UCURR_COMMON|UCURR_DEPRECATED},
   1752    {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1753    {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1754    {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1755    {"BEF", UCURR_COMMON|UCURR_DEPRECATED},
   1756    {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1757    {"BGL", UCURR_COMMON|UCURR_DEPRECATED},
   1758    {"BGM", UCURR_COMMON|UCURR_DEPRECATED},
   1759    {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1760    {"BGO", UCURR_COMMON|UCURR_DEPRECATED},
   1761    {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1762    {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1763    {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1764    {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1765    {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1766    {"BOL", UCURR_COMMON|UCURR_DEPRECATED},
   1767    {"BOP", UCURR_COMMON|UCURR_DEPRECATED},
   1768    {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1769    {"BRB", UCURR_COMMON|UCURR_DEPRECATED},
   1770    {"BRC", UCURR_COMMON|UCURR_DEPRECATED},
   1771    {"BRE", UCURR_COMMON|UCURR_DEPRECATED},
   1772    {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1773    {"BRN", UCURR_COMMON|UCURR_DEPRECATED},
   1774    {"BRR", UCURR_COMMON|UCURR_DEPRECATED},
   1775    {"BRZ", UCURR_COMMON|UCURR_DEPRECATED},
   1776    {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1777    {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1778    {"BUK", UCURR_COMMON|UCURR_DEPRECATED},
   1779    {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1780    {"BYB", UCURR_COMMON|UCURR_DEPRECATED},
   1781    {"BYN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1782    {"BYR", UCURR_COMMON|UCURR_DEPRECATED},
   1783    {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1784    {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1785    {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1786    {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1787    {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1788    {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1789    {"CLE", UCURR_COMMON|UCURR_DEPRECATED},
   1790    {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1791    {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1792    {"CNH", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1793    {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1794    {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1795    {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1796    {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1797    {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1798    {"CSD", UCURR_COMMON|UCURR_DEPRECATED},
   1799    {"CSK", UCURR_COMMON|UCURR_DEPRECATED},
   1800    {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1801    {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1802    {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1803    {"CYP", UCURR_COMMON|UCURR_DEPRECATED},
   1804    {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1805    {"DDM", UCURR_COMMON|UCURR_DEPRECATED},
   1806    {"DEM", UCURR_COMMON|UCURR_DEPRECATED},
   1807    {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1808    {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1809    {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1810    {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1811    {"ECS", UCURR_COMMON|UCURR_DEPRECATED},
   1812    {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1813    {"EEK", UCURR_COMMON|UCURR_DEPRECATED},
   1814    {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1815    {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1816    {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1817    {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1818    {"ESP", UCURR_COMMON|UCURR_DEPRECATED},
   1819    {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1820    {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1821    {"FIM", UCURR_COMMON|UCURR_DEPRECATED},
   1822    {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1823    {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1824    {"FRF", UCURR_COMMON|UCURR_DEPRECATED},
   1825    {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1826    {"GEK", UCURR_COMMON|UCURR_DEPRECATED},
   1827    {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1828    {"GHC", UCURR_COMMON|UCURR_DEPRECATED},
   1829    {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1830    {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1831    {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1832    {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1833    {"GNS", UCURR_COMMON|UCURR_DEPRECATED},
   1834    {"GQE", UCURR_COMMON|UCURR_DEPRECATED},
   1835    {"GRD", UCURR_COMMON|UCURR_DEPRECATED},
   1836    {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1837    {"GWE", UCURR_COMMON|UCURR_DEPRECATED},
   1838    {"GWP", UCURR_COMMON|UCURR_DEPRECATED},
   1839    {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1840    {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1841    {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1842    {"HRD", UCURR_COMMON|UCURR_DEPRECATED},
   1843    {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1844    {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1845    {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1846    {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1847    {"IEP", UCURR_COMMON|UCURR_DEPRECATED},
   1848    {"ILP", UCURR_COMMON|UCURR_DEPRECATED},
   1849    {"ILR", UCURR_COMMON|UCURR_DEPRECATED},
   1850    {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1851    {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1852    {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1853    {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1854    {"ISJ", UCURR_COMMON|UCURR_DEPRECATED},
   1855    {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1856    {"ITL", UCURR_COMMON|UCURR_DEPRECATED},
   1857    {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1858    {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1859    {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1860    {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1861    {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1862    {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1863    {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1864    {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1865    {"KRH", UCURR_COMMON|UCURR_DEPRECATED},
   1866    {"KRO", UCURR_COMMON|UCURR_DEPRECATED},
   1867    {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1868    {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1869    {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1870    {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1871    {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1872    {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1873    {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1874    {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1875    {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1876    {"LSM", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove?
   1877    {"LTL", UCURR_COMMON|UCURR_DEPRECATED},
   1878    {"LTT", UCURR_COMMON|UCURR_DEPRECATED},
   1879    {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1880    {"LUF", UCURR_COMMON|UCURR_DEPRECATED},
   1881    {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED},
   1882    {"LVL", UCURR_COMMON|UCURR_DEPRECATED},
   1883    {"LVR", UCURR_COMMON|UCURR_DEPRECATED},
   1884    {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1885    {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1886    {"MAF", UCURR_COMMON|UCURR_DEPRECATED},
   1887    {"MCF", UCURR_COMMON|UCURR_DEPRECATED},
   1888    {"MDC", UCURR_COMMON|UCURR_DEPRECATED},
   1889    {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1890    {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1891    {"MGF", UCURR_COMMON|UCURR_DEPRECATED},
   1892    {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1893    {"MKN", UCURR_COMMON|UCURR_DEPRECATED},
   1894    {"MLF", UCURR_COMMON|UCURR_DEPRECATED},
   1895    {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1896    {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1897    {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1898    {"MRO", UCURR_COMMON|UCURR_DEPRECATED},
   1899    {"MRU", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1900    {"MTL", UCURR_COMMON|UCURR_DEPRECATED},
   1901    {"MTP", UCURR_COMMON|UCURR_DEPRECATED},
   1902    {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1903    {"MVP", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove?
   1904    {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1905    {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1906    {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1907    {"MXP", UCURR_COMMON|UCURR_DEPRECATED},
   1908    {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1909    {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1910    {"MZE", UCURR_COMMON|UCURR_DEPRECATED},
   1911    {"MZM", UCURR_COMMON|UCURR_DEPRECATED},
   1912    {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1913    {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1914    {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1915    {"NIC", UCURR_COMMON|UCURR_DEPRECATED},
   1916    {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1917    {"NLG", UCURR_COMMON|UCURR_DEPRECATED},
   1918    {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1919    {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1920    {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1921    {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1922    {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1923    {"PEI", UCURR_COMMON|UCURR_DEPRECATED},
   1924    {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1925    {"PES", UCURR_COMMON|UCURR_DEPRECATED},
   1926    {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1927    {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1928    {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1929    {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1930    {"PLZ", UCURR_COMMON|UCURR_DEPRECATED},
   1931    {"PTE", UCURR_COMMON|UCURR_DEPRECATED},
   1932    {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1933    {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1934    {"RHD", UCURR_COMMON|UCURR_DEPRECATED},
   1935    {"ROL", UCURR_COMMON|UCURR_DEPRECATED},
   1936    {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1937    {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1938    {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1939    {"RUR", UCURR_COMMON|UCURR_DEPRECATED},
   1940    {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1941    {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1942    {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1943    {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1944    {"SDD", UCURR_COMMON|UCURR_DEPRECATED},
   1945    {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1946    {"SDP", UCURR_COMMON|UCURR_DEPRECATED},
   1947    {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1948    {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1949    {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1950    {"SIT", UCURR_COMMON|UCURR_DEPRECATED},
   1951    {"SKK", UCURR_COMMON|UCURR_DEPRECATED},
   1952    {"SLE", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1953    {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1954    {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1955    {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1956    {"SRG", UCURR_COMMON|UCURR_DEPRECATED},
   1957    {"SSP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1958    {"STD", UCURR_COMMON|UCURR_DEPRECATED},
   1959    {"STN", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1960    {"SUR", UCURR_COMMON|UCURR_DEPRECATED},
   1961    {"SVC", UCURR_COMMON|UCURR_DEPRECATED},
   1962    {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1963    {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1964    {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1965    {"TJR", UCURR_COMMON|UCURR_DEPRECATED},
   1966    {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1967    {"TMM", UCURR_COMMON|UCURR_DEPRECATED},
   1968    {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1969    {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1970    {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1971    {"TPE", UCURR_COMMON|UCURR_DEPRECATED},
   1972    {"TRL", UCURR_COMMON|UCURR_DEPRECATED},
   1973    {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1974    {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1975    {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1976    {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1977    {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1978    {"UAK", UCURR_COMMON|UCURR_DEPRECATED},
   1979    {"UGS", UCURR_COMMON|UCURR_DEPRECATED},
   1980    {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1981    {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1982    {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1983    {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1984    {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1985    {"UYP", UCURR_COMMON|UCURR_DEPRECATED},
   1986    {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1987    {"UYW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1988    {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1989    {"VEB", UCURR_COMMON|UCURR_DEPRECATED},
   1990    {"VED", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1991    {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1992    {"VES", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1993    {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1994    {"VNN", UCURR_COMMON|UCURR_DEPRECATED},
   1995    {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1996    {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1997    {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   1998    {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   1999    {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2000    {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2001    {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2002    {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2003    {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2004    {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2005    {"XCG", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2006    {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2007    {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED},
   2008    {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2009    {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2010    {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2011    {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2012    {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2013    {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2014    {"XRE", UCURR_UNCOMMON|UCURR_DEPRECATED},
   2015    {"XSU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2016    {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2017    {"XUA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2018    {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
   2019    {"YDD", UCURR_COMMON|UCURR_DEPRECATED},
   2020    {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2021    {"YUD", UCURR_COMMON|UCURR_DEPRECATED},
   2022    {"YUM", UCURR_COMMON|UCURR_DEPRECATED},
   2023    {"YUN", UCURR_COMMON|UCURR_DEPRECATED},
   2024    {"YUR", UCURR_COMMON|UCURR_DEPRECATED},
   2025    {"ZAL", UCURR_UNCOMMON|UCURR_DEPRECATED},
   2026    {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2027    {"ZMK", UCURR_COMMON|UCURR_DEPRECATED},
   2028    {"ZMW", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2029    {"ZRN", UCURR_COMMON|UCURR_DEPRECATED},
   2030    {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED},
   2031    {"ZWD", UCURR_COMMON|UCURR_DEPRECATED},
   2032    {"ZWG", UCURR_COMMON|UCURR_NON_DEPRECATED},
   2033    {"ZWL", UCURR_COMMON|UCURR_DEPRECATED},
   2034    {"ZWR", UCURR_COMMON|UCURR_DEPRECATED},
   2035    { nullptr, 0 } // Leave here to denote the end of the list.
   2036 };
   2037 
   2038 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
   2039    ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
   2040 
   2041 static int32_t U_CALLCONV
   2042 ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
   2043    UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
   2044    uint32_t currType = myContext->currType;
   2045    int32_t count = 0;
   2046 
   2047    /* Count the number of items matching the type we are looking for. */
   2048    for (int32_t idx = 0; gCurrencyList[idx].currency != nullptr; idx++) {
   2049        if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) {
   2050            count++;
   2051        }
   2052    }
   2053    return count;
   2054 }
   2055 
   2056 static const char* U_CALLCONV
   2057 ucurr_nextCurrencyList(UEnumeration *enumerator,
   2058                        int32_t* resultLength,
   2059                        UErrorCode * /*pErrorCode*/)
   2060 {
   2061    UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
   2062 
   2063    /* Find the next in the list that matches the type we are looking for. */
   2064    while (myContext->listIdx < UPRV_LENGTHOF(gCurrencyList)-1) {
   2065        const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++];
   2066        if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType))
   2067        {
   2068            if (resultLength) {
   2069                *resultLength = 3; /* Currency codes are only 3 chars long */
   2070            }
   2071            return currItem->currency;
   2072        }
   2073    }
   2074    /* We enumerated too far. */
   2075    if (resultLength) {
   2076        *resultLength = 0;
   2077    }
   2078    return nullptr;
   2079 }
   2080 
   2081 static void U_CALLCONV
   2082 ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
   2083    ((UCurrencyContext *)(enumerator->context))->listIdx = 0;
   2084 }
   2085 
   2086 static void U_CALLCONV
   2087 ucurr_closeCurrencyList(UEnumeration *enumerator) {
   2088    uprv_free(enumerator->context);
   2089    uprv_free(enumerator);
   2090 }
   2091 
   2092 static void U_CALLCONV
   2093 ucurr_createCurrencyList(UHashtable *isoCodes, UErrorCode* status){
   2094    UErrorCode localStatus = U_ZERO_ERROR;
   2095 
   2096    // Look up the CurrencyMap element in the root bundle.
   2097    UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
   2098    LocalUResourceBundlePointer currencyMapArray(ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus));
   2099 
   2100    if (U_SUCCESS(localStatus)) {
   2101        // process each entry in currency map
   2102        for (int32_t i=0; i<ures_getSize(currencyMapArray.getAlias()); i++) {
   2103            // get the currency resource
   2104            LocalUResourceBundlePointer currencyArray(ures_getByIndex(currencyMapArray.getAlias(), i, nullptr, &localStatus));
   2105            // process each currency
   2106            if (U_SUCCESS(localStatus)) {
   2107                for (int32_t j=0; j<ures_getSize(currencyArray.getAlias()); j++) {
   2108                    // get the currency resource
   2109                    LocalUResourceBundlePointer currencyRes(ures_getByIndex(currencyArray.getAlias(), j, nullptr, &localStatus));
   2110                    IsoCodeEntry *entry = (IsoCodeEntry*)uprv_malloc(sizeof(IsoCodeEntry));
   2111                    if (entry == nullptr) {
   2112                        *status = U_MEMORY_ALLOCATION_ERROR;
   2113                        return;
   2114                    }
   2115 
   2116                    // get the ISO code
   2117                    int32_t isoLength = 0;
   2118                    LocalUResourceBundlePointer idRes(ures_getByKey(currencyRes.getAlias(), "id", nullptr, &localStatus));
   2119                    if (idRes.isNull()) {
   2120                        continue;
   2121                    }
   2122                    const char16_t *isoCode = ures_getString(idRes.getAlias(), &isoLength, &localStatus);
   2123 
   2124                    // get from date
   2125                    UDate fromDate = U_DATE_MIN;
   2126                    LocalUResourceBundlePointer fromRes(ures_getByKey(currencyRes.getAlias(), "from", nullptr, &localStatus));
   2127 
   2128                    if (U_SUCCESS(localStatus)) {
   2129                        int32_t fromLength = 0;
   2130                        const int32_t *fromArray = ures_getIntVector(fromRes.getAlias(), &fromLength, &localStatus);
   2131                        int64_t currDate64 = ((uint64_t)fromArray[0]) << 32;
   2132                        currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
   2133                        fromDate = (UDate)currDate64;
   2134                    }
   2135 
   2136                    // get to date
   2137                    UDate toDate = U_DATE_MAX;
   2138                    localStatus = U_ZERO_ERROR;
   2139                    LocalUResourceBundlePointer toRes(ures_getByKey(currencyRes.getAlias(), "to", nullptr, &localStatus));
   2140 
   2141                    if (U_SUCCESS(localStatus)) {
   2142                        int32_t toLength = 0;
   2143                        const int32_t *toArray = ures_getIntVector(toRes.getAlias(), &toLength, &localStatus);
   2144                        int64_t currDate64 = (uint64_t)toArray[0] << 32;
   2145                        currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
   2146                        toDate = (UDate)currDate64;
   2147                    }
   2148 
   2149                    entry->isoCode = isoCode;
   2150                    entry->from = fromDate;
   2151                    entry->to = toDate;
   2152 
   2153                    localStatus = U_ZERO_ERROR;
   2154                    uhash_put(isoCodes, (char16_t *)isoCode, entry, &localStatus);
   2155                }
   2156            } else {
   2157                *status = localStatus;
   2158            }
   2159        }
   2160    } else {
   2161        *status = localStatus;
   2162    }
   2163 }
   2164 
   2165 static const UEnumeration gEnumCurrencyList = {
   2166    nullptr,
   2167    nullptr,
   2168    ucurr_closeCurrencyList,
   2169    ucurr_countCurrencyList,
   2170    uenum_unextDefault,
   2171    ucurr_nextCurrencyList,
   2172    ucurr_resetCurrencyList
   2173 };
   2174 U_CDECL_END
   2175 
   2176 
   2177 static void U_CALLCONV initIsoCodes(UErrorCode &status) {
   2178    U_ASSERT(gIsoCodes == nullptr);
   2179    ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
   2180 
   2181    LocalUHashtablePointer isoCodes(uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status));
   2182    if (U_FAILURE(status)) {
   2183        return;
   2184    }
   2185    uhash_setValueDeleter(isoCodes.getAlias(), deleteIsoCodeEntry);
   2186 
   2187    ucurr_createCurrencyList(isoCodes.getAlias(), &status);
   2188    if (U_FAILURE(status)) {
   2189        return;
   2190    }
   2191    gIsoCodes = isoCodes.orphan();  // Note: gIsoCodes is const. Once set up here it is never altered,
   2192                                    //       and read only access is safe without synchronization.
   2193 }
   2194 
   2195 static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) {
   2196    if (U_FAILURE(status)) { return; }
   2197    for (const auto& entry : unisets::kCurrencyEntries) {
   2198        UnicodeString exemplar(entry.exemplar);
   2199        const UnicodeSet* set = unisets::get(entry.key);
   2200        if (set == nullptr) { return; }
   2201        UnicodeSetIterator it(*set);
   2202        while (it.next()) {
   2203            UnicodeString value = it.getString();
   2204            if (value == exemplar) {
   2205                // No need to mark the exemplar character as an equivalent
   2206                continue;
   2207            }
   2208            makeEquivalent(exemplar, value, hash, status);
   2209            if (U_FAILURE(status)) { return; }
   2210        }
   2211    }
   2212 }
   2213 
   2214 static void U_CALLCONV initCurrSymbolsEquiv() {
   2215    U_ASSERT(gCurrSymbolsEquiv == nullptr);
   2216    UErrorCode status = U_ZERO_ERROR;
   2217    ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
   2218    icu::Hashtable *temp = new icu::Hashtable(status);
   2219    if (temp == nullptr) {
   2220        return;
   2221    }
   2222    if (U_FAILURE(status)) {
   2223        delete temp;
   2224        return;
   2225    }
   2226    temp->setValueDeleter(deleteUnicode);
   2227    populateCurrSymbolsEquiv(temp, status);
   2228    if (U_FAILURE(status)) {
   2229        delete temp;
   2230        return;
   2231    }
   2232    gCurrSymbolsEquiv = temp;
   2233 }
   2234 
   2235 U_CAPI UBool U_EXPORT2
   2236 ucurr_isAvailable(const char16_t* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) {
   2237    umtx_initOnce(gIsoCodesInitOnce, &initIsoCodes, *eErrorCode);
   2238    if (U_FAILURE(*eErrorCode)) {
   2239        return false;
   2240    }
   2241 
   2242    IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode);
   2243    if (result == nullptr) {
   2244        return false;
   2245    } else if (from > to) {
   2246        *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
   2247        return false;
   2248    } else if  ((from > result->to) || (to < result->from)) {
   2249        return false;
   2250    }
   2251    return true;
   2252 }
   2253 
   2254 static const icu::Hashtable* getCurrSymbolsEquiv() {
   2255    umtx_initOnce(gCurrSymbolsEquivInitOnce, &initCurrSymbolsEquiv);
   2256    return gCurrSymbolsEquiv;
   2257 }
   2258 
   2259 U_CAPI UEnumeration * U_EXPORT2
   2260 ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) {
   2261    UEnumeration *myEnum = nullptr;
   2262    UCurrencyContext *myContext;
   2263 
   2264    myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration));
   2265    if (myEnum == nullptr) {
   2266        *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
   2267        return nullptr;
   2268    }
   2269    uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration));
   2270    myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext));
   2271    if (myContext == nullptr) {
   2272        *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
   2273        uprv_free(myEnum);
   2274        return nullptr;
   2275    }
   2276    myContext->currType = currType;
   2277    myContext->listIdx = 0;
   2278    myEnum->context = myContext;
   2279    return myEnum;
   2280 }
   2281 
   2282 U_CAPI int32_t U_EXPORT2
   2283 ucurr_countCurrencies(const char* locale, 
   2284                 UDate date, 
   2285                 UErrorCode* ec)
   2286 {
   2287    int32_t currCount = 0;
   2288 
   2289    if (ec != nullptr && U_SUCCESS(*ec)) 
   2290    {
   2291        // local variables
   2292        UErrorCode localStatus = U_ZERO_ERROR;
   2293 
   2294        // get country or country_variant in `id'
   2295        CharString id = idForLocale(locale, ec);
   2296 
   2297        if (U_FAILURE(*ec))
   2298        {
   2299            return 0;
   2300        }
   2301 
   2302        // Remove variants, which is only needed for registration.
   2303        char *idDelim = strchr(id.data(), VAR_DELIM);
   2304        if (idDelim)
   2305        {
   2306            id.truncate(idDelim - id.data());
   2307        }
   2308 
   2309        // Look up the CurrencyMap element in the root bundle.
   2310        UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
   2311        UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
   2312 
   2313        // Using the id derived from the local, get the currency data
   2314        LocalUResourceBundlePointer countryArray(ures_getByKey(rb, id.data(), cm, &localStatus));
   2315 
   2316        // process each currency to see which one is valid for the given date
   2317        if (U_SUCCESS(localStatus))
   2318        {
   2319            for (int32_t i=0; i<ures_getSize(countryArray.getAlias()); i++)
   2320            {
   2321                // get the currency resource
   2322                LocalUResourceBundlePointer currencyRes(ures_getByIndex(countryArray.getAlias(), i, nullptr, &localStatus));
   2323 
   2324                // get the from date
   2325                int32_t fromLength = 0;
   2326                LocalUResourceBundlePointer fromRes(ures_getByKey(currencyRes.getAlias(), "from", nullptr, &localStatus));
   2327                const int32_t *fromArray = ures_getIntVector(fromRes.getAlias(), &fromLength, &localStatus);
   2328 
   2329                int64_t currDate64 = (int64_t)((uint64_t)(fromArray[0]) << 32);
   2330                currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
   2331                UDate fromDate = (UDate)currDate64;
   2332 
   2333                if (ures_getSize(currencyRes.getAlias())> 2)
   2334                {
   2335                    int32_t toLength = 0;
   2336                    LocalUResourceBundlePointer toRes(ures_getByKey(currencyRes.getAlias(), "to", nullptr, &localStatus));
   2337                    const int32_t *toArray = ures_getIntVector(toRes.getAlias(), &toLength, &localStatus);
   2338 
   2339                    currDate64 = (int64_t)toArray[0] << 32;
   2340                    currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
   2341                    UDate toDate = (UDate)currDate64;
   2342 
   2343                    if ((fromDate <= date) && (date < toDate))
   2344                    {
   2345                        currCount++;
   2346                    }
   2347                }
   2348                else
   2349                {
   2350                    if (fromDate <= date)
   2351                    {
   2352                        currCount++;
   2353                    }
   2354                }
   2355            } // end For loop
   2356        } // end if (U_SUCCESS(localStatus))
   2357 
   2358        // Check for errors
   2359        if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
   2360        {
   2361            // There is nothing to fallback to. 
   2362            // Report the failure/warning if possible.
   2363            *ec = localStatus;
   2364        }
   2365 
   2366        if (U_SUCCESS(*ec))
   2367        {
   2368            // no errors
   2369            return currCount;
   2370        }
   2371    }
   2372 
   2373    // If we got here, either error code is invalid or
   2374    // some argument passed is no good.
   2375    return 0;
   2376 }
   2377 
   2378 U_CAPI int32_t U_EXPORT2 
   2379 ucurr_forLocaleAndDate(const char* locale, 
   2380                UDate date, 
   2381                int32_t index,
   2382                char16_t* buff,
   2383                int32_t buffCapacity, 
   2384                UErrorCode* ec)
   2385 {
   2386    int32_t resLen = 0;
   2387 int32_t currIndex = 0;
   2388    const char16_t* s = nullptr;
   2389 
   2390    if (ec != nullptr && U_SUCCESS(*ec))
   2391    {
   2392        // check the arguments passed
   2393        if ((buff && buffCapacity) || !buffCapacity )
   2394        {
   2395            // local variables
   2396            UErrorCode localStatus = U_ZERO_ERROR;
   2397 
   2398            // get country or country_variant in `id'
   2399            CharString id = idForLocale(locale, ec);
   2400            if (U_FAILURE(*ec))
   2401            {
   2402                return 0;
   2403            }
   2404 
   2405            // Remove variants, which is only needed for registration.
   2406            char *idDelim = strchr(id.data(), VAR_DELIM);
   2407            if (idDelim)
   2408            {
   2409                id.truncate(idDelim - id.data());
   2410            }
   2411 
   2412            // Look up the CurrencyMap element in the root bundle.
   2413            UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
   2414            UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
   2415 
   2416            // Using the id derived from the local, get the currency data
   2417            LocalUResourceBundlePointer countryArray(ures_getByKey(rb, id.data(), cm, &localStatus));
   2418 
   2419            // process each currency to see which one is valid for the given date
   2420            bool matchFound = false;
   2421            if (U_SUCCESS(localStatus))
   2422            {
   2423                if ((index <= 0) || (index> ures_getSize(countryArray.getAlias())))
   2424                {
   2425                    // requested index is out of bounds
   2426                    return 0;
   2427                }
   2428 
   2429                for (int32_t i=0; i<ures_getSize(countryArray.getAlias()); i++)
   2430                {
   2431                    // get the currency resource
   2432                    LocalUResourceBundlePointer currencyRes(ures_getByIndex(countryArray.getAlias(), i, nullptr, &localStatus));
   2433                    s = ures_getStringByKey(currencyRes.getAlias(), "id", &resLen, &localStatus);
   2434 
   2435                    // get the from date
   2436                    int32_t fromLength = 0;
   2437                    LocalUResourceBundlePointer fromRes(ures_getByKey(currencyRes.getAlias(), "from", nullptr, &localStatus));
   2438                    const int32_t *fromArray = ures_getIntVector(fromRes.getAlias(), &fromLength, &localStatus);
   2439 
   2440                    int64_t currDate64 = (int64_t)((uint64_t)fromArray[0] << 32);
   2441                    currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
   2442                    UDate fromDate = (UDate)currDate64;
   2443 
   2444                    if (ures_getSize(currencyRes.getAlias()) > 2)
   2445                    {
   2446                        int32_t toLength = 0;
   2447                        LocalUResourceBundlePointer toRes(ures_getByKey(currencyRes.getAlias(), "to", nullptr, &localStatus));
   2448                        const int32_t *toArray = ures_getIntVector(toRes.getAlias(), &toLength, &localStatus);
   2449 
   2450                        currDate64 = (int64_t)toArray[0] << 32;
   2451                        currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
   2452                        UDate toDate = (UDate)currDate64;
   2453 
   2454                        if ((fromDate <= date) && (date < toDate))
   2455                        {
   2456                            currIndex++;
   2457                            if (currIndex == index)
   2458                            {
   2459                                matchFound = true;
   2460                            }
   2461                        }
   2462                    }
   2463                    else
   2464                    {
   2465                        if (fromDate <= date)
   2466                        {
   2467                            currIndex++;
   2468                            if (currIndex == index)
   2469                            {
   2470                                matchFound = true;
   2471                            }
   2472                        }
   2473                    }
   2474                    // check for loop exit
   2475                    if (matchFound)
   2476                    {
   2477                        break;
   2478                    }
   2479 
   2480                } // end For loop
   2481            }
   2482 
   2483            // Check for errors
   2484            if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
   2485            {
   2486                // There is nothing to fallback to. 
   2487                // Report the failure/warning if possible.
   2488                *ec = localStatus;
   2489            }
   2490 
   2491            if (U_SUCCESS(*ec))
   2492            {
   2493                // no errors
   2494                if((buffCapacity> resLen) && matchFound)
   2495                {
   2496                    // write out the currency value
   2497                    u_strcpy(buff, s);
   2498                }
   2499                else
   2500                {
   2501                    return 0;
   2502                }
   2503            }
   2504 
   2505            // return null terminated currency string
   2506            return u_terminateUChars(buff, buffCapacity, resLen, ec);
   2507        }
   2508        else
   2509        {
   2510            // illegal argument encountered
   2511            *ec = U_ILLEGAL_ARGUMENT_ERROR;
   2512        }
   2513 
   2514    }
   2515 
   2516    // If we got here, either error code is invalid or
   2517    // some argument passed is no good.
   2518    return resLen;
   2519 }
   2520 
   2521 static const UEnumeration defaultKeywordValues = {
   2522    nullptr,
   2523    nullptr,
   2524    ulist_close_keyword_values_iterator,
   2525    ulist_count_keyword_values,
   2526    uenum_unextDefault,
   2527    ulist_next_keyword_value, 
   2528    ulist_reset_keyword_values_iterator
   2529 };
   2530 
   2531 U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) {
   2532    // Resolve region
   2533    CharString prefRegion = ulocimp_getRegionForSupplementalData(locale, true, *status);
   2534 
   2535    // Read value from supplementalData
   2536    UList *values = ulist_createEmptyList(status);
   2537    UList *otherValues = ulist_createEmptyList(status);
   2538    UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
   2539    if (U_FAILURE(*status) || en == nullptr) {
   2540        if (en == nullptr) {
   2541            *status = U_MEMORY_ALLOCATION_ERROR;
   2542        } else {
   2543            uprv_free(en);
   2544        }
   2545        ulist_deleteList(values);
   2546        ulist_deleteList(otherValues);
   2547        return nullptr;
   2548    }
   2549    memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
   2550    en->context = values;
   2551    
   2552    UResourceBundle* rb = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status);
   2553    LocalUResourceBundlePointer bundle(ures_getByKey(rb, "CurrencyMap", rb, status));
   2554    StackUResourceBundle bundlekey, regbndl, curbndl, to;
   2555    
   2556    while (U_SUCCESS(*status) && ures_hasNext(bundle.getAlias())) {
   2557        ures_getNextResource(bundle.getAlias(), bundlekey.getAlias(), status);
   2558        if (U_FAILURE(*status)) {
   2559            break;
   2560        }
   2561        const char *region = ures_getKey(bundlekey.getAlias());
   2562        UBool isPrefRegion = prefRegion == region;
   2563        if (!isPrefRegion && commonlyUsed) {
   2564            // With commonlyUsed=true, we do not put
   2565            // currencies for other regions in the
   2566            // result list.
   2567            continue;
   2568        }
   2569        ures_getByKey(bundle.getAlias(), region, regbndl.getAlias(), status);
   2570        if (U_FAILURE(*status)) {
   2571            break;
   2572        }
   2573        while (U_SUCCESS(*status) && ures_hasNext(regbndl.getAlias())) {
   2574            ures_getNextResource(regbndl.getAlias(), curbndl.getAlias(), status);
   2575            if (ures_getType(curbndl.getAlias()) != URES_TABLE) {
   2576                // Currently, an empty ARRAY is mixed in.
   2577                continue;
   2578            }
   2579            char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
   2580            if (curID == nullptr) {
   2581                *status = U_MEMORY_ALLOCATION_ERROR;
   2582                break;
   2583            }
   2584            int32_t curIDLength = ULOC_KEYWORDS_CAPACITY;
   2585 
   2586 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
   2587            ures_getUTF8StringByKey(curbndl.getAlias(), "id", curID, &curIDLength, true, status);
   2588            /* optimize - use the utf-8 string */
   2589 #else
   2590            {
   2591                       const char16_t* defString = ures_getStringByKey(curbndl.getAlias(), "id", &curIDLength, status);
   2592                       if(U_SUCCESS(*status)) {
   2593 		   if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) {
   2594 			*status = U_BUFFER_OVERFLOW_ERROR;
   2595 		   } else {
   2596                           	u_UCharsToChars(defString, curID, curIDLength+1);
   2597 		   }
   2598                       }
   2599            }
   2600 #endif	
   2601 
   2602            if (U_FAILURE(*status)) {
   2603                break;
   2604            }
   2605            UBool hasTo = false;
   2606            ures_getByKey(curbndl.getAlias(), "to", to.getAlias(), status);
   2607            if (U_FAILURE(*status)) {
   2608                // Do nothing here...
   2609                *status = U_ZERO_ERROR;
   2610            } else {
   2611                hasTo = true;
   2612            }
   2613            if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) {
   2614                // Currently active currency for the target country
   2615                ulist_addItemEndList(values, curID, true, status);
   2616            } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) {
   2617                ulist_addItemEndList(otherValues, curID, true, status);
   2618            } else {
   2619                uprv_free(curID);
   2620            }
   2621        }
   2622        
   2623    }
   2624    if (U_SUCCESS(*status)) {
   2625        if (commonlyUsed) {
   2626            if (ulist_getListSize(values) == 0) {
   2627                // This could happen if no valid region is supplied in the input
   2628                // locale. In this case, we use the CLDR's default.
   2629                uenum_close(en);
   2630                en = ucurr_getKeywordValuesForLocale(key, "und", true, status);
   2631            }
   2632        } else {
   2633            // Consolidate the list
   2634            char *value = nullptr;
   2635            ulist_resetList(otherValues);
   2636            while ((value = (char *)ulist_getNext(otherValues)) != nullptr) {
   2637                if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) {
   2638                    char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
   2639                    if (tmpValue == nullptr) {
   2640                        *status = U_MEMORY_ALLOCATION_ERROR;
   2641                        break;
   2642                    }
   2643                    uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1);
   2644                    ulist_addItemEndList(values, tmpValue, true, status);
   2645                    if (U_FAILURE(*status)) {
   2646                        break;
   2647                    }
   2648                }
   2649            }
   2650        }
   2651        ulist_resetList((UList *)(en->context));
   2652    } else {
   2653        ulist_deleteList(values);
   2654        uprv_free(en);
   2655        values = nullptr;
   2656        en = nullptr;
   2657    }
   2658    ulist_deleteList(otherValues);
   2659    return en;
   2660 }
   2661 
   2662 
   2663 U_CAPI int32_t U_EXPORT2
   2664 ucurr_getNumericCode(const char16_t* currency) {
   2665    int32_t code = 0;
   2666    if (currency && u_strlen(currency) == ISO_CURRENCY_CODE_LENGTH) {
   2667        UErrorCode status = U_ZERO_ERROR;
   2668 
   2669        UResourceBundle* bundle = ures_openDirect(nullptr, "currencyNumericCodes", &status);
   2670        LocalUResourceBundlePointer codeMap(ures_getByKey(bundle, "codeMap", bundle, &status));
   2671        if (U_SUCCESS(status)) {
   2672            char alphaCode[ISO_CURRENCY_CODE_LENGTH+1];
   2673            myUCharsToChars(alphaCode, currency);
   2674            T_CString_toUpperCase(alphaCode);
   2675            ures_getByKey(codeMap.getAlias(), alphaCode, codeMap.getAlias(), &status);
   2676            int tmpCode = ures_getInt(codeMap.getAlias(), &status);
   2677            if (U_SUCCESS(status)) {
   2678                code = tmpCode;
   2679            }
   2680        }
   2681    }
   2682    return code;
   2683 }
   2684 #endif /* #if !UCONFIG_NO_FORMATTING */
   2685 
   2686 //eof