michael@0: /* michael@0: ****************************************************************************** michael@0: * michael@0: * Copyright (C) 1999-2011, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: michael@0: /*------------------------------------------------------------------------------ michael@0: * michael@0: * UCommonData An abstract interface for dealing with ICU Common Data Files. michael@0: * ICU Common Data Files are a grouping of a number of individual michael@0: * data items (resources, converters, tables, anything) into a michael@0: * single file or dll. The combined format includes a table of michael@0: * contents for locating the individual items by name. michael@0: * michael@0: * Two formats for the table of contents are supported, which is michael@0: * why there is an abstract inteface involved. michael@0: * michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: #include "unicode/udata.h" michael@0: #include "cstring.h" michael@0: #include "ucmndata.h" michael@0: #include "udatamem.h" michael@0: michael@0: #if defined(UDATA_DEBUG) || defined(UDATA_DEBUG_DUMP) michael@0: # include michael@0: #endif michael@0: michael@0: U_CFUNC uint16_t michael@0: udata_getHeaderSize(const DataHeader *udh) { michael@0: if(udh==NULL) { michael@0: return 0; michael@0: } else if(udh->info.isBigEndian==U_IS_BIG_ENDIAN) { michael@0: /* same endianness */ michael@0: return udh->dataHeader.headerSize; michael@0: } else { michael@0: /* opposite endianness */ michael@0: uint16_t x=udh->dataHeader.headerSize; michael@0: return (uint16_t)((x<<8)|(x>>8)); michael@0: } michael@0: } michael@0: michael@0: U_CFUNC uint16_t michael@0: udata_getInfoSize(const UDataInfo *info) { michael@0: if(info==NULL) { michael@0: return 0; michael@0: } else if(info->isBigEndian==U_IS_BIG_ENDIAN) { michael@0: /* same endianness */ michael@0: return info->size; michael@0: } else { michael@0: /* opposite endianness */ michael@0: uint16_t x=info->size; michael@0: return (uint16_t)((x<<8)|(x>>8)); michael@0: } michael@0: } michael@0: michael@0: /*-----------------------------------------------------------------------------* michael@0: * * michael@0: * Pointer TOCs. TODO: This form of table-of-contents should be removed * michael@0: * because DLLs must be relocated on loading to correct the * michael@0: * pointer values and this operation makes shared memory * michael@0: * mapping of the data much less likely to work. * michael@0: * * michael@0: *-----------------------------------------------------------------------------*/ michael@0: typedef struct { michael@0: const char *entryName; michael@0: const DataHeader *pHeader; michael@0: } PointerTOCEntry; michael@0: michael@0: michael@0: typedef struct { michael@0: uint32_t count; michael@0: uint32_t reserved; michael@0: PointerTOCEntry entry[2]; /* Actual size is from count. */ michael@0: } PointerTOC; michael@0: michael@0: michael@0: /* definition of OffsetTOC struct types moved to ucmndata.h */ michael@0: michael@0: /*-----------------------------------------------------------------------------* michael@0: * * michael@0: * entry point lookup implementations * michael@0: * * michael@0: *-----------------------------------------------------------------------------*/ michael@0: michael@0: #ifndef MIN michael@0: #define MIN(a,b) (((a)<(b)) ? (a) : (b)) michael@0: #endif michael@0: michael@0: /** michael@0: * Compare strings where we know the shared prefix length, michael@0: * and advance the prefix length as we find that the strings share even more characters. michael@0: */ michael@0: static int32_t michael@0: strcmpAfterPrefix(const char *s1, const char *s2, int32_t *pPrefixLength) { michael@0: int32_t pl=*pPrefixLength; michael@0: int32_t cmp=0; michael@0: s1+=pl; michael@0: s2+=pl; michael@0: for(;;) { michael@0: int32_t c1=(uint8_t)*s1++; michael@0: int32_t c2=(uint8_t)*s2++; michael@0: cmp=c1-c2; michael@0: if(cmp!=0 || c1==0) { /* different or done */ michael@0: break; michael@0: } michael@0: ++pl; /* increment shared same-prefix length */ michael@0: } michael@0: *pPrefixLength=pl; michael@0: return cmp; michael@0: } michael@0: michael@0: static int32_t michael@0: offsetTOCPrefixBinarySearch(const char *s, const char *names, michael@0: const UDataOffsetTOCEntry *toc, int32_t count) { michael@0: int32_t start=0; michael@0: int32_t limit=count; michael@0: /* michael@0: * Remember the shared prefix between s, start and limit, michael@0: * and don't compare that shared prefix again. michael@0: * The shared prefix should get longer as we narrow the [start, limit[ range. michael@0: */ michael@0: int32_t startPrefixLength=0; michael@0: int32_t limitPrefixLength=0; michael@0: if(count==0) { michael@0: return -1; michael@0: } michael@0: /* michael@0: * Prime the prefix lengths so that we don't keep prefixLength at 0 until michael@0: * both the start and limit indexes have moved. michael@0: * At the same time, we find if s is one of the start and (limit-1) names, michael@0: * and if not, exclude them from the actual binary search. michael@0: */ michael@0: if(0==strcmpAfterPrefix(s, names+toc[0].nameOffset, &startPrefixLength)) { michael@0: return 0; michael@0: } michael@0: ++start; michael@0: --limit; michael@0: if(0==strcmpAfterPrefix(s, names+toc[limit].nameOffset, &limitPrefixLength)) { michael@0: return limit; michael@0: } michael@0: while(starttoc; michael@0: if (toc != NULL) { michael@0: retVal = toc->count; michael@0: } michael@0: return retVal; michael@0: } michael@0: michael@0: static const DataHeader * michael@0: offsetTOCLookupFn(const UDataMemory *pData, michael@0: const char *tocEntryName, michael@0: int32_t *pLength, michael@0: UErrorCode *pErrorCode) { michael@0: const UDataOffsetTOC *toc = (UDataOffsetTOC *)pData->toc; michael@0: if(toc!=NULL) { michael@0: const char *base=(const char *)toc; michael@0: int32_t number, count=(int32_t)toc->count; michael@0: michael@0: /* perform a binary search for the data in the common data's table of contents */ michael@0: #if defined (UDATA_DEBUG_DUMP) michael@0: /* list the contents of the TOC each time .. not recommended */ michael@0: for(number=0; numberentry[number].nameOffset]); michael@0: } michael@0: #endif michael@0: number=offsetTOCPrefixBinarySearch(tocEntryName, base, toc->entry, count); michael@0: if(number>=0) { michael@0: /* found it */ michael@0: const UDataOffsetTOCEntry *entry=toc->entry+number; michael@0: #ifdef UDATA_DEBUG michael@0: fprintf(stderr, "%s: Found.\n", tocEntryName); michael@0: #endif michael@0: if((number+1) < count) { michael@0: *pLength = (int32_t)(entry[1].dataOffset - entry->dataOffset); michael@0: } else { michael@0: *pLength = -1; michael@0: } michael@0: return (const DataHeader *)(base+entry->dataOffset); michael@0: } else { michael@0: #ifdef UDATA_DEBUG michael@0: fprintf(stderr, "%s: Not found.\n", tocEntryName); michael@0: #endif michael@0: return NULL; michael@0: } michael@0: } else { michael@0: #ifdef UDATA_DEBUG michael@0: fprintf(stderr, "returning header\n"); michael@0: #endif michael@0: michael@0: return pData->pHeader; michael@0: } michael@0: } michael@0: michael@0: michael@0: static uint32_t pointerTOCEntryCount(const UDataMemory *pData) { michael@0: const PointerTOC *toc = (PointerTOC *)pData->toc; michael@0: return (uint32_t)((toc != NULL) ? (toc->count) : 0); michael@0: } michael@0: michael@0: michael@0: static const DataHeader *pointerTOCLookupFn(const UDataMemory *pData, michael@0: const char *name, michael@0: int32_t *pLength, michael@0: UErrorCode *pErrorCode) { michael@0: if(pData->toc!=NULL) { michael@0: const PointerTOC *toc = (PointerTOC *)pData->toc; michael@0: int32_t number, count=(int32_t)toc->count; michael@0: michael@0: #if defined (UDATA_DEBUG_DUMP) michael@0: /* list the contents of the TOC each time .. not recommended */ michael@0: for(number=0; numberentry[number].entryName); michael@0: } michael@0: #endif michael@0: number=pointerTOCPrefixBinarySearch(name, toc->entry, count); michael@0: if(number>=0) { michael@0: /* found it */ michael@0: #ifdef UDATA_DEBUG michael@0: fprintf(stderr, "%s: Found.\n", toc->entry[number].entryName); michael@0: #endif michael@0: *pLength=-1; michael@0: return UDataMemory_normalizeDataPointer(toc->entry[number].pHeader); michael@0: } else { michael@0: #ifdef UDATA_DEBUG michael@0: fprintf(stderr, "%s: Not found.\n", name); michael@0: #endif michael@0: return NULL; michael@0: } michael@0: } else { michael@0: return pData->pHeader; michael@0: } michael@0: } michael@0: michael@0: static const commonDataFuncs CmnDFuncs = {offsetTOCLookupFn, offsetTOCEntryCount}; michael@0: static const commonDataFuncs ToCPFuncs = {pointerTOCLookupFn, pointerTOCEntryCount}; michael@0: michael@0: michael@0: michael@0: /*----------------------------------------------------------------------* michael@0: * * michael@0: * checkCommonData Validate the format of a common data file. * michael@0: * Fill in the virtual function ptr based on TOC type * michael@0: * If the data is invalid, close the UDataMemory * michael@0: * and set the appropriate error code. * michael@0: * * michael@0: *----------------------------------------------------------------------*/ michael@0: U_CFUNC void udata_checkCommonData(UDataMemory *udm, UErrorCode *err) { michael@0: if (U_FAILURE(*err)) { michael@0: return; michael@0: } michael@0: michael@0: if(udm==NULL || udm->pHeader==NULL) { michael@0: *err=U_INVALID_FORMAT_ERROR; michael@0: } else if(!(udm->pHeader->dataHeader.magic1==0xda && michael@0: udm->pHeader->dataHeader.magic2==0x27 && michael@0: udm->pHeader->info.isBigEndian==U_IS_BIG_ENDIAN && michael@0: udm->pHeader->info.charsetFamily==U_CHARSET_FAMILY) michael@0: ) { michael@0: /* header not valid */ michael@0: *err=U_INVALID_FORMAT_ERROR; michael@0: } michael@0: else if (udm->pHeader->info.dataFormat[0]==0x43 && michael@0: udm->pHeader->info.dataFormat[1]==0x6d && michael@0: udm->pHeader->info.dataFormat[2]==0x6e && michael@0: udm->pHeader->info.dataFormat[3]==0x44 && michael@0: udm->pHeader->info.formatVersion[0]==1 michael@0: ) { michael@0: /* dataFormat="CmnD" */ michael@0: udm->vFuncs = &CmnDFuncs; michael@0: udm->toc=(const char *)udm->pHeader+udata_getHeaderSize(udm->pHeader); michael@0: } michael@0: else if(udm->pHeader->info.dataFormat[0]==0x54 && michael@0: udm->pHeader->info.dataFormat[1]==0x6f && michael@0: udm->pHeader->info.dataFormat[2]==0x43 && michael@0: udm->pHeader->info.dataFormat[3]==0x50 && michael@0: udm->pHeader->info.formatVersion[0]==1 michael@0: ) { michael@0: /* dataFormat="ToCP" */ michael@0: udm->vFuncs = &ToCPFuncs; michael@0: udm->toc=(const char *)udm->pHeader+udata_getHeaderSize(udm->pHeader); michael@0: } michael@0: else { michael@0: /* dataFormat not recognized */ michael@0: *err=U_INVALID_FORMAT_ERROR; michael@0: } michael@0: michael@0: if (U_FAILURE(*err)) { michael@0: /* If the data is no good and we memory-mapped it ourselves, michael@0: * close the memory mapping so it doesn't leak. Note that this has michael@0: * no effect on non-memory mapped data, other than clearing fields in udm. michael@0: */ michael@0: udata_close(udm); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * TODO: Add a udata_swapPackageHeader() function that swaps an ICU .dat package michael@0: * header but not its sub-items. michael@0: * This function will be needed for automatic runtime swapping. michael@0: * Sub-items should not be swapped to limit the swapping to the parts of the michael@0: * package that are actually used. michael@0: * michael@0: * Since lengths of items are implicit in the order and offsets of their michael@0: * ToC entries, and since offsets are relative to the start of the ToC, michael@0: * a swapped version may need to generate a different data structure michael@0: * with pointers to the original data items and with their lengths michael@0: * (-1 for the last one if it is not known), and maybe even pointers to the michael@0: * swapped versions of the items. michael@0: * These pointers to swapped versions would establish a cache; michael@0: * instead, each open data item could simply own the storage for its swapped michael@0: * data. This fits better with the current design. michael@0: * michael@0: * markus 2003sep18 Jitterbug 2235 michael@0: */