/* ******************************************************************************* * * Copyright (C) 1999-2000, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* * file name: udata.c * encoding: US-ASCII * tab size: 8 (not used) * indentation:4 * * created on: 1999oct25 * created by: Markus W. Scherer */ #include "unicode/utypes.h" #include "unicode/putil.h" #include "umutex.h" #include "cmemory.h" #include "cstring.h" #include "unicode/udata.h" /* configuration ------------------------------------------------------------ */ #if !defined(HAVE_DLOPEN) # define HAVE_DLOPEN 0 #endif #if !defined(UDATA_DLL) && !defined(UDATA_MAP) # define UDATA_DLL #endif #define COMMON_DATA_NAME U_ICUDATA_NAME #define COMMON_DATA_NAME_LENGTH 8 /* Tests must verify that it remains 8 characters. */ #define DATA_TYPE "dat" /* If you are excruciatingly bored turn this on .. */ /* #define UDATA_DEBUG 1 */ #if defined(UDATA_DEBUG) # include #endif /* DLL/shared library base functions ---------------------------------------- */ #ifdef WIN32 # 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 # include # define RTLD_LAZY 0 # define RTLD_GLOBAL 0 void *dlopen(const char *filename, int flag) { dllhandle *handle; # ifdef UDATA_DEBUG fprintf(stderr, "dllload: %s ", filename); # endif handle=dllload(filename); # ifdef UDATA_DEBUG fprintf(stderr, " -> %08X\n", handle ); # endif return handle; } void *dlsym(void *h, const char *symbol) { void *val=0; val=dllqueryvar((dllhandle*)h,symbol); # ifdef UDATA_DEBUG fprintf(stderr, "dllqueryvar(%08X, %s) -> %08X\n", h, symbol, val); # endif return val; } int dlclose(void *handle) { # ifdef UDATA_DEBUG fprintf(stderr, "dllfree: %08X\n", handle); # 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) #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 ------------------------------------------ */ /* we need these definitions before the common ones because MemoryMap is a field of UDataMemory; however, the mapping functions use UDataMemory, therefore they are defined later */ #define MAP_WIN32 1 #define MAP_POSIX 2 #define MAP_FILE_STREAM 3 #ifdef WIN32 typedef HANDLE MemoryMap; # define NO_MAP NULL # define IS_MAP(map) ((map)!=NULL) # define MAP_IMPLEMENTATION MAP_WIN32 /* ### Todo: auto detect mmap(). Until then, just add your platform here. */ #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 # include # include # include # ifndef MAP_FAILED # define MAP_FAILED ((void*)-1) # endif # define MAP_IMPLEMENTATION MAP_POSIX #else /* unknown platform, no memory map implementation: use FileStream/uprv_malloc() instead */ # include "filestrm.h" typedef void *MemoryMap; # define NO_MAP NULL # define IS_MAP(map) ((map)!=NULL) # define MAP_IMPLEMENTATION MAP_FILE_STREAM #endif /* common definitions ------------------------------------------------------- */ /* constants for UDataMemory flags: type of data memory */ enum { NO_DATA_MEMORY, FLAT_DATA_MEMORY, DLL_DATA_MEMORY }; /* constants for UDataMemory flags: type of TOC */ enum { NO_TOC, OFFSET_TOC, POINTER_TOC, DLL_INTRINSIC_TOC }; /* constants for UDataMemory flags: type of TOC */ #define DATA_MEMORY_TYPE_MASK 0xf #define TOC_TYPE_SHIFT 4 #define TOC_TYPE_MASK 0xf #define SET_DATA_POINTER_SHIFT 30 #define DYNAMIC_DATA_MEMORY_SHIFT 31 typedef struct { uint16_t headerSize; uint8_t magic1, magic2; } MappedData; typedef struct { MappedData dataHeader; UDataInfo info; } DataHeader; typedef const DataHeader * LookupFn(const UDataMemory *pData, const char *tocEntryName, const char *dllEntryName, UErrorCode *pErrorCode); struct UDataMemory { UDataMemory *parent; Library lib; MemoryMap map; LookupFn *lookupFn; const void *toc; const DataHeader *pHeader; uint32_t flags; int32_t refCount; }; #define IS_DATA_MEMORY_LOADED(pData) ((pData)->flags!=0) typedef struct { const char *entryName; const DataHeader *pHeader; } PointerTOCEntry; /* memory-mapping base functions -------------------------------------------- */ #if MAP_IMPLEMENTATION==MAP_WIN32 static UBool uprv_mapFile(UDataMemory *pData, const char *path, const char *basename) { char buffer[200]; HANDLE map; char *p; /* set up the mapping name and the filename */ uprv_strcpy(buffer, "icu" U_ICU_VERSION " "); uprv_strcat(buffer, path); /* replace in buffer \ with / */ for(p=buffer; *p; p++) { if (*p == '\\') { *p = '/'; } } /* open the mapping */ map=OpenFileMapping(FILE_MAP_READ, FALSE, buffer); if(map==NULL) { /* the mapping has not been created */ HANDLE file; /* open the input file */ file=CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, NULL); if(file==INVALID_HANDLE_VALUE) { return FALSE; } /* create the mapping */ map=CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, buffer); CloseHandle(file); if(map==NULL) { return FALSE; } } /* get a view of the mapping */ pData->pHeader=(const DataHeader *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); if(pData->pHeader==NULL) { CloseHandle(map); return FALSE; } pData->map=map; pData->flags=FLAT_DATA_MEMORY; return TRUE; } static void uprv_unmapFile(UDataMemory *pData) { if(pData!=NULL && pData->map!=NULL) { UnmapViewOfFile(pData->pHeader); CloseHandle(pData->map); pData->pHeader=NULL; pData->map=NULL; } } #elif MAP_IMPLEMENTATION==MAP_POSIX static UBool uprv_mapFile(UDataMemory *pData, const char *path, const char *basename) { int fd; int length; const char *dataDir; struct stat mystat; const void *data; /* determine the length of the file */ if(stat(path, &mystat)!=0 || mystat.st_size<=0) { return FALSE; } length=mystat.st_size; /* open the file */ fd=open(path, O_RDONLY); if(fd==-1) { return FALSE; } /* get a view of the mapping */ #ifndef HPUX data=mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); #else data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); #endif close(fd); /* no longer needed */ if(data==MAP_FAILED) { # ifdef UDATA_DEBUG perror("mmap"); # endif return FALSE; } # ifdef UDATA_DEBUG fprintf(stderr, "mmap of %s [%d bytes] succeeded, -> 0x%X\n", path, length, data); fflush(stderr); # endif pData->map=length; pData->pHeader=(const DataHeader *)data; pData->flags=FLAT_DATA_MEMORY; return TRUE; } static void uprv_unmapFile(UDataMemory *pData) { if(pData!=NULL && pData->map>0) { if(munmap((void *)pData->pHeader, pData->map)==-1) { # ifdef UDATA_DEBUG perror("munmap"); # endif } pData->pHeader=NULL; pData->map=0; } } #elif MAP_IMPLEMENTATION==MAP_FILE_STREAM static UBool uprv_mapFile(UDataMemory *pData, const char *path, const char *basename) { FileStream *file; int32_t fileLength; void *p; /* open the input file */ file=T_FileStream_open(path, "rb"); if(file==NULL) { return FALSE; } /* get the file length */ fileLength=T_FileStream_size(file); if(T_FileStream_error(file) || fileLength<=20) { T_FileStream_close(file); return FALSE; } /* allocate the data structure */ p=uprv_malloc(fileLength); if(p==NULL) { T_FileStream_close(file); return FALSE; } /* read the file */ if(fileLength!=T_FileStream_read(file, p, fileLength)) { uprv_free(p); T_FileStream_close(file); return FALSE; } T_FileStream_close(file); pData->map=p; pData->pHeader=(const DataHeader *)p; pData->flags=FLAT_DATA_MEMORY; return TRUE; } static void uprv_unmapFile(UDataMemory *pData) { if(pData!=NULL && pData->map!=NULL) { uprv_free(pData->map); pData->map=NULL; } } #else # error MAP_IMPLEMENTATION is set incorrectly #endif /* entry point lookup implementations --------------------------------------- */ static const DataHeader * normalizeDataPointer(const DataHeader *p) { /* allow the data to be optionally prepended with an alignment-forcing double value */ if(p==NULL || (p->dataHeader.magic1==0xda && p->dataHeader.magic2==0x27)) { return p; } else { return (const DataHeader *)((const double *)p+1); } } static const DataHeader * offsetTOCLookupFn(const UDataMemory *pData, const char *tocEntryName, const char *dllEntryName, UErrorCode *pErrorCode) { #ifdef UDATA_DEBUG fprintf(stderr, "offsetTOC[%p] looking for %s/%s\n", pData, tocEntryName,dllEntryName); #endif if(pData->toc!=NULL) { const char *base=(const char *)pData->toc; uint32_t *toc=(uint32_t *)pData->toc; uint32_t start, limit, number; /* perform a binary search for the data in the common data's table of contents */ start=0; limit=*toc++; /* number of names in this table of contents */ while(startpHeader; } } static const DataHeader * pointerTOCLookupFn(const UDataMemory *pData, const char *tocEntryName, const char *dllEntryName, UErrorCode *pErrorCode) { #ifdef UDATA_DEBUG fprintf(stderr, "ptrTOC[%p] looking for %s/%s\n", pData, tocEntryName,dllEntryName); #endif if(pData->toc!=NULL) { const PointerTOCEntry *toc=(const PointerTOCEntry *)((const uint32_t *)pData->toc+2); uint32_t start, limit, number; /* perform a binary search for the data in the common data's table of contents */ start=0; limit=*(const uint32_t *)pData->toc; /* number of names in this table of contents */ #ifdef UDATA_DEBUG fprintf(stderr, " # of ents: %d\n", limit); fflush(stderr); #endif while(startpHeader; } } 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 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&=~(1UL<lib=lib; pData->lookupFn=dllTOCLookupFn; pData->flags=DLL_DATA_MEMORY|DLL_INTRINSIC_TOC<dataHeader.magic1==0xda && pHeader->dataHeader.magic2==0x27 && pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && pHeader->info.charsetFamily==U_CHARSET_FAMILY) ) { /* header not valid */ UNLOAD_LIBRARY(lib); *pErrorCode=U_INVALID_FORMAT_ERROR; return NULL; /* 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" */ pData->lib=lib; pData->lookupFn=offsetTOCLookupFn; pData->toc=(const char *)pHeader+pHeader->dataHeader.headerSize; pData->flags=DLL_DATA_MEMORY|OFFSET_TOC<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" */ pData->lib=lib; pData->lookupFn=pointerTOCLookupFn; pData->toc=(const char *)pHeader+pHeader->dataHeader.headerSize; pData->flags=DLL_DATA_MEMORY|POINTER_TOC<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(pData); pData->flags=0; *pErrorCode=U_INVALID_FORMAT_ERROR; return NULL; } /* we have common data from a mapped file */ pData->lookupFn=offsetTOCLookupFn; pData->toc=(const char *)pHeader+pHeader->dataHeader.headerSize; pData->flags|=OFFSET_TOC<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<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<lookupFn(pCommonData, tocEntryName, dllEntryName, &errorCode); #ifdef UDATA_DEBUG 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)) ) { /* acceptable - allocate parent, too, if necessary */ if(pCommonData==&dataMemory) { /* trick: do one malloc for both the common and the entry */ pEntryData=(UDataMemory *)uprv_malloc(2*sizeof(UDataMemory)); if(pEntryData!=NULL) { pCommonData=pEntryData+1; uprv_memcpy(pCommonData, &dataMemory, sizeof(UDataMemory)); } } else { pEntryData=(UDataMemory *)uprv_malloc(sizeof(UDataMemory)); } if(pEntryData==NULL) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return NULL; } uprv_memset(pEntryData, 0, sizeof(UDataMemory)); pEntryData->parent=pCommonData; pEntryData->pHeader=pHeader; pEntryData->flags=(pCommonData->flags&DATA_MEMORY_TYPE_MASK)|1UL<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|=1UL<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|=1UL<flags&(1UL<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; } } } /* API ---------------------------------------------------------------------- */ U_CAPI UDataMemory * U_EXPORT2 udata_open(const char *path, const char *type, const char *name, UErrorCode *pErrorCode) { #ifdef UDATA_DEBUG fprintf(stderr, "udata_open(): Opening: %s . %s\n", name, type);fflush(stderr); #endif if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return NULL; } else if(name==NULL || *name==0) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return NULL; } else { return doOpenChoice(path, type, name, NULL, NULL, pErrorCode); } } U_CAPI UDataMemory * U_EXPORT2 udata_openChoice(const char *path, const char *type, const char *name, UDataMemoryIsAcceptable *isAcceptable, void *context, UErrorCode *pErrorCode) { #ifdef UDATA_DEBUG fprintf(stderr, "udata_openChoice(): Opening: %s . %s\n", name, type);fflush(stderr); #endif if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return NULL; } else if(name==NULL || *name==0 || isAcceptable==NULL) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return NULL; } else { return doOpenChoice(path, type, name, isAcceptable, context, pErrorCode); } } U_CAPI void U_EXPORT2 udata_close(UDataMemory *pData) { #ifdef UDATA_DEBUG fprintf(stderr, "udata_close()\n");fflush(stderr); #endif if(pData!=NULL && IS_DATA_MEMORY_LOADED(pData)) { unloadDataMemory(pData); if(pData->flags&(1UL<parent==pData+1) { /* this data entry was allocated together with its parent */ unloadDataMemory(pData+1); } uprv_free(pData); } else { pData->flags=0; } } } U_CAPI const void * U_EXPORT2 udata_getMemory(UDataMemory *pData) { if(pData!=NULL && pData->pHeader!=NULL) { return (char *)(pData->pHeader)+pData->pHeader->dataHeader.headerSize; } else { return NULL; } } U_CAPI void U_EXPORT2 udata_getInfo(UDataMemory *pData, UDataInfo *pInfo) { if(pInfo!=NULL) { if(pData!=NULL && pData->pHeader!=NULL) { const UDataInfo *info=&pData->pHeader->info; uint16_t size=pInfo->size; if(size>info->size) { pInfo->size=info->size; } uprv_memcpy((uint16_t *)pInfo+1, (uint16_t *)info+1, size-2); } else { pInfo->size=0; } } }