Coverage Report

Created: 2025-01-09 06:39

/src/icu/icu4c/source/common/locid.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
 **********************************************************************
5
 *   Copyright (C) 1997-2016, International Business Machines
6
 *   Corporation and others.  All Rights Reserved.
7
 **********************************************************************
8
*
9
* File locid.cpp
10
*
11
* Created by: Richard Gillam
12
*
13
* Modification History:
14
*
15
*   Date        Name        Description
16
*   02/11/97    aliu        Changed gLocPath to fgDataDirectory and added
17
*                           methods to get and set it.
18
*   04/02/97    aliu        Made operator!= inline; fixed return value
19
*                           of getName().
20
*   04/15/97    aliu        Cleanup for AIX/Win32.
21
*   04/24/97    aliu        Numerous changes per code review.
22
*   08/18/98    stephen     Changed getDisplayName()
23
*                           Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE
24
*                           Added getISOCountries(), getISOLanguages(),
25
*                           getLanguagesForCountry()
26
*   03/16/99    bertrand    rehaul.
27
*   07/21/99    stephen     Added U_CFUNC setDefault
28
*   11/09/99    weiv        Added const char * getName() const;
29
*   04/12/00    srl         removing unicodestring api's and cached hash code
30
*   08/10/01    grhoten     Change the static Locales to accessor functions
31
******************************************************************************
32
*/
33
34
#include <optional>
35
#include <string_view>
36
#include <utility>
37
38
#include "unicode/bytestream.h"
39
#include "unicode/locid.h"
40
#include "unicode/localebuilder.h"
41
#include "unicode/strenum.h"
42
#include "unicode/stringpiece.h"
43
#include "unicode/uloc.h"
44
#include "unicode/ures.h"
45
46
#include "bytesinkutil.h"
47
#include "charstr.h"
48
#include "charstrmap.h"
49
#include "cmemory.h"
50
#include "cstring.h"
51
#include "mutex.h"
52
#include "putilimp.h"
53
#include "uassert.h"
54
#include "ucln_cmn.h"
55
#include "uhash.h"
56
#include "ulocimp.h"
57
#include "umutex.h"
58
#include "uniquecharstr.h"
59
#include "ustr_imp.h"
60
#include "uvector.h"
61
62
U_NAMESPACE_BEGIN
63
64
static Locale   *gLocaleCache = nullptr;
65
static UInitOnce gLocaleCacheInitOnce {};
66
67
// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale.
68
static UMutex gDefaultLocaleMutex;
69
static UHashtable *gDefaultLocalesHashT = nullptr;
70
static Locale *gDefaultLocale = nullptr;
71
72
/**
73
 * \def ULOC_STRING_LIMIT
74
 * strings beyond this value crash in CharString
75
 */
76
293k
#define ULOC_STRING_LIMIT 357913941
77
78
U_NAMESPACE_END
79
80
typedef enum ELocalePos {
81
    eENGLISH,
82
    eFRENCH,
83
    eGERMAN,
84
    eITALIAN,
85
    eJAPANESE,
86
    eKOREAN,
87
    eCHINESE,
88
89
    eFRANCE,
90
    eGERMANY,
91
    eITALY,
92
    eJAPAN,
93
    eKOREA,
94
    eCHINA,      /* Alias for PRC */
95
    eTAIWAN,
96
    eUK,
97
    eUS,
98
    eCANADA,
99
    eCANADA_FRENCH,
100
    eROOT,
101
102
103
    //eDEFAULT,
104
    eMAX_LOCALES
105
} ELocalePos;
106
107
namespace {
108
109
//
110
// Deleter function for Locales owned by the default Locale hash table/
111
//
112
void U_CALLCONV
113
0
deleteLocale(void *obj) {
114
0
    delete static_cast<icu::Locale*>(obj);
115
0
}
116
117
UBool U_CALLCONV locale_cleanup()
118
0
{
119
0
    U_NAMESPACE_USE
120
121
0
    delete [] gLocaleCache;
122
0
    gLocaleCache = nullptr;
123
0
    gLocaleCacheInitOnce.reset();
124
125
0
    if (gDefaultLocalesHashT) {
126
0
        uhash_close(gDefaultLocalesHashT);   // Automatically deletes all elements, using deleter func.
127
0
        gDefaultLocalesHashT = nullptr;
128
0
    }
129
0
    gDefaultLocale = nullptr;
130
0
    return true;
131
0
}
132
133
4
void U_CALLCONV locale_init(UErrorCode &status) {
134
4
    U_NAMESPACE_USE
135
136
4
    U_ASSERT(gLocaleCache == nullptr);
137
4
    gLocaleCache = new Locale[static_cast<int>(eMAX_LOCALES)];
138
4
    if (gLocaleCache == nullptr) {
139
0
        status = U_MEMORY_ALLOCATION_ERROR;
140
0
        return;
141
0
    }
142
4
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
143
4
    gLocaleCache[eROOT]          = Locale("");
144
4
    gLocaleCache[eENGLISH]       = Locale("en");
145
4
    gLocaleCache[eFRENCH]        = Locale("fr");
146
4
    gLocaleCache[eGERMAN]        = Locale("de");
147
4
    gLocaleCache[eITALIAN]       = Locale("it");
148
4
    gLocaleCache[eJAPANESE]      = Locale("ja");
149
4
    gLocaleCache[eKOREAN]        = Locale("ko");
150
4
    gLocaleCache[eCHINESE]       = Locale("zh");
151
4
    gLocaleCache[eFRANCE]        = Locale("fr", "FR");
152
4
    gLocaleCache[eGERMANY]       = Locale("de", "DE");
153
4
    gLocaleCache[eITALY]         = Locale("it", "IT");
154
4
    gLocaleCache[eJAPAN]         = Locale("ja", "JP");
155
4
    gLocaleCache[eKOREA]         = Locale("ko", "KR");
156
4
    gLocaleCache[eCHINA]         = Locale("zh", "CN");
157
4
    gLocaleCache[eTAIWAN]        = Locale("zh", "TW");
158
4
    gLocaleCache[eUK]            = Locale("en", "GB");
159
4
    gLocaleCache[eUS]            = Locale("en", "US");
160
4
    gLocaleCache[eCANADA]        = Locale("en", "CA");
161
4
    gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA");
162
4
}
163
164
}  // namespace
165
166
U_NAMESPACE_BEGIN
167
168
21
Locale *locale_set_default_internal(const char *id, UErrorCode& status) {
169
    // Synchronize this entire function.
170
21
    Mutex lock(&gDefaultLocaleMutex);
171
172
21
    UBool canonicalize = false;
173
174
    // If given a nullptr string for the locale id, grab the default
175
    //   name from the system.
176
    //   (Different from most other locale APIs, where a null name means use
177
    //    the current ICU default locale.)
178
21
    if (id == nullptr) {
179
21
        id = uprv_getDefaultLocaleID();   // This function not thread safe? TODO: verify.
180
21
        canonicalize = true; // always canonicalize host ID
181
21
    }
182
183
21
    CharString localeNameBuf =
184
21
        canonicalize ? ulocimp_canonicalize(id, status) : ulocimp_getName(id, status);
185
186
21
    if (U_FAILURE(status)) {
187
0
        return gDefaultLocale;
188
0
    }
189
190
21
    if (gDefaultLocalesHashT == nullptr) {
191
21
        gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
192
21
        if (U_FAILURE(status)) {
193
0
            return gDefaultLocale;
194
0
        }
195
21
        uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale);
196
21
        ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
197
21
    }
198
199
21
    Locale* newDefault = static_cast<Locale*>(uhash_get(gDefaultLocalesHashT, localeNameBuf.data()));
200
21
    if (newDefault == nullptr) {
201
21
        newDefault = new Locale(Locale::eBOGUS);
202
21
        if (newDefault == nullptr) {
203
0
            status = U_MEMORY_ALLOCATION_ERROR;
204
0
            return gDefaultLocale;
205
0
        }
206
21
        newDefault->init(localeNameBuf.data(), false);
207
21
        uhash_put(gDefaultLocalesHashT, const_cast<char*>(newDefault->getName()), newDefault, &status);
208
21
        if (U_FAILURE(status)) {
209
0
            return gDefaultLocale;
210
0
        }
211
21
    }
212
21
    gDefaultLocale = newDefault;
213
21
    return gDefaultLocale;
214
21
}
215
216
U_NAMESPACE_END
217
218
/* sfb 07/21/99 */
219
U_CFUNC void
220
locale_set_default(const char *id)
221
0
{
222
0
    U_NAMESPACE_USE
223
0
    UErrorCode status = U_ZERO_ERROR;
224
0
    locale_set_default_internal(id, status);
225
0
}
226
/* end */
227
228
U_CFUNC const char *
229
locale_get_default()
230
4.22M
{
231
4.22M
    U_NAMESPACE_USE
232
4.22M
    return Locale::getDefault().getName();
233
4.22M
}
234
235
236
U_NAMESPACE_BEGIN
237
238
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)
239
240
/*Character separating the posix id fields*/
241
// '_'
242
// In the platform codepage.
243
74.9k
#define SEP_CHAR '_'
244
107k
#define NULL_CHAR '\0'
245
246
Locale::~Locale()
247
5.16M
{
248
5.16M
    if ((baseName != fullName) && (baseName != fullNameBuffer)) {
249
388k
        uprv_free(baseName);
250
388k
    }
251
5.16M
    baseName = nullptr;
252
    /*if fullName is on the heap, we free it*/
253
5.16M
    if (fullName != fullNameBuffer)
254
39.0k
    {
255
39.0k
        uprv_free(fullName);
256
39.0k
        fullName = nullptr;
257
39.0k
    }
258
5.16M
}
259
260
Locale::Locale()
261
2.51M
    : UObject(), fullName(fullNameBuffer), baseName(nullptr)
262
2.51M
{
263
2.51M
    init(nullptr, false);
264
2.51M
}
265
266
/*
267
 * Internal constructor to allow construction of a locale object with
268
 *   NO side effects.   (Default constructor tries to get
269
 *   the default locale.)
270
 */
271
Locale::Locale(Locale::ELocaleType)
272
1.33k
    : UObject(), fullName(fullNameBuffer), baseName(nullptr)
273
1.33k
{
274
1.33k
    setToBogus();
275
1.33k
}
276
277
278
Locale::Locale( const   char * newLanguage,
279
                const   char * newCountry,
280
                const   char * newVariant,
281
                const   char * newKeywords)
282
268k
    : UObject(), fullName(fullNameBuffer), baseName(nullptr)
283
268k
{
284
268k
    if( (newLanguage==nullptr) && (newCountry == nullptr) && (newVariant == nullptr) )
285
0
    {
286
0
        init(nullptr, false); /* shortcut */
287
0
    }
288
268k
    else
289
268k
    {
290
268k
        UErrorCode status = U_ZERO_ERROR;
291
268k
        int32_t lsize = 0;
292
268k
        int32_t csize = 0;
293
268k
        int32_t vsize = 0;
294
268k
        int32_t ksize = 0;
295
296
        // Check the sizes of the input strings.
297
298
        // Language
299
268k
        if ( newLanguage != nullptr )
300
268k
        {
301
268k
            lsize = static_cast<int32_t>(uprv_strlen(newLanguage));
302
268k
            if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap
303
0
                setToBogus();
304
0
                return;
305
0
            }
306
268k
        }
307
308
268k
        CharString togo(newLanguage, lsize, status); // start with newLanguage
309
310
        // _Country
311
268k
        if ( newCountry != nullptr )
312
16.0k
        {
313
16.0k
            csize = static_cast<int32_t>(uprv_strlen(newCountry));
314
16.0k
            if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap
315
0
                setToBogus();
316
0
                return;
317
0
            }
318
16.0k
        }
319
320
        // _Variant
321
268k
        if ( newVariant != nullptr )
322
4.63k
        {
323
            // remove leading _'s
324
5.10k
            while(newVariant[0] == SEP_CHAR)
325
470
            {
326
470
                newVariant++;
327
470
            }
328
329
            // remove trailing _'s
330
4.63k
            vsize = static_cast<int32_t>(uprv_strlen(newVariant));
331
4.63k
            if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap
332
0
                setToBogus();
333
0
                return;
334
0
            }
335
5.31k
            while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) )
336
677
            {
337
677
                vsize--;
338
677
            }
339
4.63k
        }
340
341
268k
        if ( newKeywords != nullptr)
342
4.63k
        {
343
4.63k
            ksize = static_cast<int32_t>(uprv_strlen(newKeywords));
344
4.63k
            if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) {
345
0
              setToBogus();
346
0
              return;
347
0
            }
348
4.63k
        }
349
350
        // We've checked the input sizes, now build up the full locale string..
351
352
        // newLanguage is already copied
353
354
268k
        if ( ( vsize != 0 ) || (csize != 0) )  // at least:  __v
355
9.42k
        {                                      //            ^
356
9.42k
            togo.append(SEP_CHAR, status);
357
9.42k
        }
358
359
268k
        if ( csize != 0 )
360
8.93k
        {
361
8.93k
            togo.append(newCountry, status);
362
8.93k
        }
363
364
268k
        if ( vsize != 0)
365
643
        {
366
643
            togo.append(SEP_CHAR, status)
367
643
                .append(newVariant, vsize, status);
368
643
        }
369
370
268k
        if ( ksize != 0)
371
1.62k
        {
372
1.62k
            if (uprv_strchr(newKeywords, '=')) {
373
1.48k
                togo.append('@', status); /* keyword parsing */
374
1.48k
            }
375
139
            else {
376
139
                togo.append('_', status); /* Variant parsing with a script */
377
139
                if ( vsize == 0) {
378
54
                    togo.append('_', status); /* No country found */
379
54
                }
380
139
            }
381
1.62k
            togo.append(newKeywords, status);
382
1.62k
        }
383
384
268k
        if (U_FAILURE(status)) {
385
            // Something went wrong with appending, etc.
386
0
            setToBogus();
387
0
            return;
388
0
        }
389
        // Parse it, because for example 'language' might really be a complete
390
        // string.
391
268k
        init(togo.data(), false);
392
268k
    }
393
268k
}
394
395
Locale::Locale(const Locale &other)
396
1.99M
    : UObject(other), fullName(fullNameBuffer), baseName(nullptr)
