tor-browser

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

localematcher.cpp (31885B)


      1 // © 2019 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 
      4 // localematcher.cpp
      5 // created: 2019may08 Markus W. Scherer
      6 
      7 #include <optional>
      8 
      9 #include "unicode/utypes.h"
     10 #include "unicode/localebuilder.h"
     11 #include "unicode/localematcher.h"
     12 #include "unicode/locid.h"
     13 #include "unicode/stringpiece.h"
     14 #include "unicode/uloc.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 "ustr_imp.h"
     24 #include "uvector.h"
     25 
     26 #define UND_LSR LSR("und", "", "", LSR::EXPLICIT_LSR)
     27 
     28 /**
     29 * Indicator for the lifetime of desired-locale objects passed into the LocaleMatcher.
     30 *
     31 * @draft ICU 65
     32 */
     33 enum ULocMatchLifetime {
     34    /**
     35     * Locale objects are temporary.
     36     * The matcher will make a copy of a locale that will be used beyond one function call.
     37     *
     38     * @draft ICU 65
     39     */
     40    ULOCMATCH_TEMPORARY_LOCALES,
     41    /**
     42     * Locale objects are stored at least as long as the matcher is used.
     43     * The matcher will keep only a pointer to a locale that will be used beyond one function call,
     44     * avoiding a copy.
     45     *
     46     * @draft ICU 65
     47     */
     48    ULOCMATCH_STORED_LOCALES  // TODO: permanent? cached? clone?
     49 };
     50 #ifndef U_IN_DOXYGEN
     51 typedef enum ULocMatchLifetime ULocMatchLifetime;
     52 #endif
     53 
     54 U_NAMESPACE_BEGIN
     55 
     56 LocaleMatcher::Result::Result(LocaleMatcher::Result &&src) noexcept :
     57        desiredLocale(src.desiredLocale),
     58        supportedLocale(src.supportedLocale),
     59        desiredIndex(src.desiredIndex),
     60        supportedIndex(src.supportedIndex),
     61        desiredIsOwned(src.desiredIsOwned) {
     62    if (desiredIsOwned) {
     63        src.desiredLocale = nullptr;
     64        src.desiredIndex = -1;
     65        src.desiredIsOwned = false;
     66    }
     67 }
     68 
     69 LocaleMatcher::Result::~Result() {
     70    if (desiredIsOwned) {
     71        delete desiredLocale;
     72    }
     73 }
     74 
     75 LocaleMatcher::Result &LocaleMatcher::Result::operator=(LocaleMatcher::Result &&src) noexcept {
     76    this->~Result();
     77 
     78    desiredLocale = src.desiredLocale;
     79    supportedLocale = src.supportedLocale;
     80    desiredIndex = src.desiredIndex;
     81    supportedIndex = src.supportedIndex;
     82    desiredIsOwned = src.desiredIsOwned;
     83 
     84    if (desiredIsOwned) {
     85        src.desiredLocale = nullptr;
     86        src.desiredIndex = -1;
     87        src.desiredIsOwned = false;
     88    }
     89    return *this;
     90 }
     91 
     92 Locale LocaleMatcher::Result::makeResolvedLocale(UErrorCode &errorCode) const {
     93    if (U_FAILURE(errorCode) || supportedLocale == nullptr) {
     94        return Locale::getRoot();
     95    }
     96    const Locale *bestDesired = getDesiredLocale();
     97    if (bestDesired == nullptr || *supportedLocale == *bestDesired) {
     98        return *supportedLocale;
     99    }
    100    LocaleBuilder b;
    101    b.setLocale(*supportedLocale);
    102 
    103    // Copy the region from bestDesired, if there is one.
    104    const char *region = bestDesired->getCountry();
    105    if (*region != 0) {
    106        b.setRegion(region);
    107    }
    108 
    109    // Copy the variants from bestDesired, if there are any.
    110    // Note that this will override any supportedLocale variants.
    111    // For example, "sco-ulster-fonipa" + "...-fonupa" => "sco-fonupa" (replacing ulster).
    112    const char *variants = bestDesired->getVariant();
    113    if (*variants != 0) {
    114        b.setVariant(variants);
    115    }
    116 
    117    // Copy the extensions from bestDesired, if there are any.
    118    // C++ note: The following note, copied from Java, may not be true,
    119    // as long as C++ copies by legacy ICU keyword, not by extension singleton.
    120    // Note that this will override any supportedLocale extensions.
    121    // For example, "th-u-nu-latn-ca-buddhist" + "...-u-nu-native" => "th-u-nu-native"
    122    // (replacing calendar).
    123    b.copyExtensionsFrom(*bestDesired, errorCode);
    124    return b.build(errorCode);
    125 }
    126 
    127 LocaleMatcher::Builder::Builder(LocaleMatcher::Builder &&src) noexcept :
    128        errorCode_(src.errorCode_),
    129        supportedLocales_(src.supportedLocales_),
    130        thresholdDistance_(src.thresholdDistance_),
    131        demotion_(src.demotion_),
    132        defaultLocale_(src.defaultLocale_),
    133        withDefault_(src.withDefault_),
    134        favor_(src.favor_),
    135        direction_(src.direction_) {
    136    src.supportedLocales_ = nullptr;
    137    src.defaultLocale_ = nullptr;
    138 }
    139 
    140 LocaleMatcher::Builder::~Builder() {
    141    delete supportedLocales_;
    142    delete defaultLocale_;
    143    delete maxDistanceDesired_;
    144    delete maxDistanceSupported_;
    145 }
    146 
    147 LocaleMatcher::Builder &LocaleMatcher::Builder::operator=(LocaleMatcher::Builder &&src) noexcept {
    148    this->~Builder();
    149 
    150    errorCode_ = src.errorCode_;
    151    supportedLocales_ = src.supportedLocales_;
    152    thresholdDistance_ = src.thresholdDistance_;
    153    demotion_ = src.demotion_;
    154    defaultLocale_ = src.defaultLocale_;
    155    withDefault_ = src.withDefault_,
    156    favor_ = src.favor_;
    157    direction_ = src.direction_;
    158 
    159    src.supportedLocales_ = nullptr;
    160    src.defaultLocale_ = nullptr;
    161    return *this;
    162 }
    163 
    164 void LocaleMatcher::Builder::clearSupportedLocales() {
    165    if (supportedLocales_ != nullptr) {
    166        supportedLocales_->removeAllElements();
    167    }
    168 }
    169 
    170 bool LocaleMatcher::Builder::ensureSupportedLocaleVector() {
    171    if (U_FAILURE(errorCode_)) { return false; }
    172    if (supportedLocales_ != nullptr) { return true; }
    173    LocalPointer<UVector> lpSupportedLocales(new UVector(uprv_deleteUObject, nullptr, errorCode_), errorCode_);
    174    if (U_FAILURE(errorCode_)) { return false; }
    175    supportedLocales_ = lpSupportedLocales.orphan();
    176    return true;
    177 }
    178 
    179 LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocalesFromListString(
    180        StringPiece locales) {
    181    LocalePriorityList list(locales, errorCode_);
    182    if (U_FAILURE(errorCode_)) { return *this; }
    183    clearSupportedLocales();
    184    if (!ensureSupportedLocaleVector()) { return *this; }
    185    int32_t length = list.getLengthIncludingRemoved();
    186    for (int32_t i = 0; i < length; ++i) {
    187        Locale *locale = list.orphanLocaleAt(i);
    188        if (locale == nullptr) { continue; }
    189        supportedLocales_->adoptElement(locale, errorCode_);
    190        if (U_FAILURE(errorCode_)) {
    191            break;
    192        }
    193    }
    194    return *this;
    195 }
    196 
    197 LocaleMatcher::Builder &LocaleMatcher::Builder::setSupportedLocales(Locale::Iterator &locales) {
    198    if (ensureSupportedLocaleVector()) {
    199        clearSupportedLocales();
    200        while (locales.hasNext() && U_SUCCESS(errorCode_)) {
    201            const Locale &locale = locales.next();
    202            LocalPointer<Locale> clone (locale.clone(), errorCode_);
    203            supportedLocales_->adoptElement(clone.orphan(), errorCode_);
    204        }
    205    }
    206    return *this;
    207 }
    208 
    209 LocaleMatcher::Builder &LocaleMatcher::Builder::addSupportedLocale(const Locale &locale) {
    210    if (ensureSupportedLocaleVector()) {
    211        LocalPointer<Locale> clone(locale.clone(), errorCode_);
    212        supportedLocales_->adoptElement(clone.orphan(), errorCode_);
    213    }
    214    return *this;
    215 }
    216 
    217 LocaleMatcher::Builder &LocaleMatcher::Builder::setNoDefaultLocale() {
    218    if (U_FAILURE(errorCode_)) { return *this; }
    219    delete defaultLocale_;
    220    defaultLocale_ = nullptr;
    221    withDefault_ = false;
    222    return *this;
    223 }
    224 
    225 LocaleMatcher::Builder &LocaleMatcher::Builder::setDefaultLocale(const Locale *defaultLocale) {
    226    if (U_FAILURE(errorCode_)) { return *this; }
    227    Locale *clone = nullptr;
    228    if (defaultLocale != nullptr) {
    229        clone = defaultLocale->clone();
    230        if (clone == nullptr) {
    231            errorCode_ = U_MEMORY_ALLOCATION_ERROR;
    232            return *this;
    233        }
    234    }
    235    delete defaultLocale_;
    236    defaultLocale_ = clone;
    237    withDefault_ = true;
    238    return *this;
    239 }
    240 
    241 LocaleMatcher::Builder &LocaleMatcher::Builder::setFavorSubtag(ULocMatchFavorSubtag subtag) {
    242    if (U_FAILURE(errorCode_)) { return *this; }
    243    favor_ = subtag;
    244    return *this;
    245 }
    246 
    247 LocaleMatcher::Builder &LocaleMatcher::Builder::setDemotionPerDesiredLocale(ULocMatchDemotion demotion) {
    248    if (U_FAILURE(errorCode_)) { return *this; }
    249    demotion_ = demotion;
    250    return *this;
    251 }
    252 
    253 LocaleMatcher::Builder &LocaleMatcher::Builder::setMaxDistance(const Locale &desired,
    254                                                               const Locale &supported) {
    255    if (U_FAILURE(errorCode_)) { return *this; }
    256    Locale *desiredClone = desired.clone();
    257    Locale *supportedClone = supported.clone();
    258    if (desiredClone == nullptr || supportedClone == nullptr) {
    259        delete desiredClone;  // in case only one could not be allocated
    260        delete supportedClone;
    261        errorCode_ = U_MEMORY_ALLOCATION_ERROR;
    262        return *this;
    263    }
    264    delete maxDistanceDesired_;
    265    delete maxDistanceSupported_;
    266    maxDistanceDesired_ = desiredClone;
    267    maxDistanceSupported_ = supportedClone;
    268    return *this;
    269 }
    270 
    271 #if 0
    272 /**
    273 * <i>Internal only!</i>
    274 *
    275 * @param thresholdDistance the thresholdDistance to set, with -1 = default
    276 * @return this Builder object
    277 * @internal
    278 * @deprecated This API is ICU internal only.
    279 */
    280 @Deprecated
    281 LocaleMatcher::Builder &LocaleMatcher::Builder::internalSetThresholdDistance(int32_t thresholdDistance) {
    282    if (U_FAILURE(errorCode_)) { return *this; }
    283    if (thresholdDistance > 100) {
    284        thresholdDistance = 100;
    285    }
    286    thresholdDistance_ = thresholdDistance;
    287    return *this;
    288 }
    289 #endif
    290 
    291 UBool LocaleMatcher::Builder::copyErrorTo(UErrorCode &outErrorCode) const {
    292    if (U_FAILURE(outErrorCode)) { return true; }
    293    if (U_SUCCESS(errorCode_)) { return false; }
    294    outErrorCode = errorCode_;
    295    return true;
    296 }
    297 
    298 LocaleMatcher LocaleMatcher::Builder::build(UErrorCode &errorCode) const {
    299    if (U_SUCCESS(errorCode) && U_FAILURE(errorCode_)) {
    300        errorCode = errorCode_;
    301    }
    302    return LocaleMatcher(*this, errorCode);
    303 }
    304 
    305 namespace {
    306 
    307 LSR getMaximalLsrOrUnd(const LikelySubtags &likelySubtags, const Locale &locale,
    308                       UErrorCode &errorCode) {
    309    if (U_FAILURE(errorCode) || locale.isBogus() || *locale.getName() == 0 /* "und" */) {
    310        return UND_LSR;
    311    } else {
    312        return likelySubtags.makeMaximizedLsrFrom(locale, false, errorCode);
    313    }
    314 }
    315 
    316 int32_t hashLSR(const UHashTok token) {
    317    const LSR *lsr = static_cast<const LSR *>(token.pointer);
    318    return lsr->hashCode;
    319 }
    320 
    321 UBool compareLSRs(const UHashTok t1, const UHashTok t2) {
    322    const LSR *lsr1 = static_cast<const LSR *>(t1.pointer);
    323    const LSR *lsr2 = static_cast<const LSR *>(t2.pointer);
    324    return *lsr1 == *lsr2;
    325 }
    326 
    327 }  // namespace
    328 
    329 int32_t LocaleMatcher::putIfAbsent(const LSR &lsr, int32_t i, int32_t suppLength,
    330                                   UErrorCode &errorCode) {
    331    if (U_FAILURE(errorCode)) { return suppLength; }
    332    if (!uhash_containsKey(supportedLsrToIndex, &lsr)) {
    333        uhash_putiAllowZero(supportedLsrToIndex, const_cast<LSR *>(&lsr), i, &errorCode);
    334        if (U_SUCCESS(errorCode)) {
    335            supportedLSRs[suppLength] = &lsr;
    336            supportedIndexes[suppLength++] = i;
    337        }
    338    }
    339    return suppLength;
    340 }
    341 
    342 LocaleMatcher::LocaleMatcher(const Builder &builder, UErrorCode &errorCode) :
    343        likelySubtags(*LikelySubtags::getSingleton(errorCode)),
    344        localeDistance(*LocaleDistance::getSingleton(errorCode)),
    345        thresholdDistance(builder.thresholdDistance_),
    346        demotionPerDesiredLocale(0),
    347        favorSubtag(builder.favor_),
    348        direction(builder.direction_),
    349        supportedLocales(nullptr), lsrs(nullptr), supportedLocalesLength(0),
    350        supportedLsrToIndex(nullptr),
    351        supportedLSRs(nullptr), supportedIndexes(nullptr), supportedLSRsLength(0),
    352        ownedDefaultLocale(nullptr), defaultLocale(nullptr) {
    353    if (U_FAILURE(errorCode)) { return; }
    354    const Locale *def = builder.defaultLocale_;
    355    LSR builderDefaultLSR;
    356    const LSR *defLSR = nullptr;
    357    if (def != nullptr) {
    358        ownedDefaultLocale = def->clone();
    359        if (ownedDefaultLocale == nullptr) {
    360            errorCode = U_MEMORY_ALLOCATION_ERROR;
    361            return;
    362        }
    363        def = ownedDefaultLocale;
    364        builderDefaultLSR = getMaximalLsrOrUnd(likelySubtags, *def, errorCode);
    365        if (U_FAILURE(errorCode)) { return; }
    366        defLSR = &builderDefaultLSR;
    367    }
    368    supportedLocalesLength = builder.supportedLocales_ != nullptr ?
    369        builder.supportedLocales_->size() : 0;
    370    if (supportedLocalesLength > 0) {
    371        // Store the supported locales in input order,
    372        // so that when different types are used (e.g., language tag strings)
    373        // we can return those by parallel index.
    374        supportedLocales = static_cast<const Locale **>(
    375            uprv_malloc(supportedLocalesLength * sizeof(const Locale *)));
    376        // Supported LRSs in input order.
    377        // In C++, we store these permanently to simplify ownership management
    378        // in the hash tables. Duplicate LSRs (if any) are unused overhead.
    379        lsrs = new LSR[supportedLocalesLength];
    380        if (supportedLocales == nullptr || lsrs == nullptr) {
    381            errorCode = U_MEMORY_ALLOCATION_ERROR;
    382            return;
    383        }
    384        // If the constructor fails partway, we need null pointers for destructibility.
    385        uprv_memset(supportedLocales, 0, supportedLocalesLength * sizeof(const Locale *));
    386        for (int32_t i = 0; i < supportedLocalesLength; ++i) {
    387            const Locale &locale = *static_cast<Locale *>(builder.supportedLocales_->elementAt(i));
    388            supportedLocales[i] = locale.clone();
    389            if (supportedLocales[i] == nullptr) {
    390                errorCode = U_MEMORY_ALLOCATION_ERROR;
    391                return;
    392            }
    393            const Locale &supportedLocale = *supportedLocales[i];
    394            LSR &lsr = lsrs[i] = getMaximalLsrOrUnd(likelySubtags, supportedLocale, errorCode);
    395            lsr.setHashCode();
    396            if (U_FAILURE(errorCode)) { return; }
    397        }
    398 
    399        // We need an unordered map from LSR to first supported locale with that LSR,
    400        // and an ordered list of (LSR, supported index) for
    401        // the supported locales in the following order:
    402        // 1. Default locale, if it is supported.
    403        // 2. Priority locales (aka "paradigm locales") in builder order.
    404        // 3. Remaining locales in builder order.
    405        supportedLsrToIndex = uhash_openSize(hashLSR, compareLSRs, uhash_compareLong,
    406                                             supportedLocalesLength, &errorCode);
    407        if (U_FAILURE(errorCode)) { return; }
    408        supportedLSRs = static_cast<const LSR **>(
    409            uprv_malloc(supportedLocalesLength * sizeof(const LSR *)));
    410        supportedIndexes = static_cast<int32_t *>(
    411            uprv_malloc(supportedLocalesLength * sizeof(int32_t)));
    412        if (supportedLSRs == nullptr || supportedIndexes == nullptr) {
    413            errorCode = U_MEMORY_ALLOCATION_ERROR;
    414            return;
    415        }
    416        int32_t suppLength = 0;
    417        // Determine insertion order.
    418        // Add locales immediately that are equivalent to the default.
    419        MaybeStackArray<int8_t, 100> order(supportedLocalesLength, errorCode);
    420        if (U_FAILURE(errorCode)) { return; }
    421        int32_t numParadigms = 0;
    422        for (int32_t i = 0; i < supportedLocalesLength; ++i) {
    423            const Locale &locale = *supportedLocales[i];
    424            const LSR &lsr = lsrs[i];
    425            if (defLSR == nullptr && builder.withDefault_) {
    426                // Implicit default locale = first supported locale, if not turned off.
    427                U_ASSERT(i == 0);
    428                def = &locale;
    429                defLSR = &lsr;
    430                order[i] = 1;
    431                suppLength = putIfAbsent(lsr, 0, suppLength, errorCode);
    432            } else if (defLSR != nullptr && lsr.isEquivalentTo(*defLSR)) {
    433                order[i] = 1;
    434                suppLength = putIfAbsent(lsr, i, suppLength, errorCode);
    435            } else if (localeDistance.isParadigmLSR(lsr)) {
    436                order[i] = 2;
    437                ++numParadigms;
    438            } else {
    439                order[i] = 3;
    440            }
    441            if (U_FAILURE(errorCode)) { return; }
    442        }
    443        // Add supported paradigm locales.
    444        int32_t paradigmLimit = suppLength + numParadigms;
    445        for (int32_t i = 0; i < supportedLocalesLength && suppLength < paradigmLimit; ++i) {
    446            if (order[i] == 2) {
    447                suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);
    448            }
    449        }
    450        // Add remaining supported locales.
    451        for (int32_t i = 0; i < supportedLocalesLength; ++i) {
    452            if (order[i] == 3) {
    453                suppLength = putIfAbsent(lsrs[i], i, suppLength, errorCode);
    454            }
    455        }
    456        supportedLSRsLength = suppLength;
    457        // If supportedLSRsLength < supportedLocalesLength then
    458        // we waste as many array slots as there are duplicate supported LSRs,
    459        // but the amount of wasted space is small as long as there are few duplicates.
    460    }
    461 
    462    defaultLocale = def;
    463 
    464    if (builder.demotion_ == ULOCMATCH_DEMOTION_REGION) {
    465        demotionPerDesiredLocale = localeDistance.getDefaultDemotionPerDesiredLocale();
    466    }
    467 
    468    if (thresholdDistance >= 0) {
    469        // already copied
    470    } else if (builder.maxDistanceDesired_ != nullptr) {
    471        LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceSupported_, errorCode);
    472        const LSR *pSuppLSR = &suppLSR;
    473        int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(
    474                getMaximalLsrOrUnd(likelySubtags, *builder.maxDistanceDesired_, errorCode),
    475                &pSuppLSR, 1,
    476                LocaleDistance::shiftDistance(100), favorSubtag, direction);
    477        if (U_SUCCESS(errorCode)) {
    478            // +1 for an exclusive threshold from an inclusive max.
    479            thresholdDistance = LocaleDistance::getDistanceFloor(indexAndDistance) + 1;
    480        } else {
    481            thresholdDistance = 0;
    482        }
    483    } else {
    484        thresholdDistance = localeDistance.getDefaultScriptDistance();
    485    }
    486 }
    487 
    488 LocaleMatcher::LocaleMatcher(LocaleMatcher &&src) noexcept :
    489        likelySubtags(src.likelySubtags),
    490        localeDistance(src.localeDistance),
    491        thresholdDistance(src.thresholdDistance),
    492        demotionPerDesiredLocale(src.demotionPerDesiredLocale),
    493        favorSubtag(src.favorSubtag),
    494        direction(src.direction),
    495        supportedLocales(src.supportedLocales), lsrs(src.lsrs),
    496        supportedLocalesLength(src.supportedLocalesLength),
    497        supportedLsrToIndex(src.supportedLsrToIndex),
    498        supportedLSRs(src.supportedLSRs),
    499        supportedIndexes(src.supportedIndexes),
    500        supportedLSRsLength(src.supportedLSRsLength),
    501        ownedDefaultLocale(src.ownedDefaultLocale), defaultLocale(src.defaultLocale) {
    502    src.supportedLocales = nullptr;
    503    src.lsrs = nullptr;
    504    src.supportedLocalesLength = 0;
    505    src.supportedLsrToIndex = nullptr;
    506    src.supportedLSRs = nullptr;
    507    src.supportedIndexes = nullptr;
    508    src.supportedLSRsLength = 0;
    509    src.ownedDefaultLocale = nullptr;
    510    src.defaultLocale = nullptr;
    511 }
    512 
    513 LocaleMatcher::~LocaleMatcher() {
    514    for (int32_t i = 0; i < supportedLocalesLength; ++i) {
    515        delete supportedLocales[i];
    516    }
    517    uprv_free(supportedLocales);
    518    delete[] lsrs;
    519    uhash_close(supportedLsrToIndex);
    520    uprv_free(supportedLSRs);
    521    uprv_free(supportedIndexes);
    522    delete ownedDefaultLocale;
    523 }
    524 
    525 LocaleMatcher &LocaleMatcher::operator=(LocaleMatcher &&src) noexcept {
    526    this->~LocaleMatcher();
    527 
    528    thresholdDistance = src.thresholdDistance;
    529    demotionPerDesiredLocale = src.demotionPerDesiredLocale;
    530    favorSubtag = src.favorSubtag;
    531    direction = src.direction;
    532    supportedLocales = src.supportedLocales;
    533    lsrs = src.lsrs;
    534    supportedLocalesLength = src.supportedLocalesLength;
    535    supportedLsrToIndex = src.supportedLsrToIndex;
    536    supportedLSRs = src.supportedLSRs;
    537    supportedIndexes = src.supportedIndexes;
    538    supportedLSRsLength = src.supportedLSRsLength;
    539    ownedDefaultLocale = src.ownedDefaultLocale;
    540    defaultLocale = src.defaultLocale;
    541 
    542    src.supportedLocales = nullptr;
    543    src.lsrs = nullptr;
    544    src.supportedLocalesLength = 0;
    545    src.supportedLsrToIndex = nullptr;
    546    src.supportedLSRs = nullptr;
    547    src.supportedIndexes = nullptr;
    548    src.supportedLSRsLength = 0;
    549    src.ownedDefaultLocale = nullptr;
    550    src.defaultLocale = nullptr;
    551    return *this;
    552 }
    553 
    554 class LocaleLsrIterator {
    555 public:
    556    LocaleLsrIterator(const LikelySubtags &likelySubtags, Locale::Iterator &locales,
    557                      ULocMatchLifetime lifetime) :
    558            likelySubtags(likelySubtags), locales(locales), lifetime(lifetime) {}
    559 
    560    ~LocaleLsrIterator() {
    561        if (lifetime == ULOCMATCH_TEMPORARY_LOCALES) {
    562            delete remembered;
    563        }
    564    }
    565 
    566    bool hasNext() const {
    567        return locales.hasNext();
    568    }
    569 
    570    LSR next(UErrorCode &errorCode) {
    571        current = &locales.next();
    572        return getMaximalLsrOrUnd(likelySubtags, *current, errorCode);
    573    }
    574 
    575    void rememberCurrent(int32_t desiredIndex, UErrorCode &errorCode) {
    576        if (U_FAILURE(errorCode)) { return; }
    577        bestDesiredIndex = desiredIndex;
    578        if (lifetime == ULOCMATCH_STORED_LOCALES) {
    579            remembered = current;
    580        } else {
    581            // ULOCMATCH_TEMPORARY_LOCALES
    582            delete remembered;
    583            remembered = new Locale(*current);
    584            if (remembered == nullptr) {
    585                errorCode = U_MEMORY_ALLOCATION_ERROR;
    586            }
    587        }
    588    }
    589 
    590    const Locale *orphanRemembered() {
    591        const Locale *rem = remembered;
    592        remembered = nullptr;
    593        return rem;
    594    }
    595 
    596    int32_t getBestDesiredIndex() const {
    597        return bestDesiredIndex;
    598    }
    599 
    600 private:
    601    const LikelySubtags &likelySubtags;
    602    Locale::Iterator &locales;
    603    ULocMatchLifetime lifetime;
    604    const Locale *current = nullptr, *remembered = nullptr;
    605    int32_t bestDesiredIndex = -1;
    606 };
    607 
    608 const Locale *LocaleMatcher::getBestMatch(const Locale &desiredLocale, UErrorCode &errorCode) const {
    609    if (U_FAILURE(errorCode)) { return nullptr; }
    610    std::optional<int32_t> suppIndex = getBestSuppIndex(
    611        getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
    612        nullptr, errorCode);
    613    return U_SUCCESS(errorCode) && suppIndex.has_value() ? supportedLocales[*suppIndex]
    614                                                         : defaultLocale;
    615 }
    616 
    617 const Locale *LocaleMatcher::getBestMatch(Locale::Iterator &desiredLocales,
    618                                          UErrorCode &errorCode) const {
    619    if (U_FAILURE(errorCode)) { return nullptr; }
    620    if (!desiredLocales.hasNext()) {
    621        return defaultLocale;
    622    }
    623    LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
    624    std::optional<int32_t> suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
    625    return U_SUCCESS(errorCode) && suppIndex.has_value() ? supportedLocales[*suppIndex]
    626                                                         : defaultLocale;
    627 }
    628 
    629 const Locale *LocaleMatcher::getBestMatchForListString(
    630        StringPiece desiredLocaleList, UErrorCode &errorCode) const {
    631    if (U_FAILURE(errorCode)) { return nullptr; }
    632    LocalePriorityList list(desiredLocaleList, errorCode);
    633    LocalePriorityList::Iterator iter = list.iterator();
    634    return getBestMatch(iter, errorCode);
    635 }
    636 
    637 LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
    638        const Locale &desiredLocale, UErrorCode &errorCode) const {
    639    if (U_FAILURE(errorCode)) {
    640        return Result(nullptr, defaultLocale, -1, -1, false);
    641    }
    642    std::optional<int32_t> suppIndex = getBestSuppIndex(
    643        getMaximalLsrOrUnd(likelySubtags, desiredLocale, errorCode),
    644        nullptr, errorCode);
    645    if (U_FAILURE(errorCode) || !suppIndex.has_value()) {
    646        return Result(nullptr, defaultLocale, -1, -1, false);
    647    } else {
    648        return Result(&desiredLocale, supportedLocales[*suppIndex], 0, *suppIndex, false);
    649    }
    650 }
    651 
    652 LocaleMatcher::Result LocaleMatcher::getBestMatchResult(
    653        Locale::Iterator &desiredLocales, UErrorCode &errorCode) const {
    654    if (U_FAILURE(errorCode) || !desiredLocales.hasNext()) {
    655        return Result(nullptr, defaultLocale, -1, -1, false);
    656    }
    657    LocaleLsrIterator lsrIter(likelySubtags, desiredLocales, ULOCMATCH_TEMPORARY_LOCALES);
    658    std::optional<int32_t> suppIndex = getBestSuppIndex(lsrIter.next(errorCode), &lsrIter, errorCode);
    659    if (U_FAILURE(errorCode) || !suppIndex.has_value()) {
    660        return Result(nullptr, defaultLocale, -1, -1, false);
    661    } else {
    662        return Result(lsrIter.orphanRemembered(), supportedLocales[*suppIndex],
    663                      lsrIter.getBestDesiredIndex(), *suppIndex, true);
    664    }
    665 }
    666 
    667 std::optional<int32_t> LocaleMatcher::getBestSuppIndex(LSR desiredLSR,
    668                                                       LocaleLsrIterator *remainingIter,
    669                                                       UErrorCode &errorCode) const {
    670    if (U_FAILURE(errorCode)) { return std::nullopt; }
    671    int32_t desiredIndex = 0;
    672    int32_t bestSupportedLsrIndex = -1;
    673    for (int32_t bestShiftedDistance = LocaleDistance::shiftDistance(thresholdDistance);;) {
    674        // Quick check for exact maximized LSR.
    675        if (supportedLsrToIndex != nullptr) {
    676            desiredLSR.setHashCode();
    677            UBool found = false;
    678            int32_t suppIndex = uhash_getiAndFound(supportedLsrToIndex, &desiredLSR, &found);
    679            if (found) {
    680                if (remainingIter != nullptr) {
    681                    remainingIter->rememberCurrent(desiredIndex, errorCode);
    682                }
    683                return suppIndex;
    684            }
    685        }
    686        int32_t bestIndexAndDistance = localeDistance.getBestIndexAndDistance(
    687                desiredLSR, supportedLSRs, supportedLSRsLength,
    688                bestShiftedDistance, favorSubtag, direction);
    689        if (bestIndexAndDistance >= 0) {
    690            bestShiftedDistance = LocaleDistance::getShiftedDistance(bestIndexAndDistance);
    691            if (remainingIter != nullptr) {
    692                remainingIter->rememberCurrent(desiredIndex, errorCode);
    693                if (U_FAILURE(errorCode)) { return std::nullopt; }
    694            }
    695            bestSupportedLsrIndex = LocaleDistance::getIndex(bestIndexAndDistance);
    696        }
    697        if ((bestShiftedDistance -= LocaleDistance::shiftDistance(demotionPerDesiredLocale)) <= 0) {
    698            break;
    699        }
    700        if (remainingIter == nullptr || !remainingIter->hasNext()) {
    701            break;
    702        }
    703        desiredLSR = remainingIter->next(errorCode);
    704        if (U_FAILURE(errorCode)) { return std::nullopt; }
    705        ++desiredIndex;
    706    }
    707    if (bestSupportedLsrIndex < 0) {
    708        // no good match
    709        return std::nullopt;
    710    }
    711    return supportedIndexes[bestSupportedLsrIndex];
    712 }
    713 
    714 UBool LocaleMatcher::isMatch(const Locale &desired, const Locale &supported,
    715                             UErrorCode &errorCode) const {
    716    if (U_FAILURE(errorCode)) { return false; }
    717    LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);
    718    if (U_FAILURE(errorCode)) { return false; }
    719    const LSR *pSuppLSR = &suppLSR;
    720    int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(
    721            getMaximalLsrOrUnd(likelySubtags, desired, errorCode),
    722            &pSuppLSR, 1,
    723            LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction);
    724    return indexAndDistance >= 0;
    725 }
    726 
    727 double LocaleMatcher::internalMatch(const Locale &desired, const Locale &supported, UErrorCode &errorCode) const {
    728    if (U_FAILURE(errorCode)) { return 0.; }
    729    // Returns the inverse of the distance: That is, 1-distance(desired, supported).
    730    LSR suppLSR = getMaximalLsrOrUnd(likelySubtags, supported, errorCode);
    731    if (U_FAILURE(errorCode)) { return 0.; }
    732    const LSR *pSuppLSR = &suppLSR;
    733    int32_t indexAndDistance = localeDistance.getBestIndexAndDistance(
    734            getMaximalLsrOrUnd(likelySubtags, desired, errorCode),
    735            &pSuppLSR, 1,
    736            LocaleDistance::shiftDistance(thresholdDistance), favorSubtag, direction);
    737    double distance = LocaleDistance::getDistanceDouble(indexAndDistance);
    738    return (100.0 - distance) / 100.0;
    739 }
    740 
    741 U_NAMESPACE_END
    742 
    743 // uloc_acceptLanguage() --------------------------------------------------- ***
    744 
    745 U_NAMESPACE_USE
    746 
    747 namespace {
    748 
    749 class LocaleFromTag {
    750 public:
    751    LocaleFromTag() : locale(Locale::getRoot()) {}
    752    const Locale &operator()(const char *tag) { return locale = Locale(tag); }
    753 
    754 private:
    755    // Store the locale in the converter, rather than return a reference to a temporary,
    756    // or a value which could go out of scope with the caller's reference to it.
    757    Locale locale;
    758 };
    759 
    760 int32_t acceptLanguage(UEnumeration &supportedLocales, Locale::Iterator &desiredLocales,
    761                       char *dest, int32_t capacity, UAcceptResult *acceptResult,
    762                       UErrorCode &errorCode) {
    763    if (U_FAILURE(errorCode)) { return 0; }
    764    LocaleMatcher::Builder builder;
    765    const char *locString;
    766    while ((locString = uenum_next(&supportedLocales, nullptr, &errorCode)) != nullptr) {
    767        Locale loc(locString);
    768        if (loc.isBogus()) {
    769            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    770            return 0;
    771        }
    772        builder.addSupportedLocale(loc);
    773    }
    774    LocaleMatcher matcher = builder.build(errorCode);
    775    LocaleMatcher::Result result = matcher.getBestMatchResult(desiredLocales, errorCode);
    776    if (U_FAILURE(errorCode)) { return 0; }
    777    if (result.getDesiredIndex() >= 0) {
    778        if (acceptResult != nullptr) {
    779            *acceptResult = *result.getDesiredLocale() == *result.getSupportedLocale() ?
    780                ULOC_ACCEPT_VALID : ULOC_ACCEPT_FALLBACK;
    781        }
    782        const char *bestStr = result.getSupportedLocale()->getName();
    783        int32_t bestLength = static_cast<int32_t>(uprv_strlen(bestStr));
    784        if (bestLength <= capacity) {
    785            uprv_memcpy(dest, bestStr, bestLength);
    786        }
    787        return u_terminateChars(dest, capacity, bestLength, &errorCode);
    788    } else {
    789        if (acceptResult != nullptr) {
    790            *acceptResult = ULOC_ACCEPT_FAILED;
    791        }
    792        return u_terminateChars(dest, capacity, 0, &errorCode);
    793    }
    794 }
    795 
    796 }  // namespace
    797 
    798 U_CAPI int32_t U_EXPORT2
    799 uloc_acceptLanguage(char *result, int32_t resultAvailable,
    800                    UAcceptResult *outResult,
    801                    const char **acceptList, int32_t acceptListCount,
    802                    UEnumeration *availableLocales,
    803                    UErrorCode *status) {
    804    if (U_FAILURE(*status)) { return 0; }
    805    if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
    806            (acceptList == nullptr ? acceptListCount != 0 : acceptListCount < 0) ||
    807            availableLocales == nullptr) {
    808        *status = U_ILLEGAL_ARGUMENT_ERROR;
    809        return 0;
    810    }
    811    LocaleFromTag converter;
    812    Locale::ConvertingIterator<const char **, LocaleFromTag> desiredLocales(
    813        acceptList, acceptList + acceptListCount, converter);
    814    return acceptLanguage(*availableLocales, desiredLocales,
    815                          result, resultAvailable, outResult, *status);
    816 }
    817 
    818 U_CAPI int32_t U_EXPORT2
    819 uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable,
    820                            UAcceptResult *outResult,
    821                            const char *httpAcceptLanguage,
    822                            UEnumeration *availableLocales,
    823                            UErrorCode *status) {
    824    if (U_FAILURE(*status)) { return 0; }
    825    if ((result == nullptr ? resultAvailable != 0 : resultAvailable < 0) ||
    826            httpAcceptLanguage == nullptr || availableLocales == nullptr) {
    827        *status = U_ILLEGAL_ARGUMENT_ERROR;
    828        return 0;
    829    }
    830    LocalePriorityList list(httpAcceptLanguage, *status);
    831    LocalePriorityList::Iterator desiredLocales = list.iterator();
    832    return acceptLanguage(*availableLocales, desiredLocales,
    833                          result, resultAvailable, outResult, *status);
    834 }