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