1 // Copyright (C) 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ******************************************************************************* 5 * Copyright (C) 2011-2015, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 10 #include "unicode/utypes.h" 11 12 #if !UCONFIG_NO_FORMATTING 13 14 #include "unicode/locid.h" 15 #include "unicode/tznames.h" 16 #include "unicode/uenum.h" 17 #include "cmemory.h" 18 #include "cstring.h" 19 #include "mutex.h" 20 #include "putilimp.h" 21 #include "tznames_impl.h" 22 #include "uassert.h" 23 #include "ucln_in.h" 24 #include "uhash.h" 25 #include "umutex.h" 26 #include "uvector.h" 27 28 29 U_NAMESPACE_BEGIN 30 31 // TimeZoneNames object cache handling 32 static UMutex gTimeZoneNamesLock = U_MUTEX_INITIALIZER; 33 static UHashtable *gTimeZoneNamesCache = NULL; 34 static UBool gTimeZoneNamesCacheInitialized = FALSE; 35 36 // Access count - incremented every time up to SWEEP_INTERVAL, 37 // then reset to 0 38 static int32_t gAccessCount = 0; 39 40 // Interval for calling the cache sweep function - every 100 times 41 #define SWEEP_INTERVAL 100 42 43 // Cache expiration in millisecond. When a cached entry is no 44 // longer referenced and exceeding this threshold since last 45 // access time, then the cache entry will be deleted by the sweep 46 // function. For now, 3 minutes. 47 #define CACHE_EXPIRATION 180000.0 48 49 typedef struct TimeZoneNamesCacheEntry { 50 TimeZoneNames* names; 51 int32_t refCount; 52 double lastAccess; 53 } TimeZoneNamesCacheEntry; 54 55 U_CDECL_BEGIN 56 /** 57 * Cleanup callback func 58 */ 59 static UBool U_CALLCONV timeZoneNames_cleanup(void) 60 { 61 if (gTimeZoneNamesCache != NULL) { 62 uhash_close(gTimeZoneNamesCache); 63 gTimeZoneNamesCache = NULL; 64 } 65 gTimeZoneNamesCacheInitialized = FALSE; 66 return TRUE; 67 } 68 69 /** 70 * Deleter for TimeZoneNamesCacheEntry 71 */ 72 static void U_CALLCONV 73 deleteTimeZoneNamesCacheEntry(void *obj) { 74 icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj; 75 delete (icu::TimeZoneNamesImpl*) entry->names; 76 uprv_free(entry); 77 } 78 U_CDECL_END 79 80 /** 81 * Function used for removing unreferrenced cache entries exceeding 82 * the expiration time. This function must be called with in the mutex 83 * block. 84 */ 85 static void sweepCache() { 86 int32_t pos = UHASH_FIRST; 87 const UHashElement* elem; 88 double now = (double)uprv_getUTCtime(); 89 90 while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos))) { 91 TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer; 92 if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) { 93 // delete this entry 94 uhash_removeElement(gTimeZoneNamesCache, elem); 95 } 96 } 97 } 98 99 // --------------------------------------------------- 100 // TimeZoneNamesDelegate 101 // --------------------------------------------------- 102 class TimeZoneNamesDelegate : public TimeZoneNames { 103 public: 104 TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status); 105 virtual ~TimeZoneNamesDelegate(); 106 107 virtual UBool operator==(const TimeZoneNames& other) const; 108 virtual UBool operator!=(const TimeZoneNames& other) const {return !operator==(other);}; 109 virtual TimeZoneNames* clone() const; 110 111 StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const; 112 StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const; 113 UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const; 114 UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const; 115 116 UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const; 117 UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const; 118 119 UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const; 120 121 void loadAllDisplayNames(UErrorCode& status); 122 void getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const; 123 124 MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const; 125 private: 126 TimeZoneNamesDelegate(); 127 TimeZoneNamesCacheEntry* fTZnamesCacheEntry; 128 }; 129 130 TimeZoneNamesDelegate::TimeZoneNamesDelegate() 131 : fTZnamesCacheEntry(0) { 132 } 133 134 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) { 135 Mutex lock(&gTimeZoneNamesLock); 136 if (!gTimeZoneNamesCacheInitialized) { 137 // Create empty hashtable if it is not already initialized. 138 gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); 139 if (U_SUCCESS(status)) { 140 uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free); 141 uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry); 142 gTimeZoneNamesCacheInitialized = TRUE; 143 ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup); 144 } 145 } 146 147 if (U_FAILURE(status)) { 148 return; 149 } 150 151 // Check the cache, if not available, create new one and cache 152 TimeZoneNamesCacheEntry *cacheEntry = NULL; 153 154 const char *key = locale.getName(); 155 cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key); 156 if (cacheEntry == NULL) { 157 TimeZoneNames *tznames = NULL; 158 char *newKey = NULL; 159 160 tznames = new TimeZoneNamesImpl(locale, status); 161 if (tznames == NULL) { 162 status = U_MEMORY_ALLOCATION_ERROR; 163 } 164 if (U_SUCCESS(status)) { 165 newKey = (char *)uprv_malloc(uprv_strlen(key) + 1); 166 if (newKey == NULL) { 167 status = U_MEMORY_ALLOCATION_ERROR; 168 } else { 169 uprv_strcpy(newKey, key); 170 } 171 } 172 if (U_SUCCESS(status)) { 173 cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry)); 174 if (cacheEntry == NULL) { 175 status = U_MEMORY_ALLOCATION_ERROR; 176 } else { 177 cacheEntry->names = tznames; 178 cacheEntry->refCount = 1; 179 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 180 181 uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status); 182 } 183 } 184 if (U_FAILURE(status)) { 185 if (tznames != NULL) { 186 delete tznames; 187 } 188 if (newKey != NULL) { 189 uprv_free(newKey); 190 } 191 if (cacheEntry != NULL) { 192 uprv_free(cacheEntry); 193 } 194 cacheEntry = NULL; 195 } 196 } else { 197 // Update the reference count 198 cacheEntry->refCount++; 199 cacheEntry->lastAccess = (double)uprv_getUTCtime(); 200 } 201 gAccessCount++; 202 if (gAccessCount >= SWEEP_INTERVAL) { 203 // sweep 204 sweepCache(); 205 gAccessCount = 0; 206 } 207 fTZnamesCacheEntry = cacheEntry; 208 } 209 210 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() { 211 umtx_lock(&gTimeZoneNamesLock); 212 { 213 if (fTZnamesCacheEntry) { 214 U_ASSERT(fTZnamesCacheEntry->refCount > 0); 215 // Just decrement the reference count 216 fTZnamesCacheEntry->refCount--; 217 } 218 } 219 umtx_unlock(&gTimeZoneNamesLock); 220 } 221 222 UBool 223 TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const { 224 if (this == &other) { 225 return TRUE; 226 } 227 // Just compare if the other object also use the same 228 // cache entry 229 const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other); 230 if (rhs) { 231 return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry; 232 } 233 return FALSE; 234 } 235 236 TimeZoneNames* 237 TimeZoneNamesDelegate::clone() const { 238 TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate(); 239 if (other != NULL) { 240 umtx_lock(&gTimeZoneNamesLock); 241 { 242 // Just increment the reference count 243 fTZnamesCacheEntry->refCount++; 244 other->fTZnamesCacheEntry = fTZnamesCacheEntry; 245 } 246 umtx_unlock(&gTimeZoneNamesLock); 247 } 248 return other; 249 } 250 251 StringEnumeration* 252 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const { 253 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status); 254 } 255 256 StringEnumeration* 257 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const { 258 return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status); 259 } 260 261 UnicodeString& 262 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const { 263 return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID); 264 } 265 266 UnicodeString& 267 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const { 268 return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID); 269 } 270 271 UnicodeString& 272 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const { 273 return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name); 274 } 275 276 UnicodeString& 277 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const { 278 return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name); 279 } 280 281 UnicodeString& 282 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 283 return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name); 284 } 285 286 void 287 TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode& status) { 288 fTZnamesCacheEntry->names->loadAllDisplayNames(status); 289 } 290 291 void 292 TimeZoneNamesDelegate::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const { 293 fTZnamesCacheEntry->names->getDisplayNames(tzID, types, numTypes, date, dest, status); 294 } 295 296 TimeZoneNames::MatchInfoCollection* 297 TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const { 298 return fTZnamesCacheEntry->names->find(text, start, types, status); 299 } 300 301 // --------------------------------------------------- 302 // TimeZoneNames base class 303 // --------------------------------------------------- 304 TimeZoneNames::~TimeZoneNames() { 305 } 306 307 TimeZoneNames* 308 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) { 309 TimeZoneNames *instance = NULL; 310 if (U_SUCCESS(status)) { 311 instance = new TimeZoneNamesDelegate(locale, status); 312 if (instance == NULL && U_SUCCESS(status)) { 313 status = U_MEMORY_ALLOCATION_ERROR; 314 } 315 } 316 return instance; 317 } 318 319 TimeZoneNames* 320 TimeZoneNames::createTZDBInstance(const Locale& locale, UErrorCode& status) { 321 TimeZoneNames *instance = NULL; 322 if (U_SUCCESS(status)) { 323 instance = new TZDBTimeZoneNames(locale); 324 if (instance == NULL && U_SUCCESS(status)) { 325 status = U_MEMORY_ALLOCATION_ERROR; 326 } 327 } 328 return instance; 329 } 330 331 UnicodeString& 332 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const { 333 return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name); 334 } 335 336 UnicodeString& 337 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const { 338 getTimeZoneDisplayName(tzID, type, name); 339 if (name.isEmpty()) { 340 UChar mzIDBuf[32]; 341 UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf)); 342 getMetaZoneID(tzID, date, mzID); 343 getMetaZoneDisplayName(mzID, type, name); 344 } 345 return name; 346 } 347 348 // Empty default implementation, to be overriden in tznames_impl.cpp. 349 void 350 TimeZoneNames::loadAllDisplayNames(UErrorCode& /*status*/) { 351 } 352 353 // A default, lightweight implementation of getDisplayNames. 354 // Overridden in tznames_impl.cpp. 355 void 356 TimeZoneNames::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const { 357 if (U_FAILURE(status)) { return; } 358 if (tzID.isEmpty()) { return; } 359 UnicodeString mzID; 360 for (int i = 0; i < numTypes; i++) { 361 getTimeZoneDisplayName(tzID, types[i], dest[i]); 362 if (dest[i].isEmpty()) { 363 if (mzID.isEmpty()) { 364 getMetaZoneID(tzID, date, mzID); 365 } 366 getMetaZoneDisplayName(mzID, types[i], dest[i]); 367 } 368 } 369 } 370 371 372 struct MatchInfo : UMemory { 373 UTimeZoneNameType nameType; 374 UnicodeString id; 375 int32_t matchLength; 376 UBool isTZID; 377 378 MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) { 379 this->nameType = nameType; 380 this->matchLength = matchLength; 381 if (tzID != NULL) { 382 this->id.setTo(*tzID); 383 this->isTZID = TRUE; 384 } else { 385 this->id.setTo(*mzID); 386 this->isTZID = FALSE; 387 } 388 } 389 }; 390 391 U_CDECL_BEGIN 392 static void U_CALLCONV 393 deleteMatchInfo(void *obj) { 394 delete static_cast<MatchInfo *>(obj); 395 } 396 U_CDECL_END 397 398 // --------------------------------------------------- 399 // MatchInfoCollection class 400 // --------------------------------------------------- 401 TimeZoneNames::MatchInfoCollection::MatchInfoCollection() 402 : fMatches(NULL) { 403 } 404 405 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() { 406 if (fMatches != NULL) { 407 delete fMatches; 408 } 409 } 410 411 void 412 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength, 413 const UnicodeString& tzID, UErrorCode& status) { 414 if (U_FAILURE(status)) { 415 return; 416 } 417 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL); 418 if (matchInfo == NULL) { 419 status = U_MEMORY_ALLOCATION_ERROR; 420 return; 421 } 422 matches(status)->addElement(matchInfo, status); 423 if (U_FAILURE(status)) { 424 delete matchInfo; 425 } 426 } 427 428 void 429 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength, 430 const UnicodeString& mzID, UErrorCode& status) { 431 if (U_FAILURE(status)) { 432 return; 433 } 434 MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID); 435 if (matchInfo == NULL) { 436 status = U_MEMORY_ALLOCATION_ERROR; 437 return; 438 } 439 matches(status)->addElement(matchInfo, status); 440 if (U_FAILURE(status)) { 441 delete matchInfo; 442 } 443 } 444 445 int32_t 446 TimeZoneNames::MatchInfoCollection::size() const { 447 if (fMatches == NULL) { 448 return 0; 449 } 450 return fMatches->size(); 451 } 452 453 UTimeZoneNameType 454 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const { 455 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 456 if (match) { 457 return match->nameType; 458 } 459 return UTZNM_UNKNOWN; 460 } 461 462 int32_t 463 TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const { 464 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 465 if (match) { 466 return match->matchLength; 467 } 468 return 0; 469 } 470 471 UBool 472 TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const { 473 tzID.remove(); 474 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 475 if (match && match->isTZID) { 476 tzID.setTo(match->id); 477 return TRUE; 478 } 479 return FALSE; 480 } 481 482 UBool 483 TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const { 484 mzID.remove(); 485 const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx); 486 if (match && !match->isTZID) { 487 mzID.setTo(match->id); 488 return TRUE; 489 } 490 return FALSE; 491 } 492 493 UVector* 494 TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) { 495 if (U_FAILURE(status)) { 496 return NULL; 497 } 498 if (fMatches != NULL) { 499 return fMatches; 500 } 501 fMatches = new UVector(deleteMatchInfo, NULL, status); 502 if (fMatches == NULL) { 503 status = U_MEMORY_ALLOCATION_ERROR; 504 } else if (U_FAILURE(status)) { 505 delete fMatches; 506 fMatches = NULL; 507 } 508 return fMatches; 509 } 510 511 512 U_NAMESPACE_END 513 #endif 514