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 }