Coverage Report

Created: 2026-06-13 06:44

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