123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- // © 2016 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- /*
- *******************************************************************************
- *
- * Copyright (C) 1999-2015, International Business Machines
- * Corporation and others. All Rights Reserved.
- *
- *******************************************************************************
- * file name: collationweights.cpp
- * encoding: UTF-8
- * tab size: 8 (not used)
- * indentation:4
- *
- * created on: 2001mar08 as ucol_wgt.cpp
- * created by: Markus W. Scherer
- *
- * This file contains code for allocating n collation element weights
- * between two exclusive limits.
- * It is used only internally by the collation tailoring builder.
- */
- #include "unicode/utypes.h"
- #if !UCONFIG_NO_COLLATION
- #include "cmemory.h"
- #include "collation.h"
- #include "collationweights.h"
- #include "uarrsort.h"
- #include "uassert.h"
- #ifdef UCOL_DEBUG
- # include <stdio.h>
- #endif
- U_NAMESPACE_BEGIN
- /* collation element weight allocation -------------------------------------- */
- /* helper functions for CE weights */
- static inline uint32_t
- getWeightTrail(uint32_t weight, int32_t length) {
- return (uint32_t)(weight>>(8*(4-length)))&0xff;
- }
- static inline uint32_t
- setWeightTrail(uint32_t weight, int32_t length, uint32_t trail) {
- length=8*(4-length);
- return (uint32_t)((weight&(0xffffff00<<length))|(trail<<length));
- }
- static inline uint32_t
- getWeightByte(uint32_t weight, int32_t idx) {
- return getWeightTrail(weight, idx); /* same calculation */
- }
- static inline uint32_t
- setWeightByte(uint32_t weight, int32_t idx, uint32_t byte) {
- uint32_t mask; /* 0xffffffff except a 00 "hole" for the index-th byte */
- idx*=8;
- if(idx<32) {
- mask=((uint32_t)0xffffffff)>>idx;
- } else {
- // Do not use uint32_t>>32 because on some platforms that does not shift at all
- // while we need it to become 0.
- // PowerPC: 0xffffffff>>32 = 0 (wanted)
- // x86: 0xffffffff>>32 = 0xffffffff (not wanted)
- //
- // ANSI C99 6.5.7 Bitwise shift operators:
- // "If the value of the right operand is negative
- // or is greater than or equal to the width of the promoted left operand,
- // the behavior is undefined."
- mask=0;
- }
- idx=32-idx;
- mask|=0xffffff00<<idx;
- return (uint32_t)((weight&mask)|(byte<<idx));
- }
- static inline uint32_t
- truncateWeight(uint32_t weight, int32_t length) {
- return (uint32_t)(weight&(0xffffffff<<(8*(4-length))));
- }
- static inline uint32_t
- incWeightTrail(uint32_t weight, int32_t length) {
- return (uint32_t)(weight+(1UL<<(8*(4-length))));
- }
- static inline uint32_t
- decWeightTrail(uint32_t weight, int32_t length) {
- return (uint32_t)(weight-(1UL<<(8*(4-length))));
- }
- CollationWeights::CollationWeights()
- : middleLength(0), rangeIndex(0), rangeCount(0) {
- for(int32_t i = 0; i < 5; ++i) {
- minBytes[i] = maxBytes[i] = 0;
- }
- }
- void
- CollationWeights::initForPrimary(UBool compressible) {
- middleLength=1;
- minBytes[1] = Collation::MERGE_SEPARATOR_BYTE + 1;
- maxBytes[1] = Collation::TRAIL_WEIGHT_BYTE;
- if(compressible) {
- minBytes[2] = Collation::PRIMARY_COMPRESSION_LOW_BYTE + 1;
- maxBytes[2] = Collation::PRIMARY_COMPRESSION_HIGH_BYTE - 1;
- } else {
- minBytes[2] = 2;
- maxBytes[2] = 0xff;
- }
- minBytes[3] = 2;
- maxBytes[3] = 0xff;
- minBytes[4] = 2;
- maxBytes[4] = 0xff;
- }
- void
- CollationWeights::initForSecondary() {
- // We use only the lower 16 bits for secondary weights.
- middleLength=3;
- minBytes[1] = 0;
- maxBytes[1] = 0;
- minBytes[2] = 0;
- maxBytes[2] = 0;
- minBytes[3] = Collation::LEVEL_SEPARATOR_BYTE + 1;
- maxBytes[3] = 0xff;
- minBytes[4] = 2;
- maxBytes[4] = 0xff;
- }
- void
- CollationWeights::initForTertiary() {
- // We use only the lower 16 bits for tertiary weights.
- middleLength=3;
- minBytes[1] = 0;
- maxBytes[1] = 0;
- minBytes[2] = 0;
- maxBytes[2] = 0;
- // We use only 6 bits per byte.
- // The other bits are used for case & quaternary weights.
- minBytes[3] = Collation::LEVEL_SEPARATOR_BYTE + 1;
- maxBytes[3] = 0x3f;
- minBytes[4] = 2;
- maxBytes[4] = 0x3f;
- }
- uint32_t
- CollationWeights::incWeight(uint32_t weight, int32_t length) const {
- for(;;) {
- uint32_t byte=getWeightByte(weight, length);
- if(byte<maxBytes[length]) {
- return setWeightByte(weight, length, byte+1);
- } else {
- // Roll over, set this byte to the minimum and increment the previous one.
- weight=setWeightByte(weight, length, minBytes[length]);
- --length;
- U_ASSERT(length > 0);
- }
- }
- }
- uint32_t
- CollationWeights::incWeightByOffset(uint32_t weight, int32_t length, int32_t offset) const {
- for(;;) {
- offset += getWeightByte(weight, length);
- if((uint32_t)offset <= maxBytes[length]) {
- return setWeightByte(weight, length, offset);
- } else {
- // Split the offset between this byte and the previous one.
- offset -= minBytes[length];
- weight = setWeightByte(weight, length, minBytes[length] + offset % countBytes(length));
- offset /= countBytes(length);
- --length;
- U_ASSERT(length > 0);
- }
- }
- }
- void
- CollationWeights::lengthenRange(WeightRange &range) const {
- int32_t length=range.length+1;
- range.start=setWeightTrail(range.start, length, minBytes[length]);
- range.end=setWeightTrail(range.end, length, maxBytes[length]);
- range.count*=countBytes(length);
- range.length=length;
- }
- /* for uprv_sortArray: sort ranges in weight order */
- static int32_t U_CALLCONV
- compareRanges(const void * /*context*/, const void *left, const void *right) {
- uint32_t l, r;
- l=((const CollationWeights::WeightRange *)left)->start;
- r=((const CollationWeights::WeightRange *)right)->start;
- if(l<r) {
- return -1;
- } else if(l>r) {
- return 1;
- } else {
- return 0;
- }
- }
- UBool
- CollationWeights::getWeightRanges(uint32_t lowerLimit, uint32_t upperLimit) {
- U_ASSERT(lowerLimit != 0);
- U_ASSERT(upperLimit != 0);
- /* get the lengths of the limits */
- int32_t lowerLength=lengthOfWeight(lowerLimit);
- int32_t upperLength=lengthOfWeight(upperLimit);
- #ifdef UCOL_DEBUG
- printf("length of lower limit 0x%08lx is %ld\n", lowerLimit, lowerLength);
- printf("length of upper limit 0x%08lx is %ld\n", upperLimit, upperLength);
- #endif
- U_ASSERT(lowerLength>=middleLength);
- // Permit upperLength<middleLength: The upper limit for secondaries is 0x10000.
- if(lowerLimit>=upperLimit) {
- #ifdef UCOL_DEBUG
- printf("error: no space between lower & upper limits\n");
- #endif
- return false;
- }
- /* check that neither is a prefix of the other */
- if(lowerLength<upperLength) {
- if(lowerLimit==truncateWeight(upperLimit, lowerLength)) {
- #ifdef UCOL_DEBUG
- printf("error: lower limit 0x%08lx is a prefix of upper limit 0x%08lx\n", lowerLimit, upperLimit);
- #endif
- return false;
- }
- }
- /* if the upper limit is a prefix of the lower limit then the earlier test lowerLimit>=upperLimit has caught it */
- WeightRange lower[5], middle, upper[5]; /* [0] and [1] are not used - this simplifies indexing */
- uprv_memset(lower, 0, sizeof(lower));
- uprv_memset(&middle, 0, sizeof(middle));
- uprv_memset(upper, 0, sizeof(upper));
- /*
- * With the limit lengths of 1..4, there are up to 7 ranges for allocation:
- * range minimum length
- * lower[4] 4
- * lower[3] 3
- * lower[2] 2
- * middle 1
- * upper[2] 2
- * upper[3] 3
- * upper[4] 4
- *
- * We are now going to calculate up to 7 ranges.
- * Some of them will typically overlap, so we will then have to merge and eliminate ranges.
- */
- uint32_t weight=lowerLimit;
- for(int32_t length=lowerLength; length>middleLength; --length) {
- uint32_t trail=getWeightTrail(weight, length);
- if(trail<maxBytes[length]) {
- lower[length].start=incWeightTrail(weight, length);
- lower[length].end=setWeightTrail(weight, length, maxBytes[length]);
- lower[length].length=length;
- lower[length].count=maxBytes[length]-trail;
- }
- weight=truncateWeight(weight, length-1);
- }
- if(weight<0xff000000) {
- middle.start=incWeightTrail(weight, middleLength);
- } else {
- // Prevent overflow for primary lead byte FF
- // which would yield a middle range starting at 0.
- middle.start=0xffffffff; // no middle range
- }
- weight=upperLimit;
- for(int32_t length=upperLength; length>middleLength; --length) {
- uint32_t trail=getWeightTrail(weight, length);
- if(trail>minBytes[length]) {
- upper[length].start=setWeightTrail(weight, length, minBytes[length]);
- upper[length].end=decWeightTrail(weight, length);
- upper[length].length=length;
- upper[length].count=trail-minBytes[length];
- }
- weight=truncateWeight(weight, length-1);
- }
- middle.end=decWeightTrail(weight, middleLength);
- /* set the middle range */
- middle.length=middleLength;
- if(middle.end>=middle.start) {
- middle.count=(int32_t)((middle.end-middle.start)>>(8*(4-middleLength)))+1;
- } else {
- /* no middle range, eliminate overlaps */
- for(int32_t length=4; length>middleLength; --length) {
- if(lower[length].count>0 && upper[length].count>0) {
- // Note: The lowerEnd and upperStart weights are versions of
- // lowerLimit and upperLimit (which are lowerLimit<upperLimit),
- // truncated (still less-or-equal)
- // and then with their last bytes changed to the
- // maxByte (for lowerEnd) or minByte (for upperStart).
- const uint32_t lowerEnd=lower[length].end;
- const uint32_t upperStart=upper[length].start;
- UBool merged=false;
- if(lowerEnd>upperStart) {
- // These two lower and upper ranges collide.
- // Since lowerLimit<upperLimit and lowerEnd and upperStart
- // are versions with only their last bytes modified
- // (and following ones removed/reset to 0),
- // lowerEnd>upperStart is only possible
- // if the leading bytes are equal
- // and lastByte(lowerEnd)>lastByte(upperStart).
- U_ASSERT(truncateWeight(lowerEnd, length-1)==
- truncateWeight(upperStart, length-1));
- // Intersect these two ranges.
- lower[length].end=upper[length].end;
- lower[length].count=
- (int32_t)getWeightTrail(lower[length].end, length)-
- (int32_t)getWeightTrail(lower[length].start, length)+1;
- // count might be <=0 in which case there is no room,
- // and the range-collecting code below will ignore this range.
- merged=true;
- } else if(lowerEnd==upperStart) {
- // Not possible, unless minByte==maxByte which is not allowed.
- U_ASSERT(minBytes[length]<maxBytes[length]);
- } else /* lowerEnd<upperStart */ {
- if(incWeight(lowerEnd, length)==upperStart) {
- // Merge adjacent ranges.
- lower[length].end=upper[length].end;
- lower[length].count+=upper[length].count; // might be >countBytes
- merged=true;
- }
- }
- if(merged) {
- // Remove all shorter ranges.
- // There was no room available for them between the ranges we just merged.
- upper[length].count=0;
- while(--length>middleLength) {
- lower[length].count=upper[length].count=0;
- }
- break;
- }
- }
- }
- }
- #ifdef UCOL_DEBUG
- /* print ranges */
- for(int32_t length=4; length>=2; --length) {
- if(lower[length].count>0) {
- printf("lower[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, lower[length].start, lower[length].end, lower[length].count);
- }
- }
- if(middle.count>0) {
- printf("middle .start=0x%08lx .end=0x%08lx .count=%ld\n", middle.start, middle.end, middle.count);
- }
- for(int32_t length=2; length<=4; ++length) {
- if(upper[length].count>0) {
- printf("upper[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, upper[length].start, upper[length].end, upper[length].count);
- }
- }
- #endif
- /* copy the ranges, shortest first, into the result array */
- rangeCount=0;
- if(middle.count>0) {
- uprv_memcpy(ranges, &middle, sizeof(WeightRange));
- rangeCount=1;
- }
- for(int32_t length=middleLength+1; length<=4; ++length) {
- /* copy upper first so that later the middle range is more likely the first one to use */
- if(upper[length].count>0) {
- uprv_memcpy(ranges+rangeCount, upper+length, sizeof(WeightRange));
- ++rangeCount;
- }
- if(lower[length].count>0) {
- uprv_memcpy(ranges+rangeCount, lower+length, sizeof(WeightRange));
- ++rangeCount;
- }
- }
- return rangeCount>0;
- }
- UBool
- CollationWeights::allocWeightsInShortRanges(int32_t n, int32_t minLength) {
- // See if the first few minLength and minLength+1 ranges have enough weights.
- for(int32_t i = 0; i < rangeCount && ranges[i].length <= (minLength + 1); ++i) {
- if(n <= ranges[i].count) {
- // Use the first few minLength and minLength+1 ranges.
- if(ranges[i].length > minLength) {
- // Reduce the number of weights from the last minLength+1 range
- // which might sort before some minLength ranges,
- // so that we use all weights in the minLength ranges.
- ranges[i].count = n;
- }
- rangeCount = i + 1;
- #ifdef UCOL_DEBUG
- printf("take first %ld ranges\n", rangeCount);
- #endif
- if(rangeCount>1) {
- /* sort the ranges by weight values */
- UErrorCode errorCode=U_ZERO_ERROR;
- uprv_sortArray(ranges, rangeCount, sizeof(WeightRange),
- compareRanges, nullptr, false, &errorCode);
- /* ignore error code: we know that the internal sort function will not fail here */
- }
- return true;
- }
- n -= ranges[i].count; // still >0
- }
- return false;
- }
- UBool
- CollationWeights::allocWeightsInMinLengthRanges(int32_t n, int32_t minLength) {
- // See if the minLength ranges have enough weights
- // when we split one and lengthen the following ones.
- int32_t count = 0;
- int32_t minLengthRangeCount;
- for(minLengthRangeCount = 0;
- minLengthRangeCount < rangeCount &&
- ranges[minLengthRangeCount].length == minLength;
- ++minLengthRangeCount) {
- count += ranges[minLengthRangeCount].count;
- }
- int32_t nextCountBytes = countBytes(minLength + 1);
- if(n > count * nextCountBytes) { return false; }
- // Use the minLength ranges. Merge them, and then split again as necessary.
- uint32_t start = ranges[0].start;
- uint32_t end = ranges[0].end;
- for(int32_t i = 1; i < minLengthRangeCount; ++i) {
- if(ranges[i].start < start) { start = ranges[i].start; }
- if(ranges[i].end > end) { end = ranges[i].end; }
- }
- // Calculate how to split the range between minLength (count1) and minLength+1 (count2).
- // Goal:
- // count1 + count2 * nextCountBytes = n
- // count1 + count2 = count
- // These turn into
- // (count - count2) + count2 * nextCountBytes = n
- // and then into the following count1 & count2 computations.
- int32_t count2 = (n - count) / (nextCountBytes - 1); // number of weights to be lengthened
- int32_t count1 = count - count2; // number of minLength weights
- if(count2 == 0 || (count1 + count2 * nextCountBytes) < n) {
- // round up
- ++count2;
- --count1;
- U_ASSERT((count1 + count2 * nextCountBytes) >= n);
- }
- ranges[0].start = start;
- if(count1 == 0) {
- // Make one long range.
- ranges[0].end = end;
- ranges[0].count = count;
- lengthenRange(ranges[0]);
- rangeCount = 1;
- } else {
- // Split the range, lengthen the second part.
- #ifdef UCOL_DEBUG
- printf("split the range number %ld (out of %ld minLength ranges) by %ld:%ld\n",
- splitRange, rangeCount, count1, count2);
- #endif
- // Next start = start + count1. First end = 1 before that.
- ranges[0].end = incWeightByOffset(start, minLength, count1 - 1);
- ranges[0].count = count1;
- ranges[1].start = incWeight(ranges[0].end, minLength);
- ranges[1].end = end;
- ranges[1].length = minLength; // +1 when lengthened
- ranges[1].count = count2; // *countBytes when lengthened
- lengthenRange(ranges[1]);
- rangeCount = 2;
- }
- return true;
- }
- /*
- * call getWeightRanges and then determine heuristically
- * which ranges to use for a given number of weights between (excluding)
- * two limits
- */
- UBool
- CollationWeights::allocWeights(uint32_t lowerLimit, uint32_t upperLimit, int32_t n) {
- #ifdef UCOL_DEBUG
- puts("");
- #endif
- if(!getWeightRanges(lowerLimit, upperLimit)) {
- #ifdef UCOL_DEBUG
- printf("error: unable to get Weight ranges\n");
- #endif
- return false;
- }
- /* try until we find suitably large ranges */
- for(;;) {
- /* get the smallest number of bytes in a range */
- int32_t minLength=ranges[0].length;
- if(allocWeightsInShortRanges(n, minLength)) { break; }
- if(minLength == 4) {
- #ifdef UCOL_DEBUG
- printf("error: the maximum number of %ld weights is insufficient for n=%ld\n",
- minLengthCount, n);
- #endif
- return false;
- }
- if(allocWeightsInMinLengthRanges(n, minLength)) { break; }
- /* no good match, lengthen all minLength ranges and iterate */
- #ifdef UCOL_DEBUG
- printf("lengthen the short ranges from %ld bytes to %ld and iterate\n", minLength, minLength+1);
- #endif
- for(int32_t i=0; i<rangeCount && ranges[i].length==minLength; ++i) {
- lengthenRange(ranges[i]);
- }
- }
- #ifdef UCOL_DEBUG
- puts("final ranges:");
- for(int32_t i=0; i<rangeCount; ++i) {
- printf("ranges[%ld] .start=0x%08lx .end=0x%08lx .length=%ld .count=%ld\n",
- i, ranges[i].start, ranges[i].end, ranges[i].length, ranges[i].count);
- }
- #endif
- rangeIndex = 0;
- return true;
- }
- uint32_t
- CollationWeights::nextWeight() {
- if(rangeIndex >= rangeCount) {
- return 0xffffffff;
- } else {
- /* get the next weight */
- WeightRange &range = ranges[rangeIndex];
- uint32_t weight = range.start;
- if(--range.count == 0) {
- /* this range is finished */
- ++rangeIndex;
- } else {
- /* increment the weight for the next value */
- range.start = incWeight(weight, range.length);
- U_ASSERT(range.start <= range.end);
- }
- return weight;
- }
- }
- U_NAMESPACE_END
- #endif /* #if !UCONFIG_NO_COLLATION */
|