+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
******************************************************************************
*
-* Copyright (C) 1999-2012, International Business Machines
+* Copyright (C) 1999-2016, International Business Machines
* Corporation and others. All Rights Reserved.
*
******************************************************************************
* file name: udata.cpp
-* encoding: US-ASCII
+* encoding: UTF-8
* tab size: 8 (not used)
* indentation:4
*
#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
+#include "mutex.h"
#include "putilimp.h"
+#include "uassert.h"
#include "ucln_cmn.h"
#include "ucmndata.h"
#include "udatamem.h"
/* If you are excruciatingly bored turn this on .. */
/* #define UDATA_DEBUG 1 */
+/* For debugging use of timezone data in a separate file */
+/* #define UDATA_TZFILES_DEBUG 1 */
-#if defined(UDATA_DEBUG)
+#if defined(UDATA_DEBUG) || defined(UDATA_TZFILES_DEBUG)
# include <stdio.h>
#endif
-#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
-
U_NAMESPACE_USE
/*
* Forward declarations
*/
-static UDataMemory *udata_findCachedData(const char *path);
+static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err);
/***********************************************************************
*
* that they really need, reducing the size of binaries that take advantage
* of this.
*/
-static UDataMemory *gCommonICUDataArray[10] = { NULL };
+static UDataMemory *gCommonICUDataArray[10] = { NULL }; // Access protected by icu global mutex.
-static UBool gHaveTriedToLoadCommonData = FALSE; /* See extendICUData(). */
+static u_atomic_int32_t gHaveTriedToLoadCommonData = ATOMIC_INT32_T_INITIALIZER(0); // See extendICUData().
static UHashtable *gCommonDataCache = NULL; /* Global hash table of opened ICU data files. */
+static icu::UInitOnce gCommonDataCacheInitOnce = U_INITONCE_INITIALIZER;
-static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS;
+#if U_PLATFORM_HAS_WINUWP_API == 0
+static UDataFileAccess gDataFileAccess = UDATA_DEFAULT_ACCESS; // Access not synchronized.
+ // Modifying is documented as thread-unsafe.
+#else
+static UDataFileAccess gDataFileAccess = UDATA_NO_FILES; // Windows UWP looks in one spot explicitly
+#endif
static UBool U_CALLCONV
udata_cleanup(void)
uhash_close(gCommonDataCache); /* Table owns the contents, and will delete them. */
gCommonDataCache = NULL; /* Cleanup is not thread safe. */
}
+ gCommonDataCacheInitOnce.reset();
- for (i = 0; i < LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) {
+ for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray) && gCommonICUDataArray[i] != NULL; ++i) {
udata_close(gCommonICUDataArray[i]);
gCommonICUDataArray[i] = NULL;
}
- gHaveTriedToLoadCommonData = FALSE;
+ gHaveTriedToLoadCommonData = 0;
return TRUE; /* Everything was cleaned up */
}
static UBool U_CALLCONV
-findCommonICUDataByName(const char *inBasename)
+findCommonICUDataByName(const char *inBasename, UErrorCode &err)
{
UBool found = FALSE;
int32_t i;
- UDataMemory *pData = udata_findCachedData(inBasename);
- if (pData == NULL)
+ UDataMemory *pData = udata_findCachedData(inBasename, err);
+ if (U_FAILURE(err) || pData == NULL)
return FALSE;
- for (i = 0; i < LENGTHOF(gCommonICUDataArray); ++i) {
- if ((gCommonICUDataArray[i] != NULL) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) {
- /* The data pointer is already in the array. */
- found = TRUE;
- break;
+ {
+ Mutex lock;
+ for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) {
+ if ((gCommonICUDataArray[i] != NULL) && (gCommonICUDataArray[i]->pHeader == pData->pHeader)) {
+ /* The data pointer is already in the array. */
+ found = TRUE;
+ break;
+ }
}
}
-
return found;
}
/* their locals. */
UDatamemory_assign(newCommonData, pData);
umtx_lock(NULL);
- for (i = 0; i < LENGTHOF(gCommonICUDataArray); ++i) {
+ for (i = 0; i < UPRV_LENGTHOF(gCommonICUDataArray); ++i) {
if (gCommonICUDataArray[i] == NULL) {
gCommonICUDataArray[i] = newCommonData;
- ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
didUpdate = TRUE;
break;
} else if (gCommonICUDataArray[i]->pHeader == pData->pHeader) {
}
umtx_unlock(NULL);
- if (i == LENGTHOF(gCommonICUDataArray) && warn) {
+ if (i == UPRV_LENGTHOF(gCommonICUDataArray) && warn) {
*pErr = U_USING_DEFAULT_WARNING;
}
- if (!didUpdate) {
+ if (didUpdate) {
+ ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
+ } else {
uprv_free(newCommonData);
}
return didUpdate;
setCommonICUDataPointer(const void *pData, UBool /*warn*/, UErrorCode *pErrorCode) {
UDataMemory tData;
UDataMemory_init(&tData);
- tData.pHeader = (const DataHeader *)pData;
+ UDataMemory_setData(&tData, pData);
udata_checkCommonData(&tData, pErrorCode);
return setCommonICUData(&tData, FALSE, pErrorCode);
}
uprv_free(pDCEl); /* delete 'this' */
}
- /* udata_getCacheHashTable()
- * Get the hash table used to store the data cache entries.
- * Lazy create it if it doesn't yet exist.
- */
-static UHashtable *udata_getHashTable() {
- UErrorCode err = U_ZERO_ERROR;
- UBool cacheIsInitialized;
- UHashtable *tHT = NULL;
-
- UMTX_CHECK(NULL, (gCommonDataCache != NULL), cacheIsInitialized);
-
- if (cacheIsInitialized) {
- return gCommonDataCache;
- }
-
- tHT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &err);
- /* Check for null pointer. */
- if (tHT == NULL) {
- return NULL; /* TODO: Handle this error better. */
- }
- uhash_setValueDeleter(tHT, DataCacheElement_deleter);
-
- umtx_lock(NULL);
- if (gCommonDataCache == NULL) {
- gCommonDataCache = tHT;
- tHT = NULL;
- ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
- }
- umtx_unlock(NULL);
- if (tHT != NULL) {
- uhash_close(tHT);
- }
-
+static void U_CALLCONV udata_initHashTable(UErrorCode &err) {
+ U_ASSERT(gCommonDataCache == NULL);
+ gCommonDataCache = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &err);
if (U_FAILURE(err)) {
- return NULL; /* TODO: handle this error better. */
+ return;
}
+ U_ASSERT(gCommonDataCache != NULL);
+ uhash_setValueDeleter(gCommonDataCache, DataCacheElement_deleter);
+ ucln_common_registerCleanup(UCLN_COMMON_UDATA, udata_cleanup);
+}
+
+ /* udata_getCacheHashTable()
+ * Get the hash table used to store the data cache entries.
+ * Lazy create it if it doesn't yet exist.
+ */
+static UHashtable *udata_getHashTable(UErrorCode &err) {
+ umtx_initOnce(gCommonDataCacheInitOnce, &udata_initHashTable, err);
return gCommonDataCache;
}
-static UDataMemory *udata_findCachedData(const char *path)
+static UDataMemory *udata_findCachedData(const char *path, UErrorCode &err)
{
UHashtable *htable;
UDataMemory *retVal = NULL;
DataCacheElement *el;
const char *baseName;
+ htable = udata_getHashTable(err);
+ if (U_FAILURE(err)) {
+ return NULL;
+ }
+
baseName = findBasename(path); /* Cache remembers only the base name, not the full path. */
- htable = udata_getHashTable();
umtx_lock(NULL);
el = (DataCacheElement *)uhash_get(htable, baseName);
umtx_unlock(NULL);
DataCacheElement *oldValue = NULL;
UErrorCode subErr = U_ZERO_ERROR;
+ htable = udata_getHashTable(*pErr);
if (U_FAILURE(*pErr)) {
return NULL;
}
/* Stick the new DataCacheElement into the hash table.
*/
- htable = udata_getHashTable();
umtx_lock(NULL);
oldValue = (DataCacheElement *)uhash_get(htable, path);
if (oldValue != NULL) {
* *
*----------------------------------------------------------------------*/
-#define U_DATA_PATHITER_BUFSIZ 128 /* Size of local buffer for paths */
- /* Overflow causes malloc of larger buf */
-
U_NAMESPACE_BEGIN
class UDataPathIterator
/*----------------------------------------------------------------------*
* *
- * Add a static reference to the common data library *
+ * Add a static reference to the common data library *
* Unless overridden by an explicit udata_setCommonData, this will be *
* our common data. *
* *
*----------------------------------------------------------------------*/
+#if U_PLATFORM_HAS_WINUWP_API == 0 // Windows UWP Platform does not support dll icu data at this time
extern "C" const DataHeader U_DATA_API U_ICUDATA_ENTRY_POINT;
+#endif
/*
* This would be a good place for weak-linkage declarations of
/* ??????? TODO revisit this */
if (commonDataIndex >= 0) {
/* "mini-cache" for common ICU data */
- if(commonDataIndex >= LENGTHOF(gCommonICUDataArray)) {
+ if(commonDataIndex >= UPRV_LENGTHOF(gCommonICUDataArray)) {
return NULL;
}
- if(gCommonICUDataArray[commonDataIndex] == NULL) {
+ {
+ Mutex lock;
+ if(gCommonICUDataArray[commonDataIndex] != NULL) {
+ return gCommonICUDataArray[commonDataIndex];
+ }
+#if U_PLATFORM_HAS_WINUWP_API == 0 // Windows UWP Platform does not support dll icu data at this time
int32_t i;
for(i = 0; i < commonDataIndex; ++i) {
if(gCommonICUDataArray[i]->pHeader == &U_ICUDATA_ENTRY_POINT) {
return NULL;
}
}
+#endif
+ }
- /* Add the linked-in data to the list. */
- /*
- * This is where we would check and call weakly linked partial-data-library
- * access functions.
- */
- /*
- if (uprv_getICUData_collation) {
- setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode);
- }
- if (uprv_getICUData_conversion) {
- setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode);
- }
- */
- setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode);
+ /* Add the linked-in data to the list. */
+ /*
+ * This is where we would check and call weakly linked partial-data-library
+ * access functions.
+ */
+ /*
+ if (uprv_getICUData_collation) {
+ setCommonICUDataPointer(uprv_getICUData_collation(), FALSE, pErrorCode);
}
- return gCommonICUDataArray[commonDataIndex];
+ if (uprv_getICUData_conversion) {
+ setCommonICUDataPointer(uprv_getICUData_conversion(), FALSE, pErrorCode);
+ }
+ */
+#if U_PLATFORM_HAS_WINUWP_API == 0 // Windows UWP Platform does not support dll icu data at this time
+ setCommonICUDataPointer(&U_ICUDATA_ENTRY_POINT, FALSE, pErrorCode);
+ {
+ Mutex lock;
+ return gCommonICUDataArray[commonDataIndex];
+ }
+#endif
}
#ifdef UDATA_DEBUG
fprintf(stderr, "ocd: no basename in %s, bailing.\n", path);
#endif
- *pErrorCode=U_FILE_ACCESS_ERROR;
+ if (U_SUCCESS(*pErrorCode)) {
+ *pErrorCode=U_FILE_ACCESS_ERROR;
+ }
return NULL;
}
/* Is the requested common data file already open and cached? */
/* Note that the cache is keyed by the base name only. The rest of the path, */
/* if any, is not considered. */
- {
- UDataMemory *dataToReturn = udata_findCachedData(inBasename);
- if (dataToReturn != NULL) {
- return dataToReturn;
- }
+ UDataMemory *dataToReturn = udata_findCachedData(inBasename, *pErrorCode);
+ if (dataToReturn != NULL || U_FAILURE(*pErrorCode)) {
+ return dataToReturn;
}
/* Requested item is not in the cache.
}
#endif
+ if (U_FAILURE(*pErrorCode)) {
+ return NULL;
+ }
if (!UDataMemory_isLoaded(&tData)) {
/* no common data */
*pErrorCode=U_FILE_ACCESS_ERROR;
* Use a specific mutex to avoid nested locks of the global mutex.
*/
#if MAP_IMPLEMENTATION==MAP_STDIO
- static UMTX extendICUDataMutex = NULL;
+ static UMutex extendICUDataMutex = U_MUTEX_INITIALIZER;
umtx_lock(&extendICUDataMutex);
#endif
- if(!gHaveTriedToLoadCommonData) {
+ if(!umtx_loadAcquire(gHaveTriedToLoadCommonData)) {
/* See if we can explicitly open a .dat file for the ICUData. */
pData = openCommonData(
U_ICUDATA_NAME, /* "icudt20l" , for example. */
pErr); /* setCommonICUData honors errors; NOP if error set */
}
- gHaveTriedToLoadCommonData = TRUE;
+ umtx_storeRelease(gHaveTriedToLoadCommonData, 1);
}
- didUpdate = findCommonICUDataByName(U_ICUDATA_NAME); /* Return 'true' when a racing writes out the extended */
+ didUpdate = findCommonICUDataByName(U_ICUDATA_NAME, *pErr); /* Return 'true' when a racing writes out the extended */
/* data after another thread has failed to see it (in openCommonData), so */
/* extended data can be examined. */
/* Also handles a race through here before gHaveTriedToLoadCommonData is set. */
}
}
+/*
+ * Identify the Time Zone resources that are subject to special override data loading.
+ */
+static UBool isTimeZoneFile(const char *name, const char *type) {
+ return ((uprv_strcmp(type, "res") == 0) &&
+ (uprv_strcmp(name, "zoneinfo64") == 0 ||
+ uprv_strcmp(name, "timezoneTypes") == 0 ||
+ uprv_strcmp(name, "windowsZones") == 0 ||
+ uprv_strcmp(name, "metaZones") == 0));
+}
+
/*
* A note on the ownership of Mapped Memory
*
fprintf(stderr, " tocEntryPath = %s\n", tocEntryName.data());
#endif
+#if U_PLATFORM_HAS_WINUWP_API == 0 // Windows UWP Platform does not support dll icu data at this time
if(path == NULL) {
path = COMMON_DATA_NAME; /* "icudt26e" */
}
+#else
+ // Windows UWP expects only a single data file.
+ path = COMMON_DATA_NAME; /* "icudt26e" */
+#endif
/************************ Begin loop looking for ind. files ***************/
#ifdef UDATA_DEBUG
/* End of dealing with a null basename */
dataPath = u_getDataDirectory();
+ /**** Time zone individual files override */
+ if (isICUData && isTimeZoneFile(name, type)) {
+ const char *tzFilesDir = u_getTimeZoneFilesDirectory(pErrorCode);
+ if (tzFilesDir[0] != 0) {
+#ifdef UDATA_DEBUG
+ fprintf(stderr, "Trying Time Zone Files directory = %s\n", tzFilesDir);
+#endif
+#ifdef UDATA_TZFILES_DEBUG
+ fprintf(stderr, "# dOC U_TIMEZONE_FILES_DIR: %s\n", U_TIMEZONE_FILES_DIR);
+#endif
+
+#if defined(U_TIMEZONE_PACKAGE)
+ // make tztocEntryName, like tocEntryName but with our package name
+ UErrorCode tzpkgErrorCode = U_ZERO_ERROR;
+ CharString tztocPkgPath;
+ tztocPkgPath.append(tzFilesDir, tzpkgErrorCode);
+ tztocPkgPath.append(U_FILE_SEP_CHAR, tzpkgErrorCode).append(U_TIMEZONE_PACKAGE, tzpkgErrorCode);
+ CharString tztocEntryName;
+ tztocEntryName.append(U_TIMEZONE_PACKAGE, tzpkgErrorCode);
+ if(!treeName.isEmpty()) {
+ tztocEntryName.append(U_TREE_ENTRY_SEP_CHAR, tzpkgErrorCode).append(treeName, tzpkgErrorCode);
+ }
+ tztocEntryName.append(U_TREE_ENTRY_SEP_CHAR, tzpkgErrorCode).append(name, tzpkgErrorCode);
+ if(type!=NULL && *type!=0) {
+ tztocEntryName.append(".", tzpkgErrorCode).append(type, tzpkgErrorCode);
+ }
+#ifdef UDATA_TZFILES_DEBUG
+ fprintf(stderr, "# dOC tz pkg, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: %s, tztocPkgPath.data(): %s, tztocEntryName.data(): %s, name: %s\n",
+ U_TIMEZONE_PACKAGE, tztocPkgPath.data(), tztocEntryName.data(), name);
+#endif
+ retVal = doLoadFromCommonData(FALSE, "" /*ignored*/, "" /*ignored*/, "" /*ignored*/,
+ tztocEntryName.data(), // tocEntryName, like icutz44/zoneinfo64.res
+ tztocPkgPath.data(), // path = path to pkg, like /usr/share/icu/icutz44l
+ type, name, isAcceptable, context, &subErrorCode, &tzpkgErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+ fprintf(stderr, "# dOC tz pkg, doLoadFromCommonData end; status %d, retVal %p\n", tzpkgErrorCode, retVal);
+#endif
+ if(U_SUCCESS(tzpkgErrorCode) && retVal != NULL) {
+ return retVal;
+ }
+#endif /* defined(U_TIMEZONE_PACKAGE) */
+ // The following assumes any timezone resources in tzFilesDir are in individual .res files
+#ifdef UDATA_TZFILES_DEBUG
+ fprintf(stderr, "# dOC tz files, doLoadFromIndividualFiles start; tzFilesDir: %s, tocEntryPathSuffix: %s, name: %s\n",
+ tzFilesDir, tocEntryPathSuffix, name);
+#endif
+ retVal = doLoadFromIndividualFiles(/* pkgName.data() */ "", tzFilesDir, tocEntryPathSuffix,
+ /* path */ "", type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+ fprintf(stderr, "# dOC tz files, doLoadFromIndividualFiles end; status %d, retVal %p\n", *pErrorCode, retVal);
+#endif
+ if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
+ return retVal;
+ }
+ }
+ }
+
/**** COMMON PACKAGE - only if packages are first. */
if(gDataFileAccess == UDATA_PACKAGES_FIRST) {
#ifdef UDATA_DEBUG
fprintf(stderr, "Trying packages (UDATA_PACKAGES_FIRST)\n");
#endif
/* #2 */
+#ifdef UDATA_TZFILES_DEBUG
+ if (isTimeZoneFile(name, type)) {
+ fprintf(stderr, "# dOC std common 1, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: path: %s, tocEntryName.data(): %s, name: %s\n",
+ path, tocEntryName.data(), name);
+ }
+#endif
retVal = doLoadFromCommonData(isICUData,
pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+ if (isTimeZoneFile(name, type)) {
+ fprintf(stderr, "# dOC std common 1, doLoadFromCommonData end; status %d, retVal %p\n", *pErrorCode, retVal);
+ }
+#endif
if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
return retVal;
}
#endif
/* Check to make sure that there is a dataPath to iterate over */
if ((dataPath && *dataPath) || !isICUData) {
+#ifdef UDATA_TZFILES_DEBUG
+ if (isTimeZoneFile(name, type)) {
+ fprintf(stderr, "# dOC std indiv files, doLoadFromIndividualFiles start; dataPath: %s, tocEntryPathSuffix: %s, name: %s\n",
+ dataPath, tocEntryPathSuffix, name);
+ }
+#endif
retVal = doLoadFromIndividualFiles(pkgName.data(), dataPath, tocEntryPathSuffix,
path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+ if (isTimeZoneFile(name, type)) {
+ fprintf(stderr, "# dOC std indiv files, doLoadFromIndividualFiles end; status %d, retVal %p\n", *pErrorCode, retVal);
+ }
+#endif
if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
return retVal;
}
(gDataFileAccess==UDATA_FILES_FIRST)) {
#ifdef UDATA_DEBUG
fprintf(stderr, "Trying packages (UDATA_ONLY_PACKAGES || UDATA_FILES_FIRST)\n");
+#endif
+#ifdef UDATA_TZFILES_DEBUG
+ if (isTimeZoneFile(name, type)) {
+ fprintf(stderr, "# dOC std common 2, doLoadFromCommonData start; U_TIMEZONE_PACKAGE: path: %s, tocEntryName.data(): %s, name: %s\n",
+ path, tocEntryName.data(), name);
+ }
#endif
retVal = doLoadFromCommonData(isICUData,
pkgName.data(), dataPath, tocEntryPathSuffix, tocEntryName.data(),
path, type, name, isAcceptable, context, &subErrorCode, pErrorCode);
+#ifdef UDATA_TZFILES_DEBUG
+ if (isTimeZoneFile(name, type)) {
+ fprintf(stderr, "# dOC std common 2, doLoadFromCommonData end; status %d, retVal %p\n", *pErrorCode, retVal);
+ }
+#endif
if((retVal != NULL) || U_FAILURE(*pErrorCode)) {
return retVal;
}
U_CAPI void U_EXPORT2 udata_setFileAccess(UDataFileAccess access, UErrorCode * /*status*/)
{
+ // Note: this function is documented as not thread safe.
gDataFileAccess = access;
}