Path: blob/master/thirdparty/icu4c/common/localematcher.cpp
9903 views
// © 2019 and later: Unicode, Inc. and others.1// License & terms of use: http://www.unicode.org/copyright.html23// localematcher.cpp4// created: 2019may08 Markus W. Scherer56#include <optional>78#include "unicode/utypes.h"9#include "unicode/localebuilder.h"10#include "unicode/localematcher.h"11#include "unicode/locid.h"12#include "unicode/stringpiece.h"13#include "unicode/uloc.h"14#include "unicode/uobject.h"15#include "cstring.h"16#include "localeprioritylist.h"17#include "loclikelysubtags.h"18#include "locdistance.h"19#include "lsr.h"20#include "uassert.h"21#include "uhash.h"22#include "ustr_imp.h"23#include "uvector.h"2425#define UND_LSR LSR("und", "", "", LSR::EXPLICIT_LSR)2627/**28* Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher.29*30* @draft ICU 6531*/32enum 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 6538*/39ULOCMATCH_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 6546*/47ULOCMATCH_STORED_LOCALES // TODO: permanent? cached? clone?48};49#ifndef U_IN_DOXYGEN50typedef enum ULocMatchLifetime ULocMatchLifetime;51#endif5253U_NAMESPACE_BEGIN5455LocaleMatcher::Result::Result(LocaleMatcher::Result &&src) noexcept :56desiredLocale(src.desiredLocale),57supportedLocale(src.supportedLocale),58desiredIndex(src.desiredIndex),59supportedIndex(src.supportedIndex),60desiredIsOwned(src.desiredIsOwned) {61if (desiredIsOwned) {62src.desiredLocale = nullptr;63src.desiredIndex = -1;64src.desiredIsOwned = false;65}66}6768LocaleMatcher::Result::~Result() {69if (desiredIsOwned) {70delete desiredLocale;71}72}7374LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) noexcept {75this->~Result();7677desiredLocale = src.desiredLocale;78supportedLocale = src.supportedLocale;79desiredIndex = src.desiredIndex;80supportedIndex = src.supportedIndex;81desiredIsOwned = src.desiredIsOwned;8283if (desiredIsOwned) {84src.desiredLocale = nullptr;85src.desiredIndex = -1;86src.desiredIsOwned = false;87}88return *this;89}9091Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const {92if (U_FAILURE(errorCode) || supportedLocale == nullptr) {93return Locale::getRoot();94}95const Locale *bestDesired = getDesiredLocale();96if (bestDesired == nullptr || *supportedLocale == *bestDesired) {97return *supportedLocale;98}99LocaleBuilder b;100b.setLocale(*supportedLocale);101102// Copy the region from bestDesired, if there is one.103const char *region = bestDesired->getCountry();104if (*region != 0) {105b.setRegion(region);106}107108// 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).111const char *variants = bestDesired->getVariant();112if (*variants != 0) {113b.setVariant(variants);114}115116// 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).122b.copyExtensionsFrom(*bestDesired, errorCode);123return b.build(errorCode);124}125126LocaleMatcher::Builder::Builder(LocaleMatcher::Builder &&src) noexcept :127errorCode_(src.errorCode_),128supportedLocales_(src.supportedLocales_),129thresholdDistance_(src.thresholdDistance_),130demotion_(src.demotion_),131defaultLocale_(src.defaultLocale_),132withDefault_(src.withDefault_),133favor_(src.favor_),134direction_(src.direction_) {135src.supportedLocales_ = nullptr;136src.defaultLocale_ = nullptr;137}138139LocaleMatcher::Builder::~Builder() {140delete supportedLocales_;141delete defaultLocale_;142delete maxDistanceDesired_;143delete maxDistanceSupported_;144}145146LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) noexcept {147this->~Builder();148149errorCode_ = src.errorCode_;150supportedLocales_ = src.supportedLocales_;151thresholdDistance_ = src.thresholdDistance_;152demotion_ = src.demotion_;153defaultLocale_ = src.defaultLocale_;154withDefault_ = src.withDefault_,155favor_ = src.favor_;156direction_ = src.direction_;157158src.supportedLocales_ = nullptr;159src.defaultLocale_ = nullptr;160return *this;161}162163void LocaleMatcher::Builder::clearSupportedLocales() {164if (supportedLocales_ != nullptr) {165supportedLocales_->removeAllElements();166}167}168169bool LocaleMatcher::Builder::ensureSupportedLocaleVector() {170if (U_FAILURE(errorCode_)) { return false; }171if (supportedLocales_ != nullptr) { return true; }172LocalPointer<UVector> lpSupportedLocales(new UVector(uprv_deleteUObject, nullptr, errorCode_), errorCode_);173if (U_FAILURE(errorCode_)) { return false; }174supportedLocales_ = lpSupportedLocales.orphan();175return true;176}177178LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListString(179StringPiece locales) {180LocalePriorityList list(locales, errorCode_);181if (U_FAILURE(errorCode_)) { return *this; }182clearSupportedLocales();183if (!ensureSupportedLocaleVector()) { return *this; }184int32_t length = list.getLengthIncludingRemoved();185for (int32_t i = 0; i < length; ++i) {186Locale *locale = list.orphanLocaleAt(i);187if (locale == nullptr) { continue; }188supportedLocales_->adoptElement(locale, errorCode_);189if (U_FAILURE(errorCode_)) {190break;191}192}193return *this;194}195196LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) {197if (ensureSupportedLocaleVector()) {198clearSupportedLocales();199while (locales.hasNext() && U_SUCCESS(errorCode_)) {200const Locale &locale = locales.next();201LocalPointer<Locale> clone (locale.clone(), errorCode_);202supportedLocales_->adoptElement(clone.orphan(), errorCode_);203}204}205return *this;206}207208LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) {209if (ensureSupportedLocaleVector()) {210LocalPointer<Locale> clone(locale.clone(), errorCode_);211supportedLocales_->adoptElement(clone.orphan(), errorCode_);212}213return *this;214}215216LocaleMatcher::Builder &LocaleMatcher::Builder::setNoDefaultLocale() {217if (U_FAILURE(errorCode_)) { return *this; }218delete defaultLocale_;219defaultLocale_ = nullptr;220withDefault_ = false;221return *this;222}223224LocaleMatcher::Builder &LocaleMatcher::Builder::setDefaultLocale(const Locale *defaultLocale) {225if (U_FAILURE(errorCode_)) { return *this; }226Locale *clone = nullptr;227if (defaultLocale != nullptr) {228clone = defaultLocale->clone();229if (clone == nullptr) {230errorCode_ = U_MEMORY_ALLOCATION_ERROR;231return *this;232}233}234delete defaultLocale_;235defaultLocale_ = clone;236withDefault_ = true;237return *this;238}239240LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) {241if (U_FAILURE(errorCode_)) { return *this; }242favor_ = subtag;243return *this;244}245246LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) {247if (U_FAILURE(errorCode_)) { return *this; }248demotion_ = demotion;249return *this;250}251252LocaleMatcher::Builder &LocaleMatcher::Builder::setMaxDistance(const Locale &desired,253const Locale &supported) {254if (U_FAILURE(errorCode_)) { return *this; }255Locale *desiredClone = desired.clone();256Locale *supportedClone = supported.clone();257if (desiredClone == nullptr || supportedClone == nullptr) {258delete desiredClone; // in case only one could not be allocated259delete supportedClone;260errorCode_ = U_MEMORY_ALLOCATION_ERROR;261return *this;262}263delete maxDistanceDesired_;264delete maxDistanceSupported_;265maxDistanceDesired_ = desiredClone;266maxDistanceSupported_ = supportedClone;267return *this;268}269270#if 0271/**272* <i>Internal only!</i>273*274* @param thresholdDistance the thresholdDistance to set, with -1 = default275* @return this Builder object276* @internal277* @deprecated This API is ICU internal only.278*/279@Deprecated280LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) {281if (U_FAILURE(errorCode_)) { return *this; }282if (thresholdDistance > 100) {283thresholdDistance = 100;284}285thresholdDistance_ = thresholdDistance;286return *this;287}288#endif289290UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const {291if (U_FAILURE(outErrorCode)) { return true; }292if (U_SUCCESS(errorCode_)) { return false; }293outErrorCode = errorCode_;294return true;295}296297LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const {298if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) {299errorCode = errorCode_;300}301return LocaleMatcher(*this, errorCode);302}303304namespace {305306LSR getMaximalLsrOrUnd(const LikelySubtags &likelySubtags, const Locale &locale,307UErrorCode &errorCode) {308if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) {309return UND_LSR;310} else {311return likelySubtags.makeMaximizedLsrFrom(locale, false, errorCode);312}313}314315int32_t hashLSR(const UHashTok token) {316const LSR *lsr = static_cast<const LSR *>(token.pointer);317return lsr->hashCode;318}319320UBool compareLSRs(const UHashTok t1, const UHashTok t2) {321const LSR *lsr1 = static_cast<const LSR *>(t1.pointer);322const LSR *lsr2 = static_cast<const LSR *>(t2.pointer);323return *lsr1 == *lsr2;324}325326} // namespace327328int32_t LocaleMatcher::putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength,329UErrorCode &errorCode) {330if (U_FAILURE(errorCode)) { return suppLength; }331if (!uhash_containsKey(supportedLsrToIndex, &lsr)) {332uhash_putiAllowZero(supportedLsrToIndex, const_cast<LSR *>(&lsr), i, &errorCode);333if (U_SUCCESS(errorCode)) {334supportedLSRs[suppLength] = &lsr;335supportedIndexes[suppLength++] = i;336}337}338return suppLength;339}340341LocaleMatcher::LocaleMatcher(const Builder &builder, UErrorCode &errorCode) :342likelySubtags(*LikelySubtags::getSingleton(errorCode)),343localeDistance(*LocaleDistance::getSingleton(errorCode)),344thresholdDistance(builder.thresholdDistance_),345demotionPerDesiredLocale(0),346favorSubtag(builder.favor_),347direction(builder.direction_),348supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0),349supportedLsrToIndex(nullptr),350supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0),351ownedDefaultLocale(nullptr), defaultLocale(nullptr) {352if (U_FAILURE(errorCode)) { return; }353const Locale *def = builder.defaultLocale_;354LSR builderDefaultLSR;355const LSR *defLSR = nullptr;356if (def != nullptr) {357ownedDefaultLocale = def->clone();358if (ownedDefaultLocale == nullptr) {359errorCode = U_MEMORY_ALLOCATION_ERROR;360return;361}362def = ownedDefaultLocale;363builderDefaultLSR = getMaximalLsrOrUnd(likelySubtags, *def, errorCode);364if (U_FAILURE(errorCode)) { return; }365defLSR = &builderDefaultLSR;366}367supportedLocalesLength = builder.supportedLocales_ != nullptr ?368builder.supportedLocales_->size() : 0;369if (supportedLocalesLength > 0) {370// Store the supported locales in input order,371// so that when different types are used (e.g., language tag strings)372// we can return those by parallel index.373supportedLocales = static_cast<const Locale **>(374uprv_malloc(supportedLocalesLength * sizeof(const Locale *)));375// Supported LRSs in input order.376// In C++, we store these permanently to simplify ownership management377// in the hash tables. Duplicate LSRs (if any) are unused overhead.378lsrs = new LSR[supportedLocalesLength];379if (supportedLocales == nullptr || lsrs == nullptr) {380errorCode = U_MEMORY_ALLOCATION_ERROR;381return;382}383// If the constructor fails partway, we need null pointers for destructibility.384uprv_memset(supportedLocales, 0, supportedLocalesLength * sizeof(const Locale *));385for (int32_t i = 0; i < supportedLocalesLength; ++i) {386const Locale &locale = *static_cast<Locale *>(builder.supportedLocales_->elementAt(i));387supportedLocales[i] = locale.clone();388if (supportedLocales[i] == nullptr) {389errorCode = U_MEMORY_ALLOCATION_ERROR;390return;391}392const Locale &supportedLocale = *supportedLocales[i];393LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode);394lsr.setHashCode();395if (U_FAILURE(errorCode)) { return; }396}397398// We need an unordered map from LSR to first supported locale with that LSR,399// and an ordered list of (LSR, supported index) for400// the supported locales in the following order:401// 1. Default locale, if it is supported.402// 2. Priority locales (aka "paradigm locales") in builder order.403// 3. Remaining locales in builder order.404supportedLsrToIndex = uhash_openSize(hashLSR, compareLSRs, uhash_compareLong,405supportedLocalesLength, &errorCode);406if (U_FAILURE(errorCode)) { return; }407supportedLSRs = static_cast<const LSR **>(408uprv_malloc(supportedLocalesLength * sizeof(const LSR *)));409supportedIndexes = static_cast<int32_t *>(410uprv_malloc(supportedLocalesLength * sizeof(int32_t)));411if (supportedLSRs == nullptr || supportedIndexes == nullptr) {412errorCode = U_MEMORY_ALLOCATION_ERROR;413return;414}415int32_t suppLength = 0;416// Determine insertion order.417// Add locales immediately that are equivalent to the default.418MaybeStackArray<int8_t, 100> order(supportedLocalesLength, errorCode);419if (U_FAILURE(errorCode)) { return; }420int32_t numParadigms = 0;421for (int32_t i = 0; i < supportedLocalesLength; ++i) {422const Locale &locale = *supportedLocales[i];423const LSR &lsr = lsrs[i];424if (defLSR == nullptr && builder.withDefault_) {425// Implicit default locale = first supported locale, if not turned off.426U_ASSERT(i == 0);427def = &locale;428defLSR = &lsr;429order[i] = 1;430suppLength = putIfAbsent(lsr, 0, suppLength, errorCode);431} else if (defLSR != nullptr && lsr.isEquivalentTo(*defLSR)) {432order[i] = 1;433suppLength = putIfAbsent(lsr, i, suppLength, errorCode);434} else if (localeDistance.isParadigmLSR(lsr)) {435order[i] = 2;436++numParadigms;437} else {438order[i] = 3;439}440if (U_FAILURE(errorCode)) { return; }441}442// Add supported paradigm locales.443int32_t paradigmLimit = suppLength + numParadigms;444for (int32_t i = 0; i < supportedLocalesLength && suppLength < paradigmLimit; ++i) {445if (order[i] == 2) {446suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);447}448}449// Add remaining supported locales.450for (int32_t i = 0; i < supportedLocalesLength; ++i) {451if (order[i] == 3) {452suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);453}454}455supportedLSRsLength = suppLength;456// If supportedLSRsLength < supportedLocalesLength then457// we waste as many array slots as there are duplicate supported LSRs,458// but the amount of wasted space is small as long as there are few duplicates.459}460461defaultLocale = def;462463if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) {464demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale();465}466467if (thresholdDistance >= 0) {468// already copied469} else if (builder.maxDistanceDesired_ != nullptr) {470LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceSupported_, errorCode);471const LSR *pSuppLSR = &suppLSR;472int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(473getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceDesired_, errorCode),474&pSuppLSR, 1,475LocaleDistance::shiftDistance(100), favorSubtag, direction);476if (U_SUCCESS(errorCode)) {477// +1 for an exclusive threshold from an inclusive max.478thresholdDistance = LocaleDistance::getDistanceFloor(indexAndDistance) + 1;479} else {480thresholdDistance = 0;481}482} else {483thresholdDistance = localeDistance.getDefaultScriptDistance();484}485}486487LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) noexcept :488likelySubtags(src.likelySubtags),489localeDistance(src.localeDistance),490thresholdDistance(src.thresholdDistance),491demotionPerDesiredLocale(src.demotionPerDesiredLocale),492favorSubtag(src.favorSubtag),493direction(src.direction),494supportedLocales(src.supportedLocales), lsrs(src.lsrs),495supportedLocalesLength(src.supportedLocalesLength),496supportedLsrToIndex(src.supportedLsrToIndex),497supportedLSRs(src.supportedLSRs),498supportedIndexes(src.supportedIndexes),499supportedLSRsLength(src.supportedLSRsLength),500ownedDefaultLocale(src.ownedDefaultLocale), defaultLocale(src.defaultLocale) {501src.supportedLocales = nullptr;502src.lsrs = nullptr;503src.supportedLocalesLength = 0;504src.supportedLsrToIndex = nullptr;505src.supportedLSRs = nullptr;506src.supportedIndexes = nullptr;507src.supportedLSRsLength = 0;508src.ownedDefaultLocale = nullptr;509src.defaultLocale = nullptr;510}511512LocaleMatcher::~LocaleMatcher() {513for (int32_t i = 0; i < supportedLocalesLength; ++i) {514delete supportedLocales[i];515}516uprv_free(supportedLocales);517delete[] lsrs;518uhash_close(supportedLsrToIndex);519uprv_free(supportedLSRs);520uprv_free(supportedIndexes);521delete ownedDefaultLocale;522}523524LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) noexcept {525this->~LocaleMatcher();526527thresholdDistance = src.thresholdDistance;528demotionPerDesiredLocale = src.demotionPerDesiredLocale;529favorSubtag = src.favorSubtag;530direction = src.direction;531supportedLocales = src.supportedLocales;532lsrs = src.lsrs;533supportedLocalesLength = src.supportedLocalesLength;534supportedLsrToIndex = src.supportedLsrToIndex;535supportedLSRs = src.supportedLSRs;536supportedIndexes = src.supportedIndexes;537supportedLSRsLength = src.supportedLSRsLength;538ownedDefaultLocale = src.ownedDefaultLocale;539defaultLocale = src.defaultLocale;540541src.supportedLocales = nullptr;542src.lsrs = nullptr;543src.supportedLocalesLength = 0;544src.supportedLsrToIndex = nullptr;545src.supportedLSRs = nullptr;546src.supportedIndexes = nullptr;547src.supportedLSRsLength = 0;548src.ownedDefaultLocale = nullptr;549src.defaultLocale = nullptr;550return *this;551}552553class LocaleLsrIterator {554public:555LocaleLsrIterator(const LikelySubtags &likelySubtags, Locale::Iterator &locales,556ULocMatchLifetime lifetime) :557likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {}558559~LocaleLsrIterator() {560if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) {561delete remembered;562}563}564565bool hasNext() const {566return locales.hasNext();567}568569LSR next(UErrorCode &errorCode) {570current = &locales.next();571return getMaximalLsrOrUnd(likelySubtags, *current, errorCode);572}573574void rememberCurrent(int32_t desiredIndex, UErrorCode &errorCode) {575if (U_FAILURE(errorCode)) { return; }576bestDesiredIndex = desiredIndex;577if (lifetime == ULOCMATCH_STORED_LOCALES) {578remembered = current;579} else {580// ULOCMATCH_TEMPORARY_LOCALES581delete remembered;582remembered = new Locale(*current);583if (remembered == nullptr) {584errorCode = U_MEMORY_ALLOCATION_ERROR;585}586}587}588589const Locale *orphanRemembered() {590const Locale *rem = remembered;591remembered = nullptr;592return rem;593}594595int32_t getBestDesiredIndex() const {596return bestDesiredIndex;597}598599private:600const LikelySubtags &likelySubtags;601Locale::Iterator &locales;602ULocMatchLifetime lifetime;603const Locale *current = nullptr, *remembered = nullptr;604int32_t bestDesiredIndex = -1;605};606607const Locale *LocaleMatcher::getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const {608if (U_FAILURE(errorCode)) { return nullptr; }609std::optional<int32_t> suppIndex = getBestSuppIndex(610getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),611nullptr, errorCode);612return U_SUCCESS(errorCode) && suppIndex.has_value() ? supportedLocales[*suppIndex]613: defaultLocale;614}615616const Locale *LocaleMatcher::getBestMatch(Locale::Iterator &desiredLocales,617UErrorCode &errorCode) const {618if (U_FAILURE(errorCode)) { return nullptr; }619if (!desiredLocales.hasNext()) {620return defaultLocale;621}622LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);623std::optional<int32_t> suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);624return U_SUCCESS(errorCode) && suppIndex.has_value() ? supportedLocales[*suppIndex]625: defaultLocale;626}627628const Locale *LocaleMatcher::getBestMatchForListString(629StringPiece desiredLocaleList, UErrorCode &errorCode) const {630if (U_FAILURE(errorCode)) { return nullptr; }631LocalePriorityList list(desiredLocaleList, errorCode);632LocalePriorityList::Iterator iter = list.iterator();633return getBestMatch(iter, errorCode);634}635636LocaleMatcher::Result LocaleMatcher::getBestMatchResult(637const Locale &desiredLocale, UErrorCode &errorCode) const {638if (U_FAILURE(errorCode)) {639return Result(nullptr, defaultLocale, -1, -1, false);640}641std::optional<int32_t> suppIndex = getBestSuppIndex(642getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),643nullptr, errorCode);644if (U_FAILURE(errorCode) || !suppIndex.has_value()) {645return Result(nullptr, defaultLocale, -1, -1, false);646} else {647return Result(&desiredLocale, supportedLocales[*suppIndex], 0, *suppIndex, false);648}649}650651LocaleMatcher::Result LocaleMatcher::getBestMatchResult(652Locale::Iterator &desiredLocales, UErrorCode &errorCode) const {653if (U_FAILURE(errorCode) || !desiredLocales.hasNext()) {654return Result(nullptr, defaultLocale, -1, -1, false);655}656LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);657std::optional<int32_t> suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);658if (U_FAILURE(errorCode) || !suppIndex.has_value()) {659return Result(nullptr, defaultLocale, -1, -1, false);660} else {661return Result(lsrIter.orphanRemembered(), supportedLocales[*suppIndex],662lsrIter.getBestDesiredIndex(), *suppIndex, true);663}664}665666std::optional<int32_t> LocaleMatcher::getBestSuppIndex(LSR desiredLSR,667LocaleLsrIterator *remainingIter,668UErrorCode &errorCode) const {669if (U_FAILURE(errorCode)) { return std::nullopt; }670int32_t desiredIndex = 0;671int32_t bestSupportedLsrIndex = -1;672for (int32_t bestShiftedDistance = LocaleDistance::shiftDistance(thresholdDistance);;) {673// Quick check for exact maximized LSR.674if (supportedLsrToIndex != nullptr) {675desiredLSR.setHashCode();676UBool found = false;677int32_t suppIndex = uhash_getiAndFound(supportedLsrToIndex, &desiredLSR, &found);678if (found) {679if (remainingIter != nullptr) {680remainingIter->rememberCurrent(desiredIndex, errorCode);681}682return suppIndex;683}684}685int32_t bestIndexAndDistance = localeDistance.getBestIndexAndDistance(686desiredLSR, supportedLSRs, supportedLSRsLength,687bestShiftedDistance, favorSubtag, direction);688if (bestIndexAndDistance >= 0) {689bestShiftedDistance = LocaleDistance::getShiftedDistance(bestIndexAndDistance);690if (remainingIter != nullptr) {691remainingIter->rememberCurrent(desiredIndex, errorCode);692if (U_FAILURE(errorCode)) { return std::nullopt; }693}694bestSupportedLsrIndex = LocaleDistance::getIndex(bestIndexAndDistance);695}696if ((bestShiftedDistance -= LocaleDistance::shiftDistance(demotionPerDesiredLocale)) <= 0) {697break;698}699if (remainingIter == nullptr || !remainingIter->hasNext()) {700break;701}702desiredLSR = remainingIter->next(errorCode);703if (U_FAILURE(errorCode)) { return std::nullopt; }704++desiredIndex;705}706if (bestSupportedLsrIndex < 0) {707// no good match708return std::nullopt;709}710return supportedIndexes[bestSupportedLsrIndex];711}712713UBool LocaleMatcher::isMatch(const Locale &desired, const Locale &supported,714UErrorCode &errorCode) const {715if (U_FAILURE(errorCode)) { return false; }716LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);717if (U_FAILURE(errorCode)) { return false; }718const LSR *pSuppLSR = &suppLSR;719int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(720getMaximalLsrOrUnd(likelySubtags, desired, errorCode),721&pSuppLSR, 1,722LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction);723return indexAndDistance >= 0;724}725726double LocaleMatcher::internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const {727if (U_FAILURE(errorCode)) { return 0.; }728// Returns the inverse of the distance: That is, 1-distance(desired, supported).729LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);730if (U_FAILURE(errorCode)) { return 0.; }731const LSR *pSuppLSR = &suppLSR;732int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(733getMaximalLsrOrUnd(likelySubtags, desired, errorCode),734&pSuppLSR, 1,735LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction);736double distance = LocaleDistance::getDistanceDouble(indexAndDistance);737return (100.0 - distance) / 100.0;738}739740U_NAMESPACE_END741742// uloc_acceptLanguage() --------------------------------------------------- ***743744U_NAMESPACE_USE745746namespace {747748class LocaleFromTag {749public:750LocaleFromTag() : locale(Locale::getRoot()) {}751const Locale &operator()(const char *tag) { return locale = Locale(tag); }752753private:754// Store the locale in the converter, rather than return a reference to a temporary,755// or a value which could go out of scope with the caller's reference to it.756Locale locale;757};758759int32_t acceptLanguage(UEnumeration &supportedLocales, Locale::Iterator &desiredLocales,760char *dest, int32_t capacity, UAcceptResult *acceptResult,761UErrorCode &errorCode) {762if (U_FAILURE(errorCode)) { return 0; }763LocaleMatcher::Builder builder;764const char *locString;765while ((locString = uenum_next(&supportedLocales, nullptr, &errorCode)) != nullptr) {766Locale loc(locString);767if (loc.isBogus()) {768errorCode = U_ILLEGAL_ARGUMENT_ERROR;769return 0;770}771builder.addSupportedLocale(loc);772}773LocaleMatcher matcher = builder.build(errorCode);774LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocales, errorCode);775if (U_FAILURE(errorCode)) { return 0; }776if (result.getDesiredIndex() >= 0) {777if (acceptResult != nullptr) {778*acceptResult = *result.getDesiredLocale() == *result.getSupportedLocale() ?779ULOC_ACCEPT_VALID : ULOC_ACCEPT_FALLBACK;780}781const char *bestStr = result.getSupportedLocale()->getName();782int32_t bestLength = static_cast<int32_t>(uprv_strlen(bestStr));783if (bestLength <= capacity) {784uprv_memcpy(dest, bestStr, bestLength);785}786return u_terminateChars(dest, capacity, bestLength, &errorCode);787} else {788if (acceptResult != nullptr) {789*acceptResult = ULOC_ACCEPT_FAILED;790}791return u_terminateChars(dest, capacity, 0, &errorCode);792}793}794795} // namespace796797U_CAPI int32_t U_EXPORT2798uloc_acceptLanguage(char *result, int32_t resultAvailable,799UAcceptResult *outResult,800const char **acceptList, int32_t acceptListCount,801UEnumeration *availableLocales,802UErrorCode *status) {803if (U_FAILURE(*status)) { return 0; }804if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||805(acceptList == nullptr ? acceptListCount != 0 : acceptListCount < 0) ||806availableLocales == nullptr) {807*status = U_ILLEGAL_ARGUMENT_ERROR;808return 0;809}810LocaleFromTag converter;811Locale::ConvertingIterator<const char **, LocaleFromTag> desiredLocales(812acceptList, acceptList + acceptListCount, converter);813return acceptLanguage(*availableLocales, desiredLocales,814result, resultAvailable, outResult, *status);815}816817U_CAPI int32_t U_EXPORT2818uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable,819UAcceptResult *outResult,820const char *httpAcceptLanguage,821UEnumeration *availableLocales,822UErrorCode *status) {823if (U_FAILURE(*status)) { return 0; }824if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||825httpAcceptLanguage == nullptr || availableLocales == nullptr) {826*status = U_ILLEGAL_ARGUMENT_ERROR;827return 0;828}829LocalePriorityList list(httpAcceptLanguage, *status);830LocalePriorityList::Iterator desiredLocales = list.iterator();831return acceptLanguage(*availableLocales, desiredLocales,832result, resultAvailable, outResult, *status);833}834835836