// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ****************************************************************************** * * Copyright (C) 2001-2014, International Business Machines * Corporation and others. All Rights Reserved. * ****************************************************************************** * file name: utrie2_builder.cpp * encoding: UTF-8 * tab size: 8 (not used) * indentation:4 * * created on: 2008sep26 (split off from utrie2.c) * created by: Markus W. Scherer * * This is a common implementation of a Unicode trie. * It is a kind of compressed, serializable table of 16- or 32-bit values associated with * Unicode code points (0..0x10ffff). * This is the second common version of a Unicode trie (hence the name UTrie2). * See utrie2.h for a comparison. * * This file contains only the builder code. * See utrie2.c for the runtime and enumeration code. */ // #define UTRIE2_DEBUG #ifdef UTRIE2_DEBUG # include #endif // #define UCPTRIE_DEBUG #include "unicode/utypes.h" #ifdef UCPTRIE_DEBUG #include "unicode/ucptrie.h" #include "unicode/umutablecptrie.h" #include "ucptrie_impl.h" #endif #include "cmemory.h" #include "utrie2.h" #include "utrie2_impl.h" #include "utrie.h" // for utrie2_fromUTrie() /* Implementation notes ----------------------------------------------------- */ /* * The UTRIE2_SHIFT_1, UTRIE2_SHIFT_2, UTRIE2_INDEX_SHIFT and other values * have been chosen to minimize trie sizes overall. * Most of the code is flexible enough to work with a range of values, * within certain limits. * * Exception: Support for separate values for lead surrogate code _units_ * vs. code _points_ was added after the constants were fixed, * and has not been tested nor particularly designed for different constant values. * (Especially the utrie2_enum() code that jumps to the special LSCP index-2 * part and back.) * * Requires UTRIE2_SHIFT_2<=6. Otherwise 0xc0 which is the top of the ASCII-linear data * including the bad-UTF-8-data block is not a multiple of UTRIE2_DATA_BLOCK_LENGTH * and map[block>>UTRIE2_SHIFT_2] (used in reference counting and compaction * remapping) stops working. * * Requires UTRIE2_SHIFT_1>=10 because utrie2_enumForLeadSurrogate() * assumes that a single index-2 block is used for 0x400 code points * corresponding to one lead surrogate. * * Requires UTRIE2_SHIFT_1<=16. Otherwise one single index-2 block contains * more than one Unicode plane, and the split of the index-2 table into a BMP * part and a supplementary part, with a gap in between, would not work. * * Requires UTRIE2_INDEX_SHIFT>=1 not because of the code but because * there is data with more than 64k distinct values, * for example for Unihan collation with a separate collation weight per * Han character. */ /* Building a trie ----------------------------------------------------------*/ enum { /** The null index-2 block, following the gap in the index-2 table. */ UNEWTRIE2_INDEX_2_NULL_OFFSET=UNEWTRIE2_INDEX_GAP_OFFSET+UNEWTRIE2_INDEX_GAP_LENGTH, /** The start of allocated index-2 blocks. */ UNEWTRIE2_INDEX_2_START_OFFSET=UNEWTRIE2_INDEX_2_NULL_OFFSET+UTRIE2_INDEX_2_BLOCK_LENGTH, /** * The null data block. * Length 64=0x40 even if UTRIE2_DATA_BLOCK_LENGTH is smaller, * to work with 6-bit trail bytes from 2-byte UTF-8. */ UNEWTRIE2_DATA_NULL_OFFSET=UTRIE2_DATA_START_OFFSET, /** The start of allocated data blocks. */ UNEWTRIE2_DATA_START_OFFSET=UNEWTRIE2_DATA_NULL_OFFSET+0x40, /** * The start of data blocks for U+0800 and above. * Below, compaction uses a block length of 64 for 2-byte UTF-8. * From here on, compaction uses UTRIE2_DATA_BLOCK_LENGTH. * Data values for 0x780 code points beyond ASCII. */ UNEWTRIE2_DATA_0800_OFFSET=UNEWTRIE2_DATA_START_OFFSET+0x780 }; /* Start with allocation of 16k data entries. */ #define UNEWTRIE2_INITIAL_DATA_LENGTH ((int32_t)1<<14) /* Grow about 8x each time. */ #define UNEWTRIE2_MEDIUM_DATA_LENGTH ((int32_t)1<<17) static int32_t allocIndex2Block(UNewTrie2 *trie); U_CAPI UTrie2 * U_EXPORT2 utrie2_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode) { UTrie2 *trie; UNewTrie2 *newTrie; uint32_t *data; int32_t i, j; if(U_FAILURE(*pErrorCode)) { return nullptr; } trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); newTrie=(UNewTrie2 *)uprv_malloc(sizeof(UNewTrie2)); data=(uint32_t *)uprv_malloc(UNEWTRIE2_INITIAL_DATA_LENGTH*4); if(trie==nullptr || newTrie==nullptr || data==nullptr) { uprv_free(trie); uprv_free(newTrie); uprv_free(data); *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return nullptr; } uprv_memset(trie, 0, sizeof(UTrie2)); trie->initialValue=initialValue; trie->errorValue=errorValue; trie->highStart=0x110000; trie->newTrie=newTrie; #ifdef UTRIE2_DEBUG trie->name="open"; #endif newTrie->data=data; #ifdef UCPTRIE_DEBUG newTrie->t3=umutablecptrie_open(initialValue, errorValue, pErrorCode); #endif newTrie->dataCapacity=UNEWTRIE2_INITIAL_DATA_LENGTH; newTrie->initialValue=initialValue; newTrie->errorValue=errorValue; newTrie->highStart=0x110000; newTrie->firstFreeBlock=0; /* no free block in the list */ newTrie->isCompacted=false; /* * preallocate and reset * - ASCII * - the bad-UTF-8-data block * - the null data block */ for(i=0; i<0x80; ++i) { newTrie->data[i]=initialValue; } for(; i<0xc0; ++i) { newTrie->data[i]=errorValue; } for(i=UNEWTRIE2_DATA_NULL_OFFSET; idata[i]=initialValue; } newTrie->dataNullOffset=UNEWTRIE2_DATA_NULL_OFFSET; newTrie->dataLength=UNEWTRIE2_DATA_START_OFFSET; /* set the index-2 indexes for the 2=0x80>>UTRIE2_SHIFT_2 ASCII data blocks */ for(i=0, j=0; j<0x80; ++i, j+=UTRIE2_DATA_BLOCK_LENGTH) { newTrie->index2[i]=j; newTrie->map[i]=1; } /* reference counts for the bad-UTF-8-data block */ for(; j<0xc0; ++i, j+=UTRIE2_DATA_BLOCK_LENGTH) { newTrie->map[i]=0; } /* * Reference counts for the null data block: all blocks except for the ASCII blocks. * Plus 1 so that we don't drop this block during compaction. * Plus as many as needed for lead surrogate code points. */ /* i==newTrie->dataNullOffset */ newTrie->map[i++]= (0x110000>>UTRIE2_SHIFT_2)- (0x80>>UTRIE2_SHIFT_2)+ 1+ UTRIE2_LSCP_INDEX_2_LENGTH; j+=UTRIE2_DATA_BLOCK_LENGTH; for(; jmap[i]=0; } /* * set the remaining indexes in the BMP index-2 block * to the null data block */ for(i=0x80>>UTRIE2_SHIFT_2; iindex2[i]=UNEWTRIE2_DATA_NULL_OFFSET; } /* * Fill the index gap with impossible values so that compaction * does not overlap other index-2 blocks with the gap. */ for(i=0; iindex2[UNEWTRIE2_INDEX_GAP_OFFSET+i]=-1; } /* set the indexes in the null index-2 block */ for(i=0; iindex2[UNEWTRIE2_INDEX_2_NULL_OFFSET+i]=UNEWTRIE2_DATA_NULL_OFFSET; } newTrie->index2NullOffset=UNEWTRIE2_INDEX_2_NULL_OFFSET; newTrie->index2Length=UNEWTRIE2_INDEX_2_START_OFFSET; /* set the index-1 indexes for the linear index-2 block */ for(i=0, j=0; iindex1[i]=j; } /* set the remaining index-1 indexes to the null index-2 block */ for(; iindex1[i]=UNEWTRIE2_INDEX_2_NULL_OFFSET; } /* * Preallocate and reset data for U+0080..U+07ff, * for 2-byte UTF-8 which will be compacted in 64-blocks * even if UTRIE2_DATA_BLOCK_LENGTH is smaller. */ for(i=0x80; i<0x800; i+=UTRIE2_DATA_BLOCK_LENGTH) { utrie2_set32(trie, i, initialValue, pErrorCode); } return trie; } static UNewTrie2 * cloneBuilder(const UNewTrie2 *other) { UNewTrie2 *trie; trie=(UNewTrie2 *)uprv_malloc(sizeof(UNewTrie2)); if(trie==nullptr) { return nullptr; } trie->data=(uint32_t *)uprv_malloc(other->dataCapacity*4); if(trie->data==nullptr) { uprv_free(trie); return nullptr; } #ifdef UCPTRIE_DEBUG if(other->t3==nullptr) { trie->t3=nullptr; } else { UErrorCode errorCode=U_ZERO_ERROR; trie->t3=umutablecptrie_clone(other->t3, &errorCode); } #endif trie->dataCapacity=other->dataCapacity; /* clone data */ uprv_memcpy(trie->index1, other->index1, sizeof(trie->index1)); uprv_memcpy(trie->index2, other->index2, (size_t)other->index2Length*4); trie->index2NullOffset=other->index2NullOffset; trie->index2Length=other->index2Length; uprv_memcpy(trie->data, other->data, (size_t)other->dataLength*4); trie->dataNullOffset=other->dataNullOffset; trie->dataLength=other->dataLength; /* reference counters */ if(other->isCompacted) { trie->firstFreeBlock=0; } else { uprv_memcpy(trie->map, other->map, ((size_t)other->dataLength>>UTRIE2_SHIFT_2)*4); trie->firstFreeBlock=other->firstFreeBlock; } trie->initialValue=other->initialValue; trie->errorValue=other->errorValue; trie->highStart=other->highStart; trie->isCompacted=other->isCompacted; return trie; } U_CAPI UTrie2 * U_EXPORT2 utrie2_clone(const UTrie2 *other, UErrorCode *pErrorCode) { UTrie2 *trie; if(U_FAILURE(*pErrorCode)) { return nullptr; } if(other==nullptr || (other->memory==nullptr && other->newTrie==nullptr)) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return nullptr; } trie=(UTrie2 *)uprv_malloc(sizeof(UTrie2)); if(trie==nullptr) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return nullptr; } uprv_memcpy(trie, other, sizeof(UTrie2)); if(other->memory!=nullptr) { trie->memory=uprv_malloc(other->length); if(trie->memory!=nullptr) { trie->isMemoryOwned=true; uprv_memcpy(trie->memory, other->memory, other->length); /* make the clone's pointers point to its own memory */ trie->index=(uint16_t *)trie->memory+(other->index-(uint16_t *)other->memory); if(other->data16!=nullptr) { trie->data16=(uint16_t *)trie->memory+(other->data16-(uint16_t *)other->memory); } if(other->data32!=nullptr) { trie->data32=(uint32_t *)trie->memory+(other->data32-(uint32_t *)other->memory); } } } else /* other->newTrie!=nullptr */ { trie->newTrie=cloneBuilder(other->newTrie); } if(trie->memory==nullptr && trie->newTrie==nullptr) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; uprv_free(trie); trie=nullptr; } return trie; } typedef struct NewTrieAndStatus { UTrie2 *trie; UErrorCode errorCode; UBool exclusiveLimit; /* rather than inclusive range end */ } NewTrieAndStatus; static UBool U_CALLCONV copyEnumRange(const void *context, UChar32 start, UChar32 end, uint32_t value) { NewTrieAndStatus *nt=(NewTrieAndStatus *)context; if(value!=nt->trie->initialValue) { if(nt->exclusiveLimit) { --end; } if(start==end) { utrie2_set32(nt->trie, start, value, &nt->errorCode); } else { utrie2_setRange32(nt->trie, start, end, value, true, &nt->errorCode); } return U_SUCCESS(nt->errorCode); } else { return true; } } #ifdef UTRIE2_DEBUG static long countInitial(const UTrie2 *trie) { uint32_t initialValue=trie->initialValue; int32_t length=trie->dataLength; long count=0; if(trie->data16!=nullptr) { for(int32_t i=0; idata16[i]==initialValue) { ++count; } } } else { for(int32_t i=0; idata32[i]==initialValue) { ++count; } } } return count; } static void utrie_printLengths(const UTrie *trie) { long indexLength=trie->indexLength; long dataLength=(long)trie->dataLength; long totalLength=(long)sizeof(UTrieHeader)+indexLength*2+dataLength*(trie->data32!=nullptr ? 4 : 2); printf("**UTrieLengths** index:%6ld data:%6ld serialized:%6ld\n", indexLength, dataLength, totalLength); } static void utrie2_printLengths(const UTrie2 *trie, const char *which) { long indexLength=trie->indexLength; long dataLength=(long)trie->dataLength; long totalLength=(long)sizeof(UTrie2Header)+indexLength*2+dataLength*(trie->data32!=nullptr ? 4 : 2); printf("**UTrie2Lengths(%s %s)** index:%6ld data:%6ld countInitial:%6ld serialized:%6ld\n", which, trie->name, indexLength, dataLength, countInitial(trie), totalLength); } #endif U_CAPI UTrie2 * U_EXPORT2 utrie2_cloneAsThawed(const UTrie2 *other, UErrorCode *pErrorCode) { NewTrieAndStatus context; char16_t lead; if(U_FAILURE(*pErrorCode)) { return nullptr; } if(other==nullptr || (other->memory==nullptr && other->newTrie==nullptr)) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return nullptr; } if(other->newTrie!=nullptr && !other->newTrie->isCompacted) { return utrie2_clone(other, pErrorCode); /* clone an unfrozen trie */ } /* Clone the frozen trie by enumerating it and building a new one. */ context.trie=utrie2_open(other->initialValue, other->errorValue, pErrorCode); if(U_FAILURE(*pErrorCode)) { return nullptr; } context.exclusiveLimit=false; context.errorCode=*pErrorCode; utrie2_enum(other, nullptr, copyEnumRange, &context); *pErrorCode=context.errorCode; for(lead=0xd800; lead<0xdc00; ++lead) { uint32_t value; if(other->data32==nullptr) { value=UTRIE2_GET16_FROM_U16_SINGLE_LEAD(other, lead); } else { value=UTRIE2_GET32_FROM_U16_SINGLE_LEAD(other, lead); } if(value!=other->initialValue) { utrie2_set32ForLeadSurrogateCodeUnit(context.trie, lead, value, pErrorCode); } } if(U_FAILURE(*pErrorCode)) { utrie2_close(context.trie); context.trie=nullptr; } return context.trie; } /* Almost the same as utrie2_cloneAsThawed() but copies a UTrie and freezes the clone. */ U_CAPI UTrie2 * U_EXPORT2 utrie2_fromUTrie(const UTrie *trie1, uint32_t errorValue, UErrorCode *pErrorCode) { NewTrieAndStatus context; char16_t lead; if(U_FAILURE(*pErrorCode)) { return nullptr; } if(trie1==nullptr) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return nullptr; } context.trie=utrie2_open(trie1->initialValue, errorValue, pErrorCode); if(U_FAILURE(*pErrorCode)) { return nullptr; } context.exclusiveLimit=true; context.errorCode=*pErrorCode; utrie_enum(trie1, nullptr, copyEnumRange, &context); *pErrorCode=context.errorCode; for(lead=0xd800; lead<0xdc00; ++lead) { uint32_t value; if(trie1->data32==nullptr) { value=UTRIE_GET16_FROM_LEAD(trie1, lead); } else { value=UTRIE_GET32_FROM_LEAD(trie1, lead); } if(value!=trie1->initialValue) { utrie2_set32ForLeadSurrogateCodeUnit(context.trie, lead, value, pErrorCode); } } if(U_SUCCESS(*pErrorCode)) { utrie2_freeze(context.trie, trie1->data32!=nullptr ? UTRIE2_32_VALUE_BITS : UTRIE2_16_VALUE_BITS, pErrorCode); } #ifdef UTRIE2_DEBUG if(U_SUCCESS(*pErrorCode)) { utrie_printLengths(trie1); utrie2_printLengths(context.trie, "fromUTrie"); } #endif if(U_FAILURE(*pErrorCode)) { utrie2_close(context.trie); context.trie=nullptr; } return context.trie; } static inline UBool isInNullBlock(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { int32_t i2, block; if(U_IS_LEAD(c) && forLSCP) { i2=(UTRIE2_LSCP_INDEX_2_OFFSET-(0xd800>>UTRIE2_SHIFT_2))+ (c>>UTRIE2_SHIFT_2); } else { i2=trie->index1[c>>UTRIE2_SHIFT_1]+ ((c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK); } block=trie->index2[i2]; return (UBool)(block==trie->dataNullOffset); } static int32_t allocIndex2Block(UNewTrie2 *trie) { int32_t newBlock, newTop; newBlock=trie->index2Length; newTop=newBlock+UTRIE2_INDEX_2_BLOCK_LENGTH; if(newTop>UPRV_LENGTHOF(trie->index2)) { /* * Should never occur. * Either UTRIE2_MAX_BUILD_TIME_INDEX_LENGTH is incorrect, * or the code writes more values than should be possible. */ return -1; } trie->index2Length=newTop; uprv_memcpy(trie->index2+newBlock, trie->index2+trie->index2NullOffset, UTRIE2_INDEX_2_BLOCK_LENGTH*4); return newBlock; } static int32_t getIndex2Block(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { int32_t i1, i2; if(U_IS_LEAD(c) && forLSCP) { return UTRIE2_LSCP_INDEX_2_OFFSET; } i1=c>>UTRIE2_SHIFT_1; i2=trie->index1[i1]; if(i2==trie->index2NullOffset) { i2=allocIndex2Block(trie); if(i2<0) { return -1; /* program error */ } trie->index1[i1]=i2; } return i2; } static int32_t allocDataBlock(UNewTrie2 *trie, int32_t copyBlock) { int32_t newBlock, newTop; if(trie->firstFreeBlock!=0) { /* get the first free block */ newBlock=trie->firstFreeBlock; trie->firstFreeBlock=-trie->map[newBlock>>UTRIE2_SHIFT_2]; } else { /* get a new block from the high end */ newBlock=trie->dataLength; newTop=newBlock+UTRIE2_DATA_BLOCK_LENGTH; if(newTop>trie->dataCapacity) { /* out of memory in the data array */ int32_t capacity; uint32_t *data; if(trie->dataCapacitydataCapacitydata, (size_t)trie->dataLength*4); uprv_free(trie->data); trie->data=data; trie->dataCapacity=capacity; } trie->dataLength=newTop; } uprv_memcpy(trie->data+newBlock, trie->data+copyBlock, UTRIE2_DATA_BLOCK_LENGTH*4); trie->map[newBlock>>UTRIE2_SHIFT_2]=0; return newBlock; } /* call when the block's reference counter reaches 0 */ static void releaseDataBlock(UNewTrie2 *trie, int32_t block) { /* put this block at the front of the free-block chain */ trie->map[block>>UTRIE2_SHIFT_2]=-trie->firstFreeBlock; trie->firstFreeBlock=block; } static inline UBool isWritableBlock(UNewTrie2 *trie, int32_t block) { return (UBool)(block!=trie->dataNullOffset && 1==trie->map[block>>UTRIE2_SHIFT_2]); } static inline void setIndex2Entry(UNewTrie2 *trie, int32_t i2, int32_t block) { int32_t oldBlock; ++trie->map[block>>UTRIE2_SHIFT_2]; /* increment first, in case block==oldBlock! */ oldBlock=trie->index2[i2]; if(0 == --trie->map[oldBlock>>UTRIE2_SHIFT_2]) { releaseDataBlock(trie, oldBlock); } trie->index2[i2]=block; } /** * No error checking for illegal arguments. * * @return -1 if no new data block available (out of memory in data array) * @internal */ static int32_t getDataBlock(UNewTrie2 *trie, UChar32 c, UBool forLSCP) { int32_t i2, oldBlock, newBlock; i2=getIndex2Block(trie, c, forLSCP); if(i2<0) { return -1; /* program error */ } i2+=(c>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; oldBlock=trie->index2[i2]; if(isWritableBlock(trie, oldBlock)) { return oldBlock; } /* allocate a new data block */ newBlock=allocDataBlock(trie, oldBlock); if(newBlock<0) { /* out of memory in the data array */ return -1; } setIndex2Entry(trie, i2, newBlock); return newBlock; } /** * @return true if the value was successfully set */ static void set32(UNewTrie2 *trie, UChar32 c, UBool forLSCP, uint32_t value, UErrorCode *pErrorCode) { int32_t block; if(trie==nullptr || trie->isCompacted) { *pErrorCode=U_NO_WRITE_PERMISSION; return; } #ifdef UCPTRIE_DEBUG umutablecptrie_set(trie->t3, c, value, pErrorCode); #endif block=getDataBlock(trie, c, forLSCP); if(block<0) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } trie->data[block+(c&UTRIE2_DATA_MASK)]=value; } U_CAPI void U_EXPORT2 utrie2_set32(UTrie2 *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode) { if(U_FAILURE(*pErrorCode)) { return; } if((uint32_t)c>0x10ffff) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return; } set32(trie->newTrie, c, true, value, pErrorCode); } U_CAPI void U_EXPORT2 utrie2_set32ForLeadSurrogateCodeUnit(UTrie2 *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode) { if(U_FAILURE(*pErrorCode)) { return; } if(!U_IS_LEAD(c)) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return; } set32(trie->newTrie, c, false, value, pErrorCode); } static void writeBlock(uint32_t *block, uint32_t value) { uint32_t *limit=block+UTRIE2_DATA_BLOCK_LENGTH; while(block0x10ffff || (uint32_t)end>0x10ffff || start>end) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return; } newTrie=trie->newTrie; if(newTrie==nullptr || newTrie->isCompacted) { *pErrorCode=U_NO_WRITE_PERMISSION; return; } #ifdef UCPTRIE_DEBUG umutablecptrie_setRange(newTrie->t3, start, end, value, pErrorCode); #endif if(!overwrite && value==newTrie->initialValue) { return; /* nothing to do */ } limit=end+1; if(start&UTRIE2_DATA_MASK) { UChar32 nextStart; /* set partial block at [start..following block boundary[ */ block=getDataBlock(newTrie, start, true); if(block<0) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } nextStart=(start+UTRIE2_DATA_MASK)&~UTRIE2_DATA_MASK; if(nextStart<=limit) { fillBlock(newTrie->data+block, start&UTRIE2_DATA_MASK, UTRIE2_DATA_BLOCK_LENGTH, value, newTrie->initialValue, overwrite); start=nextStart; } else { fillBlock(newTrie->data+block, start&UTRIE2_DATA_MASK, limit&UTRIE2_DATA_MASK, value, newTrie->initialValue, overwrite); return; } } /* number of positions in the last, partial block */ rest=limit&UTRIE2_DATA_MASK; /* round down limit to a block boundary */ limit&=~UTRIE2_DATA_MASK; /* iterate over all-value blocks */ if(value==newTrie->initialValue) { repeatBlock=newTrie->dataNullOffset; } else { repeatBlock=-1; } while(startinitialValue && isInNullBlock(newTrie, start, true)) { start+=UTRIE2_DATA_BLOCK_LENGTH; /* nothing to do */ continue; } /* get index value */ i2=getIndex2Block(newTrie, start, true); if(i2<0) { *pErrorCode=U_INTERNAL_PROGRAM_ERROR; return; } i2+=(start>>UTRIE2_SHIFT_2)&UTRIE2_INDEX_2_MASK; block=newTrie->index2[i2]; if(isWritableBlock(newTrie, block)) { /* already allocated */ if(overwrite && block>=UNEWTRIE2_DATA_0800_OFFSET) { /* * We overwrite all values, and it's not a * protected (ASCII-linear or 2-byte UTF-8) block: * replace with the repeatBlock. */ setRepeatBlock=true; } else { /* !overwrite, or protected block: just write the values into this block */ fillBlock(newTrie->data+block, 0, UTRIE2_DATA_BLOCK_LENGTH, value, newTrie->initialValue, overwrite); } } else if(newTrie->data[block]!=value && (overwrite || block==newTrie->dataNullOffset)) { /* * Set the repeatBlock instead of the null block or previous repeat block: * * If !isWritableBlock() then all entries in the block have the same value * because it's the null block or a range block (the repeatBlock from a previous * call to utrie2_setRange32()). * No other blocks are used multiple times before compacting. * * The null block is the only non-writable block with the initialValue because * of the repeatBlock initialization above. (If value==initialValue, then * the repeatBlock will be the null data block.) * * We set our repeatBlock if the desired value differs from the block's value, * and if we overwrite any data or if the data is all initial values * (which is the same as the block being the null block, see above). */ setRepeatBlock=true; } if(setRepeatBlock) { if(repeatBlock>=0) { setIndex2Entry(newTrie, i2, repeatBlock); } else { /* create and set and fill the repeatBlock */ repeatBlock=getDataBlock(newTrie, start, true); if(repeatBlock<0) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } writeBlock(newTrie->data+repeatBlock, value); } } start+=UTRIE2_DATA_BLOCK_LENGTH; } if(rest>0) { /* set partial block at [last block boundary..limit[ */ block=getDataBlock(newTrie, start, true); if(block<0) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } fillBlock(newTrie->data+block, 0, rest, value, newTrie->initialValue, overwrite); } } /* compaction --------------------------------------------------------------- */ static inline UBool equal_int32(const int32_t *s, const int32_t *t, int32_t length) { while(length>0 && *s==*t) { ++s; ++t; --length; } return (UBool)(length==0); } static inline UBool equal_uint32(const uint32_t *s, const uint32_t *t, int32_t length) { while(length>0 && *s==*t) { ++s; ++t; --length; } return (UBool)(length==0); } static int32_t findSameIndex2Block(const int32_t *idx, int32_t index2Length, int32_t otherBlock) { int32_t block; /* ensure that we do not even partially get past index2Length */ index2Length-=UTRIE2_INDEX_2_BLOCK_LENGTH; for(block=0; block<=index2Length; ++block) { if(equal_int32(idx+block, idx+otherBlock, UTRIE2_INDEX_2_BLOCK_LENGTH)) { return block; } } return -1; } static int32_t findSameDataBlock(const uint32_t *data, int32_t dataLength, int32_t otherBlock, int32_t blockLength) { int32_t block; /* ensure that we do not even partially get past dataLength */ dataLength-=blockLength; for(block=0; block<=dataLength; block+=UTRIE2_DATA_GRANULARITY) { if(equal_uint32(data+block, data+otherBlock, blockLength)) { return block; } } return -1; } /* * Find the start of the last range in the trie by enumerating backward. * Indexes for supplementary code points higher than this will be omitted. */ static UChar32 findHighStart(UNewTrie2 *trie, uint32_t highValue) { const uint32_t *data32; uint32_t value, initialValue; UChar32 c, prev; int32_t i1, i2, j, i2Block, prevI2Block, index2NullOffset, block, prevBlock, nullBlock; data32=trie->data; initialValue=trie->initialValue; index2NullOffset=trie->index2NullOffset; nullBlock=trie->dataNullOffset; /* set variables for previous range */ if(highValue==initialValue) { prevI2Block=index2NullOffset; prevBlock=nullBlock; } else { prevI2Block=-1; prevBlock=-1; } prev=0x110000; /* enumerate index-2 blocks */ i1=UNEWTRIE2_INDEX_1_LENGTH; c=prev; while(c>0) { i2Block=trie->index1[--i1]; if(i2Block==prevI2Block) { /* the index-2 block is the same as the previous one, and filled with highValue */ c-=UTRIE2_CP_PER_INDEX_1_ENTRY; continue; } prevI2Block=i2Block; if(i2Block==index2NullOffset) { /* this is the null index-2 block */ if(highValue!=initialValue) { return c; } c-=UTRIE2_CP_PER_INDEX_1_ENTRY; } else { /* enumerate data blocks for one index-2 block */ for(i2=UTRIE2_INDEX_2_BLOCK_LENGTH; i2>0;) { block=trie->index2[i2Block+ --i2]; if(block==prevBlock) { /* the block is the same as the previous one, and filled with highValue */ c-=UTRIE2_DATA_BLOCK_LENGTH; continue; } prevBlock=block; if(block==nullBlock) { /* this is the null data block */ if(highValue!=initialValue) { return c; } c-=UTRIE2_DATA_BLOCK_LENGTH; } else { for(j=UTRIE2_DATA_BLOCK_LENGTH; j>0;) { value=data32[block+ --j]; if(value!=highValue) { return c; } --c; } } } } } /* deliver last range */ return 0; } /* * Compact a build-time trie. * * The compaction * - removes blocks that are identical with earlier ones * - overlaps adjacent blocks as much as possible (if overlap==true) * - moves blocks in steps of the data granularity * - moves and overlaps blocks that overlap with multiple values in the overlap region * * It does not * - try to move and overlap blocks that are not already adjacent */ static void compactData(UNewTrie2 *trie) { #ifdef UTRIE2_DEBUG int32_t countSame=0, sumOverlaps=0; #endif int32_t start, newStart, movedStart; int32_t blockLength, overlap; int32_t i, mapIndex, blockCount; /* do not compact linear-ASCII data */ newStart=UTRIE2_DATA_START_OFFSET; for(start=0, i=0; startmap[i]=start; } /* * Start with a block length of 64 for 2-byte UTF-8, * then switch to UTRIE2_DATA_BLOCK_LENGTH. */ blockLength=64; blockCount=blockLength>>UTRIE2_SHIFT_2; for(start=newStart; startdataLength;) { /* * start: index of first entry of current block * newStart: index where the current block is to be moved * (right after current end of already-compacted data) */ if(start==UNEWTRIE2_DATA_0800_OFFSET) { blockLength=UTRIE2_DATA_BLOCK_LENGTH; blockCount=1; } /* skip blocks that are not used */ if(trie->map[start>>UTRIE2_SHIFT_2]<=0) { /* advance start to the next block */ start+=blockLength; /* leave newStart with the previous block! */ continue; } /* search for an identical block */ if( (movedStart=findSameDataBlock(trie->data, newStart, start, blockLength)) >=0 ) { #ifdef UTRIE2_DEBUG ++countSame; #endif /* found an identical block, set the other block's index value for the current block */ for(i=blockCount, mapIndex=start>>UTRIE2_SHIFT_2; i>0; --i) { trie->map[mapIndex++]=movedStart; movedStart+=UTRIE2_DATA_BLOCK_LENGTH; } /* advance start to the next block */ start+=blockLength; /* leave newStart with the previous block! */ continue; } /* see if the beginning of this block can be overlapped with the end of the previous block */ /* look for maximum overlap (modulo granularity) with the previous, adjacent block */ for(overlap=blockLength-UTRIE2_DATA_GRANULARITY; overlap>0 && !equal_uint32(trie->data+(newStart-overlap), trie->data+start, overlap); overlap-=UTRIE2_DATA_GRANULARITY) {} #ifdef UTRIE2_DEBUG sumOverlaps+=overlap; #endif if(overlap>0 || newStart>UTRIE2_SHIFT_2; i>0; --i) { trie->map[mapIndex++]=movedStart; movedStart+=UTRIE2_DATA_BLOCK_LENGTH; } /* move the non-overlapping indexes to their new positions */ start+=overlap; for(i=blockLength-overlap; i>0; --i) { trie->data[newStart++]=trie->data[start++]; } } else /* no overlap && newStart==start */ { for(i=blockCount, mapIndex=start>>UTRIE2_SHIFT_2; i>0; --i) { trie->map[mapIndex++]=start; start+=UTRIE2_DATA_BLOCK_LENGTH; } newStart=start; } } /* now adjust the index-2 table */ for(i=0; iindex2Length; ++i) { if(i==UNEWTRIE2_INDEX_GAP_OFFSET) { /* Gap indexes are invalid (-1). Skip over the gap. */ i+=UNEWTRIE2_INDEX_GAP_LENGTH; } trie->index2[i]=trie->map[trie->index2[i]>>UTRIE2_SHIFT_2]; } trie->dataNullOffset=trie->map[trie->dataNullOffset>>UTRIE2_SHIFT_2]; /* ensure dataLength alignment */ while((newStart&(UTRIE2_DATA_GRANULARITY-1))!=0) { trie->data[newStart++]=trie->initialValue; } #ifdef UTRIE2_DEBUG /* we saved some space */ printf("compacting UTrie2: count of 32-bit data words %lu->%lu countSame=%ld sumOverlaps=%ld\n", (long)trie->dataLength, (long)newStart, (long)countSame, (long)sumOverlaps); #endif trie->dataLength=newStart; } static void compactIndex2(UNewTrie2 *trie) { int32_t i, start, newStart, movedStart, overlap; /* do not compact linear-BMP index-2 blocks */ newStart=UTRIE2_INDEX_2_BMP_LENGTH; for(start=0, i=0; startmap[i]=start; } /* Reduce the index table gap to what will be needed at runtime. */ newStart+=UTRIE2_UTF8_2B_INDEX_2_LENGTH+((trie->highStart-0x10000)>>UTRIE2_SHIFT_1); for(start=UNEWTRIE2_INDEX_2_NULL_OFFSET; startindex2Length;) { /* * start: index of first entry of current block * newStart: index where the current block is to be moved * (right after current end of already-compacted data) */ /* search for an identical block */ if( (movedStart=findSameIndex2Block(trie->index2, newStart, start)) >=0 ) { /* found an identical block, set the other block's index value for the current block */ trie->map[start>>UTRIE2_SHIFT_1_2]=movedStart; /* advance start to the next block */ start+=UTRIE2_INDEX_2_BLOCK_LENGTH; /* leave newStart with the previous block! */ continue; } /* see if the beginning of this block can be overlapped with the end of the previous block */ /* look for maximum overlap with the previous, adjacent block */ for(overlap=UTRIE2_INDEX_2_BLOCK_LENGTH-1; overlap>0 && !equal_int32(trie->index2+(newStart-overlap), trie->index2+start, overlap); --overlap) {} if(overlap>0 || newStartmap[start>>UTRIE2_SHIFT_1_2]=newStart-overlap; /* move the non-overlapping indexes to their new positions */ start+=overlap; for(i=UTRIE2_INDEX_2_BLOCK_LENGTH-overlap; i>0; --i) { trie->index2[newStart++]=trie->index2[start++]; } } else /* no overlap && newStart==start */ { trie->map[start>>UTRIE2_SHIFT_1_2]=start; start+=UTRIE2_INDEX_2_BLOCK_LENGTH; newStart=start; } } /* now adjust the index-1 table */ for(i=0; iindex1[i]=trie->map[trie->index1[i]>>UTRIE2_SHIFT_1_2]; } trie->index2NullOffset=trie->map[trie->index2NullOffset>>UTRIE2_SHIFT_1_2]; /* * Ensure data table alignment: * Needs to be granularity-aligned for 16-bit trie * (so that dataMove will be down-shiftable), * and 2-aligned for uint32_t data. */ while((newStart&((UTRIE2_DATA_GRANULARITY-1)|1))!=0) { /* Arbitrary value: 0x3fffc not possible for real data. */ trie->index2[newStart++]=(int32_t)0xffff<%lu\n", (long)trie->index2Length, (long)newStart); #endif trie->index2Length=newStart; } static void compactTrie(UTrie2 *trie, UErrorCode *pErrorCode) { UNewTrie2 *newTrie; UChar32 highStart, suppHighStart; uint32_t highValue; newTrie=trie->newTrie; /* find highStart and round it up */ highValue=utrie2_get32(trie, 0x10ffff); highStart=findHighStart(newTrie, highValue); highStart=(highStart+(UTRIE2_CP_PER_INDEX_1_ENTRY-1))&~(UTRIE2_CP_PER_INDEX_1_ENTRY-1); if(highStart==0x110000) { highValue=trie->errorValue; } /* * Set trie->highStart only after utrie2_get32(trie, highStart). * Otherwise utrie2_get32(trie, highStart) would try to read the highValue. */ trie->highStart=newTrie->highStart=highStart; #ifdef UTRIE2_DEBUG printf("UTrie2: highStart U+%06lx highValue 0x%lx initialValue 0x%lx\n", (long)highStart, (long)highValue, (long)trie->initialValue); #endif if(highStart<0x110000) { /* Blank out [highStart..10ffff] to release associated data blocks. */ suppHighStart= highStart<=0x10000 ? 0x10000 : highStart; utrie2_setRange32(trie, suppHighStart, 0x10ffff, trie->initialValue, true, pErrorCode); if(U_FAILURE(*pErrorCode)) { return; } } compactData(newTrie); if(highStart>0x10000) { compactIndex2(newTrie); #ifdef UTRIE2_DEBUG } else { printf("UTrie2: highStart U+%04lx count of 16-bit index words %lu->%lu\n", (long)highStart, (long)trie->newTrie->index2Length, (long)UTRIE2_INDEX_1_OFFSET); #endif } /* * Store the highValue in the data array and round up the dataLength. * Must be done after compactData() because that assumes that dataLength * is a multiple of UTRIE2_DATA_BLOCK_LENGTH. */ newTrie->data[newTrie->dataLength++]=highValue; while((newTrie->dataLength&(UTRIE2_DATA_GRANULARITY-1))!=0) { newTrie->data[newTrie->dataLength++]=trie->initialValue; } newTrie->isCompacted=true; } /* serialization ------------------------------------------------------------ */ /** * Maximum length of the runtime index array. * Limited by its own 16-bit index values, and by uint16_t UTrie2Header.indexLength. * (The actual maximum length is lower, * (0x110000>>UTRIE2_SHIFT_2)+UTRIE2_UTF8_2B_INDEX_2_LENGTH+UTRIE2_MAX_INDEX_1_LENGTH.) */ #define UTRIE2_MAX_INDEX_LENGTH 0xffff /** * Maximum length of the runtime data array. * Limited by 16-bit index values that are left-shifted by UTRIE2_INDEX_SHIFT, * and by uint16_t UTrie2Header.shiftedDataLength. */ #define UTRIE2_MAX_DATA_LENGTH (0xffff<0 if the data is moved to the end of the index array */ UChar32 highStart; /* argument check */ if(U_FAILURE(*pErrorCode)) { return; } if( trie==nullptr || valueBits<0 || UTRIE2_COUNT_VALUE_BITS<=valueBits ) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return; } newTrie=trie->newTrie; if(newTrie==nullptr) { /* already frozen */ UTrie2ValueBits frozenValueBits= trie->data16!=nullptr ? UTRIE2_16_VALUE_BITS : UTRIE2_32_VALUE_BITS; if(valueBits!=frozenValueBits) { *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; } return; } /* compact if necessary */ if(!newTrie->isCompacted) { compactTrie(trie, pErrorCode); if(U_FAILURE(*pErrorCode)) { return; } } highStart=trie->highStart; if(highStart<=0x10000) { allIndexesLength=UTRIE2_INDEX_1_OFFSET; } else { allIndexesLength=newTrie->index2Length; } if(valueBits==UTRIE2_16_VALUE_BITS) { dataMove=allIndexesLength; } else { dataMove=0; } /* are indexLength and dataLength within limits? */ if( /* for unshifted indexLength */ allIndexesLength>UTRIE2_MAX_INDEX_LENGTH || /* for unshifted dataNullOffset */ (dataMove+newTrie->dataNullOffset)>0xffff || /* for unshifted 2-byte UTF-8 index-2 values */ (dataMove+UNEWTRIE2_DATA_0800_OFFSET)>0xffff || /* for shiftedDataLength */ (dataMove+newTrie->dataLength)>UTRIE2_MAX_DATA_LENGTH ) { *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR; return; } /* calculate the total serialized length */ length=sizeof(UTrie2Header)+allIndexesLength*2; if(valueBits==UTRIE2_16_VALUE_BITS) { length+=newTrie->dataLength*2; } else { length+=newTrie->dataLength*4; } trie->memory=uprv_malloc(length); if(trie->memory==nullptr) { *pErrorCode=U_MEMORY_ALLOCATION_ERROR; return; } trie->length=length; trie->isMemoryOwned=true; trie->indexLength=allIndexesLength; trie->dataLength=newTrie->dataLength; if(highStart<=0x10000) { trie->index2NullOffset=0xffff; } else { trie->index2NullOffset=static_cast(UTRIE2_INDEX_2_OFFSET+newTrie->index2NullOffset); } trie->dataNullOffset=(uint16_t)(dataMove+newTrie->dataNullOffset); trie->highValueIndex=dataMove+trie->dataLength-UTRIE2_DATA_GRANULARITY; /* set the header fields */ header=(UTrie2Header *)trie->memory; header->signature=UTRIE2_SIG; /* "Tri2" */ header->options=(uint16_t)valueBits; header->indexLength=(uint16_t)trie->indexLength; header->shiftedDataLength=(uint16_t)(trie->dataLength>>UTRIE2_INDEX_SHIFT); header->index2NullOffset=trie->index2NullOffset; header->dataNullOffset=trie->dataNullOffset; header->shiftedHighStart=(uint16_t)(highStart>>UTRIE2_SHIFT_1); /* fill the index and data arrays */ dest16=(uint16_t *)(header+1); trie->index=dest16; /* write the index-2 array values shifted right by UTRIE2_INDEX_SHIFT, after adding dataMove */ p=(uint32_t *)newTrie->index2; for(i=UTRIE2_INDEX_2_BMP_LENGTH; i>0; --i) { *dest16++=(uint16_t)((dataMove + *p++)>>UTRIE2_INDEX_SHIFT); } /* write UTF-8 2-byte index-2 values, not right-shifted */ for(i=0; i<(0xc2-0xc0); ++i) { /* C0..C1 */ *dest16++=(uint16_t)(dataMove+UTRIE2_BAD_UTF8_DATA_OFFSET); } for(; i<(0xe0-0xc0); ++i) { /* C2..DF */ *dest16++=(uint16_t)(dataMove+newTrie->index2[i<<(6-UTRIE2_SHIFT_2)]); } if(highStart>0x10000) { int32_t index1Length=(highStart-0x10000)>>UTRIE2_SHIFT_1; int32_t index2Offset=UTRIE2_INDEX_2_BMP_LENGTH+UTRIE2_UTF8_2B_INDEX_2_LENGTH+index1Length; /* write 16-bit index-1 values for supplementary code points */ p=(uint32_t *)newTrie->index1+UTRIE2_OMITTED_BMP_INDEX_1_LENGTH; for(i=index1Length; i>0; --i) { *dest16++=(uint16_t)(UTRIE2_INDEX_2_OFFSET + *p++); } /* * write the index-2 array values for supplementary code points, * shifted right by UTRIE2_INDEX_SHIFT, after adding dataMove */ p=(uint32_t *)newTrie->index2+index2Offset; for(i=newTrie->index2Length-index2Offset; i>0; --i) { *dest16++=(uint16_t)((dataMove + *p++)>>UTRIE2_INDEX_SHIFT); } } /* write the 16/32-bit data array */ switch(valueBits) { case UTRIE2_16_VALUE_BITS: /* write 16-bit data values */ trie->data16=dest16; trie->data32=nullptr; p=newTrie->data; for(i=newTrie->dataLength; i>0; --i) { *dest16++=(uint16_t)*p++; } break; case UTRIE2_32_VALUE_BITS: /* write 32-bit data values */ trie->data16=nullptr; trie->data32=(uint32_t *)dest16; uprv_memcpy(dest16, newTrie->data, (size_t)newTrie->dataLength*4); break; default: *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; return; } #ifdef UTRIE2_DEBUG utrie2_printLengths(trie, ""); #endif #ifdef UCPTRIE_DEBUG umutablecptrie_setName(newTrie->t3, trie->name); ucptrie_close( umutablecptrie_buildImmutable( newTrie->t3, UCPTRIE_TYPE_FAST, (UCPTrieValueWidth)valueBits, pErrorCode)); #endif /* Delete the UNewTrie2. */ uprv_free(newTrie->data); uprv_free(newTrie); trie->newTrie=nullptr; }