397
1.99M
{
398
1.99M
    *this = other;
399
1.99M
}
400
401
Locale::Locale(Locale&& other) noexcept
402
414k
    : UObject(other), fullName(fullNameBuffer), baseName(fullName) {
403
414k
  *this = std::move(other);
404
414k
}
405
406
5.71M
Locale& Locale::operator=(const Locale& other) {
407
5.71M
    if (this == &other) {
408
0
        return *this;
409
0
    }
410
411
5.71M
    setToBogus();
412
413
5.71M
    if (other.fullName == other.fullNameBuffer) {
414
5.67M
        uprv_strcpy(fullNameBuffer, other.fullNameBuffer);
415
5.67M
    } else if (other.fullName == nullptr) {
416
0
        fullName = nullptr;
417
35.0k
    } else {
418
35.0k
        fullName = uprv_strdup(other.fullName);
419
35.0k
        if (fullName == nullptr) return *this;
420
35.0k
    }
421
422
5.71M
    if (other.baseName == other.fullName) {
423
5.36M
        baseName = fullName;
424
5.36M
    } else if (other.baseName != nullptr) {
425
318k
        baseName = uprv_strdup(other.baseName);
426
318k
        if (baseName == nullptr) return *this;
427
318k
    }
428
429
5.71M
    uprv_strcpy(language, other.language);
430
5.71M
    uprv_strcpy(script, other.script);
431
5.71M
    uprv_strcpy(country, other.country);
432
433
5.71M
    variantBegin = other.variantBegin;
434
5.71M
    fIsBogus = other.fIsBogus;
435
436
5.71M
    return *this;
437
5.71M
}
438
439
1.65M
Locale& Locale::operator=(Locale&& other) noexcept {
440
1.65M
    if ((baseName != fullName) && (baseName != fullNameBuffer)) uprv_free(baseName);
441
1.65M
    if (fullName != fullNameBuffer) uprv_free(fullName);
442
443
1.65M
    if (other.fullName == other.fullNameBuffer || other.baseName == other.fullNameBuffer) {
444
1.64M
        uprv_strcpy(fullNameBuffer, other.fullNameBuffer);
445
1.64M
    }
446
1.65M
    if (other.fullName == other.fullNameBuffer) {
447
1.64M
        fullName = fullNameBuffer;
448
1.64M
    } else {
449
4.08k
        fullName = other.fullName;
450
4.08k
    }
451
452
1.65M
    if (other.baseName == other.fullNameBuffer) {
453
1.59M
        baseName = fullNameBuffer;
454
1.59M
    } else if (other.baseName == other.fullName) {
455
2.00k
        baseName = fullName;
456
56.0k
    } else {
457
56.0k
        baseName = other.baseName;
458
56.0k
    }
459
460
1.65M
    uprv_strcpy(language, other.language);
461
1.65M
    uprv_strcpy(script, other.script);
462
1.65M
    uprv_strcpy(country, other.country);
463
464
1.65M
    variantBegin = other.variantBegin;
465
1.65M
    fIsBogus = other.fIsBogus;
466
467
1.65M
    other.baseName = other.fullName = other.fullNameBuffer;
468
469
1.65M
    return *this;
470
1.65M
}
471
472
Locale *
473
5.92k
Locale::clone() const {
474
5.92k
    return new Locale(*this);
475
5.92k
}
476
477
bool
478
Locale::operator==( const   Locale& other) const
479
512k
{
480
512k
    return (uprv_strcmp(other.fullName, fullName) == 0);
481
512k
}
482
483
namespace {
484
485
UInitOnce gKnownCanonicalizedInitOnce {};
486
UHashtable *gKnownCanonicalized = nullptr;
487
488
constexpr const char* KNOWN_CANONICALIZED[] = {
489
    "c",
490
    // Commonly used locales known are already canonicalized
491
    "af", "af_ZA", "am", "am_ET", "ar", "ar_001", "as", "as_IN", "az", "az_AZ",
492
    "be", "be_BY", "bg", "bg_BG", "bn", "bn_IN", "bs", "bs_BA", "ca", "ca_ES",
493
    "cs", "cs_CZ", "cy", "cy_GB", "da", "da_DK", "de", "de_DE", "el", "el_GR",
494
    "en", "en_GB", "en_US", "es", "es_419", "es_ES", "et", "et_EE", "eu",
495
    "eu_ES", "fa", "fa_IR", "fi", "fi_FI", "fil", "fil_PH", "fr", "fr_FR",
496
    "ga", "ga_IE", "gl", "gl_ES", "gu", "gu_IN", "he", "he_IL", "hi", "hi_IN",
497
    "hr", "hr_HR", "hu", "hu_HU", "hy", "hy_AM", "id", "id_ID", "is", "is_IS",
498
    "it", "it_IT", "ja", "ja_JP", "jv", "jv_ID", "ka", "ka_GE", "kk", "kk_KZ",
499
    "km", "km_KH", "kn", "kn_IN", "ko", "ko_KR", "ky", "ky_KG", "lo", "lo_LA",
500
    "lt", "lt_LT", "lv", "lv_LV", "mk", "mk_MK", "ml", "ml_IN", "mn", "mn_MN",
501
    "mr", "mr_IN", "ms", "ms_MY", "my", "my_MM", "nb", "nb_NO", "ne", "ne_NP",
502
    "nl", "nl_NL", "no", "or", "or_IN", "pa", "pa_IN", "pl", "pl_PL", "ps", "ps_AF",
503
    "pt", "pt_BR", "pt_PT", "ro", "ro_RO", "ru", "ru_RU", "sd", "sd_IN", "si",
504
    "si_LK", "sk", "sk_SK", "sl", "sl_SI", "so", "so_SO", "sq", "sq_AL", "sr",
505
    "sr_Cyrl_RS", "sr_Latn", "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "ta",
506
    "ta_IN", "te", "te_IN", "th", "th_TH", "tk", "tk_TM", "tr", "tr_TR", "uk",
507
    "uk_UA", "ur", "ur_PK", "uz", "uz_UZ", "vi", "vi_VN", "yue", "yue_Hant",
508
    "yue_Hant_HK", "yue_HK", "zh", "zh_CN", "zh_Hans", "zh_Hans_CN", "zh_Hant",
509
    "zh_Hant_TW", "zh_TW", "zu", "zu_ZA"
510
};
511
512
0
UBool U_CALLCONV cleanupKnownCanonicalized() {
513
0
    gKnownCanonicalizedInitOnce.reset();
514
0
    if (gKnownCanonicalized) { uhash_close(gKnownCanonicalized); }
515
0
    return true;
516
0
}
517
518
14
void U_CALLCONV loadKnownCanonicalized(UErrorCode &status) {
519
14
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KNOWN_CANONICALIZED,
520
14
                                cleanupKnownCanonicalized);
521
14
    LocalUHashtablePointer newKnownCanonicalizedMap(
522
14
        uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status));
523
14
    for (int32_t i = 0;
524
2.50k
            U_SUCCESS(status) && i < UPRV_LENGTHOF(KNOWN_CANONICALIZED);
525
2.49k
            i++) {
526
2.49k
        uhash_puti(newKnownCanonicalizedMap.getAlias(),
527
2.49k
                   (void*)KNOWN_CANONICALIZED[i],
528
2.49k
                   1, &status);
529
2.49k
    }
530
14
    if (U_FAILURE(status)) {
531
0
        return;
532
0
    }
533
534
14
    gKnownCanonicalized = newKnownCanonicalizedMap.orphan();
535
14
}
536
537
class AliasData;
538
539
/**
540
 * A Builder class to build the alias data.
541
 */
542
class AliasDataBuilder {
543
public:
544
14
    AliasDataBuilder() {
545
14
    }
546
547
    // Build the AliasData from resource.
548
    AliasData* build(UErrorCode &status);
549
550
private:
551
    void readAlias(UResourceBundle* alias,
552
                   UniqueCharStrings* strings,
553
                   LocalMemory<const char*>& types,
554
                   LocalMemory<int32_t>& replacementIndexes,
555
                   int32_t &length,
556
                   void (*checkType)(const char* type),
557
                   void (*checkReplacement)(const UChar* replacement),
558
                   UErrorCode &status);
559
560
    // Read the languageAlias data from alias to
561
    // strings+types+replacementIndexes
562
    // The number of record will be stored into length.
563
    // Allocate length items for types, to store the type field.
564
    // Allocate length items for replacementIndexes,
565
    // to store the index in the strings for the replacement script.
566
    void readLanguageAlias(UResourceBundle* alias,
567
                           UniqueCharStrings* strings,
568
                           LocalMemory<const char*>& types,
569
                           LocalMemory<int32_t>& replacementIndexes,
570
                           int32_t &length,
571
                           UErrorCode &status);
572
573
    // Read the scriptAlias data from alias to
574
    // strings+types+replacementIndexes
575
    // Allocate length items for types, to store the type field.
576
    // Allocate length items for replacementIndexes,
577
    // to store the index in the strings for the replacement script.
578
    void readScriptAlias(UResourceBundle* alias,
579
                         UniqueCharStrings* strings,
580
                         LocalMemory<const char*>& types,
581
                         LocalMemory<int32_t>& replacementIndexes,
582
                         int32_t &length, UErrorCode &status);
583
584
    // Read the territoryAlias data from alias to
585
    // strings+types+replacementIndexes
586
    // Allocate length items for types, to store the type field.
587
    // Allocate length items for replacementIndexes,
588
    // to store the index in the strings for the replacement script.
589
    void readTerritoryAlias(UResourceBundle* alias,
590
                            UniqueCharStrings* strings,
591
                            LocalMemory<const char*>& types,
592
                            LocalMemory<int32_t>& replacementIndexes,
593
                            int32_t &length, UErrorCode &status);
594
595
    // Read the variantAlias data from alias to
596
    // strings+types+replacementIndexes
597
    // Allocate length items for types, to store the type field.
598
    // Allocate length items for replacementIndexes,
599
    // to store the index in the strings for the replacement variant.
600
    void readVariantAlias(UResourceBundle* alias,
601
                          UniqueCharStrings* strings,
602
                          LocalMemory<const char*>& types,
603
                          LocalMemory<int32_t>& replacementIndexes,
604
                          int32_t &length, UErrorCode &status);
605
606
    // Read the subdivisionAlias data from alias to
607
    // strings+types+replacementIndexes
608
    // Allocate length items for types, to store the type field.
609
    // Allocate length items for replacementIndexes,
610
    // to store the index in the strings for the replacement variant.
611
    void readSubdivisionAlias(UResourceBundle* alias,
612
                          UniqueCharStrings* strings,
613
                          LocalMemory<const char*>& types,
614
                          LocalMemory<int32_t>& replacementIndexes,
615
                          int32_t &length, UErrorCode &status);
616
};
617
618
/**
619
 * A class to hold the Alias Data.
620
 */
621
class AliasData : public UMemory {
622
public:
623
12.8k
    static const AliasData* singleton(UErrorCode& status) {
624
12.8k
        if (U_FAILURE(status)) {
625
            // Do not get into loadData if the status already has error.
626
0
            return nullptr;
627
0
        }
628
12.8k
        umtx_initOnce(AliasData::gInitOnce, &AliasData::loadData, status);
629
12.8k
        return gSingleton;
630
12.8k
    }
631
632
84.7k
    const CharStringMap& languageMap() const { return language; }
633
1.67k
    const CharStringMap& scriptMap() const { return script; }
634
7.58k
    const CharStringMap& territoryMap() const { return territory; }
635
20.0k
    const CharStringMap& variantMap() const { return variant; }
636
97
    const CharStringMap& subdivisionMap() const { return subdivision; }
637
638
    static void U_CALLCONV loadData(UErrorCode &status);
639
    static UBool U_CALLCONV cleanup();
640
641
    static UInitOnce gInitOnce;
642
643
private:
644
    AliasData(CharStringMap languageMap,
645
              CharStringMap scriptMap,
646
              CharStringMap territoryMap,
647
              CharStringMap variantMap,
648
              CharStringMap subdivisionMap,
649
              CharString* strings)
650
14
        : language(std::move(languageMap)),
651
14
          script(std::move(scriptMap)),
652
14
          territory(std::move(territoryMap)),
653
14
          variant(std::move(variantMap)),
654
14
          subdivision(std::move(subdivisionMap)),
655
14
          strings(strings) {
656
14
    }
657
658
0
    ~AliasData() {
659
0
        delete strings;
660
0
    }
661
662
    static const AliasData* gSingleton;
663
664
    CharStringMap language;
665
    CharStringMap script;
666
    CharStringMap territory;
667
    CharStringMap variant;
668
    CharStringMap subdivision;
669
    CharString* strings;
670
671
    friend class AliasDataBuilder;
672
};
673
674
675
const AliasData* AliasData::gSingleton = nullptr;
676
UInitOnce AliasData::gInitOnce {};
677
678
UBool U_CALLCONV
679
AliasData::cleanup()
680
0
{
681
0
    gInitOnce.reset();
682
0
    delete gSingleton;
683
0
    return true;
684
0
}
685
686
void
687
AliasDataBuilder::readAlias(
688
        UResourceBundle* alias,
689
        UniqueCharStrings* strings,
690
        LocalMemory<const char*>& types,
691
        LocalMemory<int32_t>& replacementIndexes,
692
        int32_t &length,
693
        void (*checkType)(const char* type),
694
        void (*checkReplacement)(const UChar* replacement),
695
70
        UErrorCode &status) {
696
70
    if (U_FAILURE(status)) {
697
0
        return;
698
0
    }
699
70
    length = ures_getSize(alias);
700
70
    const char** rawTypes = types.allocateInsteadAndCopy(length);
701
70
    if (rawTypes == nullptr) {
702
0
        status = U_MEMORY_ALLOCATION_ERROR;
703
0
        return;
704
0
    }
705
70
    int32_t* rawIndexes = replacementIndexes.allocateInsteadAndCopy(length);
706
70
    if (rawIndexes == nullptr) {
707
0
        status = U_MEMORY_ALLOCATION_ERROR;
708
0
        return;
709
0
    }
710
18.1k
    for (int i = 0; U_SUCCESS(status) && ures_hasNext(alias); i++) {
711
18.0k
        LocalUResourceBundlePointer res(
712
18.0k
            ures_getNextResource(alias, nullptr, &status));
713
18.0k
        const char* aliasFrom = ures_getKey(res.getAlias());
714
18.0k
        const UChar* aliasTo =
715
18.0k
            ures_getStringByKey(res.getAlias(), "replacement", nullptr, &status);
716
18.0k
        if (U_FAILURE(status)) return;
717
718
18.0k
        checkType(aliasFrom);
719
18.0k
        checkReplacement(aliasTo);
720
721
18.0k
        rawTypes[i] = aliasFrom;
722
18.0k
        rawIndexes[i] = strings->add(aliasTo, status);
723
18.0k
    }
724
70
}
725
726
/**
727
 * Read the languageAlias data from alias to strings+types+replacementIndexes.
728
 * Allocate length items for types, to store the type field. Allocate length
729
 * items for replacementIndexes, to store the index in the strings for the
730
 * replacement language.
731
 */
732
void
733
AliasDataBuilder::readLanguageAlias(
734
        UResourceBundle* alias,
735
        UniqueCharStrings* strings,
736
        LocalMemory<const char*>& types,
737
        LocalMemory<int32_t>& replacementIndexes,
738
        int32_t &length,
739
        UErrorCode &status)
740
14
{
741
14
    return readAlias(
742
14
        alias, strings, types, replacementIndexes, length,
743
#if U_DEBUG
744
        [](const char* type) {
745
            // Assert the aliasFrom only contains the following possibilities
746
            // language_REGION_variant
747
            // language_REGION
748
            // language_variant
749
            // language
750
            // und_variant
751
            Locale test(type);
752
            // Assert no script in aliasFrom
753
            U_ASSERT(test.getScript()[0] == '\0');
754
            // Assert when language is und, no REGION in aliasFrom.
755
            U_ASSERT(test.getLanguage()[0] != '\0' || test.getCountry()[0] == '\0');
756
        },
757
#else
758
6.97k
        [](const char*) {},
759
14
#endif
760
6.97k
        [](const UChar*) {}, status);
761
14
}
762
763
/**
764
 * Read the scriptAlias data from alias to strings+types+replacementIndexes.
765
 * Allocate length items for types, to store the type field. Allocate length
766
 * items for replacementIndexes, to store the index in the strings for the
767
 * replacement script.
768
 */
769
void
770
AliasDataBuilder::readScriptAlias(
771
        UResourceBundle* alias,
772
        UniqueCharStrings* strings,
773
        LocalMemory<const char*>& types,
774
        LocalMemory<int32_t>& replacementIndexes,
775
        int32_t &length,
776
        UErrorCode &status)
777
14
{
778
14
    return readAlias(
779
14
        alias, strings, types, replacementIndexes, length,
780
#if U_DEBUG
781
        [](const char* type) {
782
            U_ASSERT(uprv_strlen(type) == 4);
783
        },
784
        [](const UChar* replacement) {
785
            U_ASSERT(u_strlen(replacement) == 4);
786
        },
787
#else
788
14
        [](const char*) {},
789
14
        [](const UChar*) { },
790
14
#endif
791
14
        status);
792
14
}
793
794
/**
795
 * Read the territoryAlias data from alias to strings+types+replacementIndexes.
796
 * Allocate length items for types, to store the type field. Allocate length
797
 * items for replacementIndexes, to store the index in the strings for the
798
 * replacement regions.
799
 */
800
void
801
AliasDataBuilder::readTerritoryAlias(
802
        UResourceBundle* alias,
803
        UniqueCharStrings* strings,
804
        LocalMemory<const char*>& types,
805
        LocalMemory<int32_t>& replacementIndexes,
806
        int32_t &length,
807
        UErrorCode &status)
808
14
{
809
14
    return readAlias(
810
14
        alias, strings, types, replacementIndexes, length,
811
#if U_DEBUG
812
        [](const char* type) {
813
            U_ASSERT(uprv_strlen(type) == 2 || uprv_strlen(type) == 3);
814
        },
815
#else
816
8.96k
        [](const char*) {},
817
14
#endif
818
8.96k
        [](const UChar*) { },
819
14
        status);
820
14
}
821
822
/**
823
 * Read the variantAlias data from alias to strings+types+replacementIndexes.
824
 * Allocate length items for types, to store the type field. Allocate length
825
 * items for replacementIndexes, to store the index in the strings for the
826
 * replacement variant.
827
 */
828
void
829
AliasDataBuilder::readVariantAlias(
830
        UResourceBundle* alias,
831
        UniqueCharStrings* strings,
832
        LocalMemory<const char*>& types,
833
        LocalMemory<int32_t>& replacementIndexes,
834
        int32_t &length,
835
        UErrorCode &status)
836
14
{
837
14
    return readAlias(
838
14
        alias, strings, types, replacementIndexes, length,
839
#if U_DEBUG
840
        [](const char* type) {
841
            U_ASSERT(uprv_strlen(type) >= 4 && uprv_strlen(type) <= 8);
842
            U_ASSERT(uprv_strlen(type) != 4 ||
843
                     (type[0] >= '0' && type[0] <= '9'));
844
        },
845
        [](const UChar* replacement) {
846
            int32_t len = u_strlen(replacement);
847
            U_ASSERT(len >= 4 && len <= 8);
848
            U_ASSERT(len != 4 ||
849
                     (*replacement >= u'0' &&
850
                      *replacement <= u'9'));
851
        },
852
#else
853
28
        [](const char*) {},
854
28
        [](const UChar*) { },
855
14
#endif
856
14
        status);
857
14
}
858
859
/**
860
 * Read the subdivisionAlias data from alias to strings+types+replacementIndexes.
861
 * Allocate length items for types, to store the type field. Allocate length
862
 * items for replacementIndexes, to store the index in the strings for the
863
 * replacement regions.
864
 */
865
void
866
AliasDataBuilder::readSubdivisionAlias(
867
        UResourceBundle* alias,
868
        UniqueCharStrings* strings,
869
        LocalMemory<const char*>& types,
870
        LocalMemory<int32_t>& replacementIndexes,
871
        int32_t &length,
872
        UErrorCode &status)
873
14
{
874
14
    return readAlias(
875
14
        alias, strings, types, replacementIndexes, length,
876
#if U_DEBUG
877
        [](const char* type) {
878
            U_ASSERT(uprv_strlen(type) >= 3 && uprv_strlen(type) <= 8);
879
        },
880
#else
881
2.05k
        [](const char*) {},
882
14
#endif
883
2.05k
        [](const UChar*) { },
884
14
        status);
885
14
}
886
887
/**
888
 * Initializes the alias data from the ICU resource bundles. The alias data
889
 * contains alias of language, country, script and variants.
890
 *
891
 * If the alias data has already loaded, then this method simply returns without
892
 * doing anything meaningful.
893
 */
894
void U_CALLCONV
895
AliasData::loadData(UErrorCode &status)
896
14
{
897
#ifdef LOCALE_CANONICALIZATION_DEBUG
898
    UDate start = uprv_getRawUTCtime();
899
#endif  // LOCALE_CANONICALIZATION_DEBUG
900
14
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE_ALIAS, cleanup);
901
14
    AliasDataBuilder builder;
902
14
    gSingleton = builder.build(status);
903
#ifdef LOCALE_CANONICALIZATION_DEBUG
904
    UDate end = uprv_getRawUTCtime();
905
    printf("AliasData::loadData took total %f ms\n", end - start);
906
#endif  // LOCALE_CANONICALIZATION_DEBUG
907
14
}
908
909
/**
910
 * Build the alias data from resources.
911
 */
912
AliasData*
913
14
AliasDataBuilder::build(UErrorCode &status) {
914
14
    if (U_FAILURE(status)) { return nullptr; }
915
916
14
    LocalUResourceBundlePointer metadata(
917
14
        ures_openDirect(nullptr, "metadata", &status));
918
14
    LocalUResourceBundlePointer metadataAlias(
919
14
        ures_getByKey(metadata.getAlias(), "alias", nullptr, &status));
920
14
    LocalUResourceBundlePointer languageAlias(
921
14
        ures_getByKey(metadataAlias.getAlias(), "language", nullptr, &status));
922
14
    LocalUResourceBundlePointer scriptAlias(
923
14
        ures_getByKey(metadataAlias.getAlias(), "script", nullptr, &status));
924
14
    LocalUResourceBundlePointer territoryAlias(
925
14
        ures_getByKey(metadataAlias.getAlias(), "territory", nullptr, &status));
926
14
    LocalUResourceBundlePointer variantAlias(
927
14
        ures_getByKey(metadataAlias.getAlias(), "variant", nullptr, &status));
928
14
    LocalUResourceBundlePointer subdivisionAlias(
929
14
        ures_getByKey(metadataAlias.getAlias(), "subdivision", nullptr, &status));
930
931
14
    if (U_FAILURE(status)) {
932
0
        return nullptr;
933
0
    }
934
14
    int32_t languagesLength = 0, scriptLength = 0, territoryLength = 0,
935
14
            variantLength = 0, subdivisionLength = 0;
936
937
    // Read the languageAlias into languageTypes, languageReplacementIndexes
938
    // and strings
939
14
    UniqueCharStrings strings(status);
940
14
    LocalMemory<const char*> languageTypes;
941
14
    LocalMemory<int32_t> languageReplacementIndexes;
942
14
    readLanguageAlias(languageAlias.getAlias(),
943
14
                      &strings,
944
14
                      languageTypes,
945
14
                      languageReplacementIndexes,
946
14
                      languagesLength,
947
14
                      status);
948
949
    // Read the scriptAlias into scriptTypes, scriptReplacementIndexes
950
    // and strings
951
14
    LocalMemory<const char*> scriptTypes;
952
14
    LocalMemory<int32_t> scriptReplacementIndexes;
953
14
    readScriptAlias(scriptAlias.getAlias(),
954
14
                    &strings,
955
14
                    scriptTypes,
956
14
                    scriptReplacementIndexes,
957
14
                    scriptLength,
958
14
                    status);
959
960
    // Read the territoryAlias into territoryTypes, territoryReplacementIndexes
961
    // and strings
962
14
    LocalMemory<const char*> territoryTypes;
963
14
    LocalMemory<int32_t> territoryReplacementIndexes;
964
14
    readTerritoryAlias(territoryAlias.getAlias(),
965
14
                       &strings,
966
14
                       territoryTypes,
967
14
                       territoryReplacementIndexes,
968
14
                       territoryLength, status);
969
970
    // Read the variantAlias into variantTypes, variantReplacementIndexes
971
    // and strings
972
14
    LocalMemory<const char*> variantTypes;
973
14
    LocalMemory<int32_t> variantReplacementIndexes;
974
14
    readVariantAlias(variantAlias.getAlias(),
975
14
                     &strings,
976
14
                     variantTypes,
977
14
                     variantReplacementIndexes,
978
14
                     variantLength, status);
979
980
    // Read the subdivisionAlias into subdivisionTypes, subdivisionReplacementIndexes
981
    // and strings
982
14
    LocalMemory<const char*> subdivisionTypes;
983
14
    LocalMemory<int32_t> subdivisionReplacementIndexes;
984
14
    readSubdivisionAlias(subdivisionAlias.getAlias(),
985
14
                         &strings,
986
14
                         subdivisionTypes,
987
14
                         subdivisionReplacementIndexes,
988
14
                         subdivisionLength, status);
989
990
14
    if (U_FAILURE(status)) {
991
0
        return nullptr;
992
0
    }
993
994
    // We can only use strings after freeze it.
995
14
    strings.freeze();
996
997
    // Build the languageMap from languageTypes & languageReplacementIndexes
998
14
    CharStringMap languageMap(490, status);
999
6.98k
    for (int32_t i = 0; U_SUCCESS(status) && i < languagesLength; i++) {
1000
6.97k
        languageMap.put(languageTypes[i],
1001
6.97k
                        strings.get(languageReplacementIndexes[i]),
1002
6.97k
                        status);
1003
6.97k
    }
1004
1005
    // Build the scriptMap from scriptTypes & scriptReplacementIndexes
1006
14
    CharStringMap scriptMap(1, status);
1007
28
    for (int32_t i = 0; U_SUCCESS(status) && i < scriptLength; i++) {
1008
14
        scriptMap.put(scriptTypes[i],
1009
14
                      strings.get(scriptReplacementIndexes[i]),
1010
14
                      status);
1011
14
    }
1012
1013
    // Build the territoryMap from territoryTypes & territoryReplacementIndexes
1014
14
    CharStringMap territoryMap(650, status);
1015
8.97k
    for (int32_t i = 0; U_SUCCESS(status) && i < territoryLength; i++) {
1016
8.96k
        territoryMap.put(territoryTypes[i],
1017
8.96k
                         strings.get(territoryReplacementIndexes[i]),
1018
8.96k
                         status);
1019
8.96k
    }
1020
1021
    // Build the variantMap from variantTypes & variantReplacementIndexes.
1022
14
    CharStringMap variantMap(2, status);
1023
42
    for (int32_t i = 0; U_SUCCESS(status) && i < variantLength; i++) {
1024
28
        variantMap.put(variantTypes[i],
1025
28
                       strings.get(variantReplacementIndexes[i]),
1026
28
                       status);
1027
28
    }
1028
1029
    // Build the subdivisionMap from subdivisionTypes & subdivisionReplacementIndexes.
1030
14
    CharStringMap subdivisionMap(2, status);
1031
2.07k
    for (int32_t i = 0; U_SUCCESS(status) && i < subdivisionLength; i++) {
1032
2.05k
        subdivisionMap.put(subdivisionTypes[i],
1033
2.05k
                       strings.get(subdivisionReplacementIndexes[i]),
1034
2.05k
                       status);
1035
2.05k
    }
1036
1037
14
    if (U_FAILURE(status)) {
1038
0
        return nullptr;
1039
0
    }
1040
1041
    // copy hashtables
1042
14
    auto *data = new AliasData(
1043
14
        std::move(languageMap),
1044
14
        std::move(scriptMap),
1045
14
        std::move(territoryMap),
1046
14
        std::move(variantMap),
1047
14
        std::move(subdivisionMap),
1048
14
        strings.orphanCharStrings());
1049
1050
14
    if (data == nullptr) {
1051
0
        status = U_MEMORY_ALLOCATION_ERROR;
1052
0
    }
1053
14
    return data;
1054
14
}
1055
1056
/**
1057
 * A class that find the replacement values of locale fields by using AliasData.
1058
 */
