Bug Summary

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