From c84ea102a73d2b0d50a7cc563fd600f9b505bae5 Mon Sep 17 00:00:00 2001 From: Andy Heninger Date: Wed, 15 Aug 2001 22:39:42 +0000 Subject: [PATCH] ICU-1075 Data updates. More updateds to ICU data handling. X-SVN-Rev: 5467 --- icu4c/source/common/udata.c | 934 ++++++++++++-------------- icu4c/source/common/unicode/udata.h | 21 +- icu4c/source/data/Makefile.in | 14 +- icu4c/source/test/cintltst/udatatst.c | 29 +- icu4c/source/tools/Makefile.in | 14 +- 5 files changed, 485 insertions(+), 527 deletions(-) diff --git a/icu4c/source/common/udata.c b/icu4c/source/common/udata.c index c5c9cae809..0954be9553 100644 --- a/icu4c/source/common/udata.c +++ b/icu4c/source/common/udata.c @@ -29,13 +29,6 @@ /* configuration ---------------------------------------------------------- */ -#if !defined(HAVE_DLOPEN) -# define HAVE_DLOPEN 0 -#endif - -#if !defined(UDATA_DLL) && !defined(UDATA_MAP) &&!defined(UDATA_FILES) -# define UDATA_DLL -#endif #define COMMON_DATA_NAME U_ICUDATA_NAME #define COMMON_DATA_NAME_LENGTH 8 @@ -56,45 +49,14 @@ static UBool s390dll = TRUE; # include #endif -/* DLL/shared library base functions ---------------------------------------- */ +/* DLL/shared library base functions ---------------------------------------- */ +/* TODO: Dynamic loading of DLLs is no longer supported. */ +/* 390 is a special case, since it can not support file loading. */ +/* Need to abstract 390 library loading so that it appears to the rest */ +/* ICU as file loading. */ -#ifdef WIN32 -# define WIN32_LEAN_AND_MEAN -# define NOGDI -# define NOUSER -# define NOSERVICE -# define NOIME -# define NOMCX -# include - typedef HINSTANCE Library; - -# define LIB_SUFFIX ".dll" - -# define NO_LIBRARY NULL -# define IS_LIBRARY(lib) ((lib)!=NULL) -# define LOAD_LIBRARY(path, basename) LoadLibrary(path) -# define UNLOAD_LIBRARY(lib) FreeLibrary(lib) - -# define GET_LIBRARY_ENTRY(lib, entryName) GetProcAddress(lib, entryName) - -#elif HAVE_DLOPEN /* POSIX-y shared library environment. dlopen() or equivalent.. */ -# ifndef UDATA_SO_SUFFIX -# error Please define UDATA_SO_SUFFIX to the shlib suffix (i.e. '.so' ) -# endif - -# define LIB_PREFIX "lib" -# define LIB_PREFIX_LENGTH 3 -# define LIB_SUFFIX UDATA_SO_SUFFIX - -# ifdef ICU_USE_SHL_LOAD /* Some sort of compatibility stub: - * HPUX shl_load - * OS390 dllload */ - -# define RTLD_LAZY 0 -# define RTLD_GLOBAL 0 - -# ifdef OS390 +#ifdef OS390 # include # define RTLD_LAZY 0 @@ -128,106 +90,10 @@ static UBool s390dll = TRUE; # endif return dllfree((dllhandle*)handle); } -# else /* not OS390: HPUX shl_load */ -# include - - void *dlopen (const char *filename, int flag) { - void *handle; /* real type: 'shl_t' */ -# ifdef UDATA_DEBUG - fprintf(stderr, "shl_load: %s ", filename); -# endif - handle=shl_load(filename, BIND_NONFATAL | BIND_DEFERRED | DYNAMIC_PATH, 0L); -# ifdef UDATA_DEBUG - fprintf(stderr, " -> %08X\n", handle ); -# endif - return handle; - } - - void *dlsym(void *h, char *symbol) { - void *val=0; - int rv; - shl_t mysh; - - mysh=(shl_t)h; /* real type */ - - rv=shl_findsym(&mysh, symbol, TYPE_DATA, (void*)&val); -# ifdef UDATA_DEBUG - fprintf(stderr, "shl_findsym(%08X, %s) -> %08X [%d]\n", h, symbol, val, rv); -# endif - return val; - } - - int dlclose (void *handle) { -# ifndef HPUX -# ifdef UDATA_DEBUG - fprintf(stderr, "shl_unload: %08X\n", handle); -# endif - return shl_unload((shl_t)handle); -# else - /* "shl_unload .. always unloads the library.. no refcount is kept on PA32" - -- HPUX man pages [v. 11] - - Fine, we'll leak! [for now.. Jitterbug 414 has been filed ] - */ - return 0; -# endif /* HPUX */ - } -# endif /* HPUX shl_load */ -# else /* not ICU_USE_SHL_LOAD */ - /* 'de facto standard' dlopen etc. */ -# include -#ifndef RTLD_GLOBAL -#define RTLD_GLOBAL 0 -#endif -# endif - - typedef void *Library; - -# define NO_LIBRARY NULL -# define IS_LIBRARY(lib) ((lib)!=NULL) +#endif /* OS390: */ -#ifndef UDATA_DEBUG -# define LOAD_LIBRARY(path, basename) dlopen(path, RTLD_LAZY|RTLD_GLOBAL) -# define UNLOAD_LIBRARY(lib) dlclose(lib) -# define GET_LIBRARY_ENTRY(lib, entryName) dlsym(lib, entryName) -#else - void *LOAD_LIBRARY(const char *path, const char *basename) - { - void *rc; - rc = dlopen(path, RTLD_LAZY|RTLD_GLOBAL); - fprintf(stderr, "Load [%s|%s] -> %p\n", path, basename, rc); - return rc; - } - void UNLOAD_LIBRARY(void *lib) - { - dlclose(lib); - fprintf(stderr, "Unload [%p]\n", lib); - } - void * GET_LIBRARY_ENTRY(void *lib, const char *entryName) - { - void *rc; - rc = dlsym(lib, entryName); - fprintf(stderr, "Get[%p] %s->%p\n", lib, entryName, rc); - return rc; - } -#endif - /* End of dlopen or compatible functions */ -#else /* unknown platform, no DLL implementation */ -# ifndef UDATA_NO_DLL -# define UDATA_NO_DLL 1 -# endif - typedef const void *Library; - -# define NO_LIBRARY NULL -# define IS_LIBRARY(lib) ((lib)!=NULL) -# define LOAD_LIBRARY(path, basename) NULL -# define UNLOAD_LIBRARY(lib) ; - -# define GET_LIBRARY_ENTRY(lib, entryName) NULL - -#endif /* memory-mapping base definitions ------------------------------------------ */ @@ -242,9 +108,16 @@ static UBool s390dll = TRUE; #define MAP_FILE_STREAM 3 #ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# define NOGDI +# define NOUSER +# define NOSERVICE +# define NOIME +# define NOMCX +# include + typedef HANDLE MemoryMap; -# define NO_MAP NULL # define IS_MAP(map) ((map)!=NULL) # define MAP_IMPLEMENTATION MAP_WIN32 @@ -253,7 +126,6 @@ static UBool s390dll = TRUE; #elif HAVE_MMAP || defined(U_LINUX) || defined(POSIX) || defined(U_SOLARIS) || defined(AIX) || defined(HPUX) || defined(OS390) || defined(PTX) typedef size_t MemoryMap; -# define NO_MAP 0 # define IS_MAP(map) ((map)!=0) # include @@ -273,7 +145,6 @@ static UBool s390dll = TRUE; typedef void *MemoryMap; -# define NO_MAP NULL # define IS_MAP(map) ((map)!=NULL) # define MAP_IMPLEMENTATION MAP_FILE_STREAM @@ -282,28 +153,6 @@ static UBool s390dll = TRUE; /* common definitions ------------------------------------------------------- */ -/* constants for UDataMemory flags: type of data memory */ -enum { - NO_DATA_MEMORY = 0, - FLAT_DATA_MEMORY = 1, - DLL_DATA_MEMORY = 2 -}; -#define DATA_MEMORY_TYPE_MASK 0xf - -/* constants for UDataMemory flags: type of TOC */ -enum { - NO_TOC =0x00, - OFFSET_TOC =0x10, - POINTER_TOC =0x20, - DLL_INTRINSIC_TOC =0x30 -}; -#define TOC_TYPE_MASK 0xf0 - -/* constants for UDataMemory flags: type of TOC */ -#define SET_DATA_POINTER_FLAG 0x40000000 -#define DYNAMIC_DATA_MEMORY_FLAG 0x80000000 - -#define IS_DATA_MEMORY_LOADED(pData) ((pData)->flags!=0) typedef struct { @@ -331,15 +180,25 @@ LookupFn(const UDataMemory *pData, * * *----------------------------------------------------------------------------------*/ typedef struct UDataMemory { - UDataMemory *parent; /* Set if we're suballocated from some common */ - Library lib; /* OS dependent handle for DLLs, .so, etc */ MemoryMap map; /* Handle, or whatever. OS dependent. */ + /* Only set if a close operation should unmap the */ + /* associated data. */ LookupFn *lookupFn; const void *toc; /* For common memory, to find pieces within. */ const DataHeader *pHeader; /* Header. For common data, header is at top of file */ + const void *mapAddr; /* For mapped or allocated memory, the start addr. */ + /* Will be above pHeader in some cases. */ + /* Needed to allow unmapping. */ uint32_t flags; /* Memory format, TOC type, Allocation type, etc. */ - int32_t refCount; /* Not used just yet... */ -} UDataMemory; +}; + +/* constants for UDataMemory flags */ +#define MALLOCED_UDATAMEMORY_FLAG 0x80000000 /* Set flag if UDataMemory object itself + * is on heap, and must be freed when + * it is closed. + */ + +#define IS_DATA_MEMORY_LOADED(pData) ((pData)->pHeader!=NULL) static void UDataMemory_init(UDataMemory *This) { @@ -347,14 +206,26 @@ static void UDataMemory_init(UDataMemory *This) { } -static void UDataMemory_copy(UDataMemory *dest, UDataMemory *source) { +static void UDatamemory_assign(UDataMemory *dest, UDataMemory *source) { + /* UDataMemory Assignment. Destination UDataMemory must be initialized first. + * Malloced flag of the destination is preserved, + * since it indicates where the UDataMemory struct itself + * is allocated. */ + uint32_t dest_MALLOCED_UDATAMEMORY_FLAG = dest->flags & MALLOCED_UDATAMEMORY_FLAG; uprv_memcpy(dest, source, sizeof(UDataMemory)); + dest->flags &= ~MALLOCED_UDATAMEMORY_FLAG; + dest->flags |= dest_MALLOCED_UDATAMEMORY_FLAG; } -static UDataMemory *UDataMemory_createNewInstance() { +static UDataMemory *UDataMemory_createNewInstance(UErrorCode *pErr) { UDataMemory *This; This = uprv_malloc(sizeof(UDataMemory)); - UDataMemory_init(This); + if (This == NULL) { + *pErr = U_MEMORY_ALLOCATION_ERROR; } + else { + UDataMemory_init(This); + This->flags |= MALLOCED_UDATAMEMORY_FLAG; + } return This; } @@ -413,7 +284,6 @@ typedef struct { return FALSE; } pData->map=map; - pData->flags=FLAT_DATA_MEMORY; return TRUE; } @@ -473,20 +343,21 @@ typedef struct { pData->map=length; pData->pHeader=(const DataHeader *)data; - pData->flags=FLAT_DATA_MEMORY; + pData->mapAddr = data; return TRUE; } static void uprv_unmapFile(UDataMemory *pData) { if(pData!=NULL && pData->map>0) { - if(munmap((void *)pData->pHeader, pData->map)==-1) { + if(munmap((void *)pData->mapAddr, pData->map)==-1) { # ifdef UDATA_DEBUG perror("munmap"); # endif } pData->pHeader=NULL; pData->map=0; + pData->mapAddr=NULL; } } @@ -528,7 +399,7 @@ typedef struct { T_FileStream_close(file); pData->map=p; pData->pHeader=(const DataHeader *)p; - pData->flags=FLAT_DATA_MEMORY; + pData->mapAddr=p; return TRUE; } @@ -536,7 +407,9 @@ typedef struct { uprv_unmapFile(UDataMemory *pData) { if(pData!=NULL && pData->map!=NULL) { uprv_free(pData->map); - pData->map=NULL; + pData->map = NULL; + pData->mapAddr = NULL; + pData->pHeader = NULL; } } @@ -669,41 +542,23 @@ pointerTOCLookupFn(const UDataMemory *pData, } } -static const DataHeader * -dllTOCLookupFn(const UDataMemory *pData, - const char *tocEntryName, - const char *dllEntryName, - UErrorCode *pErrorCode) { -#ifndef UDATA_NO_DLL - if(pData->lib!=NULL) { - return normalizeDataPointer((const DataHeader *)GET_LIBRARY_ENTRY(pData->lib, dllEntryName)); - } -#endif - return NULL; -} /* common library functions ------------------------------------------------- */ static UDataMemory commonICUData={ NULL }; -static void +static UBool setCommonICUData(UDataMemory *pData) { UBool setThisLib=FALSE; /* in the mutex block, set the common library for this process */ umtx_lock(NULL); if(!IS_DATA_MEMORY_LOADED(&commonICUData)) { - uprv_memcpy(&commonICUData, pData, sizeof(UDataMemory)); - commonICUData.flags&=~DYNAMIC_DATA_MEMORY_FLAG; - uprv_memset(pData, 0, sizeof(UDataMemory)); + UDatamemory_assign(&commonICUData, pData); setThisLib=TRUE; } umtx_unlock(NULL); - - /* if a different thread set it first, then free the extra library instance */ - if(!setThisLib) { - udata_close(pData); - } + return setThisLib; } static char * @@ -721,19 +576,12 @@ strcpy_returnEnd(char *dest, const char *src) { * to be opened, compute a full directory path and leave * * it in pathBuffer. Returns a pointer to the null at * * the end of the computed path. * - * If a non-empty path buffer is passed in, assume that * - * it already contains the right result, and don't * - * bother to figure it all out again. * + * Overwrites any contents in the output pathBuffer * * * *------------------------------------------------------------------------------*/ static char * setPathGetBasename(const char *path, char *pathBuffer) { - if(*pathBuffer!=0) { - /* the pathBuffer is already filled, - we just need to find the basename position; - assume that the last filling function removed the basename from the buffer */ - return pathBuffer+uprv_strlen(pathBuffer); - } else if(path==NULL) { + if(path==NULL) { /* copy the ICU_DATA path to the path buffer */ path=u_getDataDirectory(); if(path!=NULL && *path!=0) { @@ -765,6 +613,7 @@ setPathGetBasename(const char *path, char *pathBuffer) { } } + static const char * findBasename(const char *path) { const char *basename=uprv_strrchr(path, U_FILE_SEP_CHAR); @@ -793,12 +642,27 @@ typedef struct DataCacheElement { UDataMemory item; } DataCacheElement; +static UHashtable *gHashTable = NULL; + + +/* + * Deleter function for DataCacheElements. + * udata cleanup function closes the hash table; hash table in turn calls back to + * here for each entry. + */ +static void U_CALLCONV DataCacheElement_deleter(void *pDCEl) { + DataCacheElement *p = (DataCacheElement *)pDCEl; + udata_close(&p->item); /* unmaps storage */ + uprv_free(p->path); /* delete the hash key string. */ + 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() { - static UHashtable *gHashTable; UErrorCode err = U_ZERO_ERROR; if (gHashTable != NULL) { @@ -807,11 +671,12 @@ static UHashtable *udata_getHashTable() { umtx_lock(NULL); if (gHashTable == NULL) { gHashTable = uhash_open(uhash_hashChars, uhash_compareChars, &err); + uhash_setValueDeleter(gHashTable, DataCacheElement_deleter); } umtx_unlock(NULL); if U_FAILURE(err) { - return NULL; + return NULL; /* TODO: handle this error better. */ } return gHashTable; } @@ -820,51 +685,142 @@ static UHashtable *udata_getHashTable() { static UDataMemory *udata_findCachedData(const char *path) { - UHashtable *htable; - UDataMemory *retVal; + UHashtable *htable; + UDataMemory *retVal = NULL; + DataCacheElement *el; - umtx_lock(NULL); htable = udata_getHashTable(); + umtx_lock(NULL); + el = (DataCacheElement *)uhash_get(htable, path); umtx_unlock(NULL); - retVal = (UDataMemory *)uhash_get(htable, path); + if (el != NULL) { + retVal = &el->item; + } return retVal; } -static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item) { +static UDataMemory *udata_cacheDataItem(const char *path, UDataMemory *item, UErrorCode *pErr) { DataCacheElement *newElement; int pathLen; - UErrorCode err = U_ZERO_ERROR; + UHashtable *htable; + UDataMemory *oldValue = NULL; + + if (U_FAILURE(*pErr)) { + return NULL; + } /* Create a new DataCacheElement - the thingy we store in the hash table - * and copy the supplied path and UDataMemoryItems into it. */ newElement = uprv_malloc(sizeof(DataCacheElement)); - UDataMemory_copy(&newElement->item, item); - newElement->item.flags &= ~DYNAMIC_DATA_MEMORY_FLAG; + if (newElement == NULL) { + *pErr = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + UDataMemory_init(&newElement->item); /*Need separte init + copy to get flags right. */ + UDatamemory_assign(&newElement->item, item); /* They're not all copied. */ pathLen = uprv_strlen(path); newElement->path = uprv_malloc(pathLen+1); + if (newElement->path == NULL) { + *pErr = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } uprv_strcpy(newElement->path, path); /* Stick the new DataCacheElement into the hash table. */ + htable = udata_getHashTable(); umtx_lock(NULL); - uhash_put(udata_getHashTable(), - newElement->path, /* Key */ - &newElement->item, /* Value */ - &err); + oldValue = uhash_get(htable, path); + if (oldValue != NULL) { + *pErr = U_USING_DEFAULT_WARNING; } + else { + uhash_put( + htable, + newElement->path, /* Key */ + newElement, /* Value */ + pErr); + } umtx_unlock(NULL); - /* Just ignore any error returned. - * 1. Failure to add to the cache is not, by itself, fatal. - * 2. The only reason the hash table would fail is if memory - * allocation fails, which means complete death is just - * around the corner anyway... - */ + + if (*pErr == U_USING_DEFAULT_WARNING || U_FAILURE(*pErr)) { + uprv_free(newElement->path); + uprv_free(newElement); + return oldValue; + } return &newElement->item; } +/*----------------------------------------------------------------------* + * * + * checkCommonData Validate the format of a common data file. * + * Fill in the TOC type in the UDataMemory * + * If the data is invalid, close the UDataMemory * + * and set the appropriate error code. * + * * + *----------------------------------------------------------------------*/ +static void checkCommonData(UDataMemory *udm, UErrorCode *err) { + if (U_FAILURE(*err)) { + return; + } + + if(!(udm->pHeader->dataHeader.magic1==0xda && + udm->pHeader->dataHeader.magic2==0x27 && + udm->pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && + udm->pHeader->info.charsetFamily==U_CHARSET_FAMILY) + ) { + /* header not valid */ + *err=U_INVALID_FORMAT_ERROR; + } + else if (udm->pHeader->info.dataFormat[0]==0x43 && + udm->pHeader->info.dataFormat[1]==0x6d && + udm->pHeader->info.dataFormat[2]==0x6e && + udm->pHeader->info.dataFormat[3]==0x44 && + udm->pHeader->info.formatVersion[0]==1 + ) { + /* dataFormat="CmnD" */ + udm->lookupFn=offsetTOCLookupFn; + udm->toc=(const char *)udm->pHeader+udm->pHeader->dataHeader.headerSize; + } + else if(udm->pHeader->info.dataFormat[0]==0x54 && + udm->pHeader->info.dataFormat[1]==0x6f && + udm->pHeader->info.dataFormat[2]==0x43 && + udm->pHeader->info.dataFormat[3]==0x50 && + udm->pHeader->info.formatVersion[0]==1 + ) { + /* dataFormat="ToCP" */ + udm->lookupFn=pointerTOCLookupFn; + udm->toc=(const char *)udm->pHeader+udm->pHeader->dataHeader.headerSize; + } + else { + /* dataFormat not recognized */ + *err=U_INVALID_FORMAT_ERROR; + } + + if (U_FAILURE(*err)) { + /* If the data is no good and we memory-mapped it ourselves, + * close the memory mapping so it doesn't leak. Note that this has + * no effect on non-memory mapped data, other than clearing fields in udm. + */ + uprv_unmapFile(udm); + } +} + + +U_CAPI void U_EXPORT2 +udata_cleanup() +{ + if (gHashTable) { /* Delete the cache of user data mappings. */ + uhash_close(gHashTable); /* Table owns the contents, and will delete them. */ + gHashTable = 0; /* Cleanup is not thread safe. */ + } + + udata_close(&commonICUData); /* Clean up common ICU Data */ +} + /* */ /* Add a static reference to the common data from a library if the */ @@ -891,150 +847,93 @@ openCommonData( const char *path, /* Path from OpenCHoice? */ UBool isICUData, /* ICU Data true if path == NULL */ UErrorCode *pErrorCode) - { +{ const char *inBasename; char *basename, *suffix; - const DataHeader *pHeader; char pathBuffer[1024]; UDataMemory tData; - UDataMemory_init(&tData); - - - /* "mini-cache" for common ICU data */ - if(isICUData && IS_DATA_MEMORY_LOADED(&commonICUData)) { - return &commonICUData; + if (U_FAILURE(*pErrorCode)) { + return NULL; } - - - /* Is this data cached? Meaning, have we seen this before */ - if (!isICUData) { + + UDataMemory_init(&tData); + + if (isICUData) { + /* "mini-cache" for common ICU data */ + if(IS_DATA_MEMORY_LOADED(&commonICUData)) { + return &commonICUData; + } + + tData.pHeader = &U_ICUDATA_ENTRY_POINT; + checkCommonData(&tData, pErrorCode); + if (U_SUCCESS(*pErrorCode)) { + setCommonICUData(&tData); + return &commonICUData; + } + else { + return NULL; + } + } + + + /* request is NOT for ICU Data. + * Is the requested data already cached? */ + { UDataMemory *dataToReturn = udata_findCachedData(path); if (dataToReturn != NULL) { return dataToReturn; } } - - - if (isICUData) { - /* ICU common data is already in our address space, thanks to a static reference */ - /* to a symbol from the data library. Just follow that pointer to set up */ - /* our ICU data. */ - pHeader = &U_ICUDATA_ENTRY_POINT; - if(!(pHeader->dataHeader.magic1==0xda && pHeader->dataHeader.magic2==0x27 && - pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && - pHeader->info.charsetFamily==U_CHARSET_FAMILY) - ) { - /* header not valid */ - *pErrorCode=U_INVALID_FORMAT_ERROR; - return NULL; - } - - if(pHeader->info.dataFormat[0]==0x43 && - pHeader->info.dataFormat[1]==0x6d && - pHeader->info.dataFormat[2]==0x6e && - pHeader->info.dataFormat[3]==0x44 && - pHeader->info.formatVersion[0]==1 - ) { - /* dataFormat="CmnD" */ - tData.lib=NULL; - tData.lookupFn=offsetTOCLookupFn; - tData.toc=(const char *)pHeader+pHeader->dataHeader.headerSize; - tData.flags= DLL_DATA_MEMORY | OFFSET_TOC; - } else if(pHeader->info.dataFormat[0]==0x54 && - pHeader->info.dataFormat[1]==0x6f && - pHeader->info.dataFormat[2]==0x43 && - pHeader->info.dataFormat[3]==0x50 && - pHeader->info.formatVersion[0]==1 - ) { - /* dataFormat="ToCP" */ - tData.lib=NULL; - tData.lookupFn=pointerTOCLookupFn; - tData.toc=(const char *)pHeader+pHeader->dataHeader.headerSize; - tData.flags=DLL_DATA_MEMORY|POINTER_TOC; - } else { - /* dataFormat not recognized */ - *pErrorCode=U_INVALID_FORMAT_ERROR; - return NULL; - } - - /* we have common data from a DLL */ - setCommonICUData(&tData); - return &commonICUData; - } - - - - /* set up path and basename */ - pathBuffer[0] = 0; + + /* Requested item is not in the cache. + * Hunt it down, trying all the fall back locations. + */ basename=setPathGetBasename(path, pathBuffer); - if(isICUData) { - inBasename=COMMON_DATA_NAME; - } else { - inBasename=findBasename(path); - if(*inBasename==0) { - /* no basename, no common data */ - *pErrorCode=U_FILE_ACCESS_ERROR; - return NULL; - } + inBasename=findBasename(path); + if(*inBasename==0) { + /* no basename, no common data */ + *pErrorCode=U_FILE_ACCESS_ERROR; + return NULL; } - - - - /* try to map a common data file */ - + + /* set up the file name */ suffix=strcpy_returnEnd(basename, inBasename); uprv_strcpy(suffix, "." DATA_TYPE); /* DATA_TYPE is ".dat" */ - + /* try path/basename first, then basename only */ - if( uprv_mapFile(&tData, pathBuffer) || - (basename!=pathBuffer && uprv_mapFile(&tData, basename)) - ) { - *basename=0; - - /* we have mapped a file, check its header */ - pHeader=tData.pHeader; - if(!(pHeader->dataHeader.magic1==0xda && pHeader->dataHeader.magic2==0x27 && - pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && - pHeader->info.charsetFamily==U_CHARSET_FAMILY && - pHeader->info.dataFormat[0]==0x43 && - pHeader->info.dataFormat[1]==0x6d && - pHeader->info.dataFormat[2]==0x6e && - pHeader->info.dataFormat[3]==0x44 && - pHeader->info.formatVersion[0]==1) - ) { - uprv_unmapFile(&tData); - tData.flags=0; - *pErrorCode=U_INVALID_FORMAT_ERROR; - return NULL; - } - - /* we have common data from a mapped file */ - tData.lookupFn=offsetTOCLookupFn; - tData.toc=(const char *)pHeader+pHeader->dataHeader.headerSize; - tData.flags|=OFFSET_TOC; - if(isICUData) { - setCommonICUData(&tData); - return &commonICUData; - } else { - // Cache the UDataMemory struct for this .dat file, - // so we won't need to hunt it down and open it again next time - // something is needed from it. - return udata_cacheDataItem(path, &tData); + uprv_mapFile(&tData, pathBuffer); + + if (!IS_DATA_MEMORY_LOADED(&tData)) { + if (basename!=pathBuffer) { + uprv_mapFile(&tData, basename); } } - - /* cleanup for the caller, to keep setPathGetBasename() consistent */ - *basename=0; - - /* no common data */ - *pErrorCode=U_FILE_ACCESS_ERROR; - return NULL; + + if (!IS_DATA_MEMORY_LOADED(&tData)) { + /* no common data */ + *pErrorCode=U_FILE_ACCESS_ERROR; + return NULL; + } + + /* we have mapped a file, check its header */ + tData.pHeader=tData.pHeader; + checkCommonData(&tData, pErrorCode); + + + /* Cache the UDataMemory struct for this .dat file, + * so we won't need to hunt it down and map it again next time + * something is needed from it. */ + return udata_cacheDataItem(path, &tData, pErrorCode); } + + U_CAPI void U_EXPORT2 udata_setCommonData(const void *data, UErrorCode *pErrorCode) { + UDataMemory dataMemory; + if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return; } @@ -1048,52 +947,18 @@ udata_setCommonData(const void *data, UErrorCode *pErrorCode) { if(IS_DATA_MEMORY_LOADED(&commonICUData)) { *pErrorCode=U_USING_DEFAULT_ERROR; return; - } else { - /* normalize the data pointer and test for validity */ - UDataMemory dataMemory={ NULL }; - const DataHeader *pHeader=normalizeDataPointer((const DataHeader *)data); - if(!(pHeader->dataHeader.magic1==0xda && pHeader->dataHeader.magic2==0x27 && - pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && - pHeader->info.charsetFamily==U_CHARSET_FAMILY) - ) { - /* header not valid */ - *pErrorCode=U_INVALID_FORMAT_ERROR; - return; - - /* which TOC type? */ - } else if(pHeader->info.dataFormat[0]==0x43 && - pHeader->info.dataFormat[1]==0x6d && - pHeader->info.dataFormat[2]==0x6e && - pHeader->info.dataFormat[3]==0x44 && - pHeader->info.formatVersion[0]==1 - ) { - /* dataFormat="CmnD" */ - dataMemory.lookupFn=offsetTOCLookupFn; - dataMemory.toc=(const char *)pHeader+pHeader->dataHeader.headerSize; - dataMemory.flags=DLL_DATA_MEMORY|OFFSET_TOC; - } else if(pHeader->info.dataFormat[0]==0x54 && - pHeader->info.dataFormat[1]==0x6f && - pHeader->info.dataFormat[2]==0x43 && - pHeader->info.dataFormat[3]==0x50 && - pHeader->info.formatVersion[0]==1 - ) { - /* dataFormat="ToCP" */ - dataMemory.lookupFn=pointerTOCLookupFn; - dataMemory.toc=(const char *)pHeader+pHeader->dataHeader.headerSize; - dataMemory.flags=DLL_DATA_MEMORY|POINTER_TOC; - } else { - /* dataFormat not recognized */ - *pErrorCode=U_INVALID_FORMAT_ERROR; - return; - } - - /* we have common data */ - dataMemory.flags|=SET_DATA_POINTER_FLAG; - setCommonICUData(&dataMemory); - if(dataMemory.flags!=0) { - /* some thread passed us */ - *pErrorCode=U_USING_DEFAULT_ERROR; - } + } + + /* normalize the data pointer and test for validity */ + UDataMemory_init(&dataMemory); + dataMemory.pHeader = normalizeDataPointer((const DataHeader *)data); + checkCommonData(&dataMemory, pErrorCode); + if (U_FAILURE(*pErrorCode)) {return;} + + /* we have common data */ + if (setCommonICUData(&dataMemory) == FALSE) { + /* some thread passed us */ + *pErrorCode=U_USING_DEFAULT_ERROR; } } @@ -1105,15 +970,86 @@ udata_setCommonData(const void *data, UErrorCode *pErrorCode) { * udata_setAppData * *---------------------------------------------------------------------------- */ - U_CAPI void U_EXPORT2 udata_setAppData(const char *path, const void *data, UErrorCode *err) { + UDataMemory udm; + + if(err==NULL || U_FAILURE(*err)) { + return; + } + if(data==NULL) { + *err=U_ILLEGAL_ARGUMENT_ERROR; + return; + } + + UDataMemory_init(&udm); + udm.pHeader = data; + checkCommonData(&udm, err); + udata_cacheDataItem(path, &udm, err); }; +/*------------------------------------------------------------------------------* + * * + * checkDataItem Given a freshly located/loaded data item, either * + * an entry in a common file or a separately loaded file, * + * sanity check its header, and see if the data is * + * acceptable to the app. * + * If the data is good, create and return a UDataMemory * + * object that can be returned to the application. * + * Return NULL on any sort of failure. * + * * + *------------------------------------------------------------------------------*/ +static UDataMemory * +checkDataItem +( + const DataHeader *pHeader, /* The data item to be checked. */ + UDataMemoryIsAcceptable *isAcceptable, /* App's call-back function */ + void *context, /* pass-thru param for above. */ + const char *type, /* pass-thru param for above. */ + const char *name, /* pass-thru param for above. */ + UErrorCode *nonFatalErr, /* Error code if this data was not acceptable */ + /* but openChoice should continue with */ + /* trying to get data from fallback path. */ + UErrorCode *fatalErr /* Bad error, caller should return immediately */ + ) +{ + UDataMemory *rDataMem = NULL; /* the new UDataMemory, to be returned. */ + + if (U_FAILURE(*fatalErr)) { + return NULL; + } -/* main data loading function ----------------------------------------------- */ + if(pHeader->dataHeader.magic1==0xda && + pHeader->dataHeader.magic2==0x27 && + pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && + (isAcceptable==NULL || isAcceptable(context, type, name, &pHeader->info)) + ) { + rDataMem=UDataMemory_createNewInstance(fatalErr); + if (U_FAILURE(*fatalErr)) { + return NULL; + } + UDataMemory_init(rDataMem); + rDataMem->pHeader = pHeader; + } else { + /* the data is not acceptable, look further */ + /* If we eventually find something good, this errorcode will be */ + /* cleared out. */ + *nonFatalErr=U_INVALID_FORMAT_ERROR; + } + return rDataMem; +} + + +/*------------------------------------------------------------------------------* + * * + * setEntryNames Files Names and DLL entry point names have different * + * rules for what's valid. For a DLL entry point name, * + " change all '.' or '-'s to '_'. For both, append * + * '.typeName' (or _typeName) to the name * + * * + *------------------------------------------------------------------------------*/ static void setEntryNames(const char *type, const char *name, char *tocEntryName, char *dllEntryName) { @@ -1140,26 +1076,57 @@ setEntryNames(const char *type, const char *name, *tocEntryName=*dllEntryName=0; } + +/* + * A note on the ownership of Mapped Memory + * + * For common format files, ownership resides with the UDataMemory object + * that lives in the cache of opened common data. These UDataMemorys are private + * to the udata implementation, and are never seen directly by users. + * + * The UDataMemory objects returned to users will have the address of some desired + * data within the mapped region, but they wont have the mapping info itself, and thus + * won't cause anything to be removed from memory when they are closed. + * + * For individual data files, the UDataMemory returned to the user holds the + * information necessary to unmap the data on close. If the user independently + * opens the same data file twice, two completely independent mappings will be made. + * + * For common data passed in from the user via udata_setAppData() or + * udata_setCommonData(), ownership remains with the user. + * + * UDataMemory objects themselves, as opposed to the memory they describe, + * can be anywhere - heap, stack/local or global. + * They have a flag bit to indicate when they're heap allocated and thus + * must be deleted when closed. + */ + + +/* main data loading function ----------------------------------------------- */ + static UDataMemory * doOpenChoice(const char *path, const char *type, const char *name, UDataMemoryIsAcceptable *isAcceptable, void *context, - UErrorCode *pErrorCode) { - char pathBuffer[1024]; - char tocEntryName[100], dllEntryName[100]; - UDataMemory dataMemory; - UDataMemory *pCommonData, *pEntryData; - const DataHeader *pHeader; - const char *inBasename; - char *basename, *suffix; - UErrorCode errorCode=U_ZERO_ERROR; - UBool isICUData= (UBool)(path==NULL); + UErrorCode *pErrorCode) +{ + char pathBuffer[1024]; + char tocEntryName[100]; + char dllEntryName[100]; + UDataMemory dataMemory; + UDataMemory *pCommonData; + UDataMemory *pEntryData; + const DataHeader *pHeader; + const char *inBasename; + char *basename; + char *suffix; + UErrorCode errorCode=U_ZERO_ERROR; + UBool isICUData= (UBool)(path==NULL); /* set up the ToC names for DLL and offset-ToC lookups */ setEntryNames(type, name, tocEntryName, dllEntryName); /* try to get common data */ - pathBuffer[0]=0; pCommonData=openCommonData(path, isICUData, &errorCode); #ifdef UDATA_DEBUG fprintf(stderr, "commonData;%p\n", pCommonData); @@ -1173,35 +1140,22 @@ doOpenChoice(const char *path, const char *type, const char *name, fprintf(stderr, "Common found: %p\n", pHeader); #endif if(pHeader!=NULL) { - /* data found in the common data, test it */ - if(pHeader->dataHeader.magic1==0xda && pHeader->dataHeader.magic2==0x27 && - pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && - (isAcceptable==NULL || isAcceptable(context, type, name, &pHeader->info)) - ) { - pEntryData=UDataMemory_createNewInstance(); - if(pEntryData==NULL) { - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - UDataMemory_init(pEntryData); - pEntryData->parent = pCommonData; - pEntryData->pHeader = pHeader; - pEntryData->flags = (pCommonData->flags&DATA_MEMORY_TYPE_MASK)|DYNAMIC_DATA_MEMORY_FLAG; -#ifdef UDATA_DEBUG - fprintf(stderr, " made data @%p\n", pEntryData); -#endif + pEntryData = checkDataItem(pHeader, isAcceptable, context, type, name, &errorCode, pErrorCode); + if (U_FAILURE(*pErrorCode)) { + return NULL; + } + if (pEntryData != NULL) { return pEntryData; - } else { - /* the data is not acceptable, look further */ - /* If we eventually find something good, this errorcode will be */ - /* cleared out. */ - errorCode=U_INVALID_FORMAT_ERROR; } } - - /* the data was not found in the common data, look further */ + } + /* TODO: fall back to alternate/extended common data for 390 + * goes HERE. + */ + + /* the data was not found in the common data, look further */ /* try to get an individual data file */ basename=setPathGetBasename(path, pathBuffer); if(isICUData) { @@ -1221,56 +1175,60 @@ doOpenChoice(const char *path, const char *type, const char *name, uprv_strcpy(suffix, tocEntryName); if( uprv_mapFile(&dataMemory, pathBuffer) || - (basename!=pathBuffer && uprv_mapFile(&dataMemory, basename)) - ) { - pHeader=dataMemory.pHeader; - if(pHeader->dataHeader.magic1==0xda && pHeader->dataHeader.magic2==0x27 && - pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && - (isAcceptable==NULL || isAcceptable(context, type, name, &pHeader->info)) - ) { - /* acceptable */ - pEntryData=(UDataMemory *)uprv_malloc(sizeof(UDataMemory)); - if(pEntryData==NULL) { - uprv_unmapFile(&dataMemory); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - dataMemory.flags |= DYNAMIC_DATA_MEMORY_FLAG; - uprv_memcpy(pEntryData, &dataMemory, sizeof(UDataMemory)); + (basename!=pathBuffer && uprv_mapFile(&dataMemory, basename))) + { + /* We mapped a file. Check out its contents. */ + pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, &errorCode, pErrorCode); + if (pEntryData != NULL) + { + /* Got good data. + * Hand off ownership of the backing memory to the user's UDataMemory. + * and return it. */ + pEntryData->mapAddr = dataMemory.mapAddr; + pEntryData->map = dataMemory.map; return pEntryData; - } else { - /* the data is not acceptable, look further */ - uprv_unmapFile(&dataMemory); - errorCode=U_INVALID_FORMAT_ERROR; } + + /* the data is not acceptable, or some error occured. Either way, unmap the memory */ + uprv_unmapFile(&dataMemory); + + /* If we had a nasty error, bail out completely. */ + if (U_FAILURE(*pErrorCode)) { + return NULL; + } + + /* Otherwise remember that we found data but didn't like it for some reason, + * and continue looking + */ + errorCode=U_INVALID_FORMAT_ERROR; } } /* try path+entryName next */ uprv_strcpy(basename, tocEntryName); if( uprv_mapFile(&dataMemory, pathBuffer) || - (basename!=pathBuffer && uprv_mapFile(&dataMemory, basename)) - ) { - pHeader=dataMemory.pHeader; - if(pHeader->dataHeader.magic1==0xda && pHeader->dataHeader.magic2==0x27 && - pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && - (isAcceptable==NULL || isAcceptable(context, type, name, &pHeader->info)) - ) { - /* acceptable */ - pEntryData=(UDataMemory *)uprv_malloc(sizeof(UDataMemory)); - if(pEntryData==NULL) { - uprv_unmapFile(&dataMemory); - *pErrorCode=U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - dataMemory.flags |= DYNAMIC_DATA_MEMORY_FLAG; - uprv_memcpy(pEntryData, &dataMemory, sizeof(UDataMemory)); + (basename!=pathBuffer && uprv_mapFile(&dataMemory, basename))) + { + pEntryData = checkDataItem(dataMemory.pHeader, isAcceptable, context, type, name, &errorCode, pErrorCode); + if (pEntryData != NULL) { + /* Data is good. + * Hand off ownership of the backing memory to the user's UDataMemory. + * and return it. */ + pEntryData->mapAddr = dataMemory.mapAddr; + pEntryData->map = dataMemory.map; return pEntryData; - } else { - /* the data is not acceptable, look further */ - uprv_unmapFile(&dataMemory); - errorCode=U_INVALID_FORMAT_ERROR; } + + /* the data is not acceptable, or some error occured. Either way, unmap the memory */ + uprv_unmapFile(&dataMemory); + + /* If we had a nasty error, bail out completely. */ + if (U_FAILURE(*pErrorCode)) { + return NULL; + } + + /* Otherwise remember that we found data but didn't like it for some reason */ + errorCode=U_INVALID_FORMAT_ERROR; } /* data not found */ @@ -1288,24 +1246,11 @@ doOpenChoice(const char *path, const char *type, const char *name, static void unloadDataMemory(UDataMemory *pData) { - if ((pData->flags & SET_DATA_POINTER_FLAG) == 0) { - switch(pData->flags&DATA_MEMORY_TYPE_MASK) { - case FLAT_DATA_MEMORY: - if(IS_MAP(pData->map)) { - uprv_unmapFile(pData); - } - break; - case DLL_DATA_MEMORY: - if(IS_LIBRARY(pData->lib)) { - UNLOAD_LIBRARY(pData->lib); - } - break; - default: - break; - } - } + uprv_unmapFile(pData); } + + /* API ---------------------------------------------------------------------- */ U_CAPI UDataMemory * U_EXPORT2 @@ -1347,18 +1292,15 @@ udata_openChoice(const char *path, const char *type, const char *name, U_CAPI void U_EXPORT2 udata_close(UDataMemory *pData) { #ifdef UDATA_DEBUG - fprintf(stderr, "udata_close()\n");fflush(stderr); + fprintf(stderr, "udata_close()\n");fflush(stderr); #endif - - if(pData!=NULL && - IS_DATA_MEMORY_LOADED(pData)) { - // TODO: maybe reference count cached data, rather than just - // permanently keeping it around? + + if(pData!=NULL) { unloadDataMemory(pData); - if(pData->flags & DYNAMIC_DATA_MEMORY_FLAG ) { + if(pData->flags & MALLOCED_UDATAMEMORY_FLAG ) { uprv_free(pData); } else { - pData->flags=0; + UDataMemory_init(pData); } } } diff --git a/icu4c/source/common/unicode/udata.h b/icu4c/source/common/unicode/udata.h index ab97997bc0..759cf2f8d4 100644 --- a/icu4c/source/common/unicode/udata.h +++ b/icu4c/source/common/unicode/udata.h @@ -268,9 +268,10 @@ udata_setCommonData(const void *data, UErrorCode *err); * data and allows you to force the it to come out of a user-specified * pointer. * - * The format of this data is that of the icu common data file, 'icudata.dat' - * Read in or memory map the whole file and then pass the address to the start of the - * data to this function. + * The format of this data is that of the icu common data file, like 'icudata.dat' + * The application must read in or otherwise construct an image of the data and then + * pass the address of it to this function. + * * * Warning: setAppData will fail with a U_USING_DEFAULT_ERROR error if * data with the specifed path that has already been opened, or @@ -288,6 +289,20 @@ udata_setCommonData(const void *data, UErrorCode *err); U_CAPI void U_EXPORT2 udata_setAppData(const char *path, const void *data, UErrorCode *err); +/** + * Clean-up function for allocated memory and any other resources owned + * by udata. Deletes all such items. Everything is returned to its initial + * state. + * + * This function is called by . Applicataions normally + * have no reason to call it directly. + * + * @draft + */ +U_CAPI void U_EXPORT2 +udata_cleanup(); + + U_CDECL_END #endif diff --git a/icu4c/source/data/Makefile.in b/icu4c/source/data/Makefile.in index 8d74d335af..82fd943ffe 100644 --- a/icu4c/source/data/Makefile.in +++ b/icu4c/source/data/Makefile.in @@ -40,7 +40,7 @@ TESTDATAOBJDIR=../test/testdata top_builddir_from_tmp = $(patsubst ..%,../..%,$(top_builddir)) INVOKE = $(LDLIBRARYPATH_ENVVAR)=$(top_builddir_from_tmp)/common:$(top_builddir_from_tmp)/tools/toolutil:$(top_builddir_from_tmp)/stubdata:$$$(LDLIBRARYPATH_ENVVAR) CURDIR=$(shell pwd) -PKGDATA = $(top_builddir_from_tmp)/tools/pkgdata/pkgdata -O $(top_builddir_from_tmp)/tools/pkgdata/icupkg.inc -d $(CURDIR) -m $(PKGDATA_MODE) +PKGDATA = $(top_builddir_from_tmp)/tools/pkgdata/pkgdata -O $(top_builddir_from_tmp)/tools/pkgdata/icupkg.inc -d $(CURDIR) ## List of phony targets @@ -105,26 +105,24 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status packagedata: build/icudata.lst $(top_builddir)/tools/pkgdata/icupkg.inc ( cd build ; \ - $(INVOKE) $(PKGDATA) -e $(ICUDATA_NAME) -s . -T . -p $(ICUDATA_NAME) icudata.lst ; \ + $(INVOKE) $(PKGDATA) -e $(ICUDATA_NAME) -s . -T . -p $(ICUDATA_NAME) -m dll icudata.lst ; \ ) $(RM) libicudata.$(SO) && ln -s $(LIB_ICUDATA_NAME).$(SO) libicudata.$(SO) ; \ packagetest: $(TESTDATAOBJDIR)/testdata.lst $(top_builddir)/tools/pkgdata/icupkg.inc - ( cd $(TESTDATAOBJDIR) ; $(INVOKE) $(PKGDATA) -T . -s . -p testdata testdata.lst ) + ( cd $(TESTDATAOBJDIR) ; $(INVOKE) $(PKGDATA) -T . -s . -p testdata -m common testdata.lst ) cleanpackage: @echo Cleaning up packaged data.. - @-( cd build ; $(INVOKE) $(PKGDATA) -s . -T . -p $(ICUDATA_NAME) icudata.lst --clean 2> /dev/null) - @-( cd $(TESTDATAOBJDIR) ; $(INVOKE) $(PKGDATA) -T . -s . -p testdata testdata.lst --clean 2> /dev/null ) + @-( cd build ; $(INVOKE) $(PKGDATA) -m dll -s . -T . -p $(ICUDATA_NAME) icudata.lst --clean 2> /dev/null) + @-( cd $(TESTDATAOBJDIR) ; $(INVOKE) $(PKGDATA) -m common -T . -s . -p testdata testdata.lst --clean 2> /dev/null ) $(RMV) $(ICUDATA_NAME).* build/icudata.lst build/*.mak ## Install ICU data. Make a convenience link 'icudata' library if it's a library mode. install-local: build/icudata.lst $(top_builddir)/tools/pkgdata/icupkg.inc install-convrtrstxt $(MKINSTALLDIRS) $(TMPDATADIR) $(DESTDIR)$(ICUDATA_DIR)/$(VERSION) - ( cd build ; $(INVOKE) $(PKGDATA) -e icudata -T . -s . -p $(ICUDATA_NAME) icudata.lst -I $(DESTDIR)$(ICUDATA_DIR)/$(VERSION) ) -ifeq ($(strip $(PKGDATA_MODE)),dll) + ( cd build ; $(INVOKE) $(PKGDATA) -m dll -e icudata -T . -s . -p $(ICUDATA_NAME) icudata.lst -I $(DESTDIR)$(ICUDATA_DIR)/$(VERSION) ) (cd $(DESTDIR)$(ICUDATA_DIR)/$(VERSION); $(RM) icudata.$(SO) && ln -s $(LIB_ICUDATA_NAME).$(SO) icudata.$(SO)) -endif install-convrtrstxt: $(top_srcdir)/../data/convrtrs.txt $(MKINSTALLDIRS) $(DESTDIR)$(pkgsysconfdir) diff --git a/icu4c/source/test/cintltst/udatatst.c b/icu4c/source/test/cintltst/udatatst.c index 0e11dbe07b..cee573b927 100644 --- a/icu4c/source/test/cintltst/udatatst.c +++ b/icu4c/source/test/cintltst/udatatst.c @@ -51,7 +51,6 @@ addUDataTest(TestNode** root) } static void TestUDataOpen(){ - int i; UDataMemory *result; UErrorCode status=U_ZERO_ERROR; const char* memMap[][2]={ @@ -79,16 +78,24 @@ static void TestUDataOpen(){ udata_close(result); } - for(i=0; i