1059
class AliasReplacer {
1060
public:
1061
    AliasReplacer(UErrorCode& status) :
1062
12.8k
            language(nullptr), script(nullptr), region(nullptr),
1063
12.8k
            extensions(nullptr),
1064
            // store value in variants only once
1065
12.8k
            variants(nullptr,
1066
3.00M
                     ([](UElement e1, UElement e2) -> UBool {
1067
3.00M
                       return 0==uprv_strcmp((const char*)e1.pointer,
1068
3.00M
                                             (const char*)e2.pointer);}),
1069
12.8k
                     status),
1070
12.8k
            data(nullptr) {
1071
12.8k
    }
1072
12.8k
    ~AliasReplacer() {
1073
12.8k
    }
1074
1075
    // Check the fields inside locale, if need to replace fields,
1076
    // place the the replaced locale ID in out and return true.
1077
    // Otherwise return false for no replacement or error.
1078
    bool replace(
1079
        const Locale& locale, CharString& out, UErrorCode& status);
1080
1081
private:
1082
    const char* language;
1083
    const char* script;
1084
    const char* region;
1085
    const char* extensions;
1086
    UVector variants;
1087
1088
    const AliasData* data;
1089
1090
222k
    inline bool notEmpty(const char* str) {
1091
222k
        return str && str[0] != NULL_CHAR;
1092
222k
    }
1093
1094
    /**
1095
     * If replacement is neither null nor empty and input is either null or empty,
1096
     * return replacement.
1097
     * If replacement is neither null nor empty but input is not empty, return input.
1098
     * If replacement is either null or empty and type is either null or empty,
1099
     * return input.
1100
     * Otherwise return null.
1101
     *   replacement     input      type        return
1102
     *    AAA             nullptr    *           AAA
1103
     *    AAA             BBB        *           BBB
1104
     *    nullptr || ""   CCC        nullptr     CCC
1105
     *    nullptr || ""   *          DDD         nullptr
1106
     */
1107
    inline const char* deleteOrReplace(
1108
2.25k
            const char* input, const char* type, const char* replacement) {
1109
2.25k
        return notEmpty(replacement) ?
1110
254
            ((input == nullptr) ?  replacement : input) :
1111
2.25k
            ((type == nullptr) ? input  : nullptr);
1112
2.25k
    }
1113
1114
1.57k
    inline bool same(const char* a, const char* b) {
1115
1.57k
        if (a == nullptr && b == nullptr) {
1116
481
            return true;
1117
481
        }
1118
1.09k
        if ((a == nullptr && b != nullptr) ||
1119
1.09k
            (a != nullptr && b == nullptr)) {
1120
278
          return false;
1121
278
        }
1122
814
        return uprv_strcmp(a, b) == 0;
1123
1.09k
    }
1124
1125
    // Gather fields and generate locale ID into out.
1126
    CharString& outputToString(CharString& out, UErrorCode& status);
1127
1128
    // Generate the lookup key.
1129
    CharString& generateKey(const char* language, const char* region,
1130
                            const char* variant, CharString& out,
1131
                            UErrorCode& status);
1132
1133
    void parseLanguageReplacement(const char* replacement,
1134
                                  const char*& replaceLanguage,
1135
                                  const char*& replaceScript,
1136
                                  const char*& replaceRegion,
1137
                                  const char*& replaceVariant,
1138
                                  const char*& replaceExtensions,
1139
                                  UVector& toBeFreed,
1140
                                  UErrorCode& status);
1141
1142
    // Replace by using languageAlias.
1143
    bool replaceLanguage(bool checkLanguage, bool checkRegion,
1144
                         bool checkVariants, UVector& toBeFreed,
1145
                         UErrorCode& status);
1146
1147
    // Replace by using territoryAlias.
1148
    bool replaceTerritory(UVector& toBeFreed, UErrorCode& status);
1149
1150
    // Replace by using scriptAlias.
1151
    bool replaceScript(UErrorCode& status);
1152
1153
    // Replace by using variantAlias.
1154
    bool replaceVariant(UErrorCode& status);
1155
1156
    // Replace by using subdivisionAlias.
1157
    bool replaceSubdivision(StringPiece subdivision,
1158
                            CharString& output, UErrorCode& status);
1159
1160
    // Replace transformed extensions.
1161
    bool replaceTransformedExtensions(
1162
        CharString& transformedExtensions, CharString& output, UErrorCode& status);
1163
};
1164
1165
CharString&
1166
AliasReplacer::generateKey(
1167
        const char* language, const char* region, const char* variant,
1168
        CharString& out, UErrorCode& status)
1169
84.7k
{
1170
84.7k
    if (U_FAILURE(status)) { return out; }
1171
84.7k
    out.append(language, status);
1172
84.7k
    if (notEmpty(region)) {
1173
10.4k
        out.append(SEP_CHAR, status)
1174
10.4k
            .append(region, status);
1175
10.4k
    }
1176
84.7k
    if (notEmpty(variant)) {
1177
26.5k
       out.append(SEP_CHAR, status)
1178
26.5k
           .append(variant, status);
1179
26.5k
    }
1180
84.7k
    return out;
1181
84.7k
}
1182
1183
void
1184
AliasReplacer::parseLanguageReplacement(
1185
    const char* replacement,
1186
    const char*& replacedLanguage,
1187
    const char*& replacedScript,
1188
    const char*& replacedRegion,
1189
    const char*& replacedVariant,
1190
    const char*& replacedExtensions,
1191
    UVector& toBeFreed,
1192
    UErrorCode& status)
1193
752
{
1194
752
    if (U_FAILURE(status)) {
1195
0
        return;
1196
0
    }
1197
752
    replacedScript = replacedRegion = replacedVariant
1198
752
        = replacedExtensions = nullptr;
1199
752
    if (uprv_strchr(replacement, '_') == nullptr) {
1200
497
        replacedLanguage = replacement;
1201
        // reach the end, just return it.
1202
497
        return;
1203
497
    }
1204
    // We have multiple field so we have to allocate and parse
1205
255
    CharString* str =
1206
255
        new CharString(replacement, static_cast<int32_t>(uprv_strlen(replacement)), status);
1207
255
    LocalPointer<CharString> lpStr(str, status);
1208
255
    toBeFreed.adoptElement(lpStr.orphan(), status);
1209
255
    if (U_FAILURE(status)) {
1210
0
        return;
1211
0
    }
1212
255
    char* data = str->data();
1213
255
    replacedLanguage = (const char*) data;
1214
255
    char* endOfField = uprv_strchr(data, '_');
1215
255
    *endOfField = '\0'; // null terminiate it.
1216
255
    endOfField++;
1217
255
    const char* start = endOfField;
1218
255
    endOfField = const_cast<char*>(uprv_strchr(start, '_'));
1219
255
    size_t len = 0;
1220
255
    if (endOfField == nullptr) {
1221
254
        len = uprv_strlen(start);
1222
254
    } else {
1223
1
        len = endOfField - start;
1224
1
        *endOfField = '\0'; // null terminiate it.
1225
1
    }
1226
255
    if (len == 4 && uprv_isASCIILetter(*start)) {
1227
        // Got a script
1228
193
        replacedScript = start;
1229
193
        if (endOfField == nullptr) {
1230
193
            return;
1231
193
        }
1232
0
        start = endOfField++;
1233
0
        endOfField = const_cast<char*>(uprv_strchr(start, '_'));
1234
0
        if (endOfField == nullptr) {
1235
0
            len = uprv_strlen(start);
1236
0
        } else {
1237
0
            len = endOfField - start;
1238
0
            *endOfField = '\0'; // null terminiate it.
1239
0
        }
1240
0
    }
1241
62
    if (len >= 2 && len <= 3) {
1242
        // Got a region
1243
61
        replacedRegion = start;
1244
61
        if (endOfField == nullptr) {
1245
61
            return;
1246
61
        }
1247
0
        start = endOfField++;
1248
0
        endOfField = const_cast<char*>(uprv_strchr(start, '_'));
1249
0
        if (endOfField == nullptr) {
1250
0
            len = uprv_strlen(start);
1251
0
        } else {
1252
0
            len = endOfField - start;
1253
0
            *endOfField = '\0'; // null terminiate it.
1254
0
        }
1255
0
    }
1256
1
    if (len >= 4) {
1257
        // Got a variant
1258
0
        replacedVariant = start;
1259
0
        if (endOfField == nullptr) {
1260
0
            return;
1261
0
        }
1262
0
        start = endOfField++;
1263
0
    }
1264
1
    replacedExtensions = start;
1265
1
}
1266
1267
bool
1268
AliasReplacer::replaceLanguage(
1269
        bool checkLanguage, bool checkRegion,
1270
        bool checkVariants, UVector& toBeFreed, UErrorCode& status)
1271
69.3k
{
1272
69.3k
    if (U_FAILURE(status)) {
1273
0
        return false;
1274
0
    }
1275
69.3k
    if (    (checkRegion && region == nullptr) ||
1276
69.3k
            (checkVariants && variants.size() == 0)) {
1277
        // Nothing to search.
1278
43.9k
        return false;
1279
43.9k
    }
1280
25.3k
    int32_t variant_size = checkVariants ? variants.size() : 1;
1281
    // Since we may have more than one variant, we need to loop through them.
1282
25.3k
    const char* searchLanguage = checkLanguage ? language : "und";
1283
25.3k
    const char* searchRegion = checkRegion ? region : nullptr;
1284
25.3k
    const char* searchVariant = nullptr;
1285
25.3k
    for (int32_t variant_index = 0;
1286
109k
            variant_index < variant_size;
1287
84.7k
            variant_index++) {
1288
84.7k
        if (checkVariants) {
1289
63.0k
            U_ASSERT(variant_index < variant_size);
1290
63.0k
            searchVariant = static_cast<const char*>(variants.elementAt(variant_index));
1291
63.0k
        }
1292
1293
84.7k
        if (searchVariant != nullptr && uprv_strlen(searchVariant) < 4) {
1294
            // Do not consider  ill-formed variant subtag.
1295
36.4k
            searchVariant = nullptr;
1296
36.4k
        }
1297
84.7k
        CharString typeKey;
1298
84.7k
        generateKey(searchLanguage, searchRegion, searchVariant, typeKey,
1299
84.7k
                    status);
1300
84.7k
        if (U_FAILURE(status)) {
1301
0
            return false;
1302
0
        }
1303
84.7k
        const char *replacement = data->languageMap().get(typeKey.data());
1304
84.7k
        if (replacement == nullptr) {
1305
            // Found no replacement data.
1306
84.0k
            continue;
1307
84.0k
        }
1308
1309
752
        const char* replacedLanguage = nullptr;
1310
752
        const char* replacedScript = nullptr;
1311
752
        const char* replacedRegion = nullptr;
1312
752
        const char* replacedVariant = nullptr;
1313
752
        const char* replacedExtensions = nullptr;
1314
752
        parseLanguageReplacement(replacement,
1315
752
                                 replacedLanguage,
1316
752
                                 replacedScript,
1317
752
                                 replacedRegion,
1318
752
                                 replacedVariant,
1319
752
                                 replacedExtensions,
1320
752
                                 toBeFreed,
1321
752
                                 status);
1322
752
        replacedLanguage =
1323
752
            (replacedLanguage != nullptr && uprv_strcmp(replacedLanguage, "und") == 0) ?
1324
475
            language : replacedLanguage;
1325
752
        replacedScript = deleteOrReplace(script, nullptr, replacedScript);
1326
752
        replacedRegion = deleteOrReplace(region, searchRegion, replacedRegion);
1327
752
        replacedVariant = deleteOrReplace(
1328
752
            searchVariant, searchVariant, replacedVariant);
1329
1330
752
        if (    same(language, replacedLanguage) &&
1331
752
                same(script, replacedScript) &&
1332
752
                same(region, replacedRegion) &&
1333
752
                same(searchVariant, replacedVariant) &&
1334
752
                replacedExtensions == nullptr) {
1335
            // Replacement produce no changes.
1336
0
            continue;
1337
0
        }
1338
1339
752
        language = replacedLanguage;
1340
752
        region = replacedRegion;
1341
752
        script = replacedScript;
1342
752
        if (searchVariant != nullptr) {
1343
286
            if (notEmpty(replacedVariant)) {
1344
0
                variants.setElementAt((void*)replacedVariant, variant_index);
1345
286
            } else {
1346
286
                variants.removeElementAt(variant_index);
1347
286
            }
1348
286
        }
1349
752
        if (replacedExtensions != nullptr) {
1350
            // DO NOTHING
1351
            // UTS35 does not specify what should we do if we have extensions in the
1352
            // replacement. Currently we know only the following 4 "BCP47 LegacyRules" have
1353
            // extensions in them languageAlias:
1354
            //  i_default => en_x_i_default
1355
            //  i_enochian => und_x_i_enochian
1356
            //  i_mingo => see_x_i_mingo
1357
            //  zh_min => nan_x_zh_min
1358
            // But all of them are already changed by code inside ultag_parse() before
1359
            // hitting this code.
1360
1
        }
1361
1362
        // Something changed by language alias data.
1363
752
        return true;
1364
752
    }
1365
    // Nothing changed by language alias data.
1366
24.6k
    return false;
1367
25.3k
}
1368
1369
bool
1370
AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status)
1371
13.2k
{
1372
13.2k
    if (U_FAILURE(status)) {
1373
0
        return false;
1374
0
    }
1375
13.2k
    if (region == nullptr) {
1376
        // No region to search.
1377
5.63k
        return false;
1378
5.63k
    }
1379
7.58k
    const char *replacement = data->territoryMap().get(region);
1380
7.58k
    if (replacement == nullptr) {
1381
        // Found no replacement data for this region.
1382
7.21k
        return false;
1383
7.21k
    }
1384
372
    const char* replacedRegion = replacement;
1385
372
    const char* firstSpace = uprv_strchr(replacement, ' ');
1386
372
    if (firstSpace != nullptr) {
1387
        // If there are are more than one region in the replacement.
1388
        // We need to check which one match based on the language.
1389
        // Cannot use nullptr for language because that will construct
1390
        // the default locale, in that case, use "und" to get the correct
1391
        // locale.
1392
321
        Locale l = LocaleBuilder()
1393
321
            .setLanguage(language == nullptr ? "und" : language)
1394
321
            .setScript(script)
1395
321
            .build(status);
1396
321
        l.addLikelySubtags(status);
1397
321
        const char* likelyRegion = l.getCountry();
1398
321
        LocalPointer<CharString> item;
1399
321
        if (likelyRegion != nullptr && uprv_strlen(likelyRegion) > 0) {
1400
222
            size_t len = uprv_strlen(likelyRegion);
1401
222
            const char* foundInReplacement = uprv_strstr(replacement,
1402
222
                                                         likelyRegion);
1403
222
            if (foundInReplacement != nullptr) {
1404
                // Assuming the case there are no three letter region code in
1405
                // the replacement of territoryAlias
1406
27
                U_ASSERT(foundInReplacement == replacement ||
1407
27
                         *(foundInReplacement-1) == ' ');
1408
27
                U_ASSERT(foundInReplacement[len] == ' ' ||
1409
27
                         foundInReplacement[len] == '\0');
1410
27
                item.adoptInsteadAndCheckErrorCode(
1411
27
                    new CharString(foundInReplacement, static_cast<int32_t>(len), status), status);
1412
27
            }
1413
222
        }
1414
321
        if (item.isNull() && U_SUCCESS(status)) {
1415
284
            item.adoptInsteadAndCheckErrorCode(
1416
284
                new CharString(replacement,
1417
284
                               static_cast<int32_t>(firstSpace - replacement), status), status);
1418
284
        }
1419
321
        if (U_FAILURE(status)) { return false; }
1420
311
        replacedRegion = item->data();
1421
311
        toBeFreed.adoptElement(item.orphan(), status);
1422
311
        if (U_FAILURE(status)) { return false; }
1423
311
    }
1424
362
    U_ASSERT(!same(region, replacedRegion));
1425
362
    region = replacedRegion;
1426
    // The region is changed by data in territory alias.
1427
362
    return true;
1428
372
}
1429
1430
bool
1431
AliasReplacer::replaceScript(UErrorCode& status)
1432
12.8k
{
1433
12.8k
    if (U_FAILURE(status)) {
1434
10
        return false;
1435
10
    }
1436
12.8k
    if (script == nullptr) {
1437
        // No script to search.
1438
11.1k
        return false;
1439
11.1k
    }
1440
1.67k
    const char *replacement = data->scriptMap().get(script);
1441
1.67k
    if (replacement == nullptr) {
1442
        // Found no replacement data for this script.
1443
1.62k
        return false;
1444
1.62k
    }
1445
52
    U_ASSERT(!same(script, replacement));
1446
52
    script = replacement;
1447
    // The script is changed by data in script alias.
1448
52
    return true;
1449
1.67k
}
1450
1451
bool
1452
AliasReplacer::replaceVariant(UErrorCode& status)
1453
12.8k
{
1454
12.8k
    if (U_FAILURE(status)) {
1455
10
        return false;
1456
10
    }
1457
    // Since we may have more than one variant, we need to loop through them.
1458
32.8k
    for (int32_t i = 0; i < variants.size(); i++) {
1459
20.0k
        const char* variant = static_cast<const char*>(variants.elementAt(i));
1460
20.0k
        const char *replacement = data->variantMap().get(variant);
1461
20.0k
        if (replacement == nullptr) {
1462
            // Found no replacement data for this variant.
1463
20.0k
            continue;
1464
20.0k
        }
1465
0
        U_ASSERT((uprv_strlen(replacement) >= 5  &&
1466
0
                  uprv_strlen(replacement) <= 8) ||
1467
0
                 (uprv_strlen(replacement) == 4 &&
1468
0
                  replacement[0] >= '0' &&
1469
0
                  replacement[0] <= '9'));
1470
0
        if (!same(variant, replacement)) {
1471
0
            variants.setElementAt((void*)replacement, i);
1472
            // Special hack to handle hepburn-heploc => alalc97
1473
0
            if (uprv_strcmp(variant, "heploc") == 0) {
1474
0
                for (int32_t j = 0; j < variants.size(); j++) {
1475
0
                     if (uprv_strcmp((const char*)(variants.elementAt(j)),
1476
0
                                     "hepburn") == 0) {
1477
0
                         variants.removeElementAt(j);
1478
0
                     }
1479
0
                }
1480
0
            }
1481
0
            return true;
1482
0
        }
1483
0
    }
1484
12.7k
    return false;
1485
12.7k
}
1486
1487
bool
1488
AliasReplacer::replaceSubdivision(
1489
    StringPiece subdivision, CharString& output, UErrorCode& status)
