Bug Summary

File:root/firefox-clang/intl/icu/source/i18n/tzgnames.cpp
Warning:line 643, column 17
Called C++ object pointer is null

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tzgnames.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D U_I18N_IMPLEMENTATION -D _LIBCPP_DISABLE_DEPRECATION_WARNINGS -D U_USING_ICU_NAMESPACE=0 -D U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 -D U_HIDE_OBSOLETE_UTF_OLD_H=1 -D UCONFIG_NO_LEGACY_CONVERSION -D UCONFIG_NO_TRANSLITERATION -D UCONFIG_NO_REGULAR_EXPRESSIONS -D UCONFIG_NO_BREAK_ITERATION -D UCONFIG_NO_IDNA -D UCONFIG_NO_MF2 -D U_CHARSET_IS_UTF8 -D UNISTR_FROM_CHAR_EXPLICIT=explicit -D UNISTR_FROM_STRING_EXPLICIT=explicit -D U_ENABLE_DYLOAD=0 -D U_DEBUG=1 -I /root/firefox-clang/config/external/icu/i18n -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -I /root/firefox-clang/intl/icu/source/common -I /root/firefox-clang/mfbt/double-conversion -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-comma -Wno-implicit-const-int-float-conversion -Wno-macro-redefined -Wno-microsoft-include -Wno-tautological-unsigned-enum-zero-compare -Wno-unreachable-code-loop-increment -Wno-unreachable-code-return -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-27-100320-3286336-1 -x c++ /root/firefox-clang/intl/icu/source/i18n/tzgnames.cpp
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4*******************************************************************************
5* Copyright (C) 2011-2016, International Business Machines Corporation and
6* others. All Rights Reserved.
7*******************************************************************************
8*/
9
10#include "unicode/utypes.h"
11
12#if !UCONFIG_NO_FORMATTING0
13
14#include "tzgnames.h"
15
16#include "unicode/basictz.h"
17#include "unicode/locdspnm.h"
18#include "unicode/rbtz.h"
19#include "unicode/simpleformatter.h"
20#include "unicode/simpletz.h"
21#include "unicode/strenum.h"
22#include "unicode/vtzone.h"
23
24#include "charstr.h"
25#include "cmemory.h"
26#include "cstring.h"
27#include "mutex.h"
28#include "uhash.h"
29#include "uassert.h"
30#include "umutex.h"
31#include "ulocimp.h"
32#include "uresimp.h"
33#include "ureslocs.h"
34#include "zonemeta.h"
35#include "tznames_impl.h"
36#include "olsontz.h"
37#include "ucln_in.h"
38
39U_NAMESPACE_BEGINnamespace icu_77 {
40
41#define ZID_KEY_MAX128 128
42
43static const char gZoneStrings[] = "zoneStrings";
44
45static const char gRegionFormatTag[] = "regionFormat";
46static const char gFallbackFormatTag[] = "fallbackFormat";
47
48static const char16_t gEmpty[] = {0x00};
49
50static const char16_t gDefRegionPattern[] = {0x7B, 0x30, 0x7D, 0x00}; // "{0}"
51static const char16_t gDefFallbackPattern[] = {0x7B, 0x31, 0x7D, 0x20, 0x28, 0x7B, 0x30, 0x7D, 0x29, 0x00}; // "{1} ({0})"
52
53static const double kDstCheckRange = static_cast<double>(184) * U_MILLIS_PER_DAY(86400000);
54
55
56
57U_CDECL_BEGINextern "C" {
58
59typedef struct PartialLocationKey {
60 const char16_t* tzID;
61 const char16_t* mzID;
62 UBool isLong;
63} PartialLocationKey;
64
65/**
66 * Hash function for partial location name hash key
67 */
68static int32_t U_CALLCONV
69hashPartialLocationKey(const UHashTok key) {
70 // <tzID>&<mzID>#[L|S]
71 PartialLocationKey *p = (PartialLocationKey *)key.pointer;
72 UnicodeString str(p->tzID);
73 str.append((char16_t)0x26)
74 .append(p->mzID, -1)
75 .append((char16_t)0x23)
76 .append((char16_t)(p->isLong ? 0x4C : 0x53));
77 return str.hashCode();
78}
79
80/**
81 * Comparer for partial location name hash key
82 */
83static UBool U_CALLCONV
84comparePartialLocationKey(const UHashTok key1, const UHashTok key2) {
85 PartialLocationKey *p1 = (PartialLocationKey *)key1.pointer;
86 PartialLocationKey *p2 = (PartialLocationKey *)key2.pointer;
87
88 if (p1 == p2) {
89 return true;
90 }
91 if (p1 == nullptr || p2 == nullptr) {
92 return false;
93 }
94 // We just check identity of tzID/mzID
95 return (p1->tzID == p2->tzID && p1->mzID == p2->mzID && p1->isLong == p2->isLong);
96}
97
98/**
99 * Deleter for GNameInfo
100 */
101static void U_CALLCONV
102deleteGNameInfo(void *obj) {
103 uprv_freeuprv_free_77(obj);
104}
105
106/**
107 * GNameInfo stores zone name information in the local trie
108 */
109typedef struct GNameInfo {
110 UTimeZoneGenericNameType type;
111 const char16_t* tzID;
112} ZNameInfo;
113
114/**
115 * GMatchInfo stores zone name match information used by find method
116 */
117typedef struct GMatchInfo {
118 const GNameInfo* gnameInfo;
119 int32_t matchLength;
120 UTimeZoneFormatTimeType timeType;
121} ZMatchInfo;
122
123U_CDECL_END}
124
125// ---------------------------------------------------
126// The class stores time zone generic name match information
127// ---------------------------------------------------
128class TimeZoneGenericNameMatchInfo : public UMemory {
129public:
130 TimeZoneGenericNameMatchInfo(UVector* matches);
131 ~TimeZoneGenericNameMatchInfo();
132
133 int32_t size() const;
134 UTimeZoneGenericNameType getGenericNameType(int32_t index) const;
135 int32_t getMatchLength(int32_t index) const;
136 UnicodeString& getTimeZoneID(int32_t index, UnicodeString& tzID) const;
137
138private:
139 UVector* fMatches; // vector of MatchEntry
140};
141
142TimeZoneGenericNameMatchInfo::TimeZoneGenericNameMatchInfo(UVector* matches)
143: fMatches(matches) {
144}
145
146TimeZoneGenericNameMatchInfo::~TimeZoneGenericNameMatchInfo() {
147 delete fMatches;
148}
149
150int32_t
151TimeZoneGenericNameMatchInfo::size() const {
152 if (fMatches == nullptr) {
153 return 0;
154 }
155 return fMatches->size();
156}
157
158UTimeZoneGenericNameType
159TimeZoneGenericNameMatchInfo::getGenericNameType(int32_t index) const {
160 GMatchInfo* minfo = static_cast<GMatchInfo*>(fMatches->elementAt(index));
161 if (minfo != nullptr) {
162 return static_cast<UTimeZoneGenericNameType>(minfo->gnameInfo->type);
163 }
164 return UTZGNM_UNKNOWN;
165}
166
167int32_t
168TimeZoneGenericNameMatchInfo::getMatchLength(int32_t index) const {
169 ZMatchInfo* minfo = static_cast<ZMatchInfo*>(fMatches->elementAt(index));
170 if (minfo != nullptr) {
171 return minfo->matchLength;
172 }
173 return -1;
174}
175
176UnicodeString&
177TimeZoneGenericNameMatchInfo::getTimeZoneID(int32_t index, UnicodeString& tzID) const {
178 GMatchInfo* minfo = static_cast<GMatchInfo*>(fMatches->elementAt(index));
179 if (minfo != nullptr && minfo->gnameInfo->tzID != nullptr) {
180 tzID.setTo(true, minfo->gnameInfo->tzID, -1);
181 } else {
182 tzID.setToBogus();
183 }
184 return tzID;
185}
186
187// ---------------------------------------------------
188// GNameSearchHandler
189// ---------------------------------------------------
190class GNameSearchHandler : public TextTrieMapSearchResultHandler {
191public:
192 GNameSearchHandler(uint32_t types);
193 virtual ~GNameSearchHandler();
194
195 UBool handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) override;
196 UVector* getMatches(int32_t& maxMatchLen);
197
198private:
199 uint32_t fTypes;
200 UVector* fResults;
201 int32_t fMaxMatchLen;
202};
203
204GNameSearchHandler::GNameSearchHandler(uint32_t types)
205: fTypes(types), fResults(nullptr), fMaxMatchLen(0) {
206}
207
208GNameSearchHandler::~GNameSearchHandler() {
209 delete fResults;
210}
211
212UBool
213GNameSearchHandler::handleMatch(int32_t matchLength, const CharacterNode *node, UErrorCode &status) {
214 if (U_FAILURE(status)) {
215 return false;
216 }
217 if (node->hasValues()) {
218 int32_t valuesCount = node->countValues();
219 for (int32_t i = 0; i < valuesCount; i++) {
220 GNameInfo *nameinfo = (ZNameInfo *)node->getValue(i);
221 if (nameinfo == nullptr) {
222 break;
223 }
224 if ((nameinfo->type & fTypes) != 0) {
225 // matches a requested type
226 if (fResults == nullptr) {
227 LocalPointer<UVector> lpResults(new UVector(uprv_freeuprv_free_77, nullptr, status), status);
228 if (U_FAILURE(status)) {
229 return false;
230 }
231 fResults = lpResults.orphan();
232 }
233 GMatchInfo* gmatch = static_cast<GMatchInfo*>(uprv_mallocuprv_malloc_77(sizeof(GMatchInfo)));
234 if (gmatch == nullptr) {
235 status = U_MEMORY_ALLOCATION_ERROR;
236 return false;
237 }
238 // add the match to the vector
239 gmatch->gnameInfo = nameinfo;
240 gmatch->matchLength = matchLength;
241 gmatch->timeType = UTZFMT_TIME_TYPE_UNKNOWN;
242 fResults->adoptElement(gmatch, status);
243 if (U_FAILURE(status)) {
244 return false;
245 }
246 if (matchLength > fMaxMatchLen) {
247 fMaxMatchLen = matchLength;
248 }
249 }
250 }
251 }
252 return true;
253}
254
255UVector*
256GNameSearchHandler::getMatches(int32_t& maxMatchLen) {
257 // give the ownership to the caller
258 UVector *results = fResults;
259 maxMatchLen = fMaxMatchLen;
260
261 // reset
262 fResults = nullptr;
263 fMaxMatchLen = 0;
264 return results;
265}
266
267static UMutex gLock;
268
269class TZGNCore : public UMemory {
270public:
271 TZGNCore(const Locale& locale, UErrorCode& status);
272 virtual ~TZGNCore();
273
274 UnicodeString& getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
275 UDate date, UnicodeString& name) const;
276
277 UnicodeString& getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const;
278
279 int32_t findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
280 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const;
281
282private:
283 Locale fLocale;
284 const TimeZoneNames* fTimeZoneNames;
285 UHashtable* fLocationNamesMap;
286 UHashtable* fPartialLocationNamesMap;
287
288 SimpleFormatter fRegionFormat;
289 SimpleFormatter fFallbackFormat;
290
291 LocaleDisplayNames* fLocaleDisplayNames;
292 ZNStringPool fStringPool;
293
294 TextTrieMap fGNamesTrie;
295 UBool fGNamesTrieFullyLoaded;
296
297 CharString fTargetRegion;
298
299 void initialize(const Locale& locale, UErrorCode& status);
300 void cleanup();
301
302 void loadStrings(const UnicodeString& tzCanonicalID);
303
304 const char16_t* getGenericLocationName(const UnicodeString& tzCanonicalID);
305
306 UnicodeString& formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type,
307 UDate date, UnicodeString& name) const;
308
309 UnicodeString& getPartialLocationName(const UnicodeString& tzCanonicalID,
310 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
311 UnicodeString& name) const;
312
313 const char16_t* getPartialLocationName(const UnicodeString& tzCanonicalID,
314 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName);
315
316 TimeZoneGenericNameMatchInfo* findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
317
318 TimeZoneNames::MatchInfoCollection* findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const;
319};
320
321
322// ---------------------------------------------------
323// TZGNCore - core implementation of TimeZoneGenericNames
324//
325// TimeZoneGenericNames is parallel to TimeZoneNames,
326// but handles run-time generated time zone names.
327// This is the main part of this module.
328// ---------------------------------------------------
329TZGNCore::TZGNCore(const Locale& locale, UErrorCode& status)
330: fLocale(locale),
331 fTimeZoneNames(nullptr),
332 fLocationNamesMap(nullptr),
333 fPartialLocationNamesMap(nullptr),
334 fLocaleDisplayNames(nullptr),
335 fStringPool(status),
336 fGNamesTrie(true, deleteGNameInfo),
337 fGNamesTrieFullyLoaded(false),
338 fTargetRegion() {
339 initialize(locale, status);
340}
341
342TZGNCore::~TZGNCore() {
343 cleanup();
344}
345
346void
347TZGNCore::initialize(const Locale& locale, UErrorCode& status) {
348 if (U_FAILURE(status)) {
349 return;
350 }
351
352 // TimeZoneNames
353 fTimeZoneNames = TimeZoneNames::createInstance(locale, status);
354 if (U_FAILURE(status)) {
355 return;
356 }
357
358 // Initialize format patterns
359 UnicodeString rpat(true, gDefRegionPattern, -1);
360 UnicodeString fpat(true, gDefFallbackPattern, -1);
361
362 UErrorCode tmpsts = U_ZERO_ERROR; // OK with fallback warning..
363 UResourceBundle *zoneStrings = ures_openures_open_77(U_ICUDATA_ZONE"icudt" "77" "l" "-" "zone", locale.getName(), &tmpsts);
364 zoneStrings = ures_getByKeyWithFallbackures_getByKeyWithFallback_77(zoneStrings, gZoneStrings, zoneStrings, &tmpsts);
365
366 if (U_SUCCESS(tmpsts)) {
367 const char16_t *regionPattern = ures_getStringByKeyWithFallbackures_getStringByKeyWithFallback_77(zoneStrings, gRegionFormatTag, nullptr, &tmpsts);
368 if (U_SUCCESS(tmpsts) && u_strlenu_strlen_77(regionPattern) > 0) {
369 rpat.setTo(regionPattern, -1);
370 }
371 tmpsts = U_ZERO_ERROR;
372 const char16_t *fallbackPattern = ures_getStringByKeyWithFallbackures_getStringByKeyWithFallback_77(zoneStrings, gFallbackFormatTag, nullptr, &tmpsts);
373 if (U_SUCCESS(tmpsts) && u_strlenu_strlen_77(fallbackPattern) > 0) {
374 fpat.setTo(fallbackPattern, -1);
375 }
376 }
377 ures_closeures_close_77(zoneStrings);
378
379 fRegionFormat.applyPatternMinMaxArguments(rpat, 1, 1, status);
380 fFallbackFormat.applyPatternMinMaxArguments(fpat, 2, 2, status);
381 if (U_FAILURE(status)) {
382 cleanup();
383 return;
384 }
385
386 // locale display names
387 fLocaleDisplayNames = LocaleDisplayNames::createInstance(locale);
388
389 // hash table for names - no key/value deleters
390 fLocationNamesMap = uhash_openuhash_open_77(uhash_hashUCharsuhash_hashUChars_77, uhash_compareUCharsuhash_compareUChars_77, nullptr, &status);
391 if (U_FAILURE(status)) {
392 cleanup();
393 return;
394 }
395
396 fPartialLocationNamesMap = uhash_openuhash_open_77(hashPartialLocationKey, comparePartialLocationKey, nullptr, &status);
397 if (U_FAILURE(status)) {
398 cleanup();
399 return;
400 }
401 uhash_setKeyDeleteruhash_setKeyDeleter_77(fPartialLocationNamesMap, uprv_freeuprv_free_77);
402 // no value deleter
403
404 // target region
405 const char* region = fLocale.getCountry();
406 int32_t regionLen = static_cast<int32_t>(uprv_strlen(region):: strlen(region));
407 if (regionLen == 0) {
408 CharString loc = ulocimp_addLikelySubtagsulocimp_addLikelySubtags_77(fLocale.getName(), status);
409 ulocimp_getSubtagsulocimp_getSubtags_77(loc.toStringPiece(), nullptr, nullptr, &fTargetRegion, nullptr, nullptr, status);
410 if (U_FAILURE(status)) {
411 cleanup();
412 return;
413 }
414 } else {
415 fTargetRegion.append(region, regionLen, status);
416 }
417
418 // preload generic names for the default zone
419 TimeZone *tz = TimeZone::createDefault();
420 const char16_t *tzID = ZoneMeta::getCanonicalCLDRID(*tz);
421 if (tzID != nullptr) {
422 loadStrings(UnicodeString(true, tzID, -1));
423 }
424 delete tz;
425}
426
427void
428TZGNCore::cleanup() {
429 delete fLocaleDisplayNames;
430 delete fTimeZoneNames;
431
432 uhash_closeuhash_close_77(fLocationNamesMap);
433 uhash_closeuhash_close_77(fPartialLocationNamesMap);
434}
435
436
437UnicodeString&
438TZGNCore::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
439 name.setToBogus();
440 switch (type) {
2
Control jumps to 'case UTZGNM_LONG:' at line 449
441 case UTZGNM_LOCATION:
442 {
443 const char16_t* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
444 if (tzCanonicalID != nullptr) {
445 getGenericLocationName(UnicodeString(true, tzCanonicalID, -1), name);
446 }
447 }
448 break;
449 case UTZGNM_LONG:
450 case UTZGNM_SHORT:
451 formatGenericNonLocationName(tz, type, date, name);
3
Calling 'TZGNCore::formatGenericNonLocationName'
452 if (name.isEmpty()) {
453 const char16_t* tzCanonicalID = ZoneMeta::getCanonicalCLDRID(tz);
454 if (tzCanonicalID != nullptr) {
455 getGenericLocationName(UnicodeString(true, tzCanonicalID, -1), name);
456 }
457 }
458 break;
459 default:
460 break;
461 }
462 return name;
463}
464
465UnicodeString&
466TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
467 if (tzCanonicalID.isEmpty()) {
468 name.setToBogus();
469 return name;
470 }
471
472 const char16_t *locname = nullptr;
473 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
474 umtx_lockumtx_lock_77(&gLock);
475 {
476 locname = nonConstThis->getGenericLocationName(tzCanonicalID);
477 }
478 umtx_unlockumtx_unlock_77(&gLock);
479
480 if (locname == nullptr) {
481 name.setToBogus();
482 } else {
483 name.setTo(locname, u_strlenu_strlen_77(locname));
484 }
485
486 return name;
487}
488
489/*
490 * This method updates the cache and must be called with a lock
491 */
492const char16_t*
493TZGNCore::getGenericLocationName(const UnicodeString& tzCanonicalID) {
494 U_ASSERT(!tzCanonicalID.isEmpty())(static_cast <bool> (!tzCanonicalID.isEmpty()) ? void (
0) : __assert_fail ("!tzCanonicalID.isEmpty()", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
495 if (tzCanonicalID.length() > ZID_KEY_MAX128) {
496 return nullptr;
497 }
498
499 UErrorCode status = U_ZERO_ERROR;
500 char16_t tzIDKey[ZID_KEY_MAX128 + 1];
501 int32_t tzIDKeyLen = tzCanonicalID.extract(tzIDKey, ZID_KEY_MAX128 + 1, status);
502 U_ASSERT(status == U_ZERO_ERROR)(static_cast <bool> (status == U_ZERO_ERROR) ? void (0)
: __assert_fail ("status == U_ZERO_ERROR", __builtin_FILE ()
, __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
; // already checked length above
503 tzIDKey[tzIDKeyLen] = 0;
504
505 const char16_t* locname = static_cast<const char16_t*>(uhash_getuhash_get_77(fLocationNamesMap, tzIDKey));
506
507 if (locname != nullptr) {
508 // gEmpty indicate the name is not available
509 if (locname == gEmpty) {
510 return nullptr;
511 }
512 return locname;
513 }
514
515 // Construct location name
516 UnicodeString name;
517 UnicodeString usCountryCode;
518 UBool isPrimary = false;
519
520 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode, &isPrimary);
521
522 if (!usCountryCode.isEmpty()) {
523 if (isPrimary) {
524 // If this is the primary zone in the country, use the country name.
525 char countryCode[ULOC_COUNTRY_CAPACITY4];
526 U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY)(static_cast <bool> (usCountryCode.length() < 4) ? void
(0) : __assert_fail ("usCountryCode.length() < 4", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
527 int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INVicu::UnicodeString::kInvariant);
528 countryCode[ccLen] = 0;
529
530 UnicodeString country;
531 fLocaleDisplayNames->regionDisplayName(countryCode, country);
532 fRegionFormat.format(country, name, status);
533 } else {
534 // If this is not the primary zone in the country,
535 // use the exemplar city name.
536
537 // getExemplarLocationName should return non-empty string
538 // if the time zone is associated with a region
539
540 UnicodeString city;
541 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, city);
542 fRegionFormat.format(city, name, status);
543 }
544 if (U_FAILURE(status)) {
545 return nullptr;
546 }
547 }
548
549 locname = name.isEmpty() ? nullptr : fStringPool.get(name, status);
550 if (U_SUCCESS(status)) {
551 // Cache the result
552 const char16_t* cacheID = ZoneMeta::findTimeZoneID(tzCanonicalID);
553 U_ASSERT(cacheID != nullptr)(static_cast <bool> (cacheID != nullptr) ? void (0) : __assert_fail
("cacheID != nullptr", __builtin_FILE (), __builtin_LINE (),
__extension__ __PRETTY_FUNCTION__))
;
554 if (locname == nullptr) {
555 // gEmpty to indicate - no location name available
556 uhash_putuhash_put_77(fLocationNamesMap, (void *)cacheID, (void *)gEmpty, &status);
557 } else {
558 uhash_putuhash_put_77(fLocationNamesMap, (void *)cacheID, (void *)locname, &status);
559 if (U_FAILURE(status)) {
560 locname = nullptr;
561 } else {
562 // put the name info into the trie
563 GNameInfo* nameinfo = static_cast<ZNameInfo*>(uprv_mallocuprv_malloc_77(sizeof(GNameInfo)));
564 if (nameinfo != nullptr) {
565 nameinfo->type = UTZGNM_LOCATION;
566 nameinfo->tzID = cacheID;
567 fGNamesTrie.put(locname, nameinfo, status);
568 }
569 }
570 }
571 }
572
573 return locname;
574}
575
576UnicodeString&
577TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const {
578 U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT)(static_cast <bool> (type == UTZGNM_LONG || type == UTZGNM_SHORT
) ? void (0) : __assert_fail ("type == UTZGNM_LONG || type == UTZGNM_SHORT"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
4
'?' condition is true
579 name.setToBogus();
580
581 const char16_t* uID = ZoneMeta::getCanonicalCLDRID(tz);
582 if (uID == nullptr) {
5
Assuming the condition is false
6
Taking false branch
583 return name;
584 }
585
586 UnicodeString tzID(true, uID, -1);
587
588 // Try to get a name from time zone first
589 UTimeZoneNameType nameType = (type
6.1
'type' is equal to UTZGNM_LONG
== UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC;
7
'?' condition is true
590 fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name);
591
592 if (!name.isEmpty()) {
8
Taking false branch
593 return name;
594 }
595
596 // Try meta zone
597 char16_t mzIDBuf[32];
598 UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf)(int32_t)(sizeof(mzIDBuf)/sizeof((mzIDBuf)[0])));
599 fTimeZoneNames->getMetaZoneID(tzID, date, mzID);
600 if (!mzID.isEmpty()) {
9
Taking true branch
601 UErrorCode status = U_ZERO_ERROR;
602 UBool useStandard = false;
603 int32_t raw, sav;
604 char16_t tmpNameBuf[ZONE_NAME_U16_MAX128];
605
606 tz.getOffset(date, false, raw, sav, status);
607 if (U_FAILURE(status)) {
10
Taking false branch
608 return name;
609 }
610
611 if (sav == 0) {
11
Assuming 'sav' is equal to 0
12
Taking true branch
612 useStandard = true;
613
614 TimeZone *tmptz = tz.clone();
13
'tmptz' initialized here
615 // Check if the zone actually uses daylight saving time around the time
616 BasicTimeZone *btz = nullptr;
617 if (dynamic_cast<OlsonTimeZone *>(tmptz) != nullptr
14
Assuming pointer value is null
15
Taking false branch
618 || dynamic_cast<SimpleTimeZone *>(tmptz) != nullptr
619 || dynamic_cast<RuleBasedTimeZone *>(tmptz) != nullptr
620 || dynamic_cast<VTimeZone *>(tmptz) != nullptr) {
621 btz = (BasicTimeZone*)tmptz;
622 }
623
624 if (btz != nullptr) {
16
Taking false branch
625 TimeZoneTransition before;
626 UBool beforTrs = btz->getPreviousTransition(date, true, before);
627 if (beforTrs
628 && (date - before.getTime() < kDstCheckRange)
629 && before.getFrom()->getDSTSavings() != 0) {
630 useStandard = false;
631 } else {
632 TimeZoneTransition after;
633 UBool afterTrs = btz->getNextTransition(date, false, after);
634 if (afterTrs
635 && (after.getTime() - date < kDstCheckRange)
636 && after.getTo()->getDSTSavings() != 0) {
637 useStandard = false;
638 }
639 }
640 } else {
641 // If not BasicTimeZone... only if the instance is not an ICU's implementation.
642 // We may get a wrong answer in edge case, but it should practically work OK.
643 tmptz->getOffset(date - kDstCheckRange, false, raw, sav, status);
17
Called C++ object pointer is null
644 if (sav != 0) {
645 useStandard = false;
646 } else {
647 tmptz->getOffset(date + kDstCheckRange, false, raw, sav, status);
648 if (sav != 0){
649 useStandard = false;
650 }
651 }
652 if (U_FAILURE(status)) {
653 delete tmptz;
654 return name;
655 }
656 }
657 delete tmptz;
658 }
659 if (useStandard) {
660 UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC)
661 ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD;
662 UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf)(int32_t)(sizeof(tmpNameBuf)/sizeof((tmpNameBuf)[0])));
663 fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName);
664 if (!stdName.isEmpty()) {
665 name.setTo(stdName);
666
667 // TODO: revisit this issue later
668 // In CLDR, a same display name is used for both generic and standard
669 // for some meta zones in some locales. This looks like a data bugs.
670 // For now, we check if the standard name is different from its generic
671 // name below.
672 char16_t genNameBuf[ZONE_NAME_U16_MAX128];
673 UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf)(int32_t)(sizeof(genNameBuf)/sizeof((genNameBuf)[0])));
674 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName);
675 if (stdName.caseCompare(mzGenericName, 0) == 0) {
676 name.setToBogus();
677 }
678 }
679 }
680 if (name.isEmpty()) {
681 // Get a name from meta zone
682 UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf)(int32_t)(sizeof(tmpNameBuf)/sizeof((tmpNameBuf)[0])));
683 fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName);
684 if (!mzName.isEmpty()) {
685 // Check if we need to use a partial location format.
686 // This check is done by comparing offset with the meta zone's
687 // golden zone at the given date.
688 char16_t idBuf[32];
689 UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf)(int32_t)(sizeof(idBuf)/sizeof((idBuf)[0])));
690 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion.data(), goldenID);
691 if (!goldenID.isEmpty() && goldenID != tzID) {
692 TimeZone *goldenZone = TimeZone::createTimeZone(goldenID);
693 int32_t raw1, sav1;
694
695 // Check offset in the golden zone with wall time.
696 // With getOffset(date, false, offsets1),
697 // you may get incorrect results because of time overlap at DST->STD
698 // transition.
699 goldenZone->getOffset(date + raw + sav, true, raw1, sav1, status);
700 delete goldenZone;
701 if (U_SUCCESS(status)) {
702 if (raw != raw1 || sav != sav1) {
703 // Now we need to use a partial location format
704 getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name);
705 } else {
706 name.setTo(mzName);
707 }
708 }
709 } else {
710 name.setTo(mzName);
711 }
712 }
713 }
714 }
715 return name;
716}
717
718UnicodeString&
719TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
720 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName,
721 UnicodeString& name) const {
722 name.setToBogus();
723 if (tzCanonicalID.isEmpty() || mzID.isEmpty() || mzDisplayName.isEmpty()) {
724 return name;
725 }
726
727 const char16_t *uplname = nullptr;
728 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
729 umtx_lockumtx_lock_77(&gLock);
730 {
731 uplname = nonConstThis->getPartialLocationName(tzCanonicalID, mzID, isLong, mzDisplayName);
732 }
733 umtx_unlockumtx_unlock_77(&gLock);
734
735 if (uplname == nullptr) {
736 name.setToBogus();
737 } else {
738 name.setTo(true, uplname, -1);
739 }
740 return name;
741}
742
743/*
744 * This method updates the cache and must be called with a lock
745 */
746const char16_t*
747TZGNCore::getPartialLocationName(const UnicodeString& tzCanonicalID,
748 const UnicodeString& mzID, UBool isLong, const UnicodeString& mzDisplayName) {
749 U_ASSERT(!tzCanonicalID.isEmpty())(static_cast <bool> (!tzCanonicalID.isEmpty()) ? void (
0) : __assert_fail ("!tzCanonicalID.isEmpty()", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
750 U_ASSERT(!mzID.isEmpty())(static_cast <bool> (!mzID.isEmpty()) ? void (0) : __assert_fail
("!mzID.isEmpty()", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
751 U_ASSERT(!mzDisplayName.isEmpty())(static_cast <bool> (!mzDisplayName.isEmpty()) ? void (
0) : __assert_fail ("!mzDisplayName.isEmpty()", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
752
753 PartialLocationKey key;
754 key.tzID = ZoneMeta::findTimeZoneID(tzCanonicalID);
755 key.mzID = ZoneMeta::findMetaZoneID(mzID);
756 key.isLong = isLong;
757 U_ASSERT(key.tzID != nullptr && key.mzID != nullptr)(static_cast <bool> (key.tzID != nullptr && key
.mzID != nullptr) ? void (0) : __assert_fail ("key.tzID != nullptr && key.mzID != nullptr"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
758
759 const char16_t* uplname = static_cast<const char16_t*>(uhash_getuhash_get_77(fPartialLocationNamesMap, &key));
760 if (uplname != nullptr) {
761 return uplname;
762 }
763
764 UnicodeString location;
765 UnicodeString usCountryCode;
766 ZoneMeta::getCanonicalCountry(tzCanonicalID, usCountryCode);
767 if (!usCountryCode.isEmpty()) {
768 char countryCode[ULOC_COUNTRY_CAPACITY4];
769 U_ASSERT(usCountryCode.length() < ULOC_COUNTRY_CAPACITY)(static_cast <bool> (usCountryCode.length() < 4) ? void
(0) : __assert_fail ("usCountryCode.length() < 4", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
770 int32_t ccLen = usCountryCode.extract(0, usCountryCode.length(), countryCode, sizeof(countryCode), US_INVicu::UnicodeString::kInvariant);
771 countryCode[ccLen] = 0;
772
773 UnicodeString regionalGolden;
774 fTimeZoneNames->getReferenceZoneID(mzID, countryCode, regionalGolden);
775 if (tzCanonicalID == regionalGolden) {
776 // Use country name
777 fLocaleDisplayNames->regionDisplayName(countryCode, location);
778 } else {
779 // Otherwise, use exemplar city name
780 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
781 }
782 } else {
783 fTimeZoneNames->getExemplarLocationName(tzCanonicalID, location);
784 if (location.isEmpty()) {
785 // This could happen when the time zone is not associated with a country,
786 // and its ID is not hierarchical, for example, CST6CDT.
787 // We use the canonical ID itself as the location for this case.
788 location.setTo(tzCanonicalID);
789 }
790 }
791
792 UErrorCode status = U_ZERO_ERROR;
793 UnicodeString name;
794 fFallbackFormat.format(location, mzDisplayName, name, status);
795 if (U_FAILURE(status)) {
796 return nullptr;
797 }
798
799 uplname = fStringPool.get(name, status);
800 if (U_SUCCESS(status)) {
801 // Add the name to cache
802 PartialLocationKey* cacheKey = static_cast<PartialLocationKey*>(uprv_mallocuprv_malloc_77(sizeof(PartialLocationKey)));
803 if (cacheKey != nullptr) {
804 cacheKey->tzID = key.tzID;
805 cacheKey->mzID = key.mzID;
806 cacheKey->isLong = key.isLong;
807 uhash_putuhash_put_77(fPartialLocationNamesMap, (void *)cacheKey, (void *)uplname, &status);
808 if (U_FAILURE(status)) {
809 uprv_freeuprv_free_77(cacheKey);
810 } else {
811 // put the name to the local trie as well
812 GNameInfo* nameinfo = static_cast<ZNameInfo*>(uprv_mallocuprv_malloc_77(sizeof(GNameInfo)));
813 if (nameinfo != nullptr) {
814 nameinfo->type = isLong ? UTZGNM_LONG : UTZGNM_SHORT;
815 nameinfo->tzID = key.tzID;
816 fGNamesTrie.put(uplname, nameinfo, status);
817 }
818 }
819 }
820 }
821 return uplname;
822}
823
824/*
825 * This method updates the cache and must be called with a lock,
826 * except initializer.
827 */
828void
829TZGNCore::loadStrings(const UnicodeString& tzCanonicalID) {
830 // load the generic location name
831 getGenericLocationName(tzCanonicalID);
832
833 // partial location names
834 UErrorCode status = U_ZERO_ERROR;
835
836 const UnicodeString *mzID;
837 UnicodeString goldenID;
838 UnicodeString mzGenName;
839 UTimeZoneNameType genNonLocTypes[] = {
840 UTZNM_LONG_GENERIC, UTZNM_SHORT_GENERIC,
841 UTZNM_UNKNOWN /*terminator*/
842 };
843
844 StringEnumeration *mzIDs = fTimeZoneNames->getAvailableMetaZoneIDs(tzCanonicalID, status);
845 while ((mzID = mzIDs->snext(status)) != nullptr) {
846 if (U_FAILURE(status)) {
847 break;
848 }
849 // if this time zone is not the golden zone of the meta zone,
850 // partial location name (such as "PT (Los Angeles)") might be
851 // available.
852 fTimeZoneNames->getReferenceZoneID(*mzID, fTargetRegion.data(), goldenID);
853 if (tzCanonicalID != goldenID) {
854 for (int32_t i = 0; genNonLocTypes[i] != UTZNM_UNKNOWN; i++) {
855 fTimeZoneNames->getMetaZoneDisplayName(*mzID, genNonLocTypes[i], mzGenName);
856 if (!mzGenName.isEmpty()) {
857 // getPartialLocationName formats a name and put it into the trie
858 getPartialLocationName(tzCanonicalID, *mzID,
859 (genNonLocTypes[i] == UTZNM_LONG_GENERIC), mzGenName);
860 }
861 }
862 }
863 }
864 delete mzIDs;
865}
866
867int32_t
868TZGNCore::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
869 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
870 timeType = UTZFMT_TIME_TYPE_UNKNOWN;
871 tzID.setToBogus();
872
873 if (U_FAILURE(status)) {
874 return 0;
875 }
876
877 // Find matches in the TimeZoneNames first
878 TimeZoneNames::MatchInfoCollection *tznamesMatches = findTimeZoneNames(text, start, types, status);
879 if (U_FAILURE(status)) {
880 return 0;
881 }
882
883 int32_t bestMatchLen = 0;
884 UTimeZoneFormatTimeType bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
885 UnicodeString bestMatchTzID;
886 // UBool isLongStandard = false; // workaround - see the comments below
887 UBool isStandard = false; // TODO: Temporary hack (on hack) for short standard name/location name conflict (found in zh_Hant), should be removed after CLDR 21m1 integration
888
889 if (tznamesMatches != nullptr) {
890 UnicodeString mzID;
891 for (int32_t i = 0; i < tznamesMatches->size(); i++) {
892 int32_t len = tznamesMatches->getMatchLengthAt(i);
893 if (len > bestMatchLen) {
894 bestMatchLen = len;
895 if (!tznamesMatches->getTimeZoneIDAt(i, bestMatchTzID)) {
896 // name for a meta zone
897 if (tznamesMatches->getMetaZoneIDAt(i, mzID)) {
898 fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion.data(), bestMatchTzID);
899 }
900 }
901 UTimeZoneNameType nameType = tznamesMatches->getNameTypeAt(i);
902 if (U_FAILURE(status)) {
903 break;
904 }
905 switch (nameType) {
906 case UTZNM_LONG_STANDARD:
907 // isLongStandard = true;
908 case UTZNM_SHORT_STANDARD: // this one is never used for generic, but just in case
909 isStandard = true; // TODO: Remove this later, see the comments above.
910 bestMatchTimeType = UTZFMT_TIME_TYPE_STANDARD;
911 break;
912 case UTZNM_LONG_DAYLIGHT:
913 case UTZNM_SHORT_DAYLIGHT: // this one is never used for generic, but just in case
914 bestMatchTimeType = UTZFMT_TIME_TYPE_DAYLIGHT;
915 break;
916 default:
917 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN;
918 }
919 }
920 }
921 delete tznamesMatches;
922 if (U_FAILURE(status)) {
923 return 0;
924 }
925
926 if (bestMatchLen == (text.length() - start)) {
927 // Full match
928
929 //tzID.setTo(bestMatchTzID);
930 //timeType = bestMatchTimeType;
931 //return bestMatchLen;
932
933 // TODO Some time zone uses a same name for the long standard name
934 // and the location name. When the match is a long standard name,
935 // then we need to check if the name is same with the location name.
936 // This is probably a data error or a design bug.
937/*
938 if (!isLongStandard) {
939 tzID.setTo(bestMatchTzID);
940 timeType = bestMatchTimeType;
941 return bestMatchLen;
942 }
943*/
944 // TODO The deprecation of commonlyUsed flag introduced the name
945 // conflict not only for long standard names, but short standard names too.
946 // These short names (found in zh_Hant) should be gone once we clean
947 // up CLDR time zone display name data. Once the short name conflict
948 // problem (with location name) is resolved, we should change the condition
949 // below back to the original one above. -Yoshito (2011-09-14)
950 if (!isStandard) {
951 tzID.setTo(bestMatchTzID);
952 timeType = bestMatchTimeType;
953 return bestMatchLen;
954 }
955 }
956 }
957
958 // Find matches in the local trie
959 TimeZoneGenericNameMatchInfo *localMatches = findLocal(text, start, types, status);
960 if (U_FAILURE(status)) {
961 return 0;
962 }
963 if (localMatches != nullptr) {
964 for (int32_t i = 0; i < localMatches->size(); i++) {
965 int32_t len = localMatches->getMatchLength(i);
966
967 // TODO See the above TODO. We use len >= bestMatchLen
968 // because of the long standard/location name collision
969 // problem. If it is also a location name, carrying
970 // timeType = UTZFMT_TIME_TYPE_STANDARD will cause a
971 // problem in SimpleDateFormat
972 if (len >= bestMatchLen) {
973 bestMatchLen = localMatches->getMatchLength(i);
974 bestMatchTimeType = UTZFMT_TIME_TYPE_UNKNOWN; // because generic
975 localMatches->getTimeZoneID(i, bestMatchTzID);
976 }
977 }
978 delete localMatches;
979 }
980
981 if (bestMatchLen > 0) {
982 timeType = bestMatchTimeType;
983 tzID.setTo(bestMatchTzID);
984 }
985 return bestMatchLen;
986}
987
988TimeZoneGenericNameMatchInfo*
989TZGNCore::findLocal(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
990 GNameSearchHandler handler(types);
991
992 TZGNCore *nonConstThis = const_cast<TZGNCore *>(this);
993
994 umtx_lockumtx_lock_77(&gLock);
995 {
996 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
997 }
998 umtx_unlockumtx_unlock_77(&gLock);
999
1000 if (U_FAILURE(status)) {
1001 return nullptr;
1002 }
1003
1004 TimeZoneGenericNameMatchInfo *gmatchInfo = nullptr;
1005
1006 int32_t maxLen = 0;
1007 UVector *results = handler.getMatches(maxLen);
1008 if (results != nullptr && ((maxLen == (text.length() - start)) || fGNamesTrieFullyLoaded)) {
1009 // perfect match
1010 gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
1011 if (gmatchInfo == nullptr) {
1012 status = U_MEMORY_ALLOCATION_ERROR;
1013 delete results;
1014 return nullptr;
1015 }
1016 return gmatchInfo;
1017 }
1018
1019 delete results;
1020
1021 // All names are not yet loaded into the local trie.
1022 // Load all available names into the trie. This could be very heavy.
1023 umtx_lockumtx_lock_77(&gLock);
1024 {
1025 if (!fGNamesTrieFullyLoaded) {
1026 StringEnumeration *tzIDs = TimeZone::createTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, nullptr, nullptr, status);
1027 if (U_SUCCESS(status)) {
1028 const UnicodeString *tzID;
1029 while ((tzID = tzIDs->snext(status)) != nullptr) {
1030 if (U_FAILURE(status)) {
1031 break;
1032 }
1033 nonConstThis->loadStrings(*tzID);
1034 }
1035 }
1036 delete tzIDs;
1037
1038 if (U_SUCCESS(status)) {
1039 nonConstThis->fGNamesTrieFullyLoaded = true;
1040 }
1041 }
1042 }
1043 umtx_unlockumtx_unlock_77(&gLock);
1044
1045 if (U_FAILURE(status)) {
1046 return nullptr;
1047 }
1048
1049 umtx_lockumtx_lock_77(&gLock);
1050 {
1051 // now try it again
1052 fGNamesTrie.search(text, start, (TextTrieMapSearchResultHandler *)&handler, status);
1053 }
1054 umtx_unlockumtx_unlock_77(&gLock);
1055
1056 results = handler.getMatches(maxLen);
1057 if (results != nullptr && maxLen > 0) {
1058 gmatchInfo = new TimeZoneGenericNameMatchInfo(results);
1059 if (gmatchInfo == nullptr) {
1060 status = U_MEMORY_ALLOCATION_ERROR;
1061 delete results;
1062 return nullptr;
1063 }
1064 }
1065
1066 return gmatchInfo;
1067}
1068
1069TimeZoneNames::MatchInfoCollection*
1070TZGNCore::findTimeZoneNames(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
1071 // Check if the target name typs is really in the TimeZoneNames
1072 uint32_t nameTypes = 0;
1073 if (types & UTZGNM_LONG) {
1074 nameTypes |= (UTZNM_LONG_GENERIC | UTZNM_LONG_STANDARD);
1075 }
1076 if (types & UTZGNM_SHORT) {
1077 nameTypes |= (UTZNM_SHORT_GENERIC | UTZNM_SHORT_STANDARD);
1078 }
1079
1080 if (types) {
1081 // Find matches in the TimeZoneNames
1082 return fTimeZoneNames->find(text, start, nameTypes, status);
1083 }
1084
1085 return nullptr;
1086}
1087
1088typedef struct TZGNCoreRef {
1089 TZGNCore* obj;
1090 int32_t refCount;
1091 double lastAccess;
1092} TZGNCoreRef;
1093
1094// TZGNCore object cache handling
1095static UMutex gTZGNLock;
1096static UHashtable *gTZGNCoreCache = nullptr;
1097static UBool gTZGNCoreCacheInitialized = false;
1098
1099// Access count - incremented every time up to SWEEP_INTERVAL,
1100// then reset to 0
1101static int32_t gAccessCount = 0;
1102
1103// Interval for calling the cache sweep function - every 100 times
1104#define SWEEP_INTERVAL100 100
1105
1106// Cache expiration in millisecond. When a cached entry is no
1107// longer referenced and exceeding this threshold since last
1108// access time, then the cache entry will be deleted by the sweep
1109// function. For now, 3 minutes.
1110#define CACHE_EXPIRATION180000.0 180000.0
1111
1112U_CDECL_BEGINextern "C" {
1113/**
1114 * Cleanup callback func
1115 */
1116static UBool U_CALLCONV tzgnCore_cleanup()
1117{
1118 if (gTZGNCoreCache != nullptr) {
1119 uhash_closeuhash_close_77(gTZGNCoreCache);
1120 gTZGNCoreCache = nullptr;
1121 }
1122 gTZGNCoreCacheInitialized = false;
1123 return true;
1124}
1125
1126/**
1127 * Deleter for TZGNCoreRef
1128 */
1129static void U_CALLCONV
1130deleteTZGNCoreRef(void *obj) {
1131 icu::TZGNCoreRef *entry = (icu::TZGNCoreRef*)obj;
1132 delete (icu::TZGNCore*) entry->obj;
1133 uprv_freeuprv_free_77(entry);
1134}
1135U_CDECL_END}
1136
1137/**
1138 * Function used for removing unreferrenced cache entries exceeding
1139 * the expiration time. This function must be called with in the mutex
1140 * block.
1141 */
1142static void sweepCache() {
1143 int32_t pos = UHASH_FIRST(-1);
1144 const UHashElement* elem;
1145 double now = static_cast<double>(uprv_getUTCtimeuprv_getUTCtime_77());
1146
1147 while ((elem = uhash_nextElementuhash_nextElement_77(gTZGNCoreCache, &pos)) != nullptr) {
1148 TZGNCoreRef* entry = static_cast<TZGNCoreRef*>(elem->value.pointer);
1149 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION180000.0) {
1150 // delete this entry
1151 uhash_removeElementuhash_removeElement_77(gTZGNCoreCache, elem);
1152 }
1153 }
1154}
1155
1156TimeZoneGenericNames::TimeZoneGenericNames()
1157: fRef(nullptr) {
1158}
1159
1160TimeZoneGenericNames::~TimeZoneGenericNames() {
1161 umtx_lockumtx_lock_77(&gTZGNLock);
1162 {
1163 U_ASSERT(fRef->refCount > 0)(static_cast <bool> (fRef->refCount > 0) ? void (
0) : __assert_fail ("fRef->refCount > 0", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
1164 // Just decrement the reference count
1165 fRef->refCount--;
1166 }
1167 umtx_unlockumtx_unlock_77(&gTZGNLock);
1168}
1169
1170TimeZoneGenericNames*
1171TimeZoneGenericNames::createInstance(const Locale& locale, UErrorCode& status) {
1172 if (U_FAILURE(status)) {
1173 return nullptr;
1174 }
1175 TimeZoneGenericNames* instance = new TimeZoneGenericNames();
1176 if (instance == nullptr) {
1177 status = U_MEMORY_ALLOCATION_ERROR;
1178 return nullptr;
1179 }
1180
1181 TZGNCoreRef *cacheEntry = nullptr;
1182 {
1183 Mutex lock(&gTZGNLock);
1184
1185 if (!gTZGNCoreCacheInitialized) {
1186 // Create empty hashtable
1187 gTZGNCoreCache = uhash_openuhash_open_77(uhash_hashCharsuhash_hashChars_77, uhash_compareCharsuhash_compareChars_77, nullptr, &status);
1188 if (U_SUCCESS(status)) {
1189 uhash_setKeyDeleteruhash_setKeyDeleter_77(gTZGNCoreCache, uprv_freeuprv_free_77);
1190 uhash_setValueDeleteruhash_setValueDeleter_77(gTZGNCoreCache, deleteTZGNCoreRef);
1191 gTZGNCoreCacheInitialized = true;
1192 ucln_i18n_registerCleanupucln_i18n_registerCleanup_77(UCLN_I18N_TIMEZONEGENERICNAMES, tzgnCore_cleanup);
1193 }
1194 }
1195 if (U_FAILURE(status)) {
1196 delete instance;
1197 return nullptr;
1198 }
1199
1200 // Check the cache, if not available, create new one and cache
1201 const char *key = locale.getName();
1202 cacheEntry = static_cast<TZGNCoreRef*>(uhash_getuhash_get_77(gTZGNCoreCache, key));
1203 if (cacheEntry == nullptr) {
1204 TZGNCore *tzgnCore = nullptr;
1205 char *newKey = nullptr;
1206
1207 tzgnCore = new TZGNCore(locale, status);
1208 if (tzgnCore == nullptr) {
1209 status = U_MEMORY_ALLOCATION_ERROR;
1210 }
1211 if (U_SUCCESS(status)) {
1212 newKey = static_cast<char*>(uprv_mallocuprv_malloc_77(uprv_strlen(key):: strlen(key) + 1));
1213 if (newKey == nullptr) {
1214 status = U_MEMORY_ALLOCATION_ERROR;
1215 } else {
1216 uprv_strcpy(newKey, key):: strcpy(newKey, key);
1217 }
1218 }
1219 if (U_SUCCESS(status)) {
1220 cacheEntry = static_cast<TZGNCoreRef*>(uprv_mallocuprv_malloc_77(sizeof(TZGNCoreRef)));
1221 if (cacheEntry == nullptr) {
1222 status = U_MEMORY_ALLOCATION_ERROR;
1223 } else {
1224 cacheEntry->obj = tzgnCore;
1225 cacheEntry->refCount = 1;
1226 cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtimeuprv_getUTCtime_77());
1227
1228 uhash_putuhash_put_77(gTZGNCoreCache, newKey, cacheEntry, &status);
1229 }
1230 }
1231 if (U_FAILURE(status)) {
1232 delete tzgnCore;
1233 if (newKey != nullptr) {
1234 uprv_freeuprv_free_77(newKey);
1235 }
1236 if (cacheEntry != nullptr) {
1237 uprv_freeuprv_free_77(cacheEntry);
1238 }
1239 cacheEntry = nullptr;
1240 }
1241 } else {
1242 // Update the reference count
1243 cacheEntry->refCount++;
1244 cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtimeuprv_getUTCtime_77());
1245 }
1246 gAccessCount++;
1247 if (gAccessCount >= SWEEP_INTERVAL100) {
1248 // sweep
1249 sweepCache();
1250 gAccessCount = 0;
1251 }
1252 } // End of mutex locked block
1253
1254 if (cacheEntry == nullptr) {
1255 delete instance;
1256 return nullptr;
1257 }
1258
1259 instance->fRef = cacheEntry;
1260 return instance;
1261}
1262
1263bool
1264TimeZoneGenericNames::operator==(const TimeZoneGenericNames& other) const {
1265 // Just compare if the other object also use the same
1266 // ref entry
1267 return fRef == other.fRef;
1268}
1269
1270TimeZoneGenericNames*
1271TimeZoneGenericNames::clone() const {
1272 TimeZoneGenericNames* other = new TimeZoneGenericNames();
1273 if (other) {
1274 umtx_lockumtx_lock_77(&gTZGNLock);
1275 {
1276 // Just increments the reference count
1277 fRef->refCount++;
1278 other->fRef = fRef;
1279 }
1280 umtx_unlockumtx_unlock_77(&gTZGNLock);
1281 }
1282 return other;
1283}
1284
1285UnicodeString&
1286TimeZoneGenericNames::getDisplayName(const TimeZone& tz, UTimeZoneGenericNameType type,
1287 UDate date, UnicodeString& name) const {
1288 return fRef->obj->getDisplayName(tz, type, date, name);
1
Calling 'TZGNCore::getDisplayName'
1289}
1290
1291UnicodeString&
1292TimeZoneGenericNames::getGenericLocationName(const UnicodeString& tzCanonicalID, UnicodeString& name) const {
1293 return fRef->obj->getGenericLocationName(tzCanonicalID, name);
1294}
1295
1296int32_t
1297TimeZoneGenericNames::findBestMatch(const UnicodeString& text, int32_t start, uint32_t types,
1298 UnicodeString& tzID, UTimeZoneFormatTimeType& timeType, UErrorCode& status) const {
1299 return fRef->obj->findBestMatch(text, start, types, tzID, timeType, status);
1300}
1301
1302U_NAMESPACE_END}
1303#endif