Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/icu4c/common/localematcher.cpp
9903 views
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
}
835
836