• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2019 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 
4 // localematcher.cpp
5 // created: 2019may08 Markus W. Scherer
6 
7 #ifndef __LOCMATCHER_H__
8 #define __LOCMATCHER_H__
9 
10 #include "unicode/utypes.h"
11 #include "unicode/localebuilder.h"
12 #include "unicode/localematcher.h"
13 #include "unicode/locid.h"
14 #include "unicode/stringpiece.h"
15 #include "unicode/uobject.h"
16 #include "cstring.h"
17 #include "localeprioritylist.h"
18 #include "loclikelysubtags.h"
19 #include "locdistance.h"
20 #include "lsr.h"
21 #include "uassert.h"
22 #include "uhash.h"
23 #include "uvector.h"
24 
25 #define UND_LSR LSR("und", "", "")
26 
27 /**
28  * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher.
29  *
30  * @draft ICU 65
31  */
32 enum ULocMatchLifetime {
33     /**
34      * Locale objects are temporary.
35      * The matcher will make a copy of a locale that will be used beyond one function call.
36      *
37      * @draft ICU 65
38      */
39     ULOCMATCH_TEMPORARY_LOCALES,
40     /**
41      * Locale objects are stored at least as long as the matcher is used.
42      * The matcher will keep only a pointer to a locale that will be used beyond one function call,
43      * avoiding a copy.
44      *
45      * @draft ICU 65
46      */
47     ULOCMATCH_STORED_LOCALES  // TODO: permanent? cached? clone?
48 };
49 #ifndef U_IN_DOXYGEN
50 typedef enum ULocMatchLifetime ULocMatchLifetime;
51 #endif
52 
53 U_NAMESPACE_BEGIN
54 
Result(LocaleMatcher::Result && src)55 LocaleMatcher::Result::Result(LocaleMatcher::Result &&src) U_NOEXCEPT :
56         desiredLocale(src.desiredLocale),
57         supportedLocale(src.supportedLocale),
58         desiredIndex(src.desiredIndex),
59         supportedIndex(src.supportedIndex),
60         desiredIsOwned(src.desiredIsOwned) {
61     if (desiredIsOwned) {
62         src.desiredLocale = nullptr;
63         src.desiredIndex = -1;
64         src.desiredIsOwned = FALSE;
65     }
66 }
67 
~Result()68 LocaleMatcher::Result::~Result() {
69     if (desiredIsOwned) {
70         delete desiredLocale;
71     }
72 }
73 
operator =(LocaleMatcher::Result && src)74 LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) U_NOEXCEPT {
75     this->~Result();
76 
77     desiredLocale = src.desiredLocale;
78     supportedLocale = src.supportedLocale;
79     desiredIndex = src.desiredIndex;
80     supportedIndex = src.supportedIndex;
81     desiredIsOwned = src.desiredIsOwned;
82 
83     if (desiredIsOwned) {
84         src.desiredLocale = nullptr;
85         src.desiredIndex = -1;
86         src.desiredIsOwned = FALSE;
87     }
88     return *this;
89 }
90 
makeResolvedLocale(UErrorCode & errorCode) const91 Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const {
92     if (U_FAILURE(errorCode) || supportedLocale == nullptr) {
93         return Locale::getRoot();
94     }
95     const Locale *bestDesired = getDesiredLocale();
96     if (bestDesired == nullptr || *supportedLocale == *bestDesired) {
97         return *supportedLocale;
98     }
99     LocaleBuilder b;
100     b.setLocale(*supportedLocale);
101 
102     // Copy the region from bestDesired, if there is one.
103     const char *region = bestDesired->getCountry();
104     if (*region != 0) {
105         b.setRegion(region);
106     }
107 
108     // Copy the variants from bestDesired, if there are any.
109     // Note that this will override any supportedLocale variants.
110     // For example, "sco-ulster-fonipa" + "...-fonupa" => "sco-fonupa" (replacing ulster).
111     const char *variants = bestDesired->getVariant();
112     if (*variants != 0) {
113         b.setVariant(variants);
114     }
115 
116     // Copy the extensions from bestDesired, if there are any.
117     // C++ note: The following note, copied from Java, may not be true,
118     // as long as C++ copies by legacy ICU keyword, not by extension singleton.
119     // Note that this will override any supportedLocale extensions.
120     // For example, "th-u-nu-latn-ca-buddhist" + "...-u-nu-native" => "th-u-nu-native"
121     // (replacing calendar).
122     b.copyExtensionsFrom(*bestDesired, errorCode);
123     return b.build(errorCode);
124 }
125 
Builder(LocaleMatcher::Builder && src)126 LocaleMatcher::Builder::Builder(LocaleMatcher::Builder &&src) U_NOEXCEPT :
127         errorCode_(src.errorCode_),
128         supportedLocales_(src.supportedLocales_),
129         thresholdDistance_(src.thresholdDistance_),
130         demotion_(src.demotion_),
131         defaultLocale_(src.defaultLocale_),
132         favor_(src.favor_) {
133     src.supportedLocales_ = nullptr;
134     src.defaultLocale_ = nullptr;
135 }
136 
~Builder()137 LocaleMatcher::Builder::~Builder() {
138     delete supportedLocales_;
139     delete defaultLocale_;
140 }
141 
operator =(LocaleMatcher::Builder && src)142 LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) U_NOEXCEPT {
143     this->~Builder();
144 
145     errorCode_ = src.errorCode_;
146     supportedLocales_ = src.supportedLocales_;
147     thresholdDistance_ = src.thresholdDistance_;
148     demotion_ = src.demotion_;
149     defaultLocale_ = src.defaultLocale_;
150     favor_ = src.favor_;
151 
152     src.supportedLocales_ = nullptr;
153     src.defaultLocale_ = nullptr;
154     return *this;
155 }
156 
clearSupportedLocales()157 void LocaleMatcher::Builder::clearSupportedLocales() {
158     if (supportedLocales_ != nullptr) {
159         supportedLocales_->removeAllElements();
160     }
161 }
162 
ensureSupportedLocaleVector()163 bool LocaleMatcher::Builder::ensureSupportedLocaleVector() {
164     if (U_FAILURE(errorCode_)) { return false; }
165     if (supportedLocales_ != nullptr) { return true; }
166     supportedLocales_ = new UVector(uprv_deleteUObject, nullptr, errorCode_);
167     if (U_FAILURE(errorCode_)) { return false; }
168     if (supportedLocales_ == nullptr) {
169         errorCode_ = U_MEMORY_ALLOCATION_ERROR;
170         return false;
171     }
172     return true;
173 }
174 
setSupportedLocalesFromListString(StringPiece locales)175 LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListString(
176         StringPiece locales) {
177     LocalePriorityList list(locales, errorCode_);
178     if (U_FAILURE(errorCode_)) { return *this; }
179     clearSupportedLocales();
180     if (!ensureSupportedLocaleVector()) { return *this; }
181     int32_t length = list.getLengthIncludingRemoved();
182     for (int32_t i = 0; i < length; ++i) {
183         Locale *locale = list.orphanLocaleAt(i);
184         if (locale == nullptr) { continue; }
185         supportedLocales_->addElement(locale, errorCode_);
186         if (U_FAILURE(errorCode_)) {
187             delete locale;
188             break;
189         }
190     }
191     return *this;
192 }
193 
setSupportedLocales(Locale::Iterator & locales)194 LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) {
195     if (U_FAILURE(errorCode_)) { return *this; }
196     clearSupportedLocales();
197     if (!ensureSupportedLocaleVector()) { return *this; }
198     while (locales.hasNext()) {
199         const Locale &locale = locales.next();
200         Locale *clone = locale.clone();
201         if (clone == nullptr) {
202             errorCode_ = U_MEMORY_ALLOCATION_ERROR;
203             break;
204         }
205         supportedLocales_->addElement(clone, errorCode_);
206         if (U_FAILURE(errorCode_)) {
207             delete clone;
208             break;
209         }
210     }
211     return *this;
212 }
213 
addSupportedLocale(const Locale & locale)214 LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) {
215     if (!ensureSupportedLocaleVector()) { return *this; }
216     Locale *clone = locale.clone();
217     if (clone == nullptr) {
218         errorCode_ = U_MEMORY_ALLOCATION_ERROR;
219         return *this;
220     }
221     supportedLocales_->addElement(clone, errorCode_);
222     if (U_FAILURE(errorCode_)) {
223         delete clone;
224     }
225     return *this;
226 }
227 
setDefaultLocale(const Locale * defaultLocale)228 LocaleMatcher::Builder &LocaleMatcher::Builder::setDefaultLocale(const Locale *defaultLocale) {
229     if (U_FAILURE(errorCode_)) { return *this; }
230     Locale *clone = nullptr;
231     if (defaultLocale != nullptr) {
232         clone = defaultLocale->clone();
233         if (clone == nullptr) {
234             errorCode_ = U_MEMORY_ALLOCATION_ERROR;
235             return *this;
236         }
237     }
238     delete defaultLocale_;
239     defaultLocale_ = clone;
240     return *this;
241 }
242 
setFavorSubtag(ULocMatchFavorSubtag subtag)243 LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) {
244     if (U_FAILURE(errorCode_)) { return *this; }
245     favor_ = subtag;
246     return *this;
247 }
248 
setDemotionPerDesiredLocale(ULocMatchDemotion demotion)249 LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) {
250     if (U_FAILURE(errorCode_)) { return *this; }
251     demotion_ = demotion;
252     return *this;
253 }
254 
255 #if 0
256 /**
257  * <i>Internal only!</i>
258  *
259  * @param thresholdDistance the thresholdDistance to set, with -1 = default
260  * @return this Builder object
261  * @internal
262  * @deprecated This API is ICU internal only.
263  */
264 @Deprecated
265 LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) {
266     if (U_FAILURE(errorCode_)) { return *this; }
267     if (thresholdDistance > 100) {
268         thresholdDistance = 100;
269     }
270     thresholdDistance_ = thresholdDistance;
271     return *this;
272 }
273 #endif
274 
copyErrorTo(UErrorCode & outErrorCode) const275 UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const {
276     if (U_FAILURE(outErrorCode)) { return TRUE; }
277     if (U_SUCCESS(errorCode_)) { return FALSE; }
278     outErrorCode = errorCode_;
279     return TRUE;
280 }
281 
build(UErrorCode & errorCode) const282 LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const {
283     if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) {
284         errorCode = errorCode_;
285     }
286     return LocaleMatcher(*this, errorCode);
287 }
288 
289 namespace {
290 
getMaximalLsrOrUnd(const XLikelySubtags & likelySubtags,const Locale & locale,UErrorCode & errorCode)291 LSR getMaximalLsrOrUnd(const XLikelySubtags &likelySubtags, const Locale &locale,
292                        UErrorCode &errorCode) {
293     if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) {
294         return UND_LSR;
295     } else {
296         return likelySubtags.makeMaximizedLsrFrom(locale, errorCode);
297     }
298 }
299 
hashLSR(const UHashTok token)300 int32_t hashLSR(const UHashTok token) {
301     const LSR *lsr = static_cast<const LSR *>(token.pointer);
302     return lsr->hashCode;
303 }
304 
compareLSRs(const UHashTok t1,const UHashTok t2)305 UBool compareLSRs(const UHashTok t1, const UHashTok t2) {
306     const LSR *lsr1 = static_cast<const LSR *>(t1.pointer);
307     const LSR *lsr2 = static_cast<const LSR *>(t2.pointer);
308     return *lsr1 == *lsr2;
309 }
310 
putIfAbsent(UHashtable * lsrToIndex,const LSR & lsr,int32_t i,UErrorCode & errorCode)311 bool putIfAbsent(UHashtable *lsrToIndex, const LSR &lsr, int32_t i, UErrorCode &errorCode) {
312     if (U_FAILURE(errorCode)) { return false; }
313     U_ASSERT(i > 0);
314     int32_t index = uhash_geti(lsrToIndex, &lsr);
315     if (index != 0) {
316         return false;
317     } else {
318         uhash_puti(lsrToIndex, const_cast<LSR *>(&lsr), i, &errorCode);
319         return U_SUCCESS(errorCode);
320     }
321 }
322 
323 }  // namespace
324 
LocaleMatcher(const Builder & builder,UErrorCode & errorCode)325 LocaleMatcher::LocaleMatcher(const Builder &builder, UErrorCode &errorCode) :
326         likelySubtags(*XLikelySubtags::getSingleton(errorCode)),
327         localeDistance(*LocaleDistance::getSingleton(errorCode)),
328         thresholdDistance(builder.thresholdDistance_),
329         demotionPerDesiredLocale(0),
330         favorSubtag(builder.favor_),
331         supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0),
332         supportedLsrToIndex(nullptr),
333         supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0),
334         ownedDefaultLocale(nullptr), defaultLocale(nullptr), defaultLocaleIndex(-1) {
335     if (U_FAILURE(errorCode)) { return; }
336     if (thresholdDistance < 0) {
337         thresholdDistance = localeDistance.getDefaultScriptDistance();
338     }
339     supportedLocalesLength = builder.supportedLocales_ != nullptr ?
340         builder.supportedLocales_->size() : 0;
341     const Locale *def = builder.defaultLocale_;
342     int32_t idef = -1;
343     if (supportedLocalesLength > 0) {
344         // Store the supported locales in input order,
345         // so that when different types are used (e.g., language tag strings)
346         // we can return those by parallel index.
347         supportedLocales = static_cast<const Locale **>(
348             uprv_malloc(supportedLocalesLength * sizeof(const Locale *)));
349         // Supported LRSs in input order.
350         // In C++, we store these permanently to simplify ownership management
351         // in the hash tables. Duplicate LSRs (if any) are unused overhead.
352         lsrs = new LSR[supportedLocalesLength];
353         if (supportedLocales == nullptr || lsrs == nullptr) {
354             errorCode = U_MEMORY_ALLOCATION_ERROR;
355             return;
356         }
357         // If the constructor fails partway, we need null pointers for destructibility.
358         uprv_memset(supportedLocales, 0, supportedLocalesLength * sizeof(const Locale *));
359         // Also find the first supported locale whose LSR is
360         // the same as that for the default locale.
361         LSR builderDefaultLSR;
362         const LSR *defLSR = nullptr;
363         if (def != nullptr) {
364             builderDefaultLSR = getMaximalLsrOrUnd(likelySubtags, *def, errorCode);
365             if (U_FAILURE(errorCode)) { return; }
366             defLSR = &builderDefaultLSR;
367         }
368         for (int32_t i = 0; i < supportedLocalesLength; ++i) {
369             const Locale &locale = *static_cast<Locale *>(builder.supportedLocales_->elementAt(i));
370             supportedLocales[i] = locale.clone();
371             if (supportedLocales[i] == nullptr) {
372                 errorCode = U_MEMORY_ALLOCATION_ERROR;
373                 return;
374             }
375             const Locale &supportedLocale = *supportedLocales[i];
376             LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode);
377             lsr.setHashCode();
378             if (U_FAILURE(errorCode)) { return; }
379             if (idef < 0 && defLSR != nullptr && lsr == *defLSR) {
380                 idef = i;
381                 defLSR = &lsr;  // owned pointer to put into supportedLsrToIndex
382                 if (*def == supportedLocale) {
383                     def = &supportedLocale;  // owned pointer to keep
384                 }
385             }
386         }
387 
388         // We need an unordered map from LSR to first supported locale with that LSR,
389         // and an ordered list of (LSR, supported index).
390         // We insert the supported locales in the following order:
391         // 1. Default locale, if it is supported.
392         // 2. Priority locales (aka "paradigm locales") in builder order.
393         // 3. Remaining locales in builder order.
394         // In Java, we use a LinkedHashMap for both map & ordered lists.
395         // In C++, we use separate structures.
396         // We over-allocate arrays of LSRs and indexes for simplicity.
397         // We reserve slots at the array starts for the default and paradigm locales,
398         // plus enough for all supported locales.
399         // If there are few paradigm locales and few duplicate supported LSRs,
400         // then the amount of wasted space is small.
401         supportedLsrToIndex = uhash_openSize(hashLSR, compareLSRs, uhash_compareLong,
402                                              supportedLocalesLength, &errorCode);
403         if (U_FAILURE(errorCode)) { return; }
404         int32_t paradigmLimit = 1 + localeDistance.getParadigmLSRsLength();
405         int32_t suppLSRsCapacity = paradigmLimit + supportedLocalesLength;
406         supportedLSRs = static_cast<const LSR **>(
407             uprv_malloc(suppLSRsCapacity * sizeof(const LSR *)));
408         supportedIndexes = static_cast<int32_t *>(
409             uprv_malloc(suppLSRsCapacity * sizeof(int32_t)));
410         if (supportedLSRs == nullptr || supportedIndexes == nullptr) {
411             errorCode = U_MEMORY_ALLOCATION_ERROR;
412             return;
413         }
414         int32_t paradigmIndex = 0;
415         int32_t otherIndex = paradigmLimit;
416         if (idef >= 0) {
417             uhash_puti(supportedLsrToIndex, const_cast<LSR *>(defLSR), idef + 1, &errorCode);
418             supportedLSRs[0] = defLSR;
419             supportedIndexes[0] = idef;
420             paradigmIndex = 1;
421         }
422         for (int32_t i = 0; i < supportedLocalesLength; ++i) {
423             if (i == idef) { continue; }
424             const Locale &locale = *supportedLocales[i];
425             const LSR &lsr = lsrs[i];
426             if (defLSR == nullptr) {
427                 U_ASSERT(i == 0);
428                 def = &locale;
429                 defLSR = &lsr;
430                 idef = 0;
431                 uhash_puti(supportedLsrToIndex, const_cast<LSR *>(&lsr), 0 + 1, &errorCode);
432                 supportedLSRs[0] = &lsr;
433                 supportedIndexes[0] = 0;
434                 paradigmIndex = 1;
435             } else if (idef >= 0 && lsr == *defLSR) {
436                 // lsr == *defLSR means that this supported locale is
437                 // a duplicate of the default locale.
438                 // Either an explicit default locale is supported, and we added it before the loop,
439                 // or there is no explicit default locale, and this is
440                 // a duplicate of the first supported locale.
441                 // In both cases, idef >= 0 now, so otherwise we can skip the comparison.
442                 // For a duplicate, putIfAbsent() is a no-op, so nothing to do.
443             } else {
444                 if (putIfAbsent(supportedLsrToIndex, lsr, i + 1, errorCode)) {
445                     if (localeDistance.isParadigmLSR(lsr)) {
446                         supportedLSRs[paradigmIndex] = &lsr;
447                         supportedIndexes[paradigmIndex++] = i;
448                     } else {
449                         supportedLSRs[otherIndex] = &lsr;
450                         supportedIndexes[otherIndex++] = i;
451                     }
452                 }
453             }
454             if (U_FAILURE(errorCode)) { return; }
455         }
456         // Squeeze out unused array slots.
457         if (paradigmIndex < paradigmLimit && paradigmLimit < otherIndex) {
458             uprv_memmove(supportedLSRs + paradigmIndex, supportedLSRs + paradigmLimit,
459                          (otherIndex - paradigmLimit) * sizeof(const LSR *));
460             uprv_memmove(supportedIndexes + paradigmIndex, supportedIndexes + paradigmLimit,
461                          (otherIndex - paradigmLimit) * sizeof(int32_t));
462         }
463         supportedLSRsLength = otherIndex - (paradigmLimit - paradigmIndex);
464     }
465 
466     if (def != nullptr && (idef < 0 || def != supportedLocales[idef])) {
467         ownedDefaultLocale = def->clone();
468         if (ownedDefaultLocale == nullptr) {
469             errorCode = U_MEMORY_ALLOCATION_ERROR;
470             return;
471         }
472         def = ownedDefaultLocale;
473     }
474     defaultLocale = def;
475     defaultLocaleIndex = idef;
476 
477     if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) {
478         demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale();
479     }
480 }
481 
LocaleMatcher(LocaleMatcher && src)482 LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) U_NOEXCEPT :
483         likelySubtags(src.likelySubtags),
484         localeDistance(src.localeDistance),
485         thresholdDistance(src.thresholdDistance),
486         demotionPerDesiredLocale(src.demotionPerDesiredLocale),
487         favorSubtag(src.favorSubtag),
488         supportedLocales(src.supportedLocales), lsrs(src.lsrs),
489         supportedLocalesLength(src.supportedLocalesLength),
490         supportedLsrToIndex(src.supportedLsrToIndex),
491         supportedLSRs(src.supportedLSRs),
492         supportedIndexes(src.supportedIndexes),
493         supportedLSRsLength(src.supportedLSRsLength),
494         ownedDefaultLocale(src.ownedDefaultLocale), defaultLocale(src.defaultLocale),
495         defaultLocaleIndex(src.defaultLocaleIndex) {
496     src.supportedLocales = nullptr;
497     src.lsrs = nullptr;
498     src.supportedLocalesLength = 0;
499     src.supportedLsrToIndex = nullptr;
500     src.supportedLSRs = nullptr;
501     src.supportedIndexes = nullptr;
502     src.supportedLSRsLength = 0;
503     src.ownedDefaultLocale = nullptr;
504     src.defaultLocale = nullptr;
505     src.defaultLocaleIndex = -1;
506 }
507 
~LocaleMatcher()508 LocaleMatcher::~LocaleMatcher() {
509     for (int32_t i = 0; i < supportedLocalesLength; ++i) {
510         delete supportedLocales[i];
511     }
512     uprv_free(supportedLocales);
513     delete[] lsrs;
514     uhash_close(supportedLsrToIndex);
515     uprv_free(supportedLSRs);
516     uprv_free(supportedIndexes);
517     delete ownedDefaultLocale;
518 }
519 
operator =(LocaleMatcher && src)520 LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) U_NOEXCEPT {
521     this->~LocaleMatcher();
522 
523     thresholdDistance = src.thresholdDistance;
524     demotionPerDesiredLocale = src.demotionPerDesiredLocale;
525     favorSubtag = src.favorSubtag;
526     supportedLocales = src.supportedLocales;
527     lsrs = src.lsrs;
528     supportedLocalesLength = src.supportedLocalesLength;
529     supportedLsrToIndex = src.supportedLsrToIndex;
530     supportedLSRs = src.supportedLSRs;
531     supportedIndexes = src.supportedIndexes;
532     supportedLSRsLength = src.supportedLSRsLength;
533     ownedDefaultLocale = src.ownedDefaultLocale;
534     defaultLocale = src.defaultLocale;
535     defaultLocaleIndex = src.defaultLocaleIndex;
536 
537     src.supportedLocales = nullptr;
538     src.lsrs = nullptr;
539     src.supportedLocalesLength = 0;
540     src.supportedLsrToIndex = nullptr;
541     src.supportedLSRs = nullptr;
542     src.supportedIndexes = nullptr;
543     src.supportedLSRsLength = 0;
544     src.ownedDefaultLocale = nullptr;
545     src.defaultLocale = nullptr;
546     src.defaultLocaleIndex = -1;
547     return *this;
548 }
549 
550 class LocaleLsrIterator {
551 public:
LocaleLsrIterator(const XLikelySubtags & likelySubtags,Locale::Iterator & locales,ULocMatchLifetime lifetime)552     LocaleLsrIterator(const XLikelySubtags &likelySubtags, Locale::Iterator &locales,
553                       ULocMatchLifetime lifetime) :
554             likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {}
555 
~LocaleLsrIterator()556     ~LocaleLsrIterator() {
557         if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) {
558             delete remembered;
559         }
560     }
561 
hasNext() const562     bool hasNext() const {
563         return locales.hasNext();
564     }
565 
next(UErrorCode & errorCode)566     LSR next(UErrorCode &errorCode) {
567         current = &locales.next();
568         return getMaximalLsrOrUnd(likelySubtags, *current, errorCode);
569     }
570 
rememberCurrent(int32_t desiredIndex,UErrorCode & errorCode)571     void rememberCurrent(int32_t desiredIndex, UErrorCode &errorCode) {
572         if (U_FAILURE(errorCode)) { return; }
573         bestDesiredIndex = desiredIndex;
574         if (lifetime == ULOCMATCH_STORED_LOCALES) {
575             remembered = current;
576         } else {
577             // ULOCMATCH_TEMPORARY_LOCALES
578             delete remembered;
579             remembered = new Locale(*current);
580             if (remembered == nullptr) {
581                 errorCode = U_MEMORY_ALLOCATION_ERROR;
582             }
583         }
584     }
585 
orphanRemembered()586     const Locale *orphanRemembered() {
587         const Locale *rem = remembered;
588         remembered = nullptr;
589         return rem;
590     }
591 
getBestDesiredIndex() const592     int32_t getBestDesiredIndex() const {
593         return bestDesiredIndex;
594     }
595 
596 private:
597     const XLikelySubtags &likelySubtags;
598     Locale::Iterator &locales;
599     ULocMatchLifetime lifetime;
600     const Locale *current = nullptr, *remembered = nullptr;
601     int32_t bestDesiredIndex = -1;
602 };
603 
getBestMatch(const Locale & desiredLocale,UErrorCode & errorCode) const604 const Locale *LocaleMatcher::getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const {
605     if (U_FAILURE(errorCode)) { return nullptr; }
606     int32_t suppIndex = getBestSuppIndex(
607         getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
608         nullptr, errorCode);
609     return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale;
610 }
611 
getBestMatch(Locale::Iterator & desiredLocales,UErrorCode & errorCode) const612 const Locale *LocaleMatcher::getBestMatch(Locale::Iterator &desiredLocales,
613                                           UErrorCode &errorCode) const {
614     if (U_FAILURE(errorCode)) { return nullptr; }
615     if (!desiredLocales.hasNext()) {
616         return defaultLocale;
617     }
618     LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
619     int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
620     return U_SUCCESS(errorCode) && suppIndex >= 0 ? supportedLocales[suppIndex] : defaultLocale;
621 }
622 
getBestMatchForListString(StringPiece desiredLocaleList,UErrorCode & errorCode) const623 const Locale *LocaleMatcher::getBestMatchForListString(
624         StringPiece desiredLocaleList, UErrorCode &errorCode) const {
625     LocalePriorityList list(desiredLocaleList, errorCode);
626     LocalePriorityList::Iterator iter = list.iterator();
627     return getBestMatch(iter, errorCode);
628 }
629 
getBestMatchResult(const Locale & desiredLocale,UErrorCode & errorCode) const630 LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
631         const Locale &desiredLocale, UErrorCode &errorCode) const {
632     if (U_FAILURE(errorCode)) {
633         return Result(nullptr, defaultLocale, -1, defaultLocaleIndex, FALSE);
634     }
635     int32_t suppIndex = getBestSuppIndex(
636         getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
637         nullptr, errorCode);
638     if (U_FAILURE(errorCode) || suppIndex < 0) {
639         return Result(nullptr, defaultLocale, -1, defaultLocaleIndex, FALSE);
640     } else {
641         return Result(&desiredLocale, supportedLocales[suppIndex], 0, suppIndex, FALSE);
642     }
643 }
644 
getBestMatchResult(Locale::Iterator & desiredLocales,UErrorCode & errorCode) const645 LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
646         Locale::Iterator &desiredLocales, UErrorCode &errorCode) const {
647     if (U_FAILURE(errorCode) || !desiredLocales.hasNext()) {
648         return Result(nullptr, defaultLocale, -1, defaultLocaleIndex, FALSE);
649     }
650     LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
651     int32_t suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
652     if (U_FAILURE(errorCode) || suppIndex < 0) {
653         return Result(nullptr, defaultLocale, -1, defaultLocaleIndex, FALSE);
654     } else {
655         return Result(lsrIter.orphanRemembered(), supportedLocales[suppIndex],
656                       lsrIter.getBestDesiredIndex(), suppIndex, TRUE);
657     }
658 }
659 
getBestSuppIndex(LSR desiredLSR,LocaleLsrIterator * remainingIter,UErrorCode & errorCode) const660 int32_t LocaleMatcher::getBestSuppIndex(LSR desiredLSR, LocaleLsrIterator *remainingIter,
661                                         UErrorCode &errorCode) const {
662     if (U_FAILURE(errorCode)) { return -1; }
663     int32_t desiredIndex = 0;
664     int32_t bestSupportedLsrIndex = -1;
665     for (int32_t bestDistance = thresholdDistance;;) {
666         // Quick check for exact maximized LSR.
667         // Returns suppIndex+1 where 0 means not found.
668         if (supportedLsrToIndex != nullptr) {
669             desiredLSR.setHashCode();
670             int32_t index = uhash_geti(supportedLsrToIndex, &desiredLSR);
671             if (index != 0) {
672                 int32_t suppIndex = index - 1;
673                 if (remainingIter != nullptr) {
674                     remainingIter->rememberCurrent(desiredIndex, errorCode);
675                 }
676                 return suppIndex;
677             }
678         }
679         int32_t bestIndexAndDistance = localeDistance.getBestIndexAndDistance(
680                 desiredLSR, supportedLSRs, supportedLSRsLength, bestDistance, favorSubtag);
681         if (bestIndexAndDistance >= 0) {
682             bestDistance = bestIndexAndDistance & 0xff;
683             if (remainingIter != nullptr) {
684                 remainingIter->rememberCurrent(desiredIndex, errorCode);
685                 if (U_FAILURE(errorCode)) { return -1; }
686             }
687             bestSupportedLsrIndex = bestIndexAndDistance >= 0 ? bestIndexAndDistance >> 8 : -1;
688         }
689         if ((bestDistance -= demotionPerDesiredLocale) <= 0) {
690             break;
691         }
692         if (remainingIter == nullptr || !remainingIter->hasNext()) {
693             break;
694         }
695         desiredLSR = remainingIter->next(errorCode);
696         if (U_FAILURE(errorCode)) { return -1; }
697         ++desiredIndex;
698     }
699     if (bestSupportedLsrIndex < 0) {
700         // no good match
701         return -1;
702     }
703     return supportedIndexes[bestSupportedLsrIndex];
704 }
705 
internalMatch(const Locale & desired,const Locale & supported,UErrorCode & errorCode) const706 double LocaleMatcher::internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const {
707     // Returns the inverse of the distance: That is, 1-distance(desired, supported).
708     LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);
709     if (U_FAILURE(errorCode)) { return 0; }
710     const LSR *pSuppLSR = &suppLSR;
711     int32_t distance = localeDistance.getBestIndexAndDistance(
712             getMaximalLsrOrUnd(likelySubtags, desired, errorCode),
713             &pSuppLSR, 1,
714             thresholdDistance, favorSubtag) & 0xff;
715     return (100 - distance) / 100.0;
716 }
717 
718 U_NAMESPACE_END
719 
720 #endif  // __LOCMATCHER_H__
721