1490
97
{
1491
97
    if (U_FAILURE(status)) {
1492
0
        return false;
1493
0
    }
1494
97
    const char *replacement = data->subdivisionMap().get(subdivision.data());
1495
97
    if (replacement != nullptr) {
1496
34
        const char* firstSpace = uprv_strchr(replacement, ' ');
1497
        // Found replacement data for this subdivision.
1498
34
        size_t len = (firstSpace != nullptr) ?
1499
34
            (firstSpace - replacement) : uprv_strlen(replacement);
1500
34
        if (2 <= len && len <= 8) {
1501
34
            output.append(replacement, static_cast<int32_t>(len), status);
1502
34
            if (2 == len) {
1503
                // Add 'zzzz' based on changes to UTS #35 for CLDR-14312.
1504
2
                output.append("zzzz", 4, status);
1505
2
            }
1506
34
        }
1507
34
        return true;
1508
34
    }
1509
63
    return false;
1510
97
}
1511
1512
bool
1513
AliasReplacer::replaceTransformedExtensions(
1514
    CharString& transformedExtensions, CharString& output, UErrorCode& status)
1515
1.66k
{
1516
    // The content of the transformedExtensions will be modified in this
1517
    // function to NUL-terminating (tkey-tvalue) pairs.
1518
1.66k
    if (U_FAILURE(status)) {
1519
0
        return false;
1520
0
    }
1521
1.66k
    int32_t len = transformedExtensions.length();
1522
1.66k
    const char* str = transformedExtensions.data();
1523
1.66k
    const char* tkey = ultag_getTKeyStart(str);
1524
1.66k
    int32_t tlangLen = (tkey == str) ? 0 :
1525
1.66k
        ((tkey == nullptr) ? len : static_cast<int32_t>((tkey - str - 1)));
1526
1.66k
    if (tlangLen > 0) {
1527
1.31k
        Locale tlang = LocaleBuilder()
1528
1.31k
            .setLanguageTag(StringPiece(str, tlangLen))
1529
1.31k
            .build(status);
1530
1.31k
        tlang.canonicalize(status);
1531
1.31k
        output = tlang.toLanguageTag<CharString>(status);
1532
1.31k
        if (U_FAILURE(status)) {
1533
227
            return false;
1534
227
        }
1535
1.08k
        T_CString_toLowerCase(output.data());
1536
1.08k
    }
1537
1.43k
    if (tkey != nullptr) {
1538
        // We need to sort the tfields by tkey
1539
391
        UVector tfields(status);
1540
391
        if (U_FAILURE(status)) {
1541
0
            return false;
1542
0
        }
1543
7.98M
        do {
1544
7.98M
            const char* tvalue = uprv_strchr(tkey, '-');
1545
7.98M
            if (tvalue == nullptr) {
1546
17
                status = U_ILLEGAL_ARGUMENT_ERROR;
1547
17
                return false;
1548
17
            }
1549
7.98M
            const char* nextTKey = ultag_getTKeyStart(tvalue);
1550
7.98M
            if (nextTKey != nullptr) {
1551
7.98M
                *const_cast<char*>(nextTKey - 1) = '\0'; // NUL terminate tvalue
1552
7.98M
            }
1553
7.98M
            tfields.insertElementAt((void*)tkey, tfields.size(), status);
1554
7.98M
            if (U_FAILURE(status)) {
1555
0
                return false;
1556
0
            }
1557
7.98M
            tkey = nextTKey;
1558
7.98M
        } while (tkey != nullptr);
1559
129M
        tfields.sort([](UElement e1, UElement e2) -> int32_t {
1560
129M
            return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1561
129M
        }, status);
1562
3.98M
        for (int32_t i = 0; i < tfields.size(); i++) {
1563
3.98M
             if (output.length() > 0) {
1564
3.98M
                 output.append('-', status);
1565
3.98M
             }
1566
3.98M
             const char* tfield = static_cast<const char*>(tfields.elementAt(i));
1567
3.98M
             const char* tvalue = uprv_strchr(tfield, '-');
1568
3.98M
             if (tvalue == nullptr) {
1569
128
                 status = U_ILLEGAL_ARGUMENT_ERROR;
1570
128
                 return false;
1571
128
             }
1572
             // Split the "tkey-tvalue" pair string so that we can canonicalize the tvalue.
1573
3.98M
             *const_cast<char*>(tvalue++) = '\0'; // NUL terminate tkey
1574
3.98M
             output.append(tfield, status).append('-', status);
1575
3.98M
             std::optional<std::string_view> bcpTValue = ulocimp_toBcpType(tfield, tvalue);
1576
3.98M
             output.append(bcpTValue.has_value() ? *bcpTValue : tvalue, status);
1577
3.98M
        }
1578
374
    }
1579
1.28k
    if (U_FAILURE(status)) {
1580
0
        return false;
1581
0
    }
1582
1.28k
    return true;
1583
1.28k
}
1584
1585
CharString&
1586
AliasReplacer::outputToString(
1587
    CharString& out, UErrorCode& status)
