Coverage Report

Created: 2023-02-22 06:51

/src/icu/source/i18n/tznames.cpp
Line
Count
Source (jump to first uncovered line)
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 = 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
0
#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
0
#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
0
{
61
0
    if (gTimeZoneNamesCache != NULL) {
62
0
        uhash_close(gTimeZoneNamesCache);
63
0
        gTimeZoneNamesCache = NULL;
64
0
    }
65
0
    gTimeZoneNamesCacheInitialized = FALSE;
66
0
    return TRUE;
67
0
}
68
69
/**
70
 * Deleter for TimeZoneNamesCacheEntry
71
 */
72
static void U_CALLCONV
73
0
deleteTimeZoneNamesCacheEntry(void *obj) {
74
0
    icu::TimeZoneNamesCacheEntry *entry = (icu::TimeZoneNamesCacheEntry*)obj;
75
0
    delete (icu::TimeZoneNamesImpl*) entry->names;
76
0
    uprv_free(entry);
77
0
}
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
0
static void sweepCache() {
86
0
    int32_t pos = UHASH_FIRST;
87
0
    const UHashElement* elem;
88
0
    double now = (double)uprv_getUTCtime();
89
90
0
    while ((elem = uhash_nextElement(gTimeZoneNamesCache, &pos)) != 0) {
91
0
        TimeZoneNamesCacheEntry *entry = (TimeZoneNamesCacheEntry *)elem->value.pointer;
92
0
        if (entry->refCount <= 0 && (now - entry->lastAccess) > CACHE_EXPIRATION) {
93
            // delete this entry
94
0
            uhash_removeElement(gTimeZoneNamesCache, elem);
95
0
        }
96
0
    }
97
0
}
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 bool operator==(const TimeZoneNames& other) const;
108
0
    virtual bool operator!=(const TimeZoneNames& other) const {return !operator==(other);}
109
    virtual TimeZoneNamesDelegate* 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
0
: fTZnamesCacheEntry(0) {
132
0
}
133
134
0
TimeZoneNamesDelegate::TimeZoneNamesDelegate(const Locale& locale, UErrorCode& status) {
135
0
    Mutex lock(&gTimeZoneNamesLock);
136
0
    if (!gTimeZoneNamesCacheInitialized) {
137
        // Create empty hashtable if it is not already initialized.
138
0
        gTimeZoneNamesCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
139
0
        if (U_SUCCESS(status)) {
140
0
            uhash_setKeyDeleter(gTimeZoneNamesCache, uprv_free);
141
0
            uhash_setValueDeleter(gTimeZoneNamesCache, deleteTimeZoneNamesCacheEntry);
142
0
            gTimeZoneNamesCacheInitialized = TRUE;
143
0
            ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONENAMES, timeZoneNames_cleanup);
144
0
        }
145
0
    }
146
147
0
    if (U_FAILURE(status)) {
148
0
        return;
149
0
    }
150
151
    // Check the cache, if not available, create new one and cache
152
0
    TimeZoneNamesCacheEntry *cacheEntry = NULL;
153
154
0
    const char *key = locale.getName();
155
0
    cacheEntry = (TimeZoneNamesCacheEntry *)uhash_get(gTimeZoneNamesCache, key);
156
0
    if (cacheEntry == NULL) {
157
0
        TimeZoneNames *tznames = NULL;
158
0
        char *newKey = NULL;
159
160
0
        tznames = new TimeZoneNamesImpl(locale, status);
161
0
        if (tznames == NULL) {
162
0
            status = U_MEMORY_ALLOCATION_ERROR;
163
0
        }
164
0
        if (U_SUCCESS(status)) {
165
0
            newKey = (char *)uprv_malloc(uprv_strlen(key) + 1);
166
0
            if (newKey == NULL) {
167
0
                status = U_MEMORY_ALLOCATION_ERROR;
168
0
            } else {
169
0
                uprv_strcpy(newKey, key);
170
0
            }
171
0
        }
172
0
        if (U_SUCCESS(status)) {
173
0
            cacheEntry = (TimeZoneNamesCacheEntry *)uprv_malloc(sizeof(TimeZoneNamesCacheEntry));
174
0
            if (cacheEntry == NULL) {
175
0
                status = U_MEMORY_ALLOCATION_ERROR;
176
0
            } else {
177
0
                cacheEntry->names = tznames;
178
0
                cacheEntry->refCount = 1;
179
0
                cacheEntry->lastAccess = (double)uprv_getUTCtime();
180
181
0
                uhash_put(gTimeZoneNamesCache, newKey, cacheEntry, &status);
182
0
            }
183
0
        }
184
0
        if (U_FAILURE(status)) {
185
0
            if (tznames != NULL) {
186
0
                delete tznames;
187
0
            }
188
0
            if (newKey != NULL) {
189
0
                uprv_free(newKey);
190
0
            }
191
0
            if (cacheEntry != NULL) {
192
0
                uprv_free(cacheEntry);
193
0
            }
194
0
            cacheEntry = NULL;
195
0
        }
196
0
    } else {
197
        // Update the reference count
198
0
        cacheEntry->refCount++;
199
0
        cacheEntry->lastAccess = (double)uprv_getUTCtime();
200
0
    }
201
0
    gAccessCount++;
202
0
    if (gAccessCount >= SWEEP_INTERVAL) {
203
        // sweep
204
0
        sweepCache();
205
0
        gAccessCount = 0;
206
0
    }
207
0
    fTZnamesCacheEntry = cacheEntry;
208
0
}
209
210
0
TimeZoneNamesDelegate::~TimeZoneNamesDelegate() {
211
0
    umtx_lock(&gTimeZoneNamesLock);
212
0
    {
213
0
        if (fTZnamesCacheEntry) {
214
0
            U_ASSERT(fTZnamesCacheEntry->refCount > 0);
215
            // Just decrement the reference count
216
0
            fTZnamesCacheEntry->refCount--;
217
0
        }
218
0
    }
219
0
    umtx_unlock(&gTimeZoneNamesLock);
220
0
}
221
222
bool
223
0
TimeZoneNamesDelegate::operator==(const TimeZoneNames& other) const {
224
0
    if (this == &other) {
225
0
        return TRUE;
226
0
    }
227
    // Just compare if the other object also use the same
228
    // cache entry
229
0
    const TimeZoneNamesDelegate* rhs = dynamic_cast<const TimeZoneNamesDelegate*>(&other);
230
0
    if (rhs) {
231
0
        return fTZnamesCacheEntry == rhs->fTZnamesCacheEntry;
232
0
    }
233
0
    return FALSE;
234
0
}
235
236
TimeZoneNamesDelegate*
237
0
TimeZoneNamesDelegate::clone() const {
238
0
    TimeZoneNamesDelegate* other = new TimeZoneNamesDelegate();
239
0
    if (other != NULL) {
240
0
        umtx_lock(&gTimeZoneNamesLock);
241
0
        {
242
            // Just increment the reference count
243
0
            fTZnamesCacheEntry->refCount++;
244
0
            other->fTZnamesCacheEntry = fTZnamesCacheEntry;
245
0
        }
246
0
        umtx_unlock(&gTimeZoneNamesLock);
247
0
    }
248
0
    return other;
249
0
}
250
251
StringEnumeration*
252
0
TimeZoneNamesDelegate::getAvailableMetaZoneIDs(UErrorCode& status) const {
253
0
    return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(status);
254
0
}
255
256
StringEnumeration*
257
0
TimeZoneNamesDelegate::getAvailableMetaZoneIDs(const UnicodeString& tzID, UErrorCode& status) const {
258
0
    return fTZnamesCacheEntry->names->getAvailableMetaZoneIDs(tzID, status);
259
0
}
260
261
UnicodeString&
262
0
TimeZoneNamesDelegate::getMetaZoneID(const UnicodeString& tzID, UDate date, UnicodeString& mzID) const {
263
0
    return fTZnamesCacheEntry->names->getMetaZoneID(tzID, date, mzID);
264
0
}
265
266
UnicodeString&
267
0
TimeZoneNamesDelegate::getReferenceZoneID(const UnicodeString& mzID, const char* region, UnicodeString& tzID) const {
268
0
    return fTZnamesCacheEntry->names->getReferenceZoneID(mzID, region, tzID);
269
0
}
270
271
UnicodeString&
272
0
TimeZoneNamesDelegate::getMetaZoneDisplayName(const UnicodeString& mzID, UTimeZoneNameType type, UnicodeString& name) const {
273
0
    return fTZnamesCacheEntry->names->getMetaZoneDisplayName(mzID, type, name);
274
0
}
275
276
UnicodeString&
277
0
TimeZoneNamesDelegate::getTimeZoneDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UnicodeString& name) const {
278
0
    return fTZnamesCacheEntry->names->getTimeZoneDisplayName(tzID, type, name);
279
0
}
280
281
UnicodeString&
282
0
TimeZoneNamesDelegate::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
283
0
    return fTZnamesCacheEntry->names->getExemplarLocationName(tzID, name);
284
0
}
285
286
void
287
0
TimeZoneNamesDelegate::loadAllDisplayNames(UErrorCode& status) {
288
0
    fTZnamesCacheEntry->names->loadAllDisplayNames(status);
289
0
}
290
291
void
292
0
TimeZoneNamesDelegate::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const {
293
0
    fTZnamesCacheEntry->names->getDisplayNames(tzID, types, numTypes, date, dest, status);
294
0
}
295
296
TimeZoneNames::MatchInfoCollection*
297
0
TimeZoneNamesDelegate::find(const UnicodeString& text, int32_t start, uint32_t types, UErrorCode& status) const {
298
0
    return fTZnamesCacheEntry->names->find(text, start, types, status);
299
0
}
300
301
// ---------------------------------------------------
302
// TimeZoneNames base class
303
// ---------------------------------------------------
304
0
TimeZoneNames::~TimeZoneNames() {
305
0
}
306
307
TimeZoneNames*
308
0
TimeZoneNames::createInstance(const Locale& locale, UErrorCode& status) {
309
0
    TimeZoneNames *instance = NULL;
310
0
    if (U_SUCCESS(status)) {
311
0
        instance = new TimeZoneNamesDelegate(locale, status);
312
0
        if (instance == NULL && U_SUCCESS(status)) {
313
0
            status = U_MEMORY_ALLOCATION_ERROR;
314
0
        }
315
0
    }
316
0
    return instance;
317
0
}
318
319
TimeZoneNames*
320
0
TimeZoneNames::createTZDBInstance(const Locale& locale, UErrorCode& status) {
321
0
    TimeZoneNames *instance = NULL;
322
0
    if (U_SUCCESS(status)) {
323
0
        instance = new TZDBTimeZoneNames(locale);
324
0
        if (instance == NULL && U_SUCCESS(status)) {
325
0
            status = U_MEMORY_ALLOCATION_ERROR;
326
0
        }
327
0
    }
328
0
    return instance;
329
0
}
330
331
UnicodeString&
332
0
TimeZoneNames::getExemplarLocationName(const UnicodeString& tzID, UnicodeString& name) const {
333
0
    return TimeZoneNamesImpl::getDefaultExemplarLocationName(tzID, name);
334
0
}
335
336
UnicodeString&
337
0
TimeZoneNames::getDisplayName(const UnicodeString& tzID, UTimeZoneNameType type, UDate date, UnicodeString& name) const {
338
0
    getTimeZoneDisplayName(tzID, type, name);
339
0
    if (name.isEmpty()) {
340
0
        UChar mzIDBuf[32];
341
0
        UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf));
342
0
        getMetaZoneID(tzID, date, mzID);
343
0
        getMetaZoneDisplayName(mzID, type, name);
344
0
    }
345
0
    return name;
346
0
}
347
348
// Empty default implementation, to be overridden in tznames_impl.cpp.
349
void
350
0
TimeZoneNames::loadAllDisplayNames(UErrorCode& /*status*/) {
351
0
}
352
353
// A default, lightweight implementation of getDisplayNames.
354
// Overridden in tznames_impl.cpp.
355
void
356
0
TimeZoneNames::getDisplayNames(const UnicodeString& tzID, const UTimeZoneNameType types[], int32_t numTypes, UDate date, UnicodeString dest[], UErrorCode& status) const {
357
0
    if (U_FAILURE(status)) { return; }
358
0
    if (tzID.isEmpty()) { return; }
359
0
    UnicodeString mzID;
360
0
    for (int i = 0; i < numTypes; i++) {
361
0
        getTimeZoneDisplayName(tzID, types[i], dest[i]);
362
0
        if (dest[i].isEmpty()) {
363
0
            if (mzID.isEmpty()) {
364
0
                getMetaZoneID(tzID, date, mzID);
365
0
            }
366
0
            getMetaZoneDisplayName(mzID, types[i], dest[i]);
367
0
        }
368
0
    }
369
0
}
370
371
372
struct MatchInfo : UMemory {
373
    UTimeZoneNameType nameType;
374
    UnicodeString id;
375
    int32_t matchLength;
376
    UBool isTZID;
377
378
0
    MatchInfo(UTimeZoneNameType nameType, int32_t matchLength, const UnicodeString* tzID, const UnicodeString* mzID) {
379
0
        this->nameType = nameType;
380
0
        this->matchLength = matchLength;
381
0
        if (tzID != NULL) {
382
0
            this->id.setTo(*tzID);
383
0
            this->isTZID = TRUE;
384
0
        } else {
385
0
            this->id.setTo(*mzID);
386
0
            this->isTZID = FALSE;
387
0
        }
388
0
    }
389
};
390
391
U_CDECL_BEGIN
392
static void U_CALLCONV
393
0
deleteMatchInfo(void *obj) {
394
0
    delete static_cast<MatchInfo *>(obj);
395
0
}
396
U_CDECL_END
397
398
// ---------------------------------------------------
399
// MatchInfoCollection class
400
// ---------------------------------------------------
401
TimeZoneNames::MatchInfoCollection::MatchInfoCollection()
402
0
: fMatches(NULL) {
403
0
}
404
405
0
TimeZoneNames::MatchInfoCollection::~MatchInfoCollection() {
406
0
    if (fMatches != NULL) {
407
0
        delete fMatches;
408
0
    }
409
0
}
410
411
void
412
TimeZoneNames::MatchInfoCollection::addZone(UTimeZoneNameType nameType, int32_t matchLength,
413
0
            const UnicodeString& tzID, UErrorCode& status) {
414
0
    if (U_FAILURE(status)) {
415
0
        return;
416
0
    }
417
0
    MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, &tzID, NULL);
418
0
    if (matchInfo == NULL) {
419
0
        status = U_MEMORY_ALLOCATION_ERROR;
420
0
        return;
421
0
    }
422
0
    matches(status)->addElementX(matchInfo, status);
423
0
    if (U_FAILURE(status)) {
424
0
        delete matchInfo;
425
0
    }
426
0
}
427
428
void
429
TimeZoneNames::MatchInfoCollection::addMetaZone(UTimeZoneNameType nameType, int32_t matchLength,
430
0
            const UnicodeString& mzID, UErrorCode& status) {
431
0
    if (U_FAILURE(status)) {
432
0
        return;
433
0
    }
434
0
    MatchInfo* matchInfo = new MatchInfo(nameType, matchLength, NULL, &mzID);
435
0
    if (matchInfo == NULL) {
436
0
        status = U_MEMORY_ALLOCATION_ERROR;
437
0
        return;
438
0
    }
439
0
    matches(status)->addElementX(matchInfo, status);
440
0
    if (U_FAILURE(status)) {
441
0
        delete matchInfo;
442
0
    }
443
0
}
444
445
int32_t
446
0
TimeZoneNames::MatchInfoCollection::size() const {
447
0
    if (fMatches == NULL) {
448
0
        return 0;
449
0
    }
450
0
    return fMatches->size();
451
0
}
452
453
UTimeZoneNameType
454
0
TimeZoneNames::MatchInfoCollection::getNameTypeAt(int32_t idx) const {
455
0
    const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
456
0
    if (match) {
457
0
        return match->nameType;
458
0
    }
459
0
    return UTZNM_UNKNOWN;
460
0
}
461
462
int32_t
463
0
TimeZoneNames::MatchInfoCollection::getMatchLengthAt(int32_t idx) const {
464
0
    const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
465
0
    if (match) {
466
0
        return match->matchLength;
467
0
    }
468
0
    return 0;
469
0
}
470
471
UBool
472
0
TimeZoneNames::MatchInfoCollection::getTimeZoneIDAt(int32_t idx, UnicodeString& tzID) const {
473
0
    tzID.remove();
474
0
    const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
475
0
    if (match && match->isTZID) {
476
0
        tzID.setTo(match->id);
477
0
        return TRUE;
478
0
    }
479
0
    return FALSE;
480
0
}
481
482
UBool
483
0
TimeZoneNames::MatchInfoCollection::getMetaZoneIDAt(int32_t idx, UnicodeString& mzID) const {
484
0
    mzID.remove();
485
0
    const MatchInfo* match = (const MatchInfo*)fMatches->elementAt(idx);
486
0
    if (match && !match->isTZID) {
487
0
        mzID.setTo(match->id);
488
0
        return TRUE;
489
0
    }
490
0
    return FALSE;
491
0
}
492
493
UVector*
494
0
TimeZoneNames::MatchInfoCollection::matches(UErrorCode& status) {
495
0
    if (U_FAILURE(status)) {
496
0
        return NULL;
497
0
    }
498
0
    if (fMatches != NULL) {
499
0
        return fMatches;
500
0
    }
501
0
    fMatches = new UVector(deleteMatchInfo, NULL, status);
502
0
    if (fMatches == NULL) {
503
0
        status = U_MEMORY_ALLOCATION_ERROR;
504
0
    } else if (U_FAILURE(status)) {
505
0
        delete fMatches;
506
0
        fMatches = NULL;
507
0
    }
508
0
    return fMatches;
509
0
}
510
511
512
U_NAMESPACE_END
513
#endif