// Default currency meta data of last resort. We try to use the // defaults encoded in the meta data resource bundle. If there is a // configuration/build error and these are not available, we use these // hard-coded defaults (which should be identical). staticconst int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 };
// EquivIterator iterates over all strings that are equivalent to a given // string, s. Note that EquivIterator will never yield s itself. class EquivIterator : public icu::UMemory { public: // Constructor. hash stores the equivalence relationships; s is the string // for which we find equivalent strings. inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s)
: _hash(hash) {
_start = _current = &s;
} inline ~EquivIterator() { }
// next returns the next equivalent string or nullptr if there are no more. // If s has no equivalent strings, next returns nullptr on the first call. const icu::UnicodeString *next(); private: const icu::Hashtable& _hash; const icu::UnicodeString* _start; const icu::UnicodeString* _current;
};
// makeEquivalent makes lhs and rhs equivalent by updating the equivalence // relations in hash accordingly. staticvoid makeEquivalent( const icu::UnicodeString &lhs, const icu::UnicodeString &rhs,
icu::Hashtable* hash, UErrorCode &status) { if (U_FAILURE(status)) { return;
} if (lhs == rhs) { // already equivalent return;
}
icu::EquivIterator leftIter(*hash, lhs);
icu::EquivIterator rightIter(*hash, rhs); const icu::UnicodeString *firstLeft = leftIter.next(); const icu::UnicodeString *firstRight = rightIter.next(); const icu::UnicodeString *nextLeft = firstLeft; const icu::UnicodeString *nextRight = firstRight; while (nextLeft != nullptr && nextRight != nullptr) { if (*nextLeft == rhs || *nextRight == lhs) { // Already equivalent return;
}
nextLeft = leftIter.next();
nextRight = rightIter.next();
} // Not equivalent. Must join.
icu::UnicodeString *newFirstLeft;
icu::UnicodeString *newFirstRight; if (firstRight == nullptr && firstLeft == nullptr) { // Neither lhs or rhs belong to an equivalence circle, so we form // a new equivalnce circle of just lhs and rhs.
newFirstLeft = new icu::UnicodeString(rhs);
newFirstRight = new icu::UnicodeString(lhs);
} elseif (firstRight == nullptr) { // lhs belongs to an equivalence circle, but rhs does not, so we link // rhs into lhs' circle.
newFirstLeft = new icu::UnicodeString(rhs);
newFirstRight = new icu::UnicodeString(*firstLeft);
} elseif (firstLeft == nullptr) { // rhs belongs to an equivlance circle, but lhs does not, so we link // lhs into rhs' circle.
newFirstLeft = new icu::UnicodeString(*firstRight);
newFirstRight = new icu::UnicodeString(lhs);
} else { // Both lhs and rhs belong to different equivalnce circles. We link // them together to form one single, larger equivalnce circle.
newFirstLeft = new icu::UnicodeString(*firstRight);
newFirstRight = new icu::UnicodeString(*firstLeft);
} if (newFirstLeft == nullptr || newFirstRight == nullptr) { delete newFirstLeft; delete newFirstRight;
status = U_MEMORY_ALLOCATION_ERROR; return;
}
hash->put(lhs, (void *) newFirstLeft, status);
hash->put(rhs, (void *) newFirstRight, status);
}
// countEquivalent counts how many strings are equivalent to s. // hash stores all the equivalnce relations. // countEquivalent does not include s itself in the count. static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) {
int32_t result = 0;
icu::EquivIterator iter(hash, s); while (iter.next() != nullptr) {
++result;
} #ifdef UCURR_DEBUG_EQUIV
{ char tmp[200];
s.extract(0,s.length(),tmp, "UTF-8");
printf("CountEquivalent('%s') = %d\n", tmp, result);
} #endif return result;
}
/** * Unfortunately, we have to convert the char16_t* currency code to char* * to use it as a resource key.
*/ staticinlinechar*
myUCharsToChars(char* resultOfLen4, const char16_t* currency) {
u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH);
resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0; return resultOfLen4;
}
/** * Internal function to look up currency data. Result is an array of * four integers. The first is the fraction digits. The second is the * rounding increment, or 0 if none. The rounding increment is in * units of 10^(-fraction_digits). The third and fourth are the same * except that they are those used in cash transactions ( cashDigits * and cashRounding ).
*/ staticconst int32_t*
_findMetaData(const char16_t* currency, UErrorCode& ec) {
if (currency == nullptr || *currency == 0) { if (U_SUCCESS(ec)) {
ec = U_ILLEGAL_ARGUMENT_ERROR;
} return LAST_RESORT_DATA;
}
// Get CurrencyMeta resource out of root locale file. [This may // move out of the root locale file later; if it does, update this // code.]
UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec);
LocalUResourceBundlePointer currencyMeta(ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec));
static UCurrRegistryKey reg(const char16_t* _iso, constchar* _id, UErrorCode* status)
{ if (status && U_SUCCESS(*status) && _iso && _id) {
CReg* n = new CReg(_iso, _id); if (n) {
umtx_lock(&gCRegLock); if (!gCRegHead) { /* register for the first time */
ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
}
n->next = gCRegHead;
gCRegHead = n;
umtx_unlock(&gCRegLock); return n;
}
*status = U_MEMORY_ALLOCATION_ERROR;
} return nullptr;
}
static UBool unreg(UCurrRegistryKey key) {
UBool found = false;
umtx_lock(&gCRegLock);
CReg** p = &gCRegHead; while (*p) { if (*p == key) {
*p = ((CReg*)key)->next; delete (CReg*)key;
found = true; break;
}
p = &((*p)->next);
}
umtx_unlock(&gCRegLock); return found;
}
staticconst char16_t* get(constchar* id) { const char16_t* result = nullptr;
umtx_lock(&gCRegLock);
CReg* p = gCRegHead;
/* register cleanup of the mutex */
ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup); while (p) { if (uprv_strcmp(id, p->id) == 0) {
result = p->iso; break;
}
p = p->next;
}
umtx_unlock(&gCRegLock); return result;
}
/* This doesn't need to be thread safe. It's for u_cleanup only. */ staticvoid cleanup() { while (gCRegHead) {
CReg* n = gCRegHead;
gCRegHead = gCRegHead->next; delete n;
}
}
};
/** * Release all static memory held by currency.
*/ /*The declaration here is needed so currency_cleanup() * can call this function.
*/ static UBool U_CALLCONV
currency_cache_cleanup();
U_CDECL_BEGIN static UBool U_CALLCONV currency_cleanup() { #if !UCONFIG_NO_SERVICE
CReg::cleanup(); #endif /* * There might be some cached currency data or isoCodes data.
*/
currency_cache_cleanup();
isoCodes_cleanup();
currSymbolsEquiv_cleanup();
// get country or country_variant in `id'
CharString id = idForLocale(locale, ec); if (U_FAILURE(*ec)) { return 0;
}
#if !UCONFIG_NO_SERVICE const char16_t* result = CReg::get(id.data()); if (result) { if(buffCapacity > u_strlen(result)) {
u_strcpy(buff, result);
}
resLen = u_strlen(result); return u_terminateUChars(buff, buffCapacity, resLen, ec);
} #endif // Remove variants, which is only needed for registration. char *idDelim = uprv_strchr(id.data(), VAR_DELIM); if (idDelim) {
id.truncate(idDelim - id.data());
}
const char16_t* s = nullptr; // Currency code from data file. if (id.isEmpty()) { // No point looking in the data for an empty string. // This is what we would get.
localStatus = U_MISSING_RESOURCE_ERROR;
} else { // Look up the CurrencyMap element in the root bundle.
localStatus = U_ZERO_ERROR;
UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
LocalUResourceBundlePointer countryArray(ures_getByKey(rb, id.data(), cm, &localStatus)); // https://unicode-org.atlassian.net/browse/ICU-21997 // Prefer to use currencies that are legal tender. if (U_SUCCESS(localStatus)) {
int32_t arrayLength = ures_getSize(countryArray.getAlias()); for (int32_t i = 0; i < arrayLength; ++i) {
LocalUResourceBundlePointer currencyReq(
ures_getByIndex(countryArray.getAlias(), i, nullptr, &localStatus)); // The currency is legal tender if it is *not* marked with tender{"false"}.
UErrorCode tenderStatus = localStatus; const char16_t *tender =
ures_getStringByKey(currencyReq.getAlias(), "tender", nullptr, &tenderStatus); bool isTender = U_FAILURE(tenderStatus) || u_strcmp(tender, u"false") != 0; if (!isTender && s != nullptr) { // We already have a non-tender currency. Ignore all following non-tender ones. continue;
} // Fetch the currency code.
s = ures_getStringByKey(currencyReq.getAlias(), "id", &resLen, &localStatus); if (isTender) { break;
}
} if (U_SUCCESS(localStatus) && s == nullptr) {
localStatus = U_MISSING_RESOURCE_ERROR;
}
}
}
if ((U_FAILURE(localStatus)) && strchr(id.data(), '_') != nullptr) { // We don't know about it. Check to see if we support the variant.
CharString parent = ulocimp_getParent(locale, *ec);
*ec = U_USING_FALLBACK_WARNING; // TODO: Loop over the parent rather than recursing and // looking again for a currency keyword. return ucurr_forLocale(parent.data(), buff, buffCapacity, ec);
} if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) { // There is nothing to fallback to. Report the failure/warning if possible.
*ec = localStatus;
} if (U_SUCCESS(*ec)) { if(buffCapacity > resLen) {
u_strcpy(buff, s);
}
} return u_terminateUChars(buff, buffCapacity, resLen, ec);
}
// end registration
/** * Modify the given locale name by removing the rightmost _-delimited * element. If there is none, empty the string ("" == root). * NOTE: The string "root" is not recognized; do not use it. * @return true if the fallback happened; false if locale is already * root ("").
*/ static UBool fallback(CharString& loc) { if (loc.isEmpty()) { returnfalse;
}
UErrorCode status = U_ZERO_ERROR; if (loc == "en_GB") { // HACK: See #13368. We need "en_GB" to fall back to "en_001" instead of "en" // in order to consume the correct data strings. This hack will be removed // when proper data sink loading is implemented here.
loc.truncate(3);
loc.append("001", status);
} else {
loc = ulocimp_getParent(loc.data(), status);
} /* char *i = uprv_strrchr(loc, '_'); if (i == nullptr) { i = loc; } *i = 0;
*/ returntrue;
}
// In the future, resource bundles may implement multi-level // fallback. That is, if a currency is not found in the en_US // Currencies data, then the en Currencies data will be searched. // Currently, if a Currencies datum exists in en_US and en, the // en_US entry hides that in en.
// We want multi-level fallback for this resource, so we implement // it manually.
// Use a separate UErrorCode here that does not propagate out of // this function.
UErrorCode ec2 = U_ZERO_ERROR;
CharString loc = ulocimp_getName(locale, ec2); if (U_FAILURE(ec2)) {
*ec = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
}
if (nameStyle == UCURR_NARROW_SYMBOL_NAME || nameStyle == UCURR_FORMAL_SYMBOL_NAME || nameStyle == UCURR_VARIANT_SYMBOL_NAME) {
CharString key; switch (nameStyle) { case UCURR_NARROW_SYMBOL_NAME:
key.append(CURRENCIES_NARROW, ec2); break; case UCURR_FORMAL_SYMBOL_NAME:
key.append(CURRENCIES_FORMAL, ec2); break; case UCURR_VARIANT_SYMBOL_NAME:
key.append(CURRENCIES_VARIANT, ec2); break; default:
*ec = U_UNSUPPORTED_ERROR; return nullptr;
}
key.append("/", ec2);
key.append(buf, ec2);
s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2); if (ec2 == U_MISSING_RESOURCE_ERROR) {
*ec = U_USING_FALLBACK_WARNING;
ec2 = U_ZERO_ERROR;
choice = UCURR_SYMBOL_NAME;
}
} if (s == nullptr) {
ures_getByKey(rb.getAlias(), CURRENCIES, rb.getAlias(), &ec2);
ures_getByKeyWithFallback(rb.getAlias(), buf, rb.getAlias(), &ec2);
s = ures_getStringByIndex(rb.getAlias(), choice, len, &ec2);
}
// If we've succeeded we're done. Otherwise, try to fallback. // If that fails (because we are already at root) then exit. if (U_SUCCESS(ec2)) { if (ec2 == U_USING_DEFAULT_WARNING
|| (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
*ec = ec2;
}
}
// We no longer support choice format data in names. Data should not contain // choice patterns. if (isChoiceFormat != nullptr) {
*isChoiceFormat = false;
} if (U_SUCCESS(ec2)) {
U_ASSERT(s != nullptr); return s;
}
// If we fail to find a match, use the ISO 4217 code
*len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
*ec = U_USING_DEFAULT_WARNING; return currency;
}
U_CAPI const char16_t* U_EXPORT2
ucurr_getPluralName(const char16_t* currency, constchar* locale,
UBool* isChoiceFormat, constchar* pluralCount,
int32_t* len, // fillin
UErrorCode* ec) { // Look up the Currencies resource for the given locale. The // Currencies locale data looks like this: //|en { //| CurrencyPlurals { //| USD{ //| one{"US dollar"} //| other{"US dollars"} //| } //| } //|}
if (U_FAILURE(*ec)) { return nullptr;
}
// Use a separate UErrorCode here that does not propagate out of // this function.
UErrorCode ec2 = U_ZERO_ERROR;
CharString loc = ulocimp_getName(locale, ec2); if (U_FAILURE(ec2)) {
*ec = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
}
s = ures_getStringByKeyWithFallback(curr.getAlias(), pluralCount, len, &ec2); if (U_FAILURE(ec2)) { // fall back to "other"
ec2 = U_ZERO_ERROR;
s = ures_getStringByKeyWithFallback(curr.getAlias(), "other", len, &ec2); if (U_FAILURE(ec2)) { // fall back to long name in Currencies return ucurr_getName(currency, locale, UCURR_LONG_NAME,
isChoiceFormat, len, ec);
}
}
// If we've succeeded we're done. Otherwise, try to fallback. // If that fails (because we are already at root) then exit. if (U_SUCCESS(ec2)) { if (ec2 == U_USING_DEFAULT_WARNING
|| (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
*ec = ec2;
}
U_ASSERT(s != nullptr); return s;
}
// If we fail to find a match, use the ISO 4217 code
*len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
*ec = U_USING_DEFAULT_WARNING; return currency;
}
//======================================================================== // Following are structure and function for parsing currency names
#define NEED_TO_BE_DELETED 0x1
// TODO: a better way to define this? #define MAX_CURRENCY_NAME_LEN 100
#ifndef MIN #define MIN(a,b) (((a)<(b)) ? (a) : (b)) #endif
#ifndef MAX #define MAX(a,b) (((a)<(b)) ? (b) : (a)) #endif
// Comparison function used in quick sort. staticint U_CALLCONV currencyNameComparator(constvoid* a, constvoid* b) { const CurrencyNameStruct* currName_1 = static_cast<const CurrencyNameStruct*>(a); const CurrencyNameStruct* currName_2 = static_cast<const CurrencyNameStruct*>(b); for (int32_t i = 0;
i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen);
++i) { if (currName_1->currencyName[i] < currName_2->currencyName[i]) { return -1;
} if (currName_1->currencyName[i] > currName_2->currencyName[i]) { return 1;
}
} if (currName_1->currencyNameLen < currName_2->currencyNameLen) { return -1;
} elseif (currName_1->currencyNameLen > currName_2->currencyNameLen) { return 1;
} return 0;
}
// Give a locale, return the maximum number of currency names associated with // this locale. // It gets currency names from resource bundles using fallback. // It is the maximum number because in the fallback chain, some of the // currency names are duplicated. // For example, given locale as "en_US", the currency names get from resource // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count // all currency names in "en_US" and "en". staticvoid
getCurrencyNameCount(constchar* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) {
U_NAMESPACE_USE
*total_currency_name_count = 0;
*total_currency_symbol_count = 0; const char16_t* s = nullptr;
CharString locale;
{
UErrorCode status = U_ZERO_ERROR;
locale.append(loc, status); if (U_FAILURE(status)) { return; }
} const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv(); for (;;) {
UErrorCode ec2 = U_ZERO_ERROR; // TODO: ures_openDirect?
LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, locale.data(), &ec2));
LocalUResourceBundlePointer curr(ures_getByKey(rb.getAlias(), CURRENCIES, nullptr, &ec2));
int32_t n = ures_getSize(curr.getAlias()); for (int32_t i=0; i<n; ++i) {
LocalUResourceBundlePointer names(ures_getByIndex(curr.getAlias(), i, nullptr, &ec2));
int32_t len;
s = ures_getStringByIndex(names.getAlias(), UCURR_SYMBOL_NAME, &len, &ec2);
++(*total_currency_symbol_count); // currency symbol if (currencySymbolsEquiv != nullptr) {
*total_currency_symbol_count += countEquivalent(*currencySymbolsEquiv, UnicodeString(true, s, len));
}
++(*total_currency_symbol_count); // iso code
++(*total_currency_name_count); // long name
}
// currency plurals
UErrorCode ec3 = U_ZERO_ERROR;
LocalUResourceBundlePointer curr_p(ures_getByKey(rb.getAlias(), CURRENCYPLURALS, nullptr, &ec3));
n = ures_getSize(curr_p.getAlias()); for (int32_t i=0; i<n; ++i) {
LocalUResourceBundlePointer names(ures_getByIndex(curr_p.getAlias(), i, nullptr, &ec3));
*total_currency_name_count += ures_getSize(names.getAlias());
}
staticvoid deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count); // Collect all available currency names associated with the given locale // (enable fallback chain). // Read currenc names defined in resource bundle "Currencies" and // "CurrencyPlural", enable fallback chain. // return the malloc-ed currency name arrays and the total number of currency // names in the array. staticvoid
collectCurrencyNames(constchar* locale,
CurrencyNameStruct** currencyNames,
int32_t* total_currency_name_count,
CurrencyNameStruct** currencySymbols,
int32_t* total_currency_symbol_count,
UErrorCode& ec) { if (U_FAILURE(ec)) {
*currencyNames = *currencySymbols = nullptr;
*total_currency_name_count = *total_currency_symbol_count = 0; return;
}
U_NAMESPACE_USE const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv(); // Look up the Currencies resource for the given locale.
UErrorCode ec2 = U_ZERO_ERROR;
#ifdef UCURR_DEBUG
printf("currency name count: %d\n", *total_currency_name_count); for (int32_t index = 0; index < *total_currency_name_count; ++index) {
printf("index: %d\n", index);
printf("iso: %s\n", (*currencyNames)[index].IsoCode); char curNameBuf[1024];
memset(curNameBuf, 0, 1024);
u_austrncpy(curNameBuf, (*currencyNames)[index].currencyName, (*currencyNames)[index].currencyNameLen);
printf("currencyName: %s\n", curNameBuf);
printf("len: %d\n", (*currencyNames)[index].currencyNameLen);
}
printf("currency symbol count: %d\n", *total_currency_symbol_count); for (int32_t index = 0; index < *total_currency_symbol_count; ++index) {
printf("index: %d\n", index);
printf("iso: %s\n", (*currencySymbols)[index].IsoCode); char curNameBuf[1024];
memset(curNameBuf, 0, 1024);
u_austrncpy(curNameBuf, (*currencySymbols)[index].currencyName, (*currencySymbols)[index].currencyNameLen);
printf("currencySymbol: %s\n", curNameBuf);
printf("len: %d\n", (*currencySymbols)[index].currencyNameLen);
} #endif // fail on hashtable errors if (U_FAILURE(ec3)) {
ec = ec3;
} elseif (U_FAILURE(ec4)) {
ec = ec4;
}
error: // clean up if we got error if (U_FAILURE(ec)) {
deleteCurrencyNames(*currencyNames, *total_currency_name_count);
deleteCurrencyNames(*currencySymbols, *total_currency_symbol_count);
*currencyNames = *currencySymbols = nullptr;
*total_currency_name_count = *total_currency_symbol_count = 0;
}
}
// @param currencyNames: currency names array // @param indexInCurrencyNames: the index of the character in currency names // array against which the comparison is done // @param key: input text char to compare against // @param begin(IN/OUT): the begin index of matching range in currency names array // @param end(IN/OUT): the end index of matching range in currency names array. static int32_t
binarySearch(const CurrencyNameStruct* currencyNames,
int32_t indexInCurrencyNames, const char16_t key,
int32_t* begin, int32_t* end) { #ifdef UCURR_DEBUG
printf("key = %x\n", key); #endif
int32_t first = *begin;
int32_t last = *end; while (first <= last) {
int32_t mid = (first + last) / 2; // compute mid point. if (indexInCurrencyNames >= currencyNames[mid].currencyNameLen) {
first = mid + 1;
} else { if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) {
first = mid + 1;
} elseif (key < currencyNames[mid].currencyName[indexInCurrencyNames]) {
last = mid - 1;
} else { // Find a match, and looking for ranges // Now do two more binary searches. First, on the left side for // the greatest L such that CurrencyNameStruct[L] < key.
int32_t L = *begin;
int32_t R = mid;
#ifdef UCURR_DEBUG
printf("mid = %d\n", mid); #endif while (L < R) {
int32_t M = (L + R) / 2; #ifdef UCURR_DEBUG
printf("L = %d, R = %d, M = %d\n", L, R, M); #endif if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) {
L = M + 1;
} else { if (currencyNames[M].currencyName[indexInCurrencyNames] < key) {
L = M + 1;
} else { #ifdef UCURR_DEBUG
U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); #endif
R = M;
}
}
} #ifdef UCURR_DEBUG
U_ASSERT(L == R); #endif
*begin = L; #ifdef UCURR_DEBUG
printf("begin = %d\n", *begin);
U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key); #endif
// Now for the second search, finding the least R such that // key < CurrencyNameStruct[R].
L = mid;
R = *end; while (L < R) {
int32_t M = (L + R) / 2; #ifdef UCURR_DEBUG
printf("L = %d, R = %d, M = %d\n", L, R, M); #endif if (currencyNames[M].currencyNameLen < indexInCurrencyNames) {
L = M + 1;
} else { if (currencyNames[M].currencyName[indexInCurrencyNames] > key) {
R = M;
} else { #ifdef UCURR_DEBUG
U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key); #endif
L = M + 1;
}
}
} #ifdef UCURR_DEBUG
U_ASSERT(L == R); #endif if (currencyNames[R].currencyName[indexInCurrencyNames] > key) {
*end = R - 1;
} else {
*end = R;
} #ifdef UCURR_DEBUG
printf("end = %d\n", *end); #endif
// now, found the range. check whether there is exact match if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) { return *begin; // find range and exact match.
} return -1; // find range, but no exact match.
}
}
}
*begin = -1;
*end = -1; return -1; // failed to find range.
}
// Linear search "text" in "currencyNames". // @param begin, end: the begin and end index in currencyNames, within which // range should the search be performed. // @param textLen: the length of the text to be compared // @param maxMatchLen(IN/OUT): passing in the computed max matching length // pass out the new max matching length // @param maxMatchIndex: the index in currencyName which has the longest // match with input text. staticvoid
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) {
int32_t initialPartialMatchLen = *partialMatchLen; for (int32_t index = begin; index <= end; ++index) {
int32_t len = currencyNames[index].currencyNameLen; if (len > *maxMatchLen && len <= textLen &&
uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(char16_t)) == 0) {
*partialMatchLen = MAX(*partialMatchLen, len);
*maxMatchIndex = index;
*maxMatchLen = len; #ifdef UCURR_DEBUG
printf("maxMatchIndex = %d, maxMatchLen = %d\n",
*maxMatchIndex, *maxMatchLen); #endif
} else { // Check for partial matches. for (int32_t i=initialPartialMatchLen; i<MIN(len, textLen); i++) { if (currencyNames[index].currencyName[i] != text[i]) { break;
}
*partialMatchLen = MAX(*partialMatchLen, i + 1);
}
}
}
}
#define LINEAR_SEARCH_THRESHOLD 10
// Find longest match between "text" and currency names in "currencyNames". // @param total_currency_count: total number of currency names in CurrencyNames. // @param textLen: the length of the text to be compared // @param maxMatchLen: passing in the computed max matching length // pass out the new max matching length // @param maxMatchIndex: the index in currencyName which has the longest // match with input text. staticvoid
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) {
*maxMatchIndex = -1;
*maxMatchLen = 0;
int32_t matchIndex = -1;
int32_t binarySearchBegin = 0;
int32_t binarySearchEnd = total_currency_count - 1; // It is a variant of binary search. // For example, given the currency names in currencyNames array are: // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E.... // and the input text is BBEXST // The first round binary search search "B" in the text against // the first char in currency names, and find the first char matching range // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B"). // The 2nd round binary search search the second "B" in the text against // the 2nd char in currency names, and narrow the matching range to // "BB BBEX BBEXYZ" (and the maximum matching "BB"). // The 3rd round returns the range as "BBEX BBEXYZ" (without changing // maximum matching). // The 4th round returns the same range (the maximum matching is "BBEX"). // The 5th round returns no matching range. for (int32_t index = 0; index < textLen; ++index) { // matchIndex saves the one with exact match till the current point. // [binarySearchBegin, binarySearchEnd] saves the matching range.
matchIndex = binarySearch(currencyNames, index,
text[index],
&binarySearchBegin, &binarySearchEnd); if (binarySearchBegin == -1) { // did not find the range break;
}
*partialMatchLen = MAX(*partialMatchLen, index + 1); if (matchIndex != -1) { // find an exact match for text from text[0] to text[index] // in currencyNames array.
*maxMatchLen = index + 1;
*maxMatchIndex = matchIndex;
} if (binarySearchEnd - binarySearchBegin < LINEAR_SEARCH_THRESHOLD) { // linear search if within threshold.
linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
text, textLen,
partialMatchLen,
maxMatchLen, maxMatchIndex); break;
}
}
}
//========================= currency name cache ===================== typedefstruct { char locale[ULOC_FULLNAME_CAPACITY]; //key // currency names, case insensitive
CurrencyNameStruct* currencyNames; // value
int32_t totalCurrencyNameCount; // currency name count // currency symbols and ISO code, case sensitive
CurrencyNameStruct* currencySymbols; // value
int32_t totalCurrencySymbolCount; // count // reference count. // reference count is set to 1 when an entry is put to cache. // it increases by 1 before accessing, and decreased by 1 after accessing. // The entry is deleted when ref count is zero, which means // the entry is replaced out of cache and no process is accessing it.
int32_t refCount;
} CurrencyNameCacheEntry;
#define CURRENCY_NAME_CACHE_NUM 10
// Reserve 10 cache entries. static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {nullptr}; // Using an index to indicate which entry to be replaced when cache is full. // It is a simple round-robin replacement strategy. static int8_t currentCacheEntryIndex = 0;
static UMutex gCurrencyCacheMutex;
// Cache deletion staticvoid
deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) { for (int32_t index = 0; index < count; ++index) { if ( (currencyNames[index].flag & NEED_TO_BE_DELETED) ) {
uprv_free(currencyNames[index].currencyName);
}
}
uprv_free(currencyNames);
}
// Cache clean up static UBool U_CALLCONV
currency_cache_cleanup() { for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) { if (currCache[i]) {
deleteCacheEntry(currCache[i]);
currCache[i] = nullptr;
}
} returntrue;
}
/** * Loads the currency name data from the cache, or from resource bundles if necessary. * The refCount is automatically incremented. It is the caller's responsibility * to decrement it when done!
*/ static CurrencyNameCacheEntry*
getCacheEntry(constchar* locale, UErrorCode& ec) {
// Make sure partialMatchLen is initialized
*partialMatchLen = 0;
int32_t max = 0;
int32_t matchIndex = -1; // case in-sensitive comparison against currency names
searchCurrencyName(currencyNames, total_currency_name_count,
upperText, textLen, partialMatchLen, &max, &matchIndex);
#ifdef UCURR_DEBUG
printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex); #endif
int32_t maxInSymbol = 0;
int32_t matchIndexInSymbol = -1; if (type != UCURR_LONG_NAME) { // not name only // case sensitive comparison against currency symbols and ISO code.
searchCurrencyName(currencySymbols, total_currency_symbol_count,
inputText, textLen,
partialMatchLen,
&maxInSymbol, &matchIndexInSymbol);
}
/** * Internal method. Given a currency ISO code and a locale, return * the "static" currency name. This is usually the same as the * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the * format is applied to the number 2.0 (to yield the more common * plural) to return a static name. * * This is used for backward compatibility with old currency logic in * DecimalFormat and DecimalFormatSymbols.
*/
U_CAPI void
uprv_getStaticCurrencyName(const char16_t* iso, constchar* loc,
icu::UnicodeString& result, UErrorCode& ec)
{
U_NAMESPACE_USE
// If the meta data is invalid, return 0.0 if (fracDigits < 0 || fracDigits > MAX_POW10) {
*ec = U_INVALID_FORMAT_ERROR;
} else { // A rounding value of 0 or 1 indicates no rounding. if (increment >= 2) { // Return (increment) / 10^(fracDigits). The only actual rounding data, // as of this writing, is CHF { 2, 5 }.
result = double(increment) / POW10[fracDigits];
}
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.21Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.