• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 *
4 *   Copyright (C) 1999-2011, International Business Machines
5 *   Corporation and others.  All Rights Reserved.
6 *
7 *******************************************************************************
8 *   file name:  ucol_wgt.cpp
9 *   encoding:   US-ASCII
10 *   tab size:   8 (not used)
11 *   indentation:4
12 *
13 *   created on: 2001mar08
14 *   created by: Markus W. Scherer
15 *
16 *   This file contains code for allocating n collation element weights
17 *   between two exclusive limits.
18 *   It is used only internally by ucol_bld.
19 */
20 
21 #include "unicode/utypes.h"
22 
23 #if !UCONFIG_NO_COLLATION
24 
25 #include "ucol_imp.h"
26 #include "ucol_wgt.h"
27 #include "cmemory.h"
28 #include "uarrsort.h"
29 
30 #ifdef UCOL_DEBUG
31 #   include <stdio.h>
32 #endif
33 
34 /* collation element weight allocation -------------------------------------- */
35 
36 /* helper functions for CE weights */
37 
38 static inline int32_t
lengthOfWeight(uint32_t weight)39 lengthOfWeight(uint32_t weight) {
40     if((weight&0xffffff)==0) {
41         return 1;
42     } else if((weight&0xffff)==0) {
43         return 2;
44     } else if((weight&0xff)==0) {
45         return 3;
46     } else {
47         return 4;
48     }
49 }
50 
51 static inline uint32_t
getWeightTrail(uint32_t weight,int32_t length)52 getWeightTrail(uint32_t weight, int32_t length) {
53     return (uint32_t)(weight>>(8*(4-length)))&0xff;
54 }
55 
56 static inline uint32_t
setWeightTrail(uint32_t weight,int32_t length,uint32_t trail)57 setWeightTrail(uint32_t weight, int32_t length, uint32_t trail) {
58     length=8*(4-length);
59     return (uint32_t)((weight&(0xffffff00<<length))|(trail<<length));
60 }
61 
62 static inline uint32_t
getWeightByte(uint32_t weight,int32_t idx)63 getWeightByte(uint32_t weight, int32_t idx) {
64     return getWeightTrail(weight, idx); /* same calculation */
65 }
66 
67 static inline uint32_t
setWeightByte(uint32_t weight,int32_t idx,uint32_t byte)68 setWeightByte(uint32_t weight, int32_t idx, uint32_t byte) {
69     uint32_t mask; /* 0xffffffff except a 00 "hole" for the index-th byte */
70 
71     idx*=8;
72     if(idx<32) {
73         mask=((uint32_t)0xffffffff)>>idx;
74     } else {
75         // Do not use uint32_t>>32 because on some platforms that does not shift at all
76         // while we need it to become 0.
77         // PowerPC: 0xffffffff>>32 = 0           (wanted)
78         // x86:     0xffffffff>>32 = 0xffffffff  (not wanted)
79         //
80         // ANSI C99 6.5.7 Bitwise shift operators:
81         // "If the value of the right operand is negative
82         // or is greater than or equal to the width of the promoted left operand,
83         // the behavior is undefined."
84         mask=0;
85     }
86     idx=32-idx;
87     mask|=0xffffff00<<idx;
88     return (uint32_t)((weight&mask)|(byte<<idx));
89 }
90 
91 static inline uint32_t
truncateWeight(uint32_t weight,int32_t length)92 truncateWeight(uint32_t weight, int32_t length) {
93     return (uint32_t)(weight&(0xffffffff<<(8*(4-length))));
94 }
95 
96 static inline uint32_t
incWeightTrail(uint32_t weight,int32_t length)97 incWeightTrail(uint32_t weight, int32_t length) {
98     return (uint32_t)(weight+(1UL<<(8*(4-length))));
99 }
100 
101 static inline uint32_t
decWeightTrail(uint32_t weight,int32_t length)102 decWeightTrail(uint32_t weight, int32_t length) {
103     return (uint32_t)(weight-(1UL<<(8*(4-length))));
104 }
105 
106 static inline uint32_t
incWeight(uint32_t weight,int32_t length,uint32_t maxByte)107 incWeight(uint32_t weight, int32_t length, uint32_t maxByte) {
108     uint32_t byte;
109 
110     for(;;) {
111         byte=getWeightByte(weight, length);
112         if(byte<maxByte) {
113             return setWeightByte(weight, length, byte+1);
114         } else {
115             /* roll over, set this byte to UCOL_BYTE_FIRST_TAILORED and increment the previous one */
116             weight=setWeightByte(weight, length, UCOL_BYTE_FIRST_TAILORED);
117             --length;
118         }
119     }
120 }
121 
122 static inline int32_t
lengthenRange(WeightRange * range,uint32_t maxByte,uint32_t countBytes)123 lengthenRange(WeightRange *range, uint32_t maxByte, uint32_t countBytes) {
124     int32_t length;
125 
126     length=range->length2+1;
127     range->start=setWeightTrail(range->start, length, UCOL_BYTE_FIRST_TAILORED);
128     range->end=setWeightTrail(range->end, length, maxByte);
129     range->count2*=countBytes;
130     range->length2=length;
131     return length;
132 }
133 
134 /* for uprv_sortArray: sort ranges in weight order */
135 static int32_t U_CALLCONV
compareRanges(const void *,const void * left,const void * right)136 compareRanges(const void * /*context*/, const void *left, const void *right) {
137     uint32_t l, r;
138 
139     l=((const WeightRange *)left)->start;
140     r=((const WeightRange *)right)->start;
141     if(l<r) {
142         return -1;
143     } else if(l>r) {
144         return 1;
145     } else {
146         return 0;
147     }
148 }
149 
150 /*
151  * take two CE weights and calculate the
152  * possible ranges of weights between the two limits, excluding them
153  * for weights with up to 4 bytes there are up to 2*4-1=7 ranges
154  */
155 static inline int32_t
getWeightRanges(uint32_t lowerLimit,uint32_t upperLimit,uint32_t maxByte,uint32_t countBytes,WeightRange ranges[7])156 getWeightRanges(uint32_t lowerLimit, uint32_t upperLimit,
157                 uint32_t maxByte, uint32_t countBytes,
158                 WeightRange ranges[7]) {
159     WeightRange lower[5], middle, upper[5]; /* [0] and [1] are not used - this simplifies indexing */
160     uint32_t weight, trail;
161     int32_t length, lowerLength, upperLength, rangeCount;
162 
163     /* assume that both lowerLimit & upperLimit are not 0 */
164 
165     /* get the lengths of the limits */
166     lowerLength=lengthOfWeight(lowerLimit);
167     upperLength=lengthOfWeight(upperLimit);
168 
169 #ifdef UCOL_DEBUG
170     printf("length of lower limit 0x%08lx is %ld\n", lowerLimit, lowerLength);
171     printf("length of upper limit 0x%08lx is %ld\n", upperLimit, upperLength);
172 #endif
173 
174     if(lowerLimit>=upperLimit) {
175 #ifdef UCOL_DEBUG
176         printf("error: no space between lower & upper limits\n");
177 #endif
178         return 0;
179     }
180 
181     /* check that neither is a prefix of the other */
182     if(lowerLength<upperLength) {
183         if(lowerLimit==truncateWeight(upperLimit, lowerLength)) {
184 #ifdef UCOL_DEBUG
185             printf("error: lower limit 0x%08lx is a prefix of upper limit 0x%08lx\n", lowerLimit, upperLimit);
186 #endif
187             return 0;
188         }
189     }
190     /* if the upper limit is a prefix of the lower limit then the earlier test lowerLimit>=upperLimit has caught it */
191 
192     /* reset local variables */
193     uprv_memset(lower, 0, sizeof(lower));
194     uprv_memset(&middle, 0, sizeof(middle));
195     uprv_memset(upper, 0, sizeof(upper));
196 
197     /*
198      * With the limit lengths of 1..4, there are up to 7 ranges for allocation:
199      * range     minimum length
200      * lower[4]  4
201      * lower[3]  3
202      * lower[2]  2
203      * middle    1
204      * upper[2]  2
205      * upper[3]  3
206      * upper[4]  4
207      *
208      * We are now going to calculate up to 7 ranges.
209      * Some of them will typically overlap, so we will then have to merge and eliminate ranges.
210      */
211     weight=lowerLimit;
212     for(length=lowerLength; length>=2; --length) {
213         trail=getWeightTrail(weight, length);
214         if(trail<maxByte) {
215             lower[length].start=incWeightTrail(weight, length);
216             lower[length].end=setWeightTrail(weight, length, maxByte);
217             lower[length].length=length;
218             lower[length].count=maxByte-trail;
219         }
220         weight=truncateWeight(weight, length-1);
221     }
222     middle.start=incWeightTrail(weight, 1);
223 
224     weight=upperLimit;
225     for(length=upperLength; length>=2; --length) {
226         trail=getWeightTrail(weight, length);
227         if(trail>UCOL_BYTE_FIRST_TAILORED) {
228             upper[length].start=setWeightTrail(weight, length, UCOL_BYTE_FIRST_TAILORED);
229             upper[length].end=decWeightTrail(weight, length);
230             upper[length].length=length;
231             upper[length].count=trail-UCOL_BYTE_FIRST_TAILORED;
232         }
233         weight=truncateWeight(weight, length-1);
234     }
235     middle.end=decWeightTrail(weight, 1);
236 
237     /* set the middle range */
238     middle.length=1;
239     if(middle.end>=middle.start) {
240         middle.count=(int32_t)((middle.end-middle.start)>>24)+1;
241     } else {
242         /* eliminate overlaps */
243         uint32_t start, end;
244 
245         /* remove the middle range */
246         middle.count=0;
247 
248         /* reduce or remove the lower ranges that go beyond upperLimit */
249         for(length=4; length>=2; --length) {
250             if(lower[length].count>0 && upper[length].count>0) {
251                 start=upper[length].start;
252                 end=lower[length].end;
253 
254                 if(end>=start || incWeight(end, length, maxByte)==start) {
255                     /* lower and upper ranges collide or are directly adjacent: merge these two and remove all shorter ranges */
256                     start=lower[length].start;
257                     end=lower[length].end=upper[length].end;
258                     /*
259                      * merging directly adjacent ranges needs to subtract the 0/1 gaps in between;
260                      * it may result in a range with count>countBytes
261                      */
262                     lower[length].count=
263                         (int32_t)(getWeightTrail(end, length)-getWeightTrail(start, length)+1+
264                                   countBytes*(getWeightByte(end, length-1)-getWeightByte(start, length-1)));
265                     upper[length].count=0;
266                     while(--length>=2) {
267                         lower[length].count=upper[length].count=0;
268                     }
269                     break;
270                 }
271             }
272         }
273     }
274 
275 #ifdef UCOL_DEBUG
276     /* print ranges */
277     for(length=4; length>=2; --length) {
278         if(lower[length].count>0) {
279             printf("lower[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, lower[length].start, lower[length].end, lower[length].count);
280         }
281     }
282     if(middle.count>0) {
283         printf("middle   .start=0x%08lx .end=0x%08lx .count=%ld\n", middle.start, middle.end, middle.count);
284     }
285     for(length=2; length<=4; ++length) {
286         if(upper[length].count>0) {
287             printf("upper[%ld] .start=0x%08lx .end=0x%08lx .count=%ld\n", length, upper[length].start, upper[length].end, upper[length].count);
288         }
289     }
290 #endif
291 
292     /* copy the ranges, shortest first, into the result array */
293     rangeCount=0;
294     if(middle.count>0) {
295         uprv_memcpy(ranges, &middle, sizeof(WeightRange));
296         rangeCount=1;
297     }
298     for(length=2; length<=4; ++length) {
299         /* copy upper first so that later the middle range is more likely the first one to use */
300         if(upper[length].count>0) {
301             uprv_memcpy(ranges+rangeCount, upper+length, sizeof(WeightRange));
302             ++rangeCount;
303         }
304         if(lower[length].count>0) {
305             uprv_memcpy(ranges+rangeCount, lower+length, sizeof(WeightRange));
306             ++rangeCount;
307         }
308     }
309     return rangeCount;
310 }
311 
312 /*
313  * call getWeightRanges and then determine heuristically
314  * which ranges to use for a given number of weights between (excluding)
315  * two limits
316  */
317 U_CFUNC int32_t
ucol_allocWeights(uint32_t lowerLimit,uint32_t upperLimit,uint32_t n,uint32_t maxByte,WeightRange ranges[7])318 ucol_allocWeights(uint32_t lowerLimit, uint32_t upperLimit,
319                   uint32_t n,
320                   uint32_t maxByte,
321                   WeightRange ranges[7]) {
322     /* number of usable byte values 3..maxByte */
323     uint32_t countBytes=maxByte-UCOL_BYTE_FIRST_TAILORED+1;
324 
325     uint32_t lengthCounts[6]; /* [0] unused, [5] to make index checks unnecessary */
326     uint32_t maxCount;
327     int32_t i, rangeCount, minLength/*, maxLength*/;
328 
329     /* countBytes to the power of index */
330     uint32_t powers[5];
331     /* gcc requires explicit initialization */
332     powers[0] = 1;
333     powers[1] = countBytes;
334     powers[2] = countBytes*countBytes;
335     powers[3] = countBytes*countBytes*countBytes;
336     powers[4] = countBytes*countBytes*countBytes*countBytes;
337 
338 #ifdef UCOL_DEBUG
339     puts("");
340 #endif
341 
342     rangeCount=getWeightRanges(lowerLimit, upperLimit, maxByte, countBytes, ranges);
343     if(rangeCount<=0) {
344 #ifdef UCOL_DEBUG
345         printf("error: unable to get Weight ranges\n");
346 #endif
347         return 0;
348     }
349 
350     /* what is the maximum number of weights with these ranges? */
351     maxCount=0;
352     for(i=0; i<rangeCount; ++i) {
353         maxCount+=(uint32_t)ranges[i].count*powers[4-ranges[i].length];
354     }
355     if(maxCount>=n) {
356 #ifdef UCOL_DEBUG
357         printf("the maximum number of %lu weights is sufficient for n=%lu\n", maxCount, n);
358 #endif
359     } else {
360 #ifdef UCOL_DEBUG
361         printf("error: the maximum number of %lu weights is insufficient for n=%lu\n", maxCount, n);
362 #endif
363         return 0;
364     }
365 
366     /* set the length2 and count2 fields */
367     for(i=0; i<rangeCount; ++i) {
368         ranges[i].length2=ranges[i].length;
369         ranges[i].count2=(uint32_t)ranges[i].count;
370     }
371 
372     /* try until we find suitably large ranges */
373     for(;;) {
374         /* get the smallest number of bytes in a range */
375         minLength=ranges[0].length2;
376 
377         /* sum up the number of elements that fit into ranges of each byte length */
378         uprv_memset(lengthCounts, 0, sizeof(lengthCounts));
379         for(i=0; i<rangeCount; ++i) {
380             lengthCounts[ranges[i].length2]+=ranges[i].count2;
381         }
382 
383         /* now try to allocate n elements in the available short ranges */
384         if(n<=(lengthCounts[minLength]+lengthCounts[minLength+1])) {
385             /* trivial cases, use the first few ranges */
386             maxCount=0;
387             rangeCount=0;
388             do {
389                 maxCount+=ranges[rangeCount].count2;
390                 ++rangeCount;
391             } while(n>maxCount);
392 #ifdef UCOL_DEBUG
393             printf("take first %ld ranges\n", rangeCount);
394 #endif
395             break;
396         } else if(n<=ranges[0].count2*countBytes) {
397             /* easy case, just make this one range large enough by lengthening it once more, possibly split it */
398             uint32_t count1, count2, power_1, power;
399 
400             /*maxLength=minLength+1;*/
401 
402             /* calculate how to split the range between maxLength-1 (count1) and maxLength (count2) */
403             power_1=powers[minLength-ranges[0].length];
404             power=power_1*countBytes;
405             count2=(n+power-1)/power;
406             count1=ranges[0].count-count2;
407 
408             /* split the range */
409 #ifdef UCOL_DEBUG
410             printf("split the first range %ld:%ld\n", count1, count2);
411 #endif
412             if(count1<1) {
413                 rangeCount=1;
414 
415                 /* lengthen the entire range to maxLength */
416                 lengthenRange(ranges, maxByte, countBytes);
417             } else {
418                 /* really split the range */
419                 uint32_t byte;
420 
421                 /* create a new range with the end and initial and current length of the old one */
422                 rangeCount=2;
423                 ranges[1].end=ranges[0].end;
424                 ranges[1].length=ranges[0].length;
425                 ranges[1].length2=minLength;
426 
427                 /* set the end of the first range according to count1 */
428                 i=ranges[0].length;
429                 byte=getWeightByte(ranges[0].start, i)+count1-1;
430 
431                 /*
432                  * ranges[0].count and count1 may be >countBytes
433                  * from merging adjacent ranges;
434                  * byte>maxByte is possible
435                  */
436                 if(byte<=maxByte) {
437                     ranges[0].end=setWeightByte(ranges[0].start, i, byte);
438                 } else /* byte>maxByte */ {
439                     ranges[0].end=setWeightByte(incWeight(ranges[0].start, i-1, maxByte), i, byte-countBytes);
440                 }
441 
442                 /* set the bytes in the end weight at length+1..length2 to maxByte */
443                 byte=(maxByte<<24)|(maxByte<<16)|(maxByte<<8)|maxByte; /* this used to be 0xffffffff */
444                 ranges[0].end=truncateWeight(ranges[0].end, i)|
445                               ((byte>>(8*i))&(byte<<(8*(4-minLength))));
446 
447                 /* set the start of the second range to immediately follow the end of the first one */
448                 ranges[1].start=incWeight(ranges[0].end, minLength, maxByte);
449 
450                 /* set the count values (informational) */
451                 ranges[0].count=count1;
452                 ranges[1].count=count2;
453 
454                 ranges[0].count2=count1*power_1;
455                 ranges[1].count2=count2*power_1; /* will be *countBytes when lengthened */
456 
457                 /* lengthen the second range to maxLength */
458                 lengthenRange(ranges+1, maxByte, countBytes);
459             }
460             break;
461         }
462 
463         /* no good match, lengthen all minLength ranges and iterate */
464 #ifdef UCOL_DEBUG
465         printf("lengthen the short ranges from %ld bytes to %ld and iterate\n", minLength, minLength+1);
466 #endif
467         for(i=0; ranges[i].length2==minLength; ++i) {
468             lengthenRange(ranges+i, maxByte, countBytes);
469         }
470     }
471 
472     if(rangeCount>1) {
473         /* sort the ranges by weight values */
474         UErrorCode errorCode=U_ZERO_ERROR;
475         uprv_sortArray(ranges, rangeCount, sizeof(WeightRange), compareRanges, NULL, FALSE, &errorCode);
476         /* ignore error code: we know that the internal sort function will not fail here */
477     }
478 
479 #ifdef UCOL_DEBUG
480     puts("final ranges:");
481     for(i=0; i<rangeCount; ++i) {
482         printf("ranges[%ld] .start=0x%08lx .end=0x%08lx .length=%ld .length2=%ld .count=%ld .count2=%lu\n",
483                i, ranges[i].start, ranges[i].end, ranges[i].length, ranges[i].length2, ranges[i].count, ranges[i].count2);
484     }
485 #endif
486 
487     /* set maxByte in ranges[0] for ucol_nextWeight() */
488     ranges[0].count=maxByte;
489 
490     return rangeCount;
491 }
492 
493 /*
494  * given a set of ranges calculated by ucol_allocWeights(),
495  * iterate through the weights
496  */
497 U_CFUNC uint32_t
ucol_nextWeight(WeightRange ranges[],int32_t * pRangeCount)498 ucol_nextWeight(WeightRange ranges[], int32_t *pRangeCount) {
499     if(*pRangeCount<=0) {
500         return 0xffffffff;
501     } else {
502         uint32_t weight, maxByte;
503 
504         /* get maxByte from the .count field */
505         maxByte=ranges[0].count;
506 
507         /* get the next weight */
508         weight=ranges[0].start;
509         if(weight==ranges[0].end) {
510             /* this range is finished, remove it and move the following ones up */
511             if(--*pRangeCount>0) {
512                 uprv_memmove(ranges, ranges+1, *pRangeCount*sizeof(WeightRange));
513                 ranges[0].count=maxByte; /* keep maxByte in ranges[0] */
514             }
515         } else {
516             /* increment the weight for the next value */
517             ranges[0].start=incWeight(weight, ranges[0].length2, maxByte);
518         }
519 
520         return weight;
521     }
522 }
523 
524 #if 0 // #ifdef UCOL_DEBUG
525 
526 static void
527 testAlloc(uint32_t lowerLimit, uint32_t upperLimit, uint32_t n, UBool enumerate) {
528     WeightRange ranges[8];
529     int32_t rangeCount;
530 
531     rangeCount=ucol_allocWeights(lowerLimit, upperLimit, n, ranges);
532     if(enumerate) {
533         uint32_t weight;
534 
535         while(n>0) {
536             weight=ucol_nextWeight(ranges, &rangeCount);
537             if(weight==0xffffffff) {
538                 printf("error: 0xffffffff with %lu more weights to go\n", n);
539                 break;
540             }
541             printf("    0x%08lx\n", weight);
542             --n;
543         }
544     }
545 }
546 
547 extern int
548 main(int argc, const char *argv[]) {
549 #if 0
550 #endif
551     testAlloc(0x364214fc, 0x44b87d23, 5, FALSE);
552     testAlloc(0x36421500, 0x44b87d23, 5, FALSE);
553     testAlloc(0x36421500, 0x44b87d23, 20, FALSE);
554     testAlloc(0x36421500, 0x44b87d23, 13700, FALSE);
555     testAlloc(0x36421500, 0x38b87d23, 1, FALSE);
556     testAlloc(0x36421500, 0x38b87d23, 20, FALSE);
557     testAlloc(0x36421500, 0x38b87d23, 200, TRUE);
558     testAlloc(0x36421500, 0x38b87d23, 13700, FALSE);
559     testAlloc(0x36421500, 0x37b87d23, 13700, FALSE);
560     testAlloc(0x36ef1500, 0x37b87d23, 13700, FALSE);
561     testAlloc(0x36421500, 0x36b87d23, 13700, FALSE);
562     testAlloc(0x36b87122, 0x36b87d23, 13700, FALSE);
563     testAlloc(0x49000000, 0x4a600000, 13700, FALSE);
564     testAlloc(0x9fffffff, 0xd0000000, 13700, FALSE);
565     testAlloc(0x9fffffff, 0xd0000000, 67400, FALSE);
566     testAlloc(0x9fffffff, 0xa0030000, 67400, FALSE);
567     testAlloc(0x9fffffff, 0xa0030000, 40000, FALSE);
568     testAlloc(0xa0000000, 0xa0030000, 40000, FALSE);
569     testAlloc(0xa0031100, 0xa0030000, 40000, FALSE);
570 #if 0
571 #endif
572     return 0;
573 }
574 
575 #endif
576 
577 #endif /* #if !UCONFIG_NO_COLLATION */
578