1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852 |
- // © 2017 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- // umutablecptrie.cpp (inspired by utrie2_builder.cpp)
- // created: 2017dec29 Markus W. Scherer
- // #define UCPTRIE_DEBUG
- #ifdef UCPTRIE_DEBUG
- # include <stdio.h>
- #endif
- #include "unicode/utypes.h"
- #include "unicode/ucptrie.h"
- #include "unicode/umutablecptrie.h"
- #include "unicode/uobject.h"
- #include "unicode/utf16.h"
- #include "cmemory.h"
- #include "uassert.h"
- #include "ucptrie_impl.h"
- // ICU-20235 In case Microsoft math.h has defined this, undefine it.
- #ifdef OVERFLOW
- #undef OVERFLOW
- #endif
- U_NAMESPACE_BEGIN
- namespace {
- constexpr int32_t MAX_UNICODE = 0x10ffff;
- constexpr int32_t UNICODE_LIMIT = 0x110000;
- constexpr int32_t BMP_LIMIT = 0x10000;
- constexpr int32_t ASCII_LIMIT = 0x80;
- constexpr int32_t I_LIMIT = UNICODE_LIMIT >> UCPTRIE_SHIFT_3;
- constexpr int32_t BMP_I_LIMIT = BMP_LIMIT >> UCPTRIE_SHIFT_3;
- constexpr int32_t ASCII_I_LIMIT = ASCII_LIMIT >> UCPTRIE_SHIFT_3;
- constexpr int32_t SMALL_DATA_BLOCKS_PER_BMP_BLOCK = (1 << (UCPTRIE_FAST_SHIFT - UCPTRIE_SHIFT_3));
- // Flag values for data blocks.
- constexpr uint8_t ALL_SAME = 0;
- constexpr uint8_t MIXED = 1;
- constexpr uint8_t SAME_AS = 2;
- /** Start with allocation of 16k data entries. */
- constexpr int32_t INITIAL_DATA_LENGTH = ((int32_t)1 << 14);
- /** Grow about 8x each time. */
- constexpr int32_t MEDIUM_DATA_LENGTH = ((int32_t)1 << 17);
- /**
- * Maximum length of the build-time data array.
- * One entry per 0x110000 code points.
- */
- constexpr int32_t MAX_DATA_LENGTH = UNICODE_LIMIT;
- // Flag values for index-3 blocks while compacting/building.
- constexpr uint8_t I3_NULL = 0;
- constexpr uint8_t I3_BMP = 1;
- constexpr uint8_t I3_16 = 2;
- constexpr uint8_t I3_18 = 3;
- constexpr int32_t INDEX_3_18BIT_BLOCK_LENGTH = UCPTRIE_INDEX_3_BLOCK_LENGTH + UCPTRIE_INDEX_3_BLOCK_LENGTH / 8;
- class AllSameBlocks;
- class MixedBlocks;
- class MutableCodePointTrie : public UMemory {
- public:
- MutableCodePointTrie(uint32_t initialValue, uint32_t errorValue, UErrorCode &errorCode);
- MutableCodePointTrie(const MutableCodePointTrie &other, UErrorCode &errorCode);
- MutableCodePointTrie(const MutableCodePointTrie &other) = delete;
- ~MutableCodePointTrie();
- MutableCodePointTrie &operator=(const MutableCodePointTrie &other) = delete;
- static MutableCodePointTrie *fromUCPMap(const UCPMap *map, UErrorCode &errorCode);
- static MutableCodePointTrie *fromUCPTrie(const UCPTrie *trie, UErrorCode &errorCode);
- uint32_t get(UChar32 c) const;
- int32_t getRange(UChar32 start, UCPMapValueFilter *filter, const void *context,
- uint32_t *pValue) const;
- void set(UChar32 c, uint32_t value, UErrorCode &errorCode);
- void setRange(UChar32 start, UChar32 end, uint32_t value, UErrorCode &errorCode);
- UCPTrie *build(UCPTrieType type, UCPTrieValueWidth valueWidth, UErrorCode &errorCode);
- private:
- void clear();
- bool ensureHighStart(UChar32 c);
- int32_t allocDataBlock(int32_t blockLength);
- int32_t getDataBlock(int32_t i);
- void maskValues(uint32_t mask);
- UChar32 findHighStart() const;
- int32_t compactWholeDataBlocks(int32_t fastILimit, AllSameBlocks &allSameBlocks);
- int32_t compactData(
- int32_t fastILimit, uint32_t *newData, int32_t newDataCapacity,
- int32_t dataNullIndex, MixedBlocks &mixedBlocks, UErrorCode &errorCode);
- int32_t compactIndex(int32_t fastILimit, MixedBlocks &mixedBlocks, UErrorCode &errorCode);
- int32_t compactTrie(int32_t fastILimit, UErrorCode &errorCode);
- uint32_t *index = nullptr;
- int32_t indexCapacity = 0;
- int32_t index3NullOffset = -1;
- uint32_t *data = nullptr;
- int32_t dataCapacity = 0;
- int32_t dataLength = 0;
- int32_t dataNullOffset = -1;
- uint32_t origInitialValue;
- uint32_t initialValue;
- uint32_t errorValue;
- UChar32 highStart;
- uint32_t highValue;
- #ifdef UCPTRIE_DEBUG
- public:
- const char *name;
- #endif
- private:
- /** Temporary array while building the final data. */
- uint16_t *index16 = nullptr;
- uint8_t flags[UNICODE_LIMIT >> UCPTRIE_SHIFT_3];
- };
- MutableCodePointTrie::MutableCodePointTrie(uint32_t iniValue, uint32_t errValue, UErrorCode &errorCode) :
- origInitialValue(iniValue), initialValue(iniValue), errorValue(errValue),
- highStart(0), highValue(initialValue)
- #ifdef UCPTRIE_DEBUG
- , name("open")
- #endif
- {
- if (U_FAILURE(errorCode)) { return; }
- index = (uint32_t *)uprv_malloc(BMP_I_LIMIT * 4);
- data = (uint32_t *)uprv_malloc(INITIAL_DATA_LENGTH * 4);
- if (index == nullptr || data == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- indexCapacity = BMP_I_LIMIT;
- dataCapacity = INITIAL_DATA_LENGTH;
- }
- MutableCodePointTrie::MutableCodePointTrie(const MutableCodePointTrie &other, UErrorCode &errorCode) :
- index3NullOffset(other.index3NullOffset),
- dataNullOffset(other.dataNullOffset),
- origInitialValue(other.origInitialValue), initialValue(other.initialValue),
- errorValue(other.errorValue),
- highStart(other.highStart), highValue(other.highValue)
- #ifdef UCPTRIE_DEBUG
- , name("mutable clone")
- #endif
- {
- if (U_FAILURE(errorCode)) { return; }
- int32_t iCapacity = highStart <= BMP_LIMIT ? BMP_I_LIMIT : I_LIMIT;
- index = (uint32_t *)uprv_malloc(iCapacity * 4);
- data = (uint32_t *)uprv_malloc(other.dataCapacity * 4);
- if (index == nullptr || data == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- indexCapacity = iCapacity;
- dataCapacity = other.dataCapacity;
- int32_t iLimit = highStart >> UCPTRIE_SHIFT_3;
- uprv_memcpy(flags, other.flags, iLimit);
- uprv_memcpy(index, other.index, iLimit * 4);
- uprv_memcpy(data, other.data, (size_t)other.dataLength * 4);
- dataLength = other.dataLength;
- U_ASSERT(other.index16 == nullptr);
- }
- MutableCodePointTrie::~MutableCodePointTrie() {
- uprv_free(index);
- uprv_free(data);
- uprv_free(index16);
- }
- MutableCodePointTrie *MutableCodePointTrie::fromUCPMap(const UCPMap *map, UErrorCode &errorCode) {
- // Use the highValue as the initialValue to reduce the highStart.
- uint32_t errorValue = ucpmap_get(map, -1);
- uint32_t initialValue = ucpmap_get(map, 0x10ffff);
- LocalPointer<MutableCodePointTrie> mutableTrie(
- new MutableCodePointTrie(initialValue, errorValue, errorCode),
- errorCode);
- if (U_FAILURE(errorCode)) {
- return nullptr;
- }
- UChar32 start = 0, end;
- uint32_t value;
- while ((end = ucpmap_getRange(map, start, UCPMAP_RANGE_NORMAL, 0,
- nullptr, nullptr, &value)) >= 0) {
- if (value != initialValue) {
- if (start == end) {
- mutableTrie->set(start, value, errorCode);
- } else {
- mutableTrie->setRange(start, end, value, errorCode);
- }
- }
- start = end + 1;
- }
- if (U_SUCCESS(errorCode)) {
- return mutableTrie.orphan();
- } else {
- return nullptr;
- }
- }
- MutableCodePointTrie *MutableCodePointTrie::fromUCPTrie(const UCPTrie *trie, UErrorCode &errorCode) {
- // Use the highValue as the initialValue to reduce the highStart.
- uint32_t errorValue;
- uint32_t initialValue;
- switch (trie->valueWidth) {
- case UCPTRIE_VALUE_BITS_16:
- errorValue = trie->data.ptr16[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET];
- initialValue = trie->data.ptr16[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET];
- break;
- case UCPTRIE_VALUE_BITS_32:
- errorValue = trie->data.ptr32[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET];
- initialValue = trie->data.ptr32[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET];
- break;
- case UCPTRIE_VALUE_BITS_8:
- errorValue = trie->data.ptr8[trie->dataLength - UCPTRIE_ERROR_VALUE_NEG_DATA_OFFSET];
- initialValue = trie->data.ptr8[trie->dataLength - UCPTRIE_HIGH_VALUE_NEG_DATA_OFFSET];
- break;
- default:
- // Unreachable if the trie is properly initialized.
- errorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return nullptr;
- }
- LocalPointer<MutableCodePointTrie> mutableTrie(
- new MutableCodePointTrie(initialValue, errorValue, errorCode),
- errorCode);
- if (U_FAILURE(errorCode)) {
- return nullptr;
- }
- UChar32 start = 0, end;
- uint32_t value;
- while ((end = ucptrie_getRange(trie, start, UCPMAP_RANGE_NORMAL, 0,
- nullptr, nullptr, &value)) >= 0) {
- if (value != initialValue) {
- if (start == end) {
- mutableTrie->set(start, value, errorCode);
- } else {
- mutableTrie->setRange(start, end, value, errorCode);
- }
- }
- start = end + 1;
- }
- if (U_SUCCESS(errorCode)) {
- return mutableTrie.orphan();
- } else {
- return nullptr;
- }
- }
- void MutableCodePointTrie::clear() {
- index3NullOffset = dataNullOffset = -1;
- dataLength = 0;
- highValue = initialValue = origInitialValue;
- highStart = 0;
- uprv_free(index16);
- index16 = nullptr;
- }
- uint32_t MutableCodePointTrie::get(UChar32 c) const {
- if ((uint32_t)c > MAX_UNICODE) {
- return errorValue;
- }
- if (c >= highStart) {
- return highValue;
- }
- int32_t i = c >> UCPTRIE_SHIFT_3;
- if (flags[i] == ALL_SAME) {
- return index[i];
- } else {
- return data[index[i] + (c & UCPTRIE_SMALL_DATA_MASK)];
- }
- }
- inline uint32_t maybeFilterValue(uint32_t value, uint32_t initialValue, uint32_t nullValue,
- UCPMapValueFilter *filter, const void *context) {
- if (value == initialValue) {
- value = nullValue;
- } else if (filter != nullptr) {
- value = filter(context, value);
- }
- return value;
- }
- UChar32 MutableCodePointTrie::getRange(
- UChar32 start, UCPMapValueFilter *filter, const void *context,
- uint32_t *pValue) const {
- if ((uint32_t)start > MAX_UNICODE) {
- return U_SENTINEL;
- }
- if (start >= highStart) {
- if (pValue != nullptr) {
- uint32_t value = highValue;
- if (filter != nullptr) { value = filter(context, value); }
- *pValue = value;
- }
- return MAX_UNICODE;
- }
- uint32_t nullValue = initialValue;
- if (filter != nullptr) { nullValue = filter(context, nullValue); }
- UChar32 c = start;
- uint32_t trieValue, value;
- bool haveValue = false;
- int32_t i = c >> UCPTRIE_SHIFT_3;
- do {
- if (flags[i] == ALL_SAME) {
- uint32_t trieValue2 = index[i];
- if (haveValue) {
- if (trieValue2 != trieValue) {
- if (filter == nullptr ||
- maybeFilterValue(trieValue2, initialValue, nullValue,
- filter, context) != value) {
- return c - 1;
- }
- trieValue = trieValue2; // may or may not help
- }
- } else {
- trieValue = trieValue2;
- value = maybeFilterValue(trieValue2, initialValue, nullValue, filter, context);
- if (pValue != nullptr) { *pValue = value; }
- haveValue = true;
- }
- c = (c + UCPTRIE_SMALL_DATA_BLOCK_LENGTH) & ~UCPTRIE_SMALL_DATA_MASK;
- } else /* MIXED */ {
- int32_t di = index[i] + (c & UCPTRIE_SMALL_DATA_MASK);
- uint32_t trieValue2 = data[di];
- if (haveValue) {
- if (trieValue2 != trieValue) {
- if (filter == nullptr ||
- maybeFilterValue(trieValue2, initialValue, nullValue,
- filter, context) != value) {
- return c - 1;
- }
- trieValue = trieValue2; // may or may not help
- }
- } else {
- trieValue = trieValue2;
- value = maybeFilterValue(trieValue2, initialValue, nullValue, filter, context);
- if (pValue != nullptr) { *pValue = value; }
- haveValue = true;
- }
- while ((++c & UCPTRIE_SMALL_DATA_MASK) != 0) {
- trieValue2 = data[++di];
- if (trieValue2 != trieValue) {
- if (filter == nullptr ||
- maybeFilterValue(trieValue2, initialValue, nullValue,
- filter, context) != value) {
- return c - 1;
- }
- }
- trieValue = trieValue2; // may or may not help
- }
- }
- ++i;
- } while (c < highStart);
- U_ASSERT(haveValue);
- if (maybeFilterValue(highValue, initialValue, nullValue,
- filter, context) != value) {
- return c - 1;
- } else {
- return MAX_UNICODE;
- }
- }
- void
- writeBlock(uint32_t *block, uint32_t value) {
- uint32_t *limit = block + UCPTRIE_SMALL_DATA_BLOCK_LENGTH;
- while (block < limit) {
- *block++ = value;
- }
- }
- bool MutableCodePointTrie::ensureHighStart(UChar32 c) {
- if (c >= highStart) {
- // Round up to a UCPTRIE_CP_PER_INDEX_2_ENTRY boundary to simplify compaction.
- c = (c + UCPTRIE_CP_PER_INDEX_2_ENTRY) & ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1);
- int32_t i = highStart >> UCPTRIE_SHIFT_3;
- int32_t iLimit = c >> UCPTRIE_SHIFT_3;
- if (iLimit > indexCapacity) {
- uint32_t *newIndex = (uint32_t *)uprv_malloc(I_LIMIT * 4);
- if (newIndex == nullptr) { return false; }
- uprv_memcpy(newIndex, index, i * 4);
- uprv_free(index);
- index = newIndex;
- indexCapacity = I_LIMIT;
- }
- do {
- flags[i] = ALL_SAME;
- index[i] = initialValue;
- } while(++i < iLimit);
- highStart = c;
- }
- return true;
- }
- int32_t MutableCodePointTrie::allocDataBlock(int32_t blockLength) {
- int32_t newBlock = dataLength;
- int32_t newTop = newBlock + blockLength;
- if (newTop > dataCapacity) {
- int32_t capacity;
- if (dataCapacity < MEDIUM_DATA_LENGTH) {
- capacity = MEDIUM_DATA_LENGTH;
- } else if (dataCapacity < MAX_DATA_LENGTH) {
- capacity = MAX_DATA_LENGTH;
- } else {
- // Should never occur.
- // Either MAX_DATA_LENGTH is incorrect,
- // or the code writes more values than should be possible.
- return -1;
- }
- uint32_t *newData = (uint32_t *)uprv_malloc(capacity * 4);
- if (newData == nullptr) {
- return -1;
- }
- uprv_memcpy(newData, data, (size_t)dataLength * 4);
- uprv_free(data);
- data = newData;
- dataCapacity = capacity;
- }
- dataLength = newTop;
- return newBlock;
- }
- /**
- * No error checking for illegal arguments.
- *
- * @return -1 if no new data block available (out of memory in data array)
- * @internal
- */
- int32_t MutableCodePointTrie::getDataBlock(int32_t i) {
- if (flags[i] == MIXED) {
- return index[i];
- }
- if (i < BMP_I_LIMIT) {
- int32_t newBlock = allocDataBlock(UCPTRIE_FAST_DATA_BLOCK_LENGTH);
- if (newBlock < 0) { return newBlock; }
- int32_t iStart = i & ~(SMALL_DATA_BLOCKS_PER_BMP_BLOCK -1);
- int32_t iLimit = iStart + SMALL_DATA_BLOCKS_PER_BMP_BLOCK;
- do {
- U_ASSERT(flags[iStart] == ALL_SAME);
- writeBlock(data + newBlock, index[iStart]);
- flags[iStart] = MIXED;
- index[iStart++] = newBlock;
- newBlock += UCPTRIE_SMALL_DATA_BLOCK_LENGTH;
- } while (iStart < iLimit);
- return index[i];
- } else {
- int32_t newBlock = allocDataBlock(UCPTRIE_SMALL_DATA_BLOCK_LENGTH);
- if (newBlock < 0) { return newBlock; }
- writeBlock(data + newBlock, index[i]);
- flags[i] = MIXED;
- index[i] = newBlock;
- return newBlock;
- }
- }
- void MutableCodePointTrie::set(UChar32 c, uint32_t value, UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) {
- return;
- }
- if ((uint32_t)c > MAX_UNICODE) {
- errorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
- int32_t block;
- if (!ensureHighStart(c) || (block = getDataBlock(c >> UCPTRIE_SHIFT_3)) < 0) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- data[block + (c & UCPTRIE_SMALL_DATA_MASK)] = value;
- }
- void
- fillBlock(uint32_t *block, UChar32 start, UChar32 limit, uint32_t value) {
- uint32_t *pLimit = block + limit;
- block += start;
- while (block < pLimit) {
- *block++ = value;
- }
- }
- void MutableCodePointTrie::setRange(UChar32 start, UChar32 end, uint32_t value, UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) {
- return;
- }
- if ((uint32_t)start > MAX_UNICODE || (uint32_t)end > MAX_UNICODE || start > end) {
- errorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
- if (!ensureHighStart(end)) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- UChar32 limit = end + 1;
- if (start & UCPTRIE_SMALL_DATA_MASK) {
- // Set partial block at [start..following block boundary[.
- int32_t block = getDataBlock(start >> UCPTRIE_SHIFT_3);
- if (block < 0) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- UChar32 nextStart = (start + UCPTRIE_SMALL_DATA_MASK) & ~UCPTRIE_SMALL_DATA_MASK;
- if (nextStart <= limit) {
- fillBlock(data + block, start & UCPTRIE_SMALL_DATA_MASK, UCPTRIE_SMALL_DATA_BLOCK_LENGTH,
- value);
- start = nextStart;
- } else {
- fillBlock(data + block, start & UCPTRIE_SMALL_DATA_MASK, limit & UCPTRIE_SMALL_DATA_MASK,
- value);
- return;
- }
- }
- // Number of positions in the last, partial block.
- int32_t rest = limit & UCPTRIE_SMALL_DATA_MASK;
- // Round down limit to a block boundary.
- limit &= ~UCPTRIE_SMALL_DATA_MASK;
- // Iterate over all-value blocks.
- while (start < limit) {
- int32_t i = start >> UCPTRIE_SHIFT_3;
- if (flags[i] == ALL_SAME) {
- index[i] = value;
- } else /* MIXED */ {
- fillBlock(data + index[i], 0, UCPTRIE_SMALL_DATA_BLOCK_LENGTH, value);
- }
- start += UCPTRIE_SMALL_DATA_BLOCK_LENGTH;
- }
- if (rest > 0) {
- // Set partial block at [last block boundary..limit[.
- int32_t block = getDataBlock(start >> UCPTRIE_SHIFT_3);
- if (block < 0) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- fillBlock(data + block, 0, rest, value);
- }
- }
- /* compaction --------------------------------------------------------------- */
- void MutableCodePointTrie::maskValues(uint32_t mask) {
- initialValue &= mask;
- errorValue &= mask;
- highValue &= mask;
- int32_t iLimit = highStart >> UCPTRIE_SHIFT_3;
- for (int32_t i = 0; i < iLimit; ++i) {
- if (flags[i] == ALL_SAME) {
- index[i] &= mask;
- }
- }
- for (int32_t i = 0; i < dataLength; ++i) {
- data[i] &= mask;
- }
- }
- template<typename UIntA, typename UIntB>
- bool equalBlocks(const UIntA *s, const UIntB *t, int32_t length) {
- while (length > 0 && *s == *t) {
- ++s;
- ++t;
- --length;
- }
- return length == 0;
- }
- bool allValuesSameAs(const uint32_t *p, int32_t length, uint32_t value) {
- const uint32_t *pLimit = p + length;
- while (p < pLimit && *p == value) { ++p; }
- return p == pLimit;
- }
- /** Search for an identical block. */
- int32_t findSameBlock(const uint16_t *p, int32_t pStart, int32_t length,
- const uint16_t *q, int32_t qStart, int32_t blockLength) {
- // Ensure that we do not even partially get past length.
- length -= blockLength;
- q += qStart;
- while (pStart <= length) {
- if (equalBlocks(p + pStart, q, blockLength)) {
- return pStart;
- }
- ++pStart;
- }
- return -1;
- }
- int32_t findAllSameBlock(const uint32_t *p, int32_t start, int32_t limit,
- uint32_t value, int32_t blockLength) {
- // Ensure that we do not even partially get past limit.
- limit -= blockLength;
- for (int32_t block = start; block <= limit; ++block) {
- if (p[block] == value) {
- for (int32_t i = 1;; ++i) {
- if (i == blockLength) {
- return block;
- }
- if (p[block + i] != value) {
- block += i;
- break;
- }
- }
- }
- }
- return -1;
- }
- /**
- * Look for maximum overlap of the beginning of the other block
- * with the previous, adjacent block.
- */
- template<typename UIntA, typename UIntB>
- int32_t getOverlap(const UIntA *p, int32_t length,
- const UIntB *q, int32_t qStart, int32_t blockLength) {
- int32_t overlap = blockLength - 1;
- U_ASSERT(overlap <= length);
- q += qStart;
- while (overlap > 0 && !equalBlocks(p + (length - overlap), q, overlap)) {
- --overlap;
- }
- return overlap;
- }
- int32_t getAllSameOverlap(const uint32_t *p, int32_t length, uint32_t value,
- int32_t blockLength) {
- int32_t min = length - (blockLength - 1);
- int32_t i = length;
- while (min < i && p[i - 1] == value) { --i; }
- return length - i;
- }
- bool isStartOfSomeFastBlock(uint32_t dataOffset, const uint32_t index[], int32_t fastILimit) {
- for (int32_t i = 0; i < fastILimit; i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK) {
- if (index[i] == dataOffset) {
- return true;
- }
- }
- return false;
- }
- /**
- * Finds the start of the last range in the trie by enumerating backward.
- * Indexes for code points higher than this will be omitted.
- */
- UChar32 MutableCodePointTrie::findHighStart() const {
- int32_t i = highStart >> UCPTRIE_SHIFT_3;
- while (i > 0) {
- bool match;
- if (flags[--i] == ALL_SAME) {
- match = index[i] == highValue;
- } else /* MIXED */ {
- const uint32_t *p = data + index[i];
- for (int32_t j = 0;; ++j) {
- if (j == UCPTRIE_SMALL_DATA_BLOCK_LENGTH) {
- match = true;
- break;
- }
- if (p[j] != highValue) {
- match = false;
- break;
- }
- }
- }
- if (!match) {
- return (i + 1) << UCPTRIE_SHIFT_3;
- }
- }
- return 0;
- }
- class AllSameBlocks {
- public:
- static constexpr int32_t NEW_UNIQUE = -1;
- static constexpr int32_t OVERFLOW = -2;
- AllSameBlocks() : length(0), mostRecent(-1) {}
- int32_t findOrAdd(int32_t index, int32_t count, uint32_t value) {
- if (mostRecent >= 0 && values[mostRecent] == value) {
- refCounts[mostRecent] += count;
- return indexes[mostRecent];
- }
- for (int32_t i = 0; i < length; ++i) {
- if (values[i] == value) {
- mostRecent = i;
- refCounts[i] += count;
- return indexes[i];
- }
- }
- if (length == CAPACITY) {
- return OVERFLOW;
- }
- mostRecent = length;
- indexes[length] = index;
- values[length] = value;
- refCounts[length++] = count;
- return NEW_UNIQUE;
- }
- /** Replaces the block which has the lowest reference count. */
- void add(int32_t index, int32_t count, uint32_t value) {
- U_ASSERT(length == CAPACITY);
- int32_t least = -1;
- int32_t leastCount = I_LIMIT;
- for (int32_t i = 0; i < length; ++i) {
- U_ASSERT(values[i] != value);
- if (refCounts[i] < leastCount) {
- least = i;
- leastCount = refCounts[i];
- }
- }
- U_ASSERT(least >= 0);
- mostRecent = least;
- indexes[least] = index;
- values[least] = value;
- refCounts[least] = count;
- }
- int32_t findMostUsed() const {
- if (length == 0) { return -1; }
- int32_t max = -1;
- int32_t maxCount = 0;
- for (int32_t i = 0; i < length; ++i) {
- if (refCounts[i] > maxCount) {
- max = i;
- maxCount = refCounts[i];
- }
- }
- return indexes[max];
- }
- private:
- static constexpr int32_t CAPACITY = 32;
- int32_t length;
- int32_t mostRecent;
- int32_t indexes[CAPACITY];
- uint32_t values[CAPACITY];
- int32_t refCounts[CAPACITY];
- };
- // Custom hash table for mixed-value blocks to be found anywhere in the
- // compacted data or index so far.
- class MixedBlocks {
- public:
- MixedBlocks() {}
- ~MixedBlocks() {
- uprv_free(table);
- }
- bool init(int32_t maxLength, int32_t newBlockLength) {
- // We store actual data indexes + 1 to reserve 0 for empty entries.
- int32_t maxDataIndex = maxLength - newBlockLength + 1;
- int32_t newLength;
- if (maxDataIndex <= 0xfff) { // 4k
- newLength = 6007;
- shift = 12;
- mask = 0xfff;
- } else if (maxDataIndex <= 0x7fff) { // 32k
- newLength = 50021;
- shift = 15;
- mask = 0x7fff;
- } else if (maxDataIndex <= 0x1ffff) { // 128k
- newLength = 200003;
- shift = 17;
- mask = 0x1ffff;
- } else {
- // maxDataIndex up to around MAX_DATA_LENGTH, ca. 1.1M
- newLength = 1500007;
- shift = 21;
- mask = 0x1fffff;
- }
- if (newLength > capacity) {
- uprv_free(table);
- table = (uint32_t *)uprv_malloc(newLength * 4);
- if (table == nullptr) {
- return false;
- }
- capacity = newLength;
- }
- length = newLength;
- uprv_memset(table, 0, length * 4);
- blockLength = newBlockLength;
- return true;
- }
- template<typename UInt>
- void extend(const UInt *data, int32_t minStart, int32_t prevDataLength, int32_t newDataLength) {
- int32_t start = prevDataLength - blockLength;
- if (start >= minStart) {
- ++start; // Skip the last block that we added last time.
- } else {
- start = minStart; // Begin with the first full block.
- }
- for (int32_t end = newDataLength - blockLength; start <= end; ++start) {
- uint32_t hashCode = makeHashCode(data, start);
- addEntry(data, start, hashCode, start);
- }
- }
- template<typename UIntA, typename UIntB>
- int32_t findBlock(const UIntA *data, const UIntB *blockData, int32_t blockStart) const {
- uint32_t hashCode = makeHashCode(blockData, blockStart);
- int32_t entryIndex = findEntry(data, blockData, blockStart, hashCode);
- if (entryIndex >= 0) {
- return (table[entryIndex] & mask) - 1;
- } else {
- return -1;
- }
- }
- int32_t findAllSameBlock(const uint32_t *data, uint32_t blockValue) const {
- uint32_t hashCode = makeHashCode(blockValue);
- int32_t entryIndex = findEntry(data, blockValue, hashCode);
- if (entryIndex >= 0) {
- return (table[entryIndex] & mask) - 1;
- } else {
- return -1;
- }
- }
- private:
- template<typename UInt>
- uint32_t makeHashCode(const UInt *blockData, int32_t blockStart) const {
- int32_t blockLimit = blockStart + blockLength;
- uint32_t hashCode = blockData[blockStart++];
- do {
- hashCode = 37 * hashCode + blockData[blockStart++];
- } while (blockStart < blockLimit);
- return hashCode;
- }
- uint32_t makeHashCode(uint32_t blockValue) const {
- uint32_t hashCode = blockValue;
- for (int32_t i = 1; i < blockLength; ++i) {
- hashCode = 37 * hashCode + blockValue;
- }
- return hashCode;
- }
- template<typename UInt>
- void addEntry(const UInt *data, int32_t blockStart, uint32_t hashCode, int32_t dataIndex) {
- U_ASSERT(0 <= dataIndex && dataIndex < (int32_t)mask);
- int32_t entryIndex = findEntry(data, data, blockStart, hashCode);
- if (entryIndex < 0) {
- table[~entryIndex] = (hashCode << shift) | (dataIndex + 1);
- }
- }
- template<typename UIntA, typename UIntB>
- int32_t findEntry(const UIntA *data, const UIntB *blockData, int32_t blockStart,
- uint32_t hashCode) const {
- uint32_t shiftedHashCode = hashCode << shift;
- int32_t initialEntryIndex = (hashCode % (length - 1)) + 1; // 1..length-1
- for (int32_t entryIndex = initialEntryIndex;;) {
- uint32_t entry = table[entryIndex];
- if (entry == 0) {
- return ~entryIndex;
- }
- if ((entry & ~mask) == shiftedHashCode) {
- int32_t dataIndex = (entry & mask) - 1;
- if (equalBlocks(data + dataIndex, blockData + blockStart, blockLength)) {
- return entryIndex;
- }
- }
- entryIndex = nextIndex(initialEntryIndex, entryIndex);
- }
- }
- int32_t findEntry(const uint32_t *data, uint32_t blockValue, uint32_t hashCode) const {
- uint32_t shiftedHashCode = hashCode << shift;
- int32_t initialEntryIndex = (hashCode % (length - 1)) + 1; // 1..length-1
- for (int32_t entryIndex = initialEntryIndex;;) {
- uint32_t entry = table[entryIndex];
- if (entry == 0) {
- return ~entryIndex;
- }
- if ((entry & ~mask) == shiftedHashCode) {
- int32_t dataIndex = (entry & mask) - 1;
- if (allValuesSameAs(data + dataIndex, blockLength, blockValue)) {
- return entryIndex;
- }
- }
- entryIndex = nextIndex(initialEntryIndex, entryIndex);
- }
- }
- inline int32_t nextIndex(int32_t initialEntryIndex, int32_t entryIndex) const {
- // U_ASSERT(0 < initialEntryIndex && initialEntryIndex < length);
- return (entryIndex + initialEntryIndex) % length;
- }
- // Hash table.
- // The length is a prime number, larger than the maximum data length.
- // The "shift" lower bits store a data index + 1.
- // The remaining upper bits store a partial hashCode of the block data values.
- uint32_t *table = nullptr;
- int32_t capacity = 0;
- int32_t length = 0;
- int32_t shift = 0;
- uint32_t mask = 0;
- int32_t blockLength = 0;
- };
- int32_t MutableCodePointTrie::compactWholeDataBlocks(int32_t fastILimit, AllSameBlocks &allSameBlocks) {
- #ifdef UCPTRIE_DEBUG
- bool overflow = false;
- #endif
- // ASCII data will be stored as a linear table, even if the following code
- // does not yet count it that way.
- int32_t newDataCapacity = ASCII_LIMIT;
- // Add room for a small data null block in case it would match the start of
- // a fast data block where dataNullOffset must not be set in that case.
- newDataCapacity += UCPTRIE_SMALL_DATA_BLOCK_LENGTH;
- // Add room for special values (errorValue, highValue) and padding.
- newDataCapacity += 4;
- int32_t iLimit = highStart >> UCPTRIE_SHIFT_3;
- int32_t blockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH;
- int32_t inc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK;
- for (int32_t i = 0; i < iLimit; i += inc) {
- if (i == fastILimit) {
- blockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH;
- inc = 1;
- }
- uint32_t value = index[i];
- if (flags[i] == MIXED) {
- // Really mixed?
- const uint32_t *p = data + value;
- value = *p;
- if (allValuesSameAs(p + 1, blockLength - 1, value)) {
- flags[i] = ALL_SAME;
- index[i] = value;
- // Fall through to ALL_SAME handling.
- } else {
- newDataCapacity += blockLength;
- continue;
- }
- } else {
- U_ASSERT(flags[i] == ALL_SAME);
- if (inc > 1) {
- // Do all of the fast-range data block's ALL_SAME parts have the same value?
- bool allSame = true;
- int32_t next_i = i + inc;
- for (int32_t j = i + 1; j < next_i; ++j) {
- U_ASSERT(flags[j] == ALL_SAME);
- if (index[j] != value) {
- allSame = false;
- break;
- }
- }
- if (!allSame) {
- // Turn it into a MIXED block.
- if (getDataBlock(i) < 0) {
- return -1;
- }
- newDataCapacity += blockLength;
- continue;
- }
- }
- }
- // Is there another ALL_SAME block with the same value?
- int32_t other = allSameBlocks.findOrAdd(i, inc, value);
- if (other == AllSameBlocks::OVERFLOW) {
- // The fixed-size array overflowed. Slow check for a duplicate block.
- #ifdef UCPTRIE_DEBUG
- if (!overflow) {
- puts("UCPTrie AllSameBlocks overflow");
- overflow = true;
- }
- #endif
- int32_t jInc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK;
- for (int32_t j = 0;; j += jInc) {
- if (j == i) {
- allSameBlocks.add(i, inc, value);
- break;
- }
- if (j == fastILimit) {
- jInc = 1;
- }
- if (flags[j] == ALL_SAME && index[j] == value) {
- allSameBlocks.add(j, jInc + inc, value);
- other = j;
- break;
- // We could keep counting blocks with the same value
- // before we add the first one, which may improve compaction in rare cases,
- // but it would make it slower.
- }
- }
- }
- if (other >= 0) {
- flags[i] = SAME_AS;
- index[i] = other;
- } else {
- // New unique same-value block.
- newDataCapacity += blockLength;
- }
- }
- return newDataCapacity;
- }
- #ifdef UCPTRIE_DEBUG
- # define DEBUG_DO(expr) expr
- #else
- # define DEBUG_DO(expr)
- #endif
- #ifdef UCPTRIE_DEBUG
- // Braille symbols: U+28xx = UTF-8 E2 A0 80..E2 A3 BF
- int32_t appendValue(char s[], int32_t length, uint32_t value) {
- value ^= value >> 16;
- value ^= value >> 8;
- s[length] = 0xE2;
- s[length + 1] = (char)(0xA0 + ((value >> 6) & 3));
- s[length + 2] = (char)(0x80 + (value & 0x3F));
- return length + 3;
- }
- void printBlock(const uint32_t *block, int32_t blockLength, uint32_t value,
- UChar32 start, int32_t overlap, uint32_t initialValue) {
- char s[UCPTRIE_FAST_DATA_BLOCK_LENGTH * 3 + 3];
- int32_t length = 0;
- int32_t i;
- for (i = 0; i < overlap; ++i) {
- length = appendValue(s, length, 0); // Braille blank
- }
- s[length++] = '|';
- for (; i < blockLength; ++i) {
- if (block != nullptr) {
- value = block[i];
- }
- if (value == initialValue) {
- value = 0x40; // Braille lower left dot
- }
- length = appendValue(s, length, value);
- }
- s[length] = 0;
- start += overlap;
- if (start <= 0xffff) {
- printf(" %04lX %s|\n", (long)start, s);
- } else if (start <= 0xfffff) {
- printf(" %5lX %s|\n", (long)start, s);
- } else {
- printf(" %6lX %s|\n", (long)start, s);
- }
- }
- #endif
- /**
- * Compacts a build-time trie.
- *
- * The compaction
- * - removes blocks that are identical with earlier ones
- * - overlaps each new non-duplicate block as much as possible with the previously-written one
- * - works with fast-range data blocks whose length is a multiple of that of
- * higher-code-point data blocks
- *
- * It does not try to find an optimal order of writing, deduplicating, and overlapping blocks.
- */
- int32_t MutableCodePointTrie::compactData(
- int32_t fastILimit, uint32_t *newData, int32_t newDataCapacity,
- int32_t dataNullIndex, MixedBlocks &mixedBlocks, UErrorCode &errorCode) {
- #ifdef UCPTRIE_DEBUG
- int32_t countSame=0, sumOverlaps=0;
- bool printData = dataLength == 29088 /* line.brk */ ||
- // dataLength == 30048 /* CanonIterData */ ||
- dataLength == 50400 /* zh.txt~stroke */;
- #endif
- // The linear ASCII data has been copied into newData already.
- int32_t newDataLength = 0;
- for (int32_t i = 0; newDataLength < ASCII_LIMIT;
- newDataLength += UCPTRIE_FAST_DATA_BLOCK_LENGTH, i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK) {
- index[i] = newDataLength;
- #ifdef UCPTRIE_DEBUG
- if (printData) {
- printBlock(newData + newDataLength, UCPTRIE_FAST_DATA_BLOCK_LENGTH, 0, newDataLength, 0, initialValue);
- }
- #endif
- }
- int32_t blockLength = UCPTRIE_FAST_DATA_BLOCK_LENGTH;
- if (!mixedBlocks.init(newDataCapacity, blockLength)) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- mixedBlocks.extend(newData, 0, 0, newDataLength);
- int32_t iLimit = highStart >> UCPTRIE_SHIFT_3;
- int32_t inc = SMALL_DATA_BLOCKS_PER_BMP_BLOCK;
- int32_t fastLength = 0;
- for (int32_t i = ASCII_I_LIMIT; i < iLimit; i += inc) {
- if (i == fastILimit) {
- blockLength = UCPTRIE_SMALL_DATA_BLOCK_LENGTH;
- inc = 1;
- fastLength = newDataLength;
- if (!mixedBlocks.init(newDataCapacity, blockLength)) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- mixedBlocks.extend(newData, 0, 0, newDataLength);
- }
- if (flags[i] == ALL_SAME) {
- uint32_t value = index[i];
- // Find an earlier part of the data array of length blockLength
- // that is filled with this value.
- int32_t n = mixedBlocks.findAllSameBlock(newData, value);
- // If we find a match, and the current block is the data null block,
- // and it is not a fast block but matches the start of a fast block,
- // then we need to continue looking.
- // This is because this small block is shorter than the fast block,
- // and not all of the rest of the fast block is filled with this value.
- // Otherwise trie.getRange() would detect that the fast block starts at
- // dataNullOffset and assume incorrectly that it is filled with the null value.
- while (n >= 0 && i == dataNullIndex && i >= fastILimit && n < fastLength &&
- isStartOfSomeFastBlock(n, index, fastILimit)) {
- n = findAllSameBlock(newData, n + 1, newDataLength, value, blockLength);
- }
- if (n >= 0) {
- DEBUG_DO(++countSame);
- index[i] = n;
- } else {
- n = getAllSameOverlap(newData, newDataLength, value, blockLength);
- DEBUG_DO(sumOverlaps += n);
- #ifdef UCPTRIE_DEBUG
- if (printData) {
- printBlock(nullptr, blockLength, value, i << UCPTRIE_SHIFT_3, n, initialValue);
- }
- #endif
- index[i] = newDataLength - n;
- int32_t prevDataLength = newDataLength;
- while (n < blockLength) {
- newData[newDataLength++] = value;
- ++n;
- }
- mixedBlocks.extend(newData, 0, prevDataLength, newDataLength);
- }
- } else if (flags[i] == MIXED) {
- const uint32_t *block = data + index[i];
- int32_t n = mixedBlocks.findBlock(newData, block, 0);
- if (n >= 0) {
- DEBUG_DO(++countSame);
- index[i] = n;
- } else {
- n = getOverlap(newData, newDataLength, block, 0, blockLength);
- DEBUG_DO(sumOverlaps += n);
- #ifdef UCPTRIE_DEBUG
- if (printData) {
- printBlock(block, blockLength, 0, i << UCPTRIE_SHIFT_3, n, initialValue);
- }
- #endif
- index[i] = newDataLength - n;
- int32_t prevDataLength = newDataLength;
- while (n < blockLength) {
- newData[newDataLength++] = block[n++];
- }
- mixedBlocks.extend(newData, 0, prevDataLength, newDataLength);
- }
- } else /* SAME_AS */ {
- uint32_t j = index[i];
- index[i] = index[j];
- }
- }
- #ifdef UCPTRIE_DEBUG
- /* we saved some space */
- printf("compacting UCPTrie: count of 32-bit data words %lu->%lu countSame=%ld sumOverlaps=%ld\n",
- (long)dataLength, (long)newDataLength, (long)countSame, (long)sumOverlaps);
- #endif
- return newDataLength;
- }
- int32_t MutableCodePointTrie::compactIndex(int32_t fastILimit, MixedBlocks &mixedBlocks,
- UErrorCode &errorCode) {
- int32_t fastIndexLength = fastILimit >> (UCPTRIE_FAST_SHIFT - UCPTRIE_SHIFT_3);
- if ((highStart >> UCPTRIE_FAST_SHIFT) <= fastIndexLength) {
- // Only the linear fast index, no multi-stage index tables.
- index3NullOffset = UCPTRIE_NO_INDEX3_NULL_OFFSET;
- return fastIndexLength;
- }
- // Condense the fast index table.
- // Also, does it contain an index-3 block with all dataNullOffset?
- uint16_t fastIndex[UCPTRIE_BMP_INDEX_LENGTH]; // fastIndexLength
- int32_t i3FirstNull = -1;
- for (int32_t i = 0, j = 0; i < fastILimit; ++j) {
- uint32_t i3 = index[i];
- fastIndex[j] = (uint16_t)i3;
- if (i3 == (uint32_t)dataNullOffset) {
- if (i3FirstNull < 0) {
- i3FirstNull = j;
- } else if (index3NullOffset < 0 &&
- (j - i3FirstNull + 1) == UCPTRIE_INDEX_3_BLOCK_LENGTH) {
- index3NullOffset = i3FirstNull;
- }
- } else {
- i3FirstNull = -1;
- }
- // Set the index entries that compactData() skipped.
- // Needed when the multi-stage index covers the fast index range as well.
- int32_t iNext = i + SMALL_DATA_BLOCKS_PER_BMP_BLOCK;
- while (++i < iNext) {
- i3 += UCPTRIE_SMALL_DATA_BLOCK_LENGTH;
- index[i] = i3;
- }
- }
- if (!mixedBlocks.init(fastIndexLength, UCPTRIE_INDEX_3_BLOCK_LENGTH)) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- mixedBlocks.extend(fastIndex, 0, 0, fastIndexLength);
- // Examine index-3 blocks. For each determine one of:
- // - same as the index-3 null block
- // - same as a fast-index block
- // - 16-bit indexes
- // - 18-bit indexes
- // We store this in the first flags entry for the index-3 block.
- //
- // Also determine an upper limit for the index-3 table length.
- int32_t index3Capacity = 0;
- i3FirstNull = index3NullOffset;
- bool hasLongI3Blocks = false;
- // If the fast index covers the whole BMP, then
- // the multi-stage index is only for supplementary code points.
- // Otherwise, the multi-stage index covers all of Unicode.
- int32_t iStart = fastILimit < BMP_I_LIMIT ? 0 : BMP_I_LIMIT;
- int32_t iLimit = highStart >> UCPTRIE_SHIFT_3;
- for (int32_t i = iStart; i < iLimit;) {
- int32_t j = i;
- int32_t jLimit = i + UCPTRIE_INDEX_3_BLOCK_LENGTH;
- uint32_t oredI3 = 0;
- bool isNull = true;
- do {
- uint32_t i3 = index[j];
- oredI3 |= i3;
- if (i3 != (uint32_t)dataNullOffset) {
- isNull = false;
- }
- } while (++j < jLimit);
- if (isNull) {
- flags[i] = I3_NULL;
- if (i3FirstNull < 0) {
- if (oredI3 <= 0xffff) {
- index3Capacity += UCPTRIE_INDEX_3_BLOCK_LENGTH;
- } else {
- index3Capacity += INDEX_3_18BIT_BLOCK_LENGTH;
- hasLongI3Blocks = true;
- }
- i3FirstNull = 0;
- }
- } else {
- if (oredI3 <= 0xffff) {
- int32_t n = mixedBlocks.findBlock(fastIndex, index, i);
- if (n >= 0) {
- flags[i] = I3_BMP;
- index[i] = n;
- } else {
- flags[i] = I3_16;
- index3Capacity += UCPTRIE_INDEX_3_BLOCK_LENGTH;
- }
- } else {
- flags[i] = I3_18;
- index3Capacity += INDEX_3_18BIT_BLOCK_LENGTH;
- hasLongI3Blocks = true;
- }
- }
- i = j;
- }
- int32_t index2Capacity = (iLimit - iStart) >> UCPTRIE_SHIFT_2_3;
- // Length of the index-1 table, rounded up.
- int32_t index1Length = (index2Capacity + UCPTRIE_INDEX_2_MASK) >> UCPTRIE_SHIFT_1_2;
- // Index table: Fast index, index-1, index-3, index-2.
- // +1 for possible index table padding.
- int32_t index16Capacity = fastIndexLength + index1Length + index3Capacity + index2Capacity + 1;
- index16 = (uint16_t *)uprv_malloc(index16Capacity * 2);
- if (index16 == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- uprv_memcpy(index16, fastIndex, fastIndexLength * 2);
- if (!mixedBlocks.init(index16Capacity, UCPTRIE_INDEX_3_BLOCK_LENGTH)) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- MixedBlocks longI3Blocks;
- if (hasLongI3Blocks) {
- if (!longI3Blocks.init(index16Capacity, INDEX_3_18BIT_BLOCK_LENGTH)) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- }
- // Compact the index-3 table and write an uncompacted version of the index-2 table.
- uint16_t index2[UNICODE_LIMIT >> UCPTRIE_SHIFT_2]; // index2Capacity
- int32_t i2Length = 0;
- i3FirstNull = index3NullOffset;
- int32_t index3Start = fastIndexLength + index1Length;
- int32_t indexLength = index3Start;
- for (int32_t i = iStart; i < iLimit; i += UCPTRIE_INDEX_3_BLOCK_LENGTH) {
- int32_t i3;
- uint8_t f = flags[i];
- if (f == I3_NULL && i3FirstNull < 0) {
- // First index-3 null block. Write & overlap it like a normal block, then remember it.
- f = dataNullOffset <= 0xffff ? I3_16 : I3_18;
- i3FirstNull = 0;
- }
- if (f == I3_NULL) {
- i3 = index3NullOffset;
- } else if (f == I3_BMP) {
- i3 = index[i];
- } else if (f == I3_16) {
- int32_t n = mixedBlocks.findBlock(index16, index, i);
- if (n >= 0) {
- i3 = n;
- } else {
- if (indexLength == index3Start) {
- // No overlap at the boundary between the index-1 and index-3 tables.
- n = 0;
- } else {
- n = getOverlap(index16, indexLength,
- index, i, UCPTRIE_INDEX_3_BLOCK_LENGTH);
- }
- i3 = indexLength - n;
- int32_t prevIndexLength = indexLength;
- while (n < UCPTRIE_INDEX_3_BLOCK_LENGTH) {
- index16[indexLength++] = index[i + n++];
- }
- mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength);
- if (hasLongI3Blocks) {
- longI3Blocks.extend(index16, index3Start, prevIndexLength, indexLength);
- }
- }
- } else {
- U_ASSERT(f == I3_18);
- U_ASSERT(hasLongI3Blocks);
- // Encode an index-3 block that contains one or more data indexes exceeding 16 bits.
- int32_t j = i;
- int32_t jLimit = i + UCPTRIE_INDEX_3_BLOCK_LENGTH;
- int32_t k = indexLength;
- do {
- ++k;
- uint32_t v = index[j++];
- uint32_t upperBits = (v & 0x30000) >> 2;
- index16[k++] = v;
- v = index[j++];
- upperBits |= (v & 0x30000) >> 4;
- index16[k++] = v;
- v = index[j++];
- upperBits |= (v & 0x30000) >> 6;
- index16[k++] = v;
- v = index[j++];
- upperBits |= (v & 0x30000) >> 8;
- index16[k++] = v;
- v = index[j++];
- upperBits |= (v & 0x30000) >> 10;
- index16[k++] = v;
- v = index[j++];
- upperBits |= (v & 0x30000) >> 12;
- index16[k++] = v;
- v = index[j++];
- upperBits |= (v & 0x30000) >> 14;
- index16[k++] = v;
- v = index[j++];
- upperBits |= (v & 0x30000) >> 16;
- index16[k++] = v;
- index16[k - 9] = upperBits;
- } while (j < jLimit);
- int32_t n = longI3Blocks.findBlock(index16, index16, indexLength);
- if (n >= 0) {
- i3 = n | 0x8000;
- } else {
- if (indexLength == index3Start) {
- // No overlap at the boundary between the index-1 and index-3 tables.
- n = 0;
- } else {
- n = getOverlap(index16, indexLength,
- index16, indexLength, INDEX_3_18BIT_BLOCK_LENGTH);
- }
- i3 = (indexLength - n) | 0x8000;
- int32_t prevIndexLength = indexLength;
- if (n > 0) {
- int32_t start = indexLength;
- while (n < INDEX_3_18BIT_BLOCK_LENGTH) {
- index16[indexLength++] = index16[start + n++];
- }
- } else {
- indexLength += INDEX_3_18BIT_BLOCK_LENGTH;
- }
- mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength);
- if (hasLongI3Blocks) {
- longI3Blocks.extend(index16, index3Start, prevIndexLength, indexLength);
- }
- }
- }
- if (index3NullOffset < 0 && i3FirstNull >= 0) {
- index3NullOffset = i3;
- }
- // Set the index-2 table entry.
- index2[i2Length++] = i3;
- }
- U_ASSERT(i2Length == index2Capacity);
- U_ASSERT(indexLength <= index3Start + index3Capacity);
- if (index3NullOffset < 0) {
- index3NullOffset = UCPTRIE_NO_INDEX3_NULL_OFFSET;
- }
- if (indexLength >= (UCPTRIE_NO_INDEX3_NULL_OFFSET + UCPTRIE_INDEX_3_BLOCK_LENGTH)) {
- // The index-3 offsets exceed 15 bits, or
- // the last one cannot be distinguished from the no-null-block value.
- errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
- return 0;
- }
- // Compact the index-2 table and write the index-1 table.
- static_assert(UCPTRIE_INDEX_2_BLOCK_LENGTH == UCPTRIE_INDEX_3_BLOCK_LENGTH,
- "must re-init mixedBlocks");
- int32_t blockLength = UCPTRIE_INDEX_2_BLOCK_LENGTH;
- int32_t i1 = fastIndexLength;
- for (int32_t i = 0; i < i2Length; i += blockLength) {
- int32_t n;
- if ((i2Length - i) >= blockLength) {
- // normal block
- U_ASSERT(blockLength == UCPTRIE_INDEX_2_BLOCK_LENGTH);
- n = mixedBlocks.findBlock(index16, index2, i);
- } else {
- // highStart is inside the last index-2 block. Shorten it.
- blockLength = i2Length - i;
- n = findSameBlock(index16, index3Start, indexLength,
- index2, i, blockLength);
- }
- int32_t i2;
- if (n >= 0) {
- i2 = n;
- } else {
- if (indexLength == index3Start) {
- // No overlap at the boundary between the index-1 and index-3/2 tables.
- n = 0;
- } else {
- n = getOverlap(index16, indexLength, index2, i, blockLength);
- }
- i2 = indexLength - n;
- int32_t prevIndexLength = indexLength;
- while (n < blockLength) {
- index16[indexLength++] = index2[i + n++];
- }
- mixedBlocks.extend(index16, index3Start, prevIndexLength, indexLength);
- }
- // Set the index-1 table entry.
- index16[i1++] = i2;
- }
- U_ASSERT(i1 == index3Start);
- U_ASSERT(indexLength <= index16Capacity);
- #ifdef UCPTRIE_DEBUG
- /* we saved some space */
- printf("compacting UCPTrie: count of 16-bit index words %lu->%lu\n",
- (long)iLimit, (long)indexLength);
- #endif
- return indexLength;
- }
- int32_t MutableCodePointTrie::compactTrie(int32_t fastILimit, UErrorCode &errorCode) {
- // Find the real highStart and round it up.
- U_ASSERT((highStart & (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) == 0);
- highValue = get(MAX_UNICODE);
- int32_t realHighStart = findHighStart();
- realHighStart = (realHighStart + (UCPTRIE_CP_PER_INDEX_2_ENTRY - 1)) &
- ~(UCPTRIE_CP_PER_INDEX_2_ENTRY - 1);
- if (realHighStart == UNICODE_LIMIT) {
- highValue = initialValue;
- }
- #ifdef UCPTRIE_DEBUG
- printf("UCPTrie: highStart U+%06lx highValue 0x%lx initialValue 0x%lx\n",
- (long)realHighStart, (long)highValue, (long)initialValue);
- #endif
- // We always store indexes and data values for the fast range.
- // Pin highStart to the top of that range while building.
- UChar32 fastLimit = fastILimit << UCPTRIE_SHIFT_3;
- if (realHighStart < fastLimit) {
- for (int32_t i = (realHighStart >> UCPTRIE_SHIFT_3); i < fastILimit; ++i) {
- flags[i] = ALL_SAME;
- index[i] = highValue;
- }
- highStart = fastLimit;
- } else {
- highStart = realHighStart;
- }
- uint32_t asciiData[ASCII_LIMIT];
- for (int32_t i = 0; i < ASCII_LIMIT; ++i) {
- asciiData[i] = get(i);
- }
- // First we look for which data blocks have the same value repeated over the whole block,
- // deduplicate such blocks, find a good null data block (for faster enumeration),
- // and get an upper bound for the necessary data array length.
- AllSameBlocks allSameBlocks;
- int32_t newDataCapacity = compactWholeDataBlocks(fastILimit, allSameBlocks);
- if (newDataCapacity < 0) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- uint32_t *newData = (uint32_t *)uprv_malloc(newDataCapacity * 4);
- if (newData == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- return 0;
- }
- uprv_memcpy(newData, asciiData, sizeof(asciiData));
- int32_t dataNullIndex = allSameBlocks.findMostUsed();
- MixedBlocks mixedBlocks;
- int32_t newDataLength = compactData(fastILimit, newData, newDataCapacity,
- dataNullIndex, mixedBlocks, errorCode);
- if (U_FAILURE(errorCode)) { return 0; }
- U_ASSERT(newDataLength <= newDataCapacity);
- uprv_free(data);
- data = newData;
- dataCapacity = newDataCapacity;
- dataLength = newDataLength;
- if (dataLength > (0x3ffff + UCPTRIE_SMALL_DATA_BLOCK_LENGTH)) {
- // The offset of the last data block is too high to be stored in the index table.
- errorCode = U_INDEX_OUTOFBOUNDS_ERROR;
- return 0;
- }
- if (dataNullIndex >= 0) {
- dataNullOffset = index[dataNullIndex];
- #ifdef UCPTRIE_DEBUG
- if (data[dataNullOffset] != initialValue) {
- printf("UCPTrie initialValue %lx -> more common nullValue %lx\n",
- (long)initialValue, (long)data[dataNullOffset]);
- }
- #endif
- initialValue = data[dataNullOffset];
- } else {
- dataNullOffset = UCPTRIE_NO_DATA_NULL_OFFSET;
- }
- int32_t indexLength = compactIndex(fastILimit, mixedBlocks, errorCode);
- highStart = realHighStart;
- return indexLength;
- }
- UCPTrie *MutableCodePointTrie::build(UCPTrieType type, UCPTrieValueWidth valueWidth, UErrorCode &errorCode) {
- if (U_FAILURE(errorCode)) {
- return nullptr;
- }
- if (type < UCPTRIE_TYPE_FAST || UCPTRIE_TYPE_SMALL < type ||
- valueWidth < UCPTRIE_VALUE_BITS_16 || UCPTRIE_VALUE_BITS_8 < valueWidth) {
- errorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return nullptr;
- }
- // The mutable trie always stores 32-bit values.
- // When we build a UCPTrie for a smaller value width, we first mask off unused bits
- // before compacting the data.
- switch (valueWidth) {
- case UCPTRIE_VALUE_BITS_32:
- break;
- case UCPTRIE_VALUE_BITS_16:
- maskValues(0xffff);
- break;
- case UCPTRIE_VALUE_BITS_8:
- maskValues(0xff);
- break;
- default:
- break;
- }
- UChar32 fastLimit = type == UCPTRIE_TYPE_FAST ? BMP_LIMIT : UCPTRIE_SMALL_LIMIT;
- int32_t indexLength = compactTrie(fastLimit >> UCPTRIE_SHIFT_3, errorCode);
- if (U_FAILURE(errorCode)) {
- clear();
- return nullptr;
- }
- // Ensure data table alignment: The index length must be even for uint32_t data.
- if (valueWidth == UCPTRIE_VALUE_BITS_32 && (indexLength & 1) != 0) {
- index16[indexLength++] = 0xffee; // arbitrary value
- }
- // Make the total trie structure length a multiple of 4 bytes by padding the data table,
- // and store special values as the last two data values.
- int32_t length = indexLength * 2;
- if (valueWidth == UCPTRIE_VALUE_BITS_16) {
- if (((indexLength ^ dataLength) & 1) != 0) {
- // padding
- data[dataLength++] = errorValue;
- }
- if (data[dataLength - 1] != errorValue || data[dataLength - 2] != highValue) {
- data[dataLength++] = highValue;
- data[dataLength++] = errorValue;
- }
- length += dataLength * 2;
- } else if (valueWidth == UCPTRIE_VALUE_BITS_32) {
- // 32-bit data words never need padding to a multiple of 4 bytes.
- if (data[dataLength - 1] != errorValue || data[dataLength - 2] != highValue) {
- if (data[dataLength - 1] != highValue) {
- data[dataLength++] = highValue;
- }
- data[dataLength++] = errorValue;
- }
- length += dataLength * 4;
- } else {
- int32_t and3 = (length + dataLength) & 3;
- if (and3 == 0 && data[dataLength - 1] == errorValue && data[dataLength - 2] == highValue) {
- // all set
- } else if(and3 == 3 && data[dataLength - 1] == highValue) {
- data[dataLength++] = errorValue;
- } else {
- while (and3 != 2) {
- data[dataLength++] = highValue;
- and3 = (and3 + 1) & 3;
- }
- data[dataLength++] = highValue;
- data[dataLength++] = errorValue;
- }
- length += dataLength;
- }
- // Calculate the total length of the UCPTrie as a single memory block.
- length += sizeof(UCPTrie);
- U_ASSERT((length & 3) == 0);
- uint8_t *bytes = (uint8_t *)uprv_malloc(length);
- if (bytes == nullptr) {
- errorCode = U_MEMORY_ALLOCATION_ERROR;
- clear();
- return nullptr;
- }
- UCPTrie *trie = reinterpret_cast<UCPTrie *>(bytes);
- uprv_memset(trie, 0, sizeof(UCPTrie));
- trie->indexLength = indexLength;
- trie->dataLength = dataLength;
- trie->highStart = highStart;
- // Round up shifted12HighStart to a multiple of 0x1000 for easy testing from UTF-8 lead bytes.
- // Runtime code needs to then test for the real highStart as well.
- trie->shifted12HighStart = (highStart + 0xfff) >> 12;
- trie->type = type;
- trie->valueWidth = valueWidth;
- trie->index3NullOffset = index3NullOffset;
- trie->dataNullOffset = dataNullOffset;
- trie->nullValue = initialValue;
- bytes += sizeof(UCPTrie);
- // Fill the index and data arrays.
- uint16_t *dest16 = (uint16_t *)bytes;
- trie->index = dest16;
- if (highStart <= fastLimit) {
- // Condense only the fast index from the mutable-trie index.
- for (int32_t i = 0, j = 0; j < indexLength; i += SMALL_DATA_BLOCKS_PER_BMP_BLOCK, ++j) {
- *dest16++ = (uint16_t)index[i]; // dest16[j]
- }
- } else {
- uprv_memcpy(dest16, index16, indexLength * 2);
- dest16 += indexLength;
- }
- bytes += indexLength * 2;
- // Write the data array.
- const uint32_t *p = data;
- switch (valueWidth) {
- case UCPTRIE_VALUE_BITS_16:
- // Write 16-bit data values.
- trie->data.ptr16 = dest16;
- for (int32_t i = dataLength; i > 0; --i) {
- *dest16++ = (uint16_t)*p++;
- }
- break;
- case UCPTRIE_VALUE_BITS_32:
- // Write 32-bit data values.
- trie->data.ptr32 = (uint32_t *)bytes;
- uprv_memcpy(bytes, p, (size_t)dataLength * 4);
- break;
- case UCPTRIE_VALUE_BITS_8:
- // Write 8-bit data values.
- trie->data.ptr8 = bytes;
- for (int32_t i = dataLength; i > 0; --i) {
- *bytes++ = (uint8_t)*p++;
- }
- break;
- default:
- // Will not occur, valueWidth checked at the beginning.
- break;
- }
- #ifdef UCPTRIE_DEBUG
- trie->name = name;
- ucptrie_printLengths(trie, "");
- #endif
- clear();
- return trie;
- }
- } // namespace
- U_NAMESPACE_END
- U_NAMESPACE_USE
- U_CAPI UMutableCPTrie * U_EXPORT2
- umutablecptrie_open(uint32_t initialValue, uint32_t errorValue, UErrorCode *pErrorCode) {
- if (U_FAILURE(*pErrorCode)) {
- return nullptr;
- }
- LocalPointer<MutableCodePointTrie> trie(
- new MutableCodePointTrie(initialValue, errorValue, *pErrorCode), *pErrorCode);
- if (U_FAILURE(*pErrorCode)) {
- return nullptr;
- }
- return reinterpret_cast<UMutableCPTrie *>(trie.orphan());
- }
- U_CAPI UMutableCPTrie * U_EXPORT2
- umutablecptrie_clone(const UMutableCPTrie *other, UErrorCode *pErrorCode) {
- if (U_FAILURE(*pErrorCode)) {
- return nullptr;
- }
- if (other == nullptr) {
- return nullptr;
- }
- LocalPointer<MutableCodePointTrie> clone(
- new MutableCodePointTrie(*reinterpret_cast<const MutableCodePointTrie *>(other), *pErrorCode), *pErrorCode);
- if (U_FAILURE(*pErrorCode)) {
- return nullptr;
- }
- return reinterpret_cast<UMutableCPTrie *>(clone.orphan());
- }
- U_CAPI void U_EXPORT2
- umutablecptrie_close(UMutableCPTrie *trie) {
- delete reinterpret_cast<MutableCodePointTrie *>(trie);
- }
- U_CAPI UMutableCPTrie * U_EXPORT2
- umutablecptrie_fromUCPMap(const UCPMap *map, UErrorCode *pErrorCode) {
- if (U_FAILURE(*pErrorCode)) {
- return nullptr;
- }
- if (map == nullptr) {
- *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return nullptr;
- }
- return reinterpret_cast<UMutableCPTrie *>(MutableCodePointTrie::fromUCPMap(map, *pErrorCode));
- }
- U_CAPI UMutableCPTrie * U_EXPORT2
- umutablecptrie_fromUCPTrie(const UCPTrie *trie, UErrorCode *pErrorCode) {
- if (U_FAILURE(*pErrorCode)) {
- return nullptr;
- }
- if (trie == nullptr) {
- *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
- return nullptr;
- }
- return reinterpret_cast<UMutableCPTrie *>(MutableCodePointTrie::fromUCPTrie(trie, *pErrorCode));
- }
- U_CAPI uint32_t U_EXPORT2
- umutablecptrie_get(const UMutableCPTrie *trie, UChar32 c) {
- return reinterpret_cast<const MutableCodePointTrie *>(trie)->get(c);
- }
- namespace {
- UChar32 getRange(const void *trie, UChar32 start,
- UCPMapValueFilter *filter, const void *context, uint32_t *pValue) {
- return reinterpret_cast<const MutableCodePointTrie *>(trie)->
- getRange(start, filter, context, pValue);
- }
- } // namespace
- U_CAPI UChar32 U_EXPORT2
- umutablecptrie_getRange(const UMutableCPTrie *trie, UChar32 start,
- UCPMapRangeOption option, uint32_t surrogateValue,
- UCPMapValueFilter *filter, const void *context, uint32_t *pValue) {
- return ucptrie_internalGetRange(getRange, trie, start,
- option, surrogateValue,
- filter, context, pValue);
- }
- U_CAPI void U_EXPORT2
- umutablecptrie_set(UMutableCPTrie *trie, UChar32 c, uint32_t value, UErrorCode *pErrorCode) {
- if (U_FAILURE(*pErrorCode)) {
- return;
- }
- reinterpret_cast<MutableCodePointTrie *>(trie)->set(c, value, *pErrorCode);
- }
- U_CAPI void U_EXPORT2
- umutablecptrie_setRange(UMutableCPTrie *trie, UChar32 start, UChar32 end,
- uint32_t value, UErrorCode *pErrorCode) {
- if (U_FAILURE(*pErrorCode)) {
- return;
- }
- reinterpret_cast<MutableCodePointTrie *>(trie)->setRange(start, end, value, *pErrorCode);
- }
- /* Compact and internally serialize the trie. */
- U_CAPI UCPTrie * U_EXPORT2
- umutablecptrie_buildImmutable(UMutableCPTrie *trie, UCPTrieType type, UCPTrieValueWidth valueWidth,
- UErrorCode *pErrorCode) {
- if (U_FAILURE(*pErrorCode)) {
- return nullptr;
- }
- return reinterpret_cast<MutableCodePointTrie *>(trie)->build(type, valueWidth, *pErrorCode);
- }
- #ifdef UCPTRIE_DEBUG
- U_CFUNC void umutablecptrie_setName(UMutableCPTrie *trie, const char *name) {
- reinterpret_cast<MutableCodePointTrie *>(trie)->name = name;
- }
- #endif
|