1588
3.27k
{
1589
3.27k
    if (U_FAILURE(status)) { return out; }
1590
3.27k
    out.append(language, status);
1591
3.27k
    if (notEmpty(script)) {
1592
307
        out.append(SEP_CHAR, status)
1593
307
            .append(script, status);
1594
307
    }
1595
3.27k
    if (notEmpty(region)) {
1596
469
        out.append(SEP_CHAR, status)
1597
469
            .append(region, status);
1598
469
    }
1599
3.27k
    if (variants.size() > 0) {
1600
1.00k
        if (!notEmpty(script) && !notEmpty(region)) {
1601
805
          out.append(SEP_CHAR, status);
1602
805
        }
1603
131k
        variants.sort([](UElement e1, UElement e2) -> int32_t {
1604
131k
            return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1605
131k
        }, status);
1606
1.00k
        int32_t variantsStart = out.length();
1607
20.9k
        for (int32_t i = 0; i < variants.size(); i++) {
1608
19.9k
             out.append(SEP_CHAR, status)
1609
19.9k
                 .append(static_cast<const char*>(variants.elementAt(i)),
1610
19.9k
                         status);
1611
19.9k
        }
1612
1.00k
        T_CString_toUpperCase(out.data() + variantsStart);
1613
1.00k
    }
1614
3.27k
    if (notEmpty(extensions)) {
1615
0
        CharString tmp("und_", status);
1616
0
        tmp.append(extensions, status);
1617
0
        Locale tmpLocale(tmp.data());
1618
        // only support x extension inside CLDR for now.
1619
0
        U_ASSERT(extensions[0] == 'x');
1620
0
        out.append(tmpLocale.getName() + 1, status);
1621
0
    }
1622
3.27k
    return out;
1623
3.27k
}
1624
1625
bool
1626
AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status)
1627
12.8k
{
1628
12.8k
    data = AliasData::singleton(status);
1629
12.8k
    if (U_FAILURE(status)) {
1630
0
        return false;
1631
0
    }
1632
12.8k
    U_ASSERT(data != nullptr);
1633
12.8k
    out.clear();
1634
12.8k
    language = locale.getLanguage();
1635
12.8k
    if (!notEmpty(language)) {
1636
2.00k
        language = nullptr;
1637
2.00k
    }
1638
12.8k
    script = locale.getScript();
1639
12.8k
    if (!notEmpty(script)) {
1640
11.3k
        script = nullptr;
1641
11.3k
    }
1642
12.8k
    region = locale.getCountry();
1643
12.8k
    if (!notEmpty(region)) {
1644
5.65k
        region = nullptr;
1645
5.65k
    }
1646
12.8k
    const char* variantsStr = locale.getVariant();
1647
12.8k
    CharString variantsBuff(variantsStr, -1, status);
1648
12.8k
    if (!variantsBuff.isEmpty()) {
1649
1.20k
        if (U_FAILURE(status)) { return false; }
1650
1.20k
        char* start = variantsBuff.data();
1651
1.20k
        T_CString_toLowerCase(start);
1652
1.20k
        char* end;
1653
32.2k
        while ((end = uprv_strchr(start, SEP_CHAR)) != nullptr &&
1654
32.2k
               U_SUCCESS(status)) {
1655
31.0k
            *end = NULL_CHAR;  // null terminate inside variantsBuff
1656
            // do not add "" or duplicate data to variants
1657
31.0k
            if (*start && !variants.contains(start)) {
1658
19.2k
                variants.addElement(start, status);
1659
19.2k
            }
1660
31.0k
            start = end + 1;
1661
31.0k
        }
1662
        // do not add "" or duplicate data to variants
1663
1.20k
        if (*start && !variants.contains(start)) {
1664
1.10k
            variants.addElement(start, status);
1665
1.10k
        }
1666
1.20k
    }
1667
12.8k
    if (U_FAILURE(status)) { return false; }
1668
1669
    // Sort the variants
1670
157k
    variants.sort([](UElement e1, UElement e2) -> int32_t {
1671
157k
        return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1672
157k
    }, status);
1673
1674
    // A changed count to assert when loop too many times.
1675
12.8k
    int changed = 0;
1676
    // A UVector to to hold CharString allocated by the replace* method
1677
    // and freed when out of scope from his function.
1678
12.8k
    UVector stringsToBeFreed([](void *obj) { delete static_cast<CharString*>(obj); },
1679
12.8k
                             nullptr, 10, status);
1680
13.9k
    while (U_SUCCESS(status)) {
1681
        // Something wrong with the data cause looping here more than 10 times
1682
        // already.
1683
13.9k
        U_ASSERT(changed < 5);
1684
        // From observation of key in data/misc/metadata.txt
1685
        // we know currently we only need to search in the following combination
1686
        // of fields for type in languageAlias:
1687
        // * lang_region_variant
1688
        // * lang_region
1689
        // * lang_variant
1690
        // * lang
1691
        // * und_variant
1692
        // This assumption is ensured by the U_ASSERT in readLanguageAlias
1693
        //
1694
        //                      lang  REGION variant
1695
13.9k
        if (    replaceLanguage(true, true,  true,  stringsToBeFreed, status) ||
1696
13.9k
                replaceLanguage(true, true,  false, stringsToBeFreed, status) ||
1697
13.9k
                replaceLanguage(true, false, true,  stringsToBeFreed, status) ||
1698
13.9k
                replaceLanguage(true, false, false, stringsToBeFreed, status) ||
1699
13.9k
                replaceLanguage(false,false, true,  stringsToBeFreed, status) ||
1700
13.9k
                replaceTerritory(stringsToBeFreed, status) ||
1701
13.9k
                replaceScript(status) ||
1702
13.9k
                replaceVariant(status)) {
1703
            // Some values in data is changed, try to match from the beginning
1704
            // again.
1705
1.16k
            changed++;
1706
1.16k
            continue;
1707
1.16k
        }
1708
        // Nothing changed. Break out.
1709
12.8k
        break;
1710
13.9k
    }  // while(1)
1711
1712
12.8k
    if (U_FAILURE(status)) { return false; }
1713
    // Nothing changed and we know the order of the variants are not change
1714
    // because we have no variant or only one.
1715
12.7k
    const char* extensionsStr = locale_getKeywordsStart(locale.getName());
1716
12.7k
    if (changed == 0 && variants.size() <= 1 && extensionsStr == nullptr) {
1717
9.52k
        return false;
1718
9.52k
    }
1719
3.27k
    outputToString(out, status);
1720
3.27k
    if (U_FAILURE(status)) {
1721
0
        return false;
1722
0
    }
1723
3.27k
    if (extensionsStr != nullptr) {
1724
2.26k
        changed = 0;
1725
2.26k
        Locale temp(locale);
1726
2.26k
        LocalPointer<icu::StringEnumeration> iter(locale.createKeywords(status));
1727
2.26k
        if (U_SUCCESS(status) && !iter.isNull()) {
1728
2.20k
            const char* key;
1729
7.33k
            while ((key = iter->next(nullptr, status)) != nullptr) {
1730
5.53k
                if (uprv_strcmp("sd", key) == 0 || uprv_strcmp("rg", key) == 0 ||
1731
5.53k
                        uprv_strcmp("t", key) == 0) {
1732
1.79k
                    auto value = locale.getKeywordValue<CharString>(key, status);
1733
1.79k
                    if (U_FAILURE(status)) {
1734
36
                        status = U_ZERO_ERROR;
1735
36
                        continue;
1736
36
                    }
1737
1.75k
                    CharString replacement;
1738
1.75k
                    if (uprv_strlen(key) == 2) {
1739
97
                        if (replaceSubdivision(value.toStringPiece(), replacement, status)) {
1740
34
                            changed++;
1741
34
                            temp.setKeywordValue(key, replacement.data(), status);
1742
34
                        }
1743
1.66k
                    } else {
1744
1.66k
                        U_ASSERT(uprv_strcmp(key, "t") == 0);
1745
1.66k
                        if (replaceTransformedExtensions(value, replacement, status)) {
1746
1.28k
                            changed++;
1747
1.28k
                            temp.setKeywordValue(key, replacement.data(), status);
1748
1.28k
                        }
1749
1.66k
                    }
1750
1.75k
                    if (U_FAILURE(status)) {
1751
395
                        return false;
1752
395
                    }
1753
1.75k
                }
1754
5.53k
            }
1755
2.20k
        }
1756
1.87k
        if (changed != 0) {
1757
1.28k
            extensionsStr = locale_getKeywordsStart(temp.getName());
1758
1.28k
        }
1759
1.87k
        out.append(extensionsStr, status);
1760
1.87k
    }
1761
2.87k
    if (U_FAILURE(status)) {
1762
64
        return false;
1763
64
    }
1764
    // If the tag is not changed, return.
1765
2.81k
    if (uprv_strcmp(out.data(), locale.getName()) == 0) {
1766
661
        out.clear();
1767
661
        return false;
1768
661
    }
1769
2.15k
    return true;
1770
2.81k
}
1771
1772
// Return true if the locale is changed during canonicalization.
1773
// The replaced value then will be put into out.
1774
bool
1775
canonicalizeLocale(const Locale& locale, CharString& out, UErrorCode& status)
1776
12.8k
{
1777
12.8k
    if (U_FAILURE(status)) { return false; }
1778
12.8k
    AliasReplacer replacer(status);
1779
12.8k
    return replacer.replace(locale, out, status);
1780
12.8k
}
1781
1782
// Function to optimize for known cases without so we can skip the loading
1783
// of resources in the startup time until we really need it.
1784
bool
1785
isKnownCanonicalizedLocale(const char* locale, UErrorCode& status)
1786
15.0k
{
1787
15.0k
    if (U_FAILURE(status)) { return false; }
1788
1789
15.0k
    if (    uprv_strcmp(locale, "c") == 0 ||
1790
15.0k
            uprv_strcmp(locale, "en") == 0 ||
1791
15.0k
            uprv_strcmp(locale, "en_US") == 0) {
1792
41
        return true;
1793
41
    }
1794
1795
    // common well-known Canonicalized.
1796
14.9k
    umtx_initOnce(gKnownCanonicalizedInitOnce,
1797
14.9k
                  &loadKnownCanonicalized, status);
1798
14.9k
    if (U_FAILURE(status)) {
1799
0
        return false;
1800
0
    }
1801
14.9k
    U_ASSERT(gKnownCanonicalized != nullptr);
1802
14.9k
    return uhash_geti(gKnownCanonicalized, locale) != 0;
1803
14.9k
}
1804
1805
}  // namespace
1806
1807
U_NAMESPACE_END
1808
1809
// Function for testing.
1810
U_EXPORT const char* const*
1811
ulocimp_getKnownCanonicalizedLocaleForTest(int32_t& length)
1812
0
{
1813
0
    U_NAMESPACE_USE
1814
0
    length = UPRV_LENGTHOF(KNOWN_CANONICALIZED);
1815
0
    return KNOWN_CANONICALIZED;
1816
0
}
1817
1818
// Function for testing.
1819
U_EXPORT bool
1820
ulocimp_isCanonicalizedLocaleForTest(const char* localeName)
1821
0
{
1822
0
    U_NAMESPACE_USE
1823
0
    Locale l(localeName);
1824
0
    UErrorCode status = U_ZERO_ERROR;
1825
0
    CharString temp;
1826
0
    return !canonicalizeLocale(l, temp, status) && U_SUCCESS(status);
1827
0
}
1828
1829
U_NAMESPACE_BEGIN
1830
1831
/*This function initializes a Locale from a C locale ID*/
1832
Locale& Locale::init(const char* localeID, UBool canonicalize)
1833
2.89M
{
1834
2.89M
    fIsBogus = false;
1835
    /* Free our current storage */
1836
2.89M
    if ((baseName != fullName) && (baseName != fullNameBuffer)) {
1837
2.79M
        uprv_free(baseName);
1838
2.79M
    }
1839
2.89M
    baseName = nullptr;
1840
2.89M
    if(fullName != fullNameBuffer) {
1841
2.75k
        uprv_free(fullName);
1842
2.75k
        fullName = fullNameBuffer;
1843
2.75k
    }
1844
1845
    // not a loop:
1846
    // just an easy way to have a common error-exit
1847
    // without goto and without another function
1848
2.89M
    do {
1849
2.89M
        char *separator;
1850
2.89M
        char *field[5] = {nullptr};
1851
2.89M
        int32_t fieldLen[5] = {0};
1852
2.89M
        int32_t fieldIdx;
1853
2.89M
        int32_t variantField;
1854
2.89M
        int32_t length;
1855
2.89M
        UErrorCode err;
1856
1857
2.89M
        if(localeID == nullptr) {
1858
            // not an error, just set the default locale
1859
2.51M
            return *this = getDefault();
1860
2.51M
        }
1861
1862
        /* preset all fields to empty */
1863
381k
        language[0] = script[0] = country[0] = 0;
1864
1865
        // "canonicalize" the locale ID to ICU/Java format
1866
381k
        err = U_ZERO_ERROR;
1867
381k
        length = canonicalize ?
1868
15.0k
            uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) :
1869
381k
            uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err);
1870
1871
381k
        if (err == U_BUFFER_OVERFLOW_ERROR || length >= static_cast<int32_t>(sizeof(fullNameBuffer))) {
1872
10.4k
            U_ASSERT(baseName == nullptr);
1873
            /*Go to heap for the fullName if necessary*/
1874
10.4k
            char* newFullName = static_cast<char*>(uprv_malloc(sizeof(char) * (length + 1)));
1875
10.4k
            if (newFullName == nullptr) {
1876
0
                break; // error: out of memory
1877
0
            }
1878
10.4k
            fullName = newFullName;
1879
10.4k
            err = U_ZERO_ERROR;
1880
10.4k
            length = canonicalize ?
1881
863
                uloc_canonicalize(localeID, fullName, length+1, &err) :
1882
10.4k
                uloc_getName(localeID, fullName, length+1, &err);
1883
10.4k
        }
1884
381k
        if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) {
1885
            /* should never occur */
1886
5.02k
            break;
1887
5.02k
        }
1888
1889
376k
        variantBegin = length;
1890
1891
        /* after uloc_getName/canonicalize() we know that only '_' are separators */
1892
        /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */
1893
376k
        separator = field[0] = fullName;
1894
376k
        fieldIdx = 1;
1895
376k
        char* at = uprv_strchr(fullName, '@');
1896
561k
        while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != nullptr &&
1897
561k
               fieldIdx < UPRV_LENGTHOF(field)-1 &&
1898
561k
               (at == nullptr || separator < at)) {
1899
184k
            field[fieldIdx] = separator + 1;
1900
184k
            fieldLen[fieldIdx - 1] = static_cast<int32_t>(separator - field[fieldIdx - 1]);
1901
184k
            fieldIdx++;
1902
184k
        }
1903
        // variant may contain @foo or .foo POSIX cruft; remove it
1904
376k
        separator = uprv_strchr(field[fieldIdx-1], '@');
1905
376k
        char* sep2 = uprv_strchr(field[fieldIdx-1], '.');
1906
376k
        if (separator!=nullptr || sep2!=nullptr) {
1907
57.7k
            if (separator==nullptr || (sep2!=nullptr && separator > sep2)) {
1908
1.28k
                separator = sep2;
1909
1.28k
            }
1910
57.7k
            fieldLen[fieldIdx - 1] = static_cast<int32_t>(separator - field[fieldIdx - 1]);
1911
318k
        } else {
1912
318k
            fieldLen[fieldIdx - 1] = length - static_cast<int32_t>(field[fieldIdx - 1] - fullName);
1913
318k
        }
1914
1915
376k
        if (fieldLen[0] >= static_cast<int32_t>(sizeof(language)))
1916
40
        {
1917
40
            break; // error: the language field is too long
1918
40
        }
1919
1920
376k
        variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */
1921
376k
        if (fieldLen[0] > 0) {
1922
            /* We have a language */
1923
178k
            uprv_memcpy(language, fullName, fieldLen[0]);
1924
178k
            language[fieldLen[0]] = 0;
1925
178k
        }
1926
376k
        if (fieldLen[1] == 4 && uprv_isASCIILetter(field[1][0]) &&
1927
376k
                uprv_isASCIILetter(field[1][1]) && uprv_isASCIILetter(field[1][2]) &&
1928
376k
                uprv_isASCIILetter(field[1][3])) {
1929
            /* We have at least a script */
1930
29.9k
            uprv_memcpy(script, field[1], fieldLen[1]);
1931
29.9k
            script[fieldLen[1]] = 0;
1932
29.9k
            variantField++;
1933
29.9k
        }
1934
1935
376k
        if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) {
1936
            /* We have a country */
1937
73.9k
            uprv_memcpy(country, field[variantField], fieldLen[variantField]);
1938
73.9k
            country[fieldLen[variantField]] = 0;
1939
73.9k
            variantField++;
1940
302k
        } else if (fieldLen[variantField] == 0) {
1941
302k
            variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */
1942
302k
        }
1943
1944
376k
        if (fieldLen[variantField] > 0) {
1945
            /* We have a variant */
1946
32.1k
            variantBegin = static_cast<int32_t>(field[variantField] - fullName);
1947
32.1k
        }
1948
1949
376k
        err = U_ZERO_ERROR;
1950
376k
        initBaseName(err);
1951
376k
        if (U_FAILURE(err)) {
1952
0
            break;
1953
0
        }
1954
1955
376k
        if (canonicalize) {
1956
15.0k
            if (!isKnownCanonicalizedLocale(fullName, err)) {
1957
12.8k
                CharString replaced;
1958
                // Not sure it is already canonicalized
1959
12.8k
                if (canonicalizeLocale(*this, replaced, err)) {
1960
2.15k
                    U_ASSERT(U_SUCCESS(err));
1961
                    // If need replacement, call init again.
1962
2.15k
                    init(replaced.data(), false);
1963
2.15k
                }
1964
12.8k
                if (U_FAILURE(err)) {
1965
469
                    break;
1966
469
                }
1967
12.8k
            }
1968
15.0k
        }   // if (canonicalize) {
1969
1970
        // successful end of init()
1971
376k
        return *this;
1972
376k
    } while(0); /*loop doesn't iterate*/
1973
1974
    // when an error occurs, then set this object to "bogus" (there is no UErrorCode here)
1975
5.53k
    setToBogus();
1976
1977
5.53k
    return *this;
1978
2.89M
}
1979
1980
/*
1981
 * Set up the base name.
1982
 * If there are no key words, it's exactly the full name.
1983
 * If key words exist, it's the full name truncated at the '@' character.
1984
 * Need to set up both at init() and after setting a keyword.
1985
 */
1986
void
1987
388k
Locale::initBaseName(UErrorCode &status) {
1988
388k
    if (U_FAILURE(status)) {
1989
0
        return;
1990
0
    }
1991
388k
    U_ASSERT(baseName==nullptr || baseName==fullName);
1992
388k
    const char *atPtr = uprv_strchr(fullName, '@');
1993
388k
    const char *eqPtr = uprv_strchr(fullName, '=');
1994
388k
    if (atPtr && eqPtr && atPtr < eqPtr) {
1995
        // Key words exist.
1996
63.8k
        int32_t baseNameLength = static_cast<int32_t>(atPtr - fullName);
1997
63.8k
        char* newBaseName = static_cast<char*>(uprv_malloc(baseNameLength + 1));
1998
63.8k
        if (newBaseName == nullptr) {
1999
0
            status = U_MEMORY_ALLOCATION_ERROR;
2000
0
            return;
2001
0
        }
2002
63.8k
        baseName = newBaseName;
2003
63.8k
        uprv_strncpy(baseName, fullName, baseNameLength);
2004
63.8k
        baseName[baseNameLength] = 0;
2005
2006
        // The original computation of variantBegin leaves it equal to the length
2007
        // of fullName if there is no variant.  It should instead be
2008
        // the length of the baseName.
2009
63.8k
        if (variantBegin > baseNameLength) {
2010
47.4k
            variantBegin = baseNameLength;
2011
47.4k
        }
2012
324k
    } else {
2013
324k
        baseName = fullName;
2014
324k
    }
2015
388k
}
2016
2017
2018
int32_t
2019
Locale::hashCode() const
2020
608k
{
2021
608k
    return ustr_hashCharsN(fullName, static_cast<int32_t>(uprv_strlen(fullName)));
2022
608k
}
2023
2024
void
2025
5.72M
Locale::setToBogus() {
2026
    /* Free our current storage */
2027
5.72M
    if((baseName != fullName) && (baseName != fullNameBuffer)) {
2028
4.51M
        uprv_free(baseName);
2029
4.51M
    }
2030
5.72M
    baseName = nullptr;
2031
5.72M
    if(fullName != fullNameBuffer) {
2032
415
        uprv_free(fullName);
2033
415
        fullName = fullNameBuffer;
2034
415
    }
2035
5.72M
    *fullNameBuffer = 0;
2036
5.72M
    *language = 0;
2037
5.72M
    *script = 0;
2038
5.72M
    *country = 0;
2039
5.72M
    fIsBogus = true;
2040
5.72M
    variantBegin = 0;
2041
5.72M
}
2042
2043
const Locale& U_EXPORT2
2044
Locale::getDefault()
2045
6.76M
{
2046
6.76M
    {
2047
6.76M
        Mutex lock(&gDefaultLocaleMutex);
2048
6.76M
        if (gDefaultLocale != nullptr) {
2049
6.76M
            return *gDefaultLocale;
2050
6.76M
        }
2051
6.76M
    }
2052
21
    UErrorCode status = U_ZERO_ERROR;
2053
21
    return *locale_set_default_internal(nullptr, status);
2054
6.76M
}
2055
2056
2057
2058
void U_EXPORT2
2059
Locale::setDefault( const   Locale&     newLocale,
2060
                            UErrorCode&  status)
2061
0
{
2062
0
    if (U_FAILURE(status)) {
2063
0
        return;
2064
0
    }
2065
2066
    /* Set the default from the full name string of the supplied locale.
2067
     * This is a convenient way to access the default locale caching mechanisms.
2068
     */
2069
0
    const char *localeID = newLocale.getName();
2070
0
    locale_set_default_internal(localeID, status);
2071
0
}
2072
2073
void
2074
24.8k
Locale::addLikelySubtags(UErrorCode& status) {
2075
24.8k
    if (U_FAILURE(status)) {
2076
8
        return;
2077
8
    }
2078
2079
24.8k
    CharString maximizedLocaleID = ulocimp_addLikelySubtags(fullName, status);
2080
2081
24.8k
    if (U_FAILURE(status)) {
2082
1.24k
        return;
2083
1.24k
    }
2084
2085
23.5k
    init(maximizedLocaleID.data(), /*canonicalize=*/false);
2086
23.5k
    if (isBogus()) {
2087
38
        status = U_ILLEGAL_ARGUMENT_ERROR;
2088
38
    }
2089
23.5k
}
2090
2091
void
2092
20.3k
Locale::minimizeSubtags(UErrorCode& status) {
2093
20.3k
    Locale::minimizeSubtags(false, status);
2094
20.3k
}
2095
void
2096
20.3k
Locale::minimizeSubtags(bool favorScript, UErrorCode& status) {
2097
20.3k
    if (U_FAILURE(status)) {
2098
0
        return;
2099
0
    }
2100
2101
20.3k
    CharString minimizedLocaleID = ulocimp_minimizeSubtags(fullName, favorScript, status);
2102
2103
20.3k
    if (U_FAILURE(status)) {
2104
463
        return;
2105
463
    }
2106
2107
19.8k
    init(minimizedLocaleID.data(), /*canonicalize=*/false);
2108
19.8k
    if (isBogus()) {
2109
4
        status = U_ILLEGAL_ARGUMENT_ERROR;
2110
4
    }
2111
19.8k
}
2112
2113
void
2114
4.00k
Locale::canonicalize(UErrorCode& status) {
2115
4.00k
    if (U_FAILURE(status)) {
2116
211
        return;
2117
211
    }
2118
3.79k
    if (isBogus()) {
2119
51
        status = U_ILLEGAL_ARGUMENT_ERROR;
2120
51
        return;
2121
51
    }
2122
3.73k
    CharString uncanonicalized(fullName, status);
2123
3.73k
    if (U_FAILURE(status)) {
2124
0
        return;
2125
0
    }
2126
3.73k
    init(uncanonicalized.data(), /*canonicalize=*/true);
2127
3.73k
    if (isBogus()) {
2128
489
        status = U_ILLEGAL_ARGUMENT_ERROR;
2129
489
    }
2130
3.73k
}
2131
2132
Locale U_EXPORT2
2133
Locale::forLanguageTag(StringPiece tag, UErrorCode& status)
2134
1.31k
{
2135
1.31k
    Locale result(Locale::eBOGUS);
2136
2137
1.31k
    if (U_FAILURE(status)) {
2138
0
        return result;
2139
0
    }
2140
2141
    // If a BCP 47 language tag is passed as the language parameter to the
2142
    // normal Locale constructor, it will actually fall back to invoking
2143
    // uloc_forLanguageTag() to parse it if it somehow is able to detect that
2144
    // the string actually is BCP 47. This works well for things like strings
2145
    // using BCP 47 extensions, but it does not at all work for things like
2146
    // legacy language tags (marked as “Type: grandfathered” in BCP 47,
2147
    // e.g., "en-GB-oed") which are possible to also
2148
    // interpret as ICU locale IDs and because of that won't trigger the BCP 47
2149
    // parsing. Therefore the code here explicitly calls uloc_forLanguageTag()
2150
    // and then Locale::init(), instead of just calling the normal constructor.
2151
2152
1.31k
    int32_t parsedLength;
2153
1.31k
    CharString localeID = ulocimp_forLanguageTag(
2154
1.31k
            tag.data(),
2155
1.31k
            tag.length(),
2156
1.31k
            &parsedLength,
2157
1.31k
            status);
2158
2159
1.31k
    if (U_FAILURE(status)) {
2160
0
        return result;
2161
0
    }
2162
2163
1.31k
    if (parsedLength != tag.size()) {
2164
127
        status = U_ILLEGAL_ARGUMENT_ERROR;
2165
127
        return result;
2166
127
    }
2167
2168
1.18k
    result.init(localeID.data(), /*canonicalize=*/false);
2169
1.18k
    if (result.isBogus()) {
2170
2
        status = U_ILLEGAL_ARGUMENT_ERROR;
2171
2
    }
2172
1.18k
    return result;
2173
1.31k
}
2174
2175
void
2176
Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const
2177
3.24k
{
2178
3.24k
    if (U_FAILURE(status)) {
2179
0
        return;
2180
0
    }
2181
2182
3.24k
    if (fIsBogus) {
2183
65
        status = U_ILLEGAL_ARGUMENT_ERROR;
2184
65
        return;
2185
65
    }
2186
2187
3.17k
    ulocimp_toLanguageTag(fullName, sink, /*strict=*/false, status);
2188
3.17k
}
2189
2190
Locale U_EXPORT2
2191
Locale::createFromName (const char *name)
2192
51.2k
{
2193
51.2k
    if (name) {
2194
51.2k
        Locale l("");
2195
51.2k
        l.init(name, false);
2196
51.2k
        return l;
2197
51.2k
    }
2198
0
    else {
2199
0
        return getDefault();
2200
0
    }
2201
51.2k
}
2202
2203
Locale U_EXPORT2
2204
0
Locale::createCanonical(const char* name) {
2205
0
    Locale loc("");
2206
0
    loc.init(name, true);
2207
0
    return loc;
2208
0
}
2209
2210
const char *
2211
Locale::getISO3Language() const
2212
0
{
2213
0
    return uloc_getISO3Language(fullName);
2214
0
}
2215
2216
2217
const char *
2218
Locale::getISO3Country() const
2219
0
{
2220
0
    return uloc_getISO3Country(fullName);
2221
0
}
2222
2223
/**
2224
 * Return the LCID value as specified in the "LocaleID" resource for this
2225
 * locale.  The LocaleID must be expressed as a hexadecimal number, from
2226
 * one to four digits.  If the LocaleID resource is not present, or is
2227
 * in an incorrect format, 0 is returned.  The LocaleID is for use in
2228
 * Windows (it is an LCID), but is available on all platforms.
2229
 */
2230
uint32_t
2231
Locale::getLCID() const
2232
0
{
2233
0
    return uloc_getLCID(fullName);
2234
0
}
2235
2236
const char* const* U_EXPORT2 Locale::getISOCountries()
2237
0
{
2238
0
    return uloc_getISOCountries();
2239
0
}
2240
2241
const char* const* U_EXPORT2 Locale::getISOLanguages()
2242
0
{
2243
0
    return uloc_getISOLanguages();
2244
0
}
2245
2246
// Set the locale's data based on a posix id.
2247
void Locale::setFromPOSIXID(const char *posixID)
2248
11.2k
{
2249
11.2k
    init(posixID, true);
2250
11.2k
}
2251
2252
const Locale & U_EXPORT2
2253
Locale::getRoot()
2254
228
{
2255
228
    return getLocale(eROOT);
2256
228
}
2257
2258
const Locale & U_EXPORT2
2259
Locale::getEnglish()
2260
2.78k
{
2261
2.78k
    return getLocale(eENGLISH);
2262
2.78k
}
2263
2264
const Locale & U_EXPORT2
2265
Locale::getFrench()
2266
0
{
2267
0
    return getLocale(eFRENCH);
2268
0
}
2269
2270
const Locale & U_EXPORT2
2271
Locale::getGerman()
2272
0
{
2273
0
    return getLocale(eGERMAN);
2274
0
}
2275
2276
const Locale & U_EXPORT2
2277
Locale::getItalian()
2278
0
{
2279
0
    return getLocale(eITALIAN);
2280
0
}
2281
2282
const Locale & U_EXPORT2
2283
Locale::getJapanese()
2284
0
{
2285
0
    return getLocale(eJAPANESE);
2286
0
}
2287
2288
const Locale & U_EXPORT2
2289
Locale::getKorean()
2290
0
{
2291
0
    return getLocale(eKOREAN);
2292
0
}
2293
2294
const Locale & U_EXPORT2
2295
Locale::getChinese()
2296
0
{
2297
0
    return getLocale(eCHINESE);
2298
0
}
2299
2300
const Locale & U_EXPORT2
2301
Locale::getSimplifiedChinese()
2302
0
{
2303
0
    return getLocale(eCHINA);
2304
0
}
2305
2306
const Locale & U_EXPORT2
2307
Locale::getTraditionalChinese()
2308
0
{
2309
0
    return getLocale(eTAIWAN);
2310
0
}
2311
2312
2313
const Locale & U_EXPORT2
2314
Locale::getFrance()
2315
0
{
2316
0
    return getLocale(eFRANCE);
2317
0
}
2318
2319
const Locale & U_EXPORT2
2320
Locale::getGermany()
2321
0
{
2322
0
    return getLocale(eGERMANY);
2323
0
}
2324
2325
const Locale & U_EXPORT2
2326
Locale::getItaly()
2327
0
{
2328
0
    return getLocale(eITALY);
2329
0
}
2330
2331
const Locale & U_EXPORT2
2332
Locale::getJapan()
2333
0
{
2334
0
    return getLocale(eJAPAN);
2335
0
}
2336
2337
const Locale & U_EXPORT2
2338
Locale::getKorea()
2339
0
{
2340
0
    return getLocale(eKOREA);
2341
0
}
2342
2343
const Locale & U_EXPORT2
2344
Locale::getChina()
2345
0
{
2346
0
    return getLocale(eCHINA);
2347
0
}
2348
2349
const Locale & U_EXPORT2
2350
Locale::getPRC()
2351
0
{
2352
0
    return getLocale(eCHINA);
2353
0
}
2354
2355
const Locale & U_EXPORT2
2356
Locale::getTaiwan()
2357
0
{
2358
0
    return getLocale(eTAIWAN);
2359
0
}
2360
2361
const Locale & U_EXPORT2
2362
Locale::getUK()
2363
0
{
2364
0
    return getLocale(eUK);
2365
0
}
2366
2367
const Locale & U_EXPORT2
2368
Locale::getUS()
2369
0
{
2370
0
    return getLocale(eUS);
2371
0
}
2372
2373
const Locale & U_EXPORT2
2374
Locale::getCanada()
2375
0
{
2376
0
    return getLocale(eCANADA);
2377
0
}
2378
2379
const Locale & U_EXPORT2
2380
Locale::getCanadaFrench()
2381
0
{
2382
0
    return getLocale(eCANADA_FRENCH);
2383
0
}
2384
2385
const Locale &
2386
Locale::getLocale(int locid)
2387
3.01k
{
2388
3.01k
    Locale *localeCache = getLocaleCache();
2389
3.01k
    U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0));
2390
3.01k
    if (localeCache == nullptr) {
2391
        // Failure allocating the locale cache.
2392
        //   The best we can do is return a nullptr reference.
2393
0
        locid = 0;
2394
0
    }
2395
3.01k
    return localeCache[locid]; /*operating on nullptr*/
2396
3.01k
}
2397
2398
/*
2399
This function is defined this way in order to get around static
2400
initialization and static destruction.
2401
 */
2402
Locale *
2403
Locale::getLocaleCache()
2404
3.01k
{
2405
3.01k
    UErrorCode status = U_ZERO_ERROR;
2406
3.01k
    umtx_initOnce(gLocaleCacheInitOnce, locale_init, status);
2407
3.01k
    return gLocaleCache;
2408
3.01k
}
2409
2410
class KeywordEnumeration : public StringEnumeration {
2411
protected:
2412
    CharString keywords;
2413
private:
2414
    const char *current;
2415
    static const char fgClassID;
2416
2417
public:
2418
0
    static UClassID U_EXPORT2 getStaticClassID() { return (UClassID)&fgClassID; }
2419
0
    virtual UClassID getDynamicClassID() const override { return getStaticClassID(); }
2420
public:
2421
    KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status)
2422
8.34k
        : keywords(), current(keywords.data()) {
2423
8.34k
        if(U_SUCCESS(status) && keywordLen != 0) {
2424
8.34k
            if(keys == nullptr || keywordLen < 0) {
2425
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
2426
8.34k
            } else {
2427
8.34k
                keywords.append(keys, keywordLen, status);
2428
8.34k
                current = keywords.data() + currentIndex;
2429
8.34k
            }
2430
8.34k
        }
2431
8.34k
    }
2432
2433
    virtual ~KeywordEnumeration();
2434
2435
    virtual StringEnumeration * clone() const override
2436
0
    {
2437
0
        UErrorCode status = U_ZERO_ERROR;
2438
0
        return new KeywordEnumeration(
2439
0
                keywords.data(), keywords.length(),
2440
0
                static_cast<int32_t>(current - keywords.data()), status);
2441
0
    }
2442
2443
0
    virtual int32_t count(UErrorCode& status) const override {
2444
0
        if (U_FAILURE(status)) { return 0; }
2445
0
        const char *kw = keywords.data();
2446
0
        int32_t result = 0;
2447
0
        while(*kw) {
2448
0
            result++;
2449
0
            kw += uprv_strlen(kw)+1;
2450
0
        }
2451
0
        return result;
2452
0
    }
2453
2454
25.0k
    virtual const char* next(int32_t* resultLength, UErrorCode& status) override {
2455
25.0k
        const char* result;
2456
25.0k
        int32_t len;
2457
25.0k
        if(U_SUCCESS(status) && *current != 0) {
2458
17.1k
            result = current;
2459
17.1k
            len = static_cast<int32_t>(uprv_strlen(current));
2460
17.1k
            current += len+1;
2461
17.1k
            if(resultLength != nullptr) {
2462
891
                *resultLength = len;
2463
891
            }
2464
17.1k
        } else {
2465
7.88k
            if(resultLength != nullptr) {
2466
245
                *resultLength = 0;
2467
245
            }
2468
7.88k
            result = nullptr;
2469
7.88k
        }
2470
25.0k
        return result;
2471
25.0k
    }
2472
2473
0
    virtual const UnicodeString* snext(UErrorCode& status) override {
2474
0
        if (U_FAILURE(status)) { return nullptr; }
2475
0
        int32_t resultLength = 0;
2476
0
        const char *s = next(&resultLength, status);
2477
0
        return setChars(s, resultLength, status);
2478
0
    }
2479
2480
0
    virtual void reset(UErrorCode& status) override {
2481
0
        if (U_FAILURE(status)) { return; }
2482
0
        current = keywords.data();
2483
0
    }
2484
};
2485
2486
const char KeywordEnumeration::fgClassID = '\0';
2487
2488
// Out-of-line virtual destructor to serve as the "key function".
2489
8.34k
KeywordEnumeration::~KeywordEnumeration() = default;
2490
2491
// A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in
2492
// the next() method for each keyword before returning it.
2493
class UnicodeKeywordEnumeration : public KeywordEnumeration {
2494
public:
2495
    using KeywordEnumeration::KeywordEnumeration;
2496
    virtual ~UnicodeKeywordEnumeration();
2497
2498
2.63k
    virtual const char* next(int32_t* resultLength, UErrorCode& status) override {
2499
2.63k
        const char* legacy_key = KeywordEnumeration::next(nullptr, status);
2500
2.89k
        while (U_SUCCESS(status) && legacy_key != nullptr) {
2501
2.46k
            const char* key = uloc_toUnicodeLocaleKey(legacy_key);
2502
2.46k
            if (key != nullptr) {
2503
2.20k
                if (resultLength != nullptr) {
2504
2.11k
                    *resultLength = static_cast<int32_t>(uprv_strlen(key));
2505
2.11k
                }
2506
2.20k
                return key;
2507
2.20k
            }
2508
            // Not a Unicode keyword, could be a t, x or other, continue to look at the next one.
2509
258
            legacy_key = KeywordEnumeration::next(nullptr, status);
2510
258
        }
2511
426
        if (resultLength != nullptr) *resultLength = 0;
2512
426
        return nullptr;
2513
2.63k
    }
2514
0
    virtual int32_t count(UErrorCode& status) const override {
2515
0
        if (U_FAILURE(status)) { return 0; }
2516
0
        const char *kw = keywords.data();
2517
0
        int32_t result = 0;
2518
0
        while(*kw) {
2519
0
            if (uloc_toUnicodeLocaleKey(kw) != nullptr) {
2520
0
                result++;
2521
0
            }
2522
0
            kw += uprv_strlen(kw)+1;
2523
0
        }
2524
0
        return result;
2525
0
    }
2526
};
2527
2528
// Out-of-line virtual destructor to serve as the "key function".
2529
UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default;
2530
2531
StringEnumeration *
2532
Locale::createKeywords(UErrorCode &status) const
2533
8.51k
{
2534
8.51k
    StringEnumeration *result = nullptr;
2535
2536
8.51k
    if (U_FAILURE(status)) {
2537
0
        return result;
2538
0
    }
2539
2540
8.51k
    const char* variantStart = uprv_strchr(fullName, '@');
2541
8.51k
    const char* assignment = uprv_strchr(fullName, '=');
2542
8.51k
    if(variantStart) {
2543
7.99k
        if(assignment > variantStart) {
2544
7.92k
            CharString keywords = ulocimp_getKeywords(variantStart + 1, '@', false, status);
2545
7.92k
            if (U_SUCCESS(status) && !keywords.isEmpty()) {
2546
7.92k
                result = new KeywordEnumeration(keywords.data(), keywords.length(), 0, status);
2547
7.92k
                if (!result) {
2548
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2549
0
                }
2550
7.92k
            }
2551
7.92k
        } else {
2552
67
            status = U_INVALID_FORMAT_ERROR;
2553
67
        }
2554
7.99k
    }
2555
8.51k
    return result;
2556
8.51k
}
2557
2558
StringEnumeration *
2559
Locale::createUnicodeKeywords(UErrorCode &status) const
2560
516
{
2561
516
    StringEnumeration *result = nullptr;
2562
2563
516
    if (U_FAILURE(status)) {
2564
0
        return result;
2565
0
    }
2566
2567
516
    const char* variantStart = uprv_strchr(fullName, '@');
2568
516
    const char* assignment = uprv_strchr(fullName, '=');
2569
516
    if(variantStart) {
2570
433
        if(assignment > variantStart) {
2571
426
            CharString keywords = ulocimp_getKeywords(variantStart + 1, '@', false, status);
2572
426
            if (U_SUCCESS(status) && !keywords.isEmpty()) {
2573
426
                result = new UnicodeKeywordEnumeration(keywords.data(), keywords.length(), 0, status);
2574
426
                if (!result) {
2575
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2576
0
                }
2577
426
            }
2578
426
        } else {
2579
7
            status = U_INVALID_FORMAT_ERROR;
2580
7
        }
2581
433
    }
2582
516
    return result;
2583
516
}
2584
2585
int32_t
2586
Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const
2587
499k
{
2588
499k
    return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status);
2589
499k
}
2590
2591
void
2592
16.0k
Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const {
2593
16.0k
    if (U_FAILURE(status)) {
2594
316
        return;
2595
316
    }
2596
2597
15.7k
    if (fIsBogus) {
2598
391
        status = U_ILLEGAL_ARGUMENT_ERROR;
2599
391
        return;
2600
391
    }
2601
2602
15.3k
    ulocimp_getKeywordValue(fullName, keywordName, sink, status);
2603
15.3k
}
2604
2605
void
2606
Locale::getUnicodeKeywordValue(StringPiece keywordName,
2607
                               ByteSink& sink,
2608
2.10k
                               UErrorCode& status) const {
2609
2.10k
    if (U_FAILURE(status)) {
2610
142
        return;
2611
142
    }
2612
2613
1.96k
    std::optional<std::string_view> legacy_key = ulocimp_toLegacyKeyWithFallback(keywordName);
2614
1.96k
    if (!legacy_key.has_value()) {
2615
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2616
0
        return;
2617
0
    }
2618
2619
1.96k
    auto legacy_value = getKeywordValue<CharString>(*legacy_key, status);
2620
2621
1.96k
    if (U_FAILURE(status)) {
2622
4
        return;
2623
4
    }
2624
2625
1.96k
    std::optional<std::string_view> unicode_value =
2626
1.96k
        ulocimp_toBcpTypeWithFallback(keywordName, legacy_value.toStringPiece());
2627
1.96k
    if (!unicode_value.has_value()) {
2628
66
        status = U_ILLEGAL_ARGUMENT_ERROR;
2629
66
        return;
2630
66
    }
2631
2632
1.89k
    sink.Append(unicode_value->data(), static_cast<int32_t>(unicode_value->size()));
2633
1.89k
}
2634
2635
void
2636
Locale::setKeywordValue(StringPiece keywordName,
2637
                        StringPiece keywordValue,
2638
16.0k
                        UErrorCode& status) {
2639
16.0k
    if (U_FAILURE(status)) { return; }
2640
16.0k
    if (keywordName.empty()) {
2641
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2642
0
        return;
2643
0
    }
2644
16.0k
    if (status == U_STRING_NOT_TERMINATED_WARNING) {
2645
11
        status = U_ZERO_ERROR;
2646
11
    }
2647
2648
16.0k
    int32_t length = static_cast<int32_t>(uprv_strlen(fullName));
2649
16.0k
    int32_t capacity = fullName == fullNameBuffer ? ULOC_FULLNAME_CAPACITY : length + 1;
2650
2651
16.0k
    const char* start = locale_getKeywordsStart(fullName);
2652
16.0k
    int32_t offset = start == nullptr ? length : start - fullName;
2653
2654
17.5k
    for (;;) {
2655
        // Remove -1 from the capacity so that this function can guarantee NUL termination.
2656
17.5k
        CheckedArrayByteSink sink(fullName + offset, capacity - offset - 1);
2657
2658
17.5k
        int32_t reslen = ulocimp_setKeywordValue(
2659
17.5k
            {fullName + offset, static_cast<std::string_view::size_type>(length - offset)},
2660
17.5k
            keywordName,
2661
17.5k
            keywordValue,
2662
17.5k
            sink,
2663
17.5k
            status);
2664
2665
17.5k
        if (status == U_BUFFER_OVERFLOW_ERROR) {
2666
1.44k
            capacity = reslen + offset + 1;
2667
1.44k
            char* newFullName = static_cast<char*>(uprv_malloc(capacity));
2668
1.44k
            if (newFullName == nullptr) {
2669
0
                status = U_MEMORY_ALLOCATION_ERROR;
2670
0
                return;
2671
0
            }
2672
1.44k
            uprv_memcpy(newFullName, fullName, length + 1);
2673
1.44k
            if (fullName != fullNameBuffer) {
2674
1.24k
                if (baseName == fullName) {
2675
6
                    baseName = newFullName; // baseName should not point to freed memory.
2676
6
                }
2677
                // if fullName is already on the heap, need to free it.
2678
1.24k
                uprv_free(fullName);
2679
1.24k
            }
2680
1.44k
            fullName = newFullName;
2681
1.44k
            status = U_ZERO_ERROR;
2682
1.44k
            continue;
2683
1.44k
        }
2684
2685
16.0k
        if (U_FAILURE(status)) { return; }
2686
16.0k
        u_terminateChars(fullName, capacity, reslen + offset, &status);
2687
16.0k
        break;
2688
16.0k
    }
2689
2690
16.0k
    if (baseName == fullName) {
2691
        // May have added the first keyword, meaning that the fullName is no longer also the baseName.
2692
11.5k
        initBaseName(status);
2693
11.5k
    }
2694
16.0k
}
2695
2696
void
2697
Locale::setUnicodeKeywordValue(StringPiece keywordName,
2698
                               StringPiece keywordValue,
2699
4.73k
                               UErrorCode& status) {
2700
4.73k
    if (U_FAILURE(status)) {
2701
0
        return;
2702
0
    }
2703
2704
4.73k
    std::optional<std::string_view> legacy_key = ulocimp_toLegacyKeyWithFallback(keywordName);
2705
4.73k
    if (!legacy_key.has_value()) {
2706
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2707
0
        return;
2708
0
    }
2709
2710
4.73k
    std::string_view value;
2711
2712
4.73k
    if (!keywordValue.empty()) {
2713
4.73k
        std::optional<std::string_view> legacy_value =
2714
4.73k
            ulocimp_toLegacyTypeWithFallback(keywordName, keywordValue);
2715
4.73k
        if (!legacy_value.has_value()) {
2716
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
2717
0
            return;
2718
0
        }
2719
4.73k
        value = *legacy_value;
2720
4.73k
    }
2721
2722
4.73k
    setKeywordValue(*legacy_key, value, status);
2723
4.73k
}
2724
2725
const char *
2726
364k
Locale::getBaseName() const {
2727
364k
    return baseName;
2728
364k
}
2729
2730
0
Locale::Iterator::~Iterator() = default;
2731
2732
//eof
2733
U_NAMESPACE_END