ICU-6444 Modified the code not to depend on gUStringTable. Instead, use the data in resource directly.

X-SVN-Rev: 27554
This commit is contained in:
Yoshito Umaoka 2010-02-12 19:00:53 +00:00
parent 3ea9a0e230
commit d8db690e28
3 changed files with 66 additions and 153 deletions

View File

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 1997-2009, International Business Machines Corporation and *
* Copyright (C) 1997-2010, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
@ -1088,29 +1088,45 @@ TimeZone::getEquivalentID(const UnicodeString& id, int32_t index) {
// ---------------------------------------
UnicodeString&
TimeZone::dereferOlsonLink(const UnicodeString& linkTo, UnicodeString& linkFrom) {
// These two methods are used by ZoneMeta class only.
const UChar*
TimeZone::dereferOlsonLink(const UnicodeString& id) {
const UChar *result = NULL;
UErrorCode ec = U_ZERO_ERROR;
linkFrom.remove();
UResourceBundle *top = ures_openDirect(0, kZONEINFO, &ec);
UResourceBundle *res = getZoneByName(top, linkTo, NULL, ec);
UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &ec);
// resolve zone index by name
UResourceBundle *names = ures_getByKey(rb, kNAMES, NULL, &ec);
int32_t idx = findInStringArray(names, id, ec);
result = ures_getStringByIndex(names, idx, NULL, &ec);
// open the zone bundle by index
ures_getByKey(rb, kZONES, rb, &ec);
ures_getByIndex(rb, idx, rb, &ec);
if (U_SUCCESS(ec)) {
if (ures_getSize(res) == 1) {
int32_t deref = ures_getInt(res, &ec);
UResourceBundle *nres = ures_getByKey(top, kNAMES, NULL, &ec); // dereference Names section
int32_t len;
const UChar* tmp = ures_getStringByIndex(nres, deref, &len, &ec);
if (ures_getSize(rb) == 1) {
// this is a link - dereference the link
int32_t deref = ures_getInt(rb, &ec);
const UChar* tmp = ures_getStringByIndex(names, deref, NULL, &ec);
if (U_SUCCESS(ec)) {
linkFrom.setTo(tmp, len);
result = tmp;
}
ures_close(nres);
} else {
linkFrom.setTo(linkTo);
}
}
ures_close(res);
ures_close(top);
return linkFrom;
ures_close(names);
ures_close(rb);
return result;
}
UResourceBundle*
TimeZone::getIDArray(UErrorCode& status) {
UResourceBundle *rb = ures_openDirect(NULL, kZONEINFO, &status);
ures_getByKey(rb, kNAMES, rb, &status);
return rb;
}
// ---------------------------------------

View File

@ -746,12 +746,14 @@ private:
* Resolve a link in Olson tzdata. When the given id is known and it's not a link,
* the id itself is returned. When the given id is known and it is a link, then
* dereferenced zone id is returned. When the given id is unknown, then it returns
* empty string.
* @param linkTo Input zone id string
* @param linkFrom Receives the dereferenced zone id string
* @return The reference to the result (linkFrom)
* NULL.
* @param id Receives the dereferenced zone id string
* @return the dereferenced zone or NULL
*/
static UnicodeString& dereferOlsonLink(const UnicodeString& linkTo, UnicodeString& linkFrom);
static const UChar* dereferOlsonLink(const UnicodeString& id);
/* Retuns a resource bundle array contains all time zone IDs. This is only used by ZoneMeta class. */
static UResourceBundle* getIDArray(UErrorCode& status);
/**
* Parses the given custom time zone identifier

View File

@ -28,85 +28,9 @@ static UHashtable *gCanonicalMap = NULL;
static UHashtable *gOlsonToMeta = NULL;
static UBool gCanonicalMapInitialized = FALSE;
static UBool gOlsonToMetaInitialized = FALSE;
static UChar **gUStringTable = NULL;
static int32_t gUStringCount = 0;
static int32_t gUStringAlloc = 0;
// Currently (ICU 4.1.3+), gUStringTable only contains strings allocated in the section of
// createCanonicalMap that iterates over the enumerator created with TimeZone::createEnumeration.
// And currently, that allocates a total of 22 strings. So USTRING_ALLOC_START is defined to
// be adequate for that set, and USTRING_ALLOC_INCR is a reasonable expansion increment. In
// future versions of ICU, these numbers may need adjusting to avoid excessive reallocs, or to
// avoid allocating unused memory (but in any case the effects are small).
#define USTRING_ALLOC_START 24
#define USTRING_ALLOC_INCR 12
U_CDECL_BEGIN
// We have switched CanonicalMap to use const UChar* strings for the key and for the id field of
// CanonicalMapEntry; that is because for the most part these now point into UChar strings in the
// shared data file, in order to reduce process-specific dynamically-allocated memory. Consequently,
// there is no longer a deleter for the key field, and the deleter for CanonicalMapEntry
// no longer frees the id field. However, for the few strings that are obtained from the
// TimeZone::createEnumeration() enumerator or from TimeZone::dereferOlsonLink instead of the
// data file, we do need to allocate copies. In order to ensure that these strings are freed by
// zoneMeta_cleanup(), we need to create a little memory manager for them; this is in the form of
// a table that tracks the strings allocated for this purpose. The following three functions
// (along with the gUStringXxxxx statics) are used to allocate and free such strings.
// The following allocs space for a UChar* string of the specified length, puts a pointer to the string
// in gUStringTable, and returns either a pointer to the allocated string space, or NULL for failure.
static UChar * allocUStringInTable(int32_t uStringLen) {
UChar * uStringSpace = NULL;
// initialize the table if necessary
umtx_lock(&gZoneMetaLock);
if (gUStringTable == NULL) {
gUStringTable = (UChar**)uprv_malloc(USTRING_ALLOC_START*sizeof(UChar*));
if (gUStringTable != NULL) {
gUStringAlloc = USTRING_ALLOC_START;
}
}
if (gUStringTable != NULL) {
// expand the table if necessary
if (gUStringCount == gUStringAlloc) {
UChar ** newTable = (UChar**)uprv_realloc(gUStringTable, (gUStringAlloc+USTRING_ALLOC_INCR)*sizeof(UChar*));
if (newTable != NULL) {
gUStringTable = newTable;
gUStringAlloc += USTRING_ALLOC_INCR;
}
}
// add the string if possible
if (gUStringCount < gUStringAlloc) {
uStringSpace = (UChar*)uprv_malloc(uStringLen*sizeof(UChar));
if (uStringSpace != NULL) {
gUStringTable[gUStringCount++] = uStringSpace;
}
}
}
umtx_unlock(&gZoneMetaLock);
return uStringSpace;
}
static void removeLastUStringFromTable(void) {
umtx_lock(&gZoneMetaLock);
if (gUStringCount > 0) {
free(gUStringTable[--gUStringCount]);
}
umtx_unlock(&gZoneMetaLock);
}
static void freeUStringTable(void) {
int32_t uStringCount = gUStringCount;
gUStringCount = 0;
gUStringAlloc = 0;
if (gUStringTable != NULL) {
while (uStringCount > 0) {
free(gUStringTable[--uStringCount]);
}
free(gUStringTable);
gUStringTable = NULL;
}
}
/**
* Cleanup callback func
@ -127,8 +51,6 @@ static UBool U_CALLCONV zoneMeta_cleanup(void)
}
gOlsonToMetaInitialized = FALSE;
freeUStringTable();
return TRUE;
}
@ -172,9 +94,6 @@ U_CDECL_END
U_NAMESPACE_BEGIN
#define ZID_KEY_MAX 128
static const char gZoneStringsTag[] = "zoneStrings";
static const char gUseMetazoneTag[] = "um";
static const char gSupplementalData[] = "supplementalData";
static const char gMapTimezonesTag[] = "mapTimezones";
static const char gZoneFormattingTag[] = "zoneFormatting";
@ -186,9 +105,6 @@ static const char gMultizoneTag[] = "multizone";
static const char gMetaZones[] = "metaZones";
static const char gMetazoneInfo[] = "metazoneInfo";
static const char gWorldChar[] = "001";
#define WORLD_LEN 3
static const UChar gWorld[] = {0x30, 0x30, 0x31, 0x00}; // "001"
static const UChar gDefaultFrom[] = {0x31, 0x39, 0x37, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31,
@ -282,8 +198,7 @@ ZoneMeta::createCanonicalMap(void) {
UResourceBundle *tzitem = NULL;
UResourceBundle *aliases = NULL;
StringEnumeration* tzenum = NULL;
int32_t numZones;
UResourceBundle *idArray = NULL;
canonicalMap = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
if (U_FAILURE(status)) {
@ -382,35 +297,34 @@ ZoneMeta::createCanonicalMap(void) {
// Also, when we update Olson tzdata, new zones may be added.
// This code scans all available zones in zoneinfo.res, and if any of them are
// missing, add them to the map.
tzenum = TimeZone::createEnumeration();
numZones = tzenum->count(status);
idArray = TimeZone::getIDArray(status);
if (U_SUCCESS(status)) {
const UChar *zone;
int32_t numZones = ures_getSize(idArray);
int32_t i;
UnicodeString zoneStr;
for (i = 0; i < numZones; i++) {
const UnicodeString *zone = tzenum->snext(status);
zone = ures_getStringByIndex(idArray, i, NULL, &status);
if (U_FAILURE(status)) {
// We should not get here.
// ignore this
status = U_ZERO_ERROR;
continue;
}
UChar zoneUChars[ZID_KEY_MAX];
int32_t zoneUCharsLen = zone->extract(zoneUChars, ZID_KEY_MAX, status) + 1; // Add one for NUL termination
if (U_FAILURE(status) || status==U_STRING_NOT_TERMINATED_WARNING) {
status = U_ZERO_ERROR;
continue; // zone id is too long to extract
}
CanonicalMapEntry *entry = (CanonicalMapEntry*)uhash_get(canonicalMap, zoneUChars);
CanonicalMapEntry *entry = (CanonicalMapEntry*)uhash_get(canonicalMap, zone);
if (entry) {
// Already included in CLDR data
continue;
}
zoneStr.setTo(zone, -1);
// Not in CLDR data, but it could be new one whose alias is available
// in CLDR.
int32_t nTzdataEquivalent = TimeZone::countEquivalentIDs(*zone);
int32_t nTzdataEquivalent = TimeZone::countEquivalentIDs(zoneStr);
int32_t j;
for (j = 0; j < nTzdataEquivalent; j++) {
UnicodeString alias = TimeZone::getEquivalentID(*zone, j);
if (alias == *zone) {
UnicodeString alias = TimeZone::getEquivalentID(zoneStr, j);
if (alias == zoneStr) {
continue;
}
UChar aliasUChars[ZID_KEY_MAX];
@ -426,33 +340,20 @@ ZoneMeta::createCanonicalMap(void) {
}
// Create a new map entry
CanonicalMapEntry* newEntry = (CanonicalMapEntry*)uprv_malloc(sizeof(CanonicalMapEntry));
int32_t idLen;
if (newEntry == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
goto error_cleanup;
}
if (entry == NULL) {
// Set dereferenced zone ID as the canonical ID
UnicodeString derefZone;
TimeZone::dereferOlsonLink(*zone, derefZone);
if (derefZone.length() == 0) {
// It should never happen.. but just in case
derefZone = *zone;
}
idLen = derefZone.length() + 1;
newEntry->id = allocUStringInTable(idLen);
if (newEntry->id == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
uprv_free(newEntry);
goto error_cleanup;
}
// Copy NULL terminated string
derefZone.extract((UChar*)(newEntry->id), idLen, status);
if (U_FAILURE(status)) {
removeLastUStringFromTable();
uprv_free(newEntry);
goto error_cleanup;
const UChar* derefZone = TimeZone::dereferOlsonLink(zoneStr);
if (derefZone == NULL) {
// it should never happen..
delete newEntry;
continue;
}
newEntry->id = derefZone;
// No territory information available
newEntry->country = NULL;
} else {
@ -461,15 +362,9 @@ ZoneMeta::createCanonicalMap(void) {
newEntry->country = entry->country;
}
// Put this entry in the hashtable
UChar *key = allocUStringInTable(zoneUCharsLen);
if (key == NULL) {
status = U_MEMORY_ALLOCATION_ERROR;
deleteCanonicalMapEntry(newEntry);
goto error_cleanup;
}
u_strncpy(key, zoneUChars, zoneUCharsLen);
uhash_put(canonicalMap, key, newEntry, &status);
// Put this entry in the hashtable.
// key is treated as const, but must be passed as non-const.
uhash_put(canonicalMap, (UChar*)zone, newEntry, &status);
if (U_FAILURE(status)) {
goto error_cleanup;
}
@ -480,7 +375,7 @@ normal_cleanup:
ures_close(aliases);
ures_close(tzitem);
ures_close(zoneFormatting);
delete tzenum;
ures_close(idArray);
return canonicalMap;
error_cleanup: