tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tznames.cpp (16966B)


      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-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;
     33 static UHashtable *gTimeZoneNamesCache = nullptr;
     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()
     60 {
     61    if (gTimeZoneNamesCache != nullptr) {
     62        uhash_close(gTimeZoneNamesCache);
     63        gTimeZoneNamesCache = nullptr;
     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    if (entry->refCount <= 1) {
     76        delete (icu::TimeZoneNamesImpl*) entry->names;
     77        uprv_free(entry);
     78    } else {
     79        entry->refCount--;
     80    }
     81 }
     82 U_CDECL_END
     83 
     84 /**
     85 * Function used for removing unreferrenced cache entries exceeding
     86 * the expiration time. This function must be called with in the mutex
     87 * block.
     88 */
     89 static void sweepCache() {
     90    int32_t pos = UHASH_FIRST;
     91    const UHashElement* elem;
     92    double now = static_cast<double>(uprv_getUTCtime());
     93 
     94    while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos)) != nullptr) {
     95        TimeZoneNamesCacheEntry* entry = static_cast<TimeZoneNamesCacheEntry*>(elem->value.pointer);
     96        if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
     97            // delete this entry
     98            uhash_removeElement(gTimeZoneNamesCache, elem);
     99        }
    100    }
    101 }
    102 
    103 // ---------------------------------------------------
    104 // TimeZoneNamesDelegate
    105 // ---------------------------------------------------
    106 class TimeZoneNamesDelegate : public TimeZoneNames {
    107 public:
    108    TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status);
    109    virtual ~TimeZoneNamesDelegate();
    110 
    111    virtual bool operator==(const TimeZoneNames& other) const override;
    112    virtual bool operator!=(const TimeZoneNames& other) const {return !operator==(other);}
    113    virtual TimeZoneNamesDelegate* clone() const override;
    114 
    115    StringEnumeration* getAvailableMetaZoneIDs(UErrorCode& status) const override;
    116    StringEnumeration* getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const override;
    117    UnicodeString& getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const override;
    118    UnicodeString& getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const override;
    119 
    120    UnicodeString& getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const override;
    121    UnicodeString& getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const override;
    122 
    123    UnicodeString& getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const override;
    124 
    125    void loadAllDisplayNames(UErrorCode& status) override;
    126    void getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const override;
    127 
    128    MatchInfoCollection* find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const override;
    129 private:
    130    TimeZoneNamesDelegate();
    131    TimeZoneNamesCacheEntry*    fTZnamesCacheEntry;
    132 };
    133 
    134 TimeZoneNamesDelegate::TimeZoneNamesDelegate()
    135 : fTZnamesCacheEntry(nullptr) {
    136 }
    137 
    138 TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) {
    139    Mutex lock(&gTimeZoneNamesLock);
    140    if (!gTimeZoneNamesCacheInitialized) {
    141        // Create empty hashtable if it is not already initialized.
    142        gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
    143        if (U_SUCCESS(status)) {
    144            uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free);
    145            uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry);
    146            gTimeZoneNamesCacheInitialized = true;
    147            ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup);
    148        }
    149    }
    150 
    151    if (U_FAILURE(status)) {
    152        return;
    153    }
    154 
    155    // Check the cache, if not available, create new one and cache
    156    TimeZoneNamesCacheEntry *cacheEntry = nullptr;
    157 
    158    const char *key = locale.getName();
    159    cacheEntry = static_cast<TimeZoneNamesCacheEntry*>(uhash_get(gTimeZoneNamesCache, key));
    160    if (cacheEntry == nullptr) {
    161        TimeZoneNames *tznames = nullptr;
    162        char *newKey = nullptr;
    163 
    164        tznames = new TimeZoneNamesImpl(locale, status);
    165        if (tznames == nullptr) {
    166            status = U_MEMORY_ALLOCATION_ERROR;
    167        }
    168        if (U_SUCCESS(status)) {
    169            newKey = static_cast<char*>(uprv_malloc(uprv_strlen(key) + 1));
    170            if (newKey == nullptr) {
    171                status = U_MEMORY_ALLOCATION_ERROR;
    172            } else {
    173                uprv_strcpy(newKey, key);
    174            }
    175        }
    176        if (U_SUCCESS(status)) {
    177            cacheEntry = static_cast<TimeZoneNamesCacheEntry*>(uprv_malloc(sizeof(TimeZoneNamesCacheEntry)));
    178            if (cacheEntry == nullptr) {
    179                status = U_MEMORY_ALLOCATION_ERROR;
    180            } else {
    181                cacheEntry->names = tznames;
    182                // The initial refCount is 2 because the entry is referenced both
    183                // by this TimeZoneDelegate and by the gTimeZoneNamesCache
    184                cacheEntry->refCount = 2;
    185                cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtime());
    186 
    187                uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status);
    188            }
    189        }
    190        if (U_FAILURE(status)) {
    191            delete tznames;
    192            if (newKey != nullptr) {
    193                uprv_free(newKey);
    194            }
    195            if (cacheEntry != nullptr) {
    196                uprv_free(cacheEntry);
    197            }
    198            cacheEntry = nullptr;
    199        }
    200    } else {
    201        // Update the reference count
    202        cacheEntry->refCount++;
    203        cacheEntry->lastAccess = static_cast<double>(uprv_getUTCtime());
    204    }
    205    gAccessCount++;
    206    if (gAccessCount >= SWEEP_INTERVAL) {
    207        // sweep
    208        sweepCache();
    209        gAccessCount = 0;
    210    }
    211    fTZnamesCacheEntry = cacheEntry;
    212 }
    213 
    214 TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
    215    umtx_lock(&gTimeZoneNamesLock);
    216    {
    217        if (fTZnamesCacheEntry) {
    218            if (fTZnamesCacheEntry->refCount <= 1) {
    219                delete fTZnamesCacheEntry->names;
    220                uprv_free(fTZnamesCacheEntry);
    221            } else {
    222                // Just decrement the reference count
    223                fTZnamesCacheEntry->refCount--;
    224            }
    225        }
    226    }
    227    umtx_unlock(&gTimeZoneNamesLock);
    228 }
    229 
    230 bool
    231 TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const {
    232    if (this == &other) {
    233        return true;
    234    }
    235    // Just compare if the other object also use the same
    236    // cache entry
    237    const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other);
    238    if (rhs) {
    239        return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry;
    240    }
    241    return false;
    242 }
    243 
    244 TimeZoneNamesDelegate*
    245 TimeZoneNamesDelegate::clone() const {
    246    TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate();
    247    if (other != nullptr) {
    248        umtx_lock(&gTimeZoneNamesLock);
    249        {
    250            // Just increment the reference count
    251            fTZnamesCacheEntry->refCount++;
    252            other->fTZnamesCacheEntry = fTZnamesCacheEntry;
    253        }
    254        umtx_unlock(&gTimeZoneNamesLock);
    255    }
    256    return other;
    257 }
    258 
    259 StringEnumeration*
    260 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
    261    return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
    262 }
    263 
    264 StringEnumeration*
    265 TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
    266    return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
    267 }
    268 
    269 UnicodeString&
    270 TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
    271    return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
    272 }
    273 
    274 UnicodeString&
    275 TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
    276    return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
    277 }
    278 
    279 UnicodeString&
    280 TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
    281    return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
    282 }
    283 
    284 UnicodeString&
    285 TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
    286    return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
    287 }
    288 
    289 UnicodeString&
    290 TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
    291    return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
    292 }
    293 
    294 void
    295 TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode& status) {
    296    fTZnamesCacheEntry->names->loadAllDisplayNames(status);
    297 }
    298 
    299 void
    300 TimeZoneNamesDelegate::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const {
    301    fTZnamesCacheEntry->names->getDisplayNames(tzID, types, numTypes, date, dest, status);
    302 }
    303 
    304 TimeZoneNames::MatchInfoCollection*
    305 TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
    306    return fTZnamesCacheEntry->names->find(text, start, types, status);
    307 }
    308 
    309 // ---------------------------------------------------
    310 // TimeZoneNames base class
    311 // ---------------------------------------------------
    312 TimeZoneNames::~TimeZoneNames() {
    313 }
    314 
    315 TimeZoneNames*
    316 TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
    317    TimeZoneNames *instance = nullptr;
    318    if (U_SUCCESS(status)) {
    319        instance = new TimeZoneNamesDelegate(locale, status);
    320        if (instance == nullptr && U_SUCCESS(status)) {
    321            status = U_MEMORY_ALLOCATION_ERROR;
    322        }
    323    }
    324    return instance;
    325 }
    326 
    327 TimeZoneNames*
    328 TimeZoneNames::createTZDBInstance(const Locale& locale, UErrorCode& status) {
    329    TimeZoneNames *instance = nullptr;
    330    if (U_SUCCESS(status)) {
    331        instance = new TZDBTimeZoneNames(locale);
    332        if (instance == nullptr && U_SUCCESS(status)) {
    333            status = U_MEMORY_ALLOCATION_ERROR;
    334        }
    335    }
    336    return instance;
    337 }
    338 
    339 UnicodeString&
    340 TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
    341    return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
    342 }
    343 
    344 UnicodeString&
    345 TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
    346    getTimeZoneDisplayName(tzID, type, name);
    347    if (name.isEmpty()) {
    348        char16_t mzIDBuf[32];
    349        UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
    350        getMetaZoneID(tzID, date, mzID);
    351        getMetaZoneDisplayName(mzID, type, name);
    352    }
    353    return name;
    354 }
    355 
    356 // Empty default implementation, to be overridden in tznames_impl.cpp.
    357 void
    358 TimeZoneNames::loadAllDisplayNames(UErrorCode& /*status*/) {
    359 }
    360 
    361 // A default, lightweight implementation of getDisplayNames.
    362 // Overridden in tznames_impl.cpp.
    363 void
    364 TimeZoneNames::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const {
    365    if (U_FAILURE(status)) { return; }
    366    if (tzID.isEmpty()) { return; }
    367    UnicodeString mzID;
    368    for (int i = 0; i < numTypes; i++) {
    369        getTimeZoneDisplayName(tzID, types[i], dest[i]);
    370        if (dest[i].isEmpty()) {
    371            if (mzID.isEmpty()) {
    372                getMetaZoneID(tzID, date, mzID);
    373            }
    374            getMetaZoneDisplayName(mzID, types[i], dest[i]);
    375        }
    376    }
    377 }
    378 
    379 
    380 struct MatchInfo : UMemory {
    381    UTimeZoneNameType nameType;
    382    UnicodeString id;
    383    int32_t matchLength;
    384    UBool isTZID;
    385 
    386    MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
    387        this->nameType = nameType;
    388        this->matchLength = matchLength;
    389        if (tzID != nullptr) {
    390            this->id.setTo(*tzID);
    391            this->isTZID = true;
    392        } else {
    393            this->id.setTo(*mzID);
    394            this->isTZID = false;
    395        }
    396    }
    397 };
    398 
    399 U_CDECL_BEGIN
    400 static void U_CALLCONV
    401 deleteMatchInfo(void *obj) {
    402    delete static_cast<MatchInfo *>(obj);
    403 }
    404 U_CDECL_END
    405 
    406 // ---------------------------------------------------
    407 // MatchInfoCollection class
    408 // ---------------------------------------------------
    409 TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
    410 : fMatches(nullptr) {
    411 }
    412 
    413 TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
    414    delete fMatches;
    415 }
    416 
    417 void
    418 TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
    419            const UnicodeString& tzID, UErrorCode& status) {
    420    if (U_FAILURE(status)) {
    421        return;
    422    }
    423    LocalPointer <MatchInfo> matchInfo(new MatchInfo(nameType, matchLength, &tzID, nullptr), status);
    424    UVector *matchesVec = matches(status);
    425    if (U_FAILURE(status)) {
    426        return;
    427    }
    428    matchesVec->adoptElement(matchInfo.orphan(), status);
    429 }
    430 
    431 void
    432 TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
    433            const UnicodeString& mzID, UErrorCode& status) {
    434    if (U_FAILURE(status)) {
    435        return;
    436    }
    437    LocalPointer<MatchInfo> matchInfo(new MatchInfo(nameType, matchLength, nullptr, &mzID), status);
    438    UVector *matchesVec = matches(status);
    439    if (U_FAILURE(status)) {
    440        return;
    441    }
    442    matchesVec->adoptElement(matchInfo.orphan(), status);
    443 }
    444 
    445 int32_t
    446 TimeZoneNames::MatchInfoCollection::size() const {
    447    if (fMatches == nullptr) {
    448        return 0;
    449    }
    450    return fMatches->size();
    451 }
    452 
    453 UTimeZoneNameType
    454 TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
    455    const MatchInfo* match = static_cast<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 = static_cast<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 = static_cast<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 = static_cast<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 nullptr;
    497    }
    498    if (fMatches != nullptr) {
    499        return fMatches;
    500    }
    501    fMatches = new UVector(deleteMatchInfo, nullptr, status);
    502    if (fMatches == nullptr) {
    503        status = U_MEMORY_ALLOCATION_ERROR;
    504    } else if (U_FAILURE(status)) {
    505        delete fMatches;
    506        fMatches = nullptr;
    507    }
    508    return fMatches;
    509 }
    510 
    511 
    512 U_NAMESPACE_END
    513 #endif