|
1 /* |
|
2 ******************************************************************************* |
|
3 * |
|
4 * Copyright (C) 2003-2013, International Business Machines |
|
5 * Corporation and others. All Rights Reserved. |
|
6 * |
|
7 ******************************************************************************* |
|
8 * file name: uarrsort.c |
|
9 * encoding: US-ASCII |
|
10 * tab size: 8 (not used) |
|
11 * indentation:4 |
|
12 * |
|
13 * created on: 2003aug04 |
|
14 * created by: Markus W. Scherer |
|
15 * |
|
16 * Internal function for sorting arrays. |
|
17 */ |
|
18 |
|
19 #include "unicode/utypes.h" |
|
20 #include "cmemory.h" |
|
21 #include "uarrsort.h" |
|
22 |
|
23 enum { |
|
24 /** |
|
25 * "from Knuth" |
|
26 * |
|
27 * A binary search over 8 items performs 4 comparisons: |
|
28 * log2(8)=3 to subdivide, +1 to check for equality. |
|
29 * A linear search over 8 items on average also performs 4 comparisons. |
|
30 */ |
|
31 MIN_QSORT=9, |
|
32 STACK_ITEM_SIZE=200 |
|
33 }; |
|
34 |
|
35 /* UComparator convenience implementations ---------------------------------- */ |
|
36 |
|
37 U_CAPI int32_t U_EXPORT2 |
|
38 uprv_uint16Comparator(const void *context, const void *left, const void *right) { |
|
39 return (int32_t)*(const uint16_t *)left - (int32_t)*(const uint16_t *)right; |
|
40 } |
|
41 |
|
42 U_CAPI int32_t U_EXPORT2 |
|
43 uprv_int32Comparator(const void *context, const void *left, const void *right) { |
|
44 return *(const int32_t *)left - *(const int32_t *)right; |
|
45 } |
|
46 |
|
47 U_CAPI int32_t U_EXPORT2 |
|
48 uprv_uint32Comparator(const void *context, const void *left, const void *right) { |
|
49 uint32_t l=*(const uint32_t *)left, r=*(const uint32_t *)right; |
|
50 |
|
51 /* compare directly because (l-r) would overflow the int32_t result */ |
|
52 if(l<r) { |
|
53 return -1; |
|
54 } else if(l==r) { |
|
55 return 0; |
|
56 } else /* l>r */ { |
|
57 return 1; |
|
58 } |
|
59 } |
|
60 |
|
61 /* Insertion sort using binary search --------------------------------------- */ |
|
62 |
|
63 U_CAPI int32_t U_EXPORT2 |
|
64 uprv_stableBinarySearch(char *array, int32_t limit, void *item, int32_t itemSize, |
|
65 UComparator *cmp, const void *context) { |
|
66 int32_t start=0; |
|
67 UBool found=FALSE; |
|
68 |
|
69 /* Binary search until we get down to a tiny sub-array. */ |
|
70 while((limit-start)>=MIN_QSORT) { |
|
71 int32_t i=(start+limit)/2; |
|
72 int32_t diff=cmp(context, item, array+i*itemSize); |
|
73 if(diff==0) { |
|
74 /* |
|
75 * Found the item. We look for the *last* occurrence of such |
|
76 * an item, for stable sorting. |
|
77 * If we knew that there will be only few equal items, |
|
78 * we could break now and enter the linear search. |
|
79 * However, if there are many equal items, then it should be |
|
80 * faster to continue with the binary search. |
|
81 * It seems likely that we either have all unique items |
|
82 * (where found will never become TRUE in the insertion sort) |
|
83 * or potentially many duplicates. |
|
84 */ |
|
85 found=TRUE; |
|
86 start=i+1; |
|
87 } else if(diff<0) { |
|
88 limit=i; |
|
89 } else { |
|
90 start=i; |
|
91 } |
|
92 } |
|
93 |
|
94 /* Linear search over the remaining tiny sub-array. */ |
|
95 while(start<limit) { |
|
96 int32_t diff=cmp(context, item, array+start*itemSize); |
|
97 if(diff==0) { |
|
98 found=TRUE; |
|
99 } else if(diff<0) { |
|
100 break; |
|
101 } |
|
102 ++start; |
|
103 } |
|
104 return found ? (start-1) : ~start; |
|
105 } |
|
106 |
|
107 static void |
|
108 doInsertionSort(char *array, int32_t length, int32_t itemSize, |
|
109 UComparator *cmp, const void *context, void *pv) { |
|
110 int32_t j; |
|
111 |
|
112 for(j=1; j<length; ++j) { |
|
113 char *item=array+j*itemSize; |
|
114 int32_t insertionPoint=uprv_stableBinarySearch(array, j, item, itemSize, cmp, context); |
|
115 if(insertionPoint<0) { |
|
116 insertionPoint=~insertionPoint; |
|
117 } else { |
|
118 ++insertionPoint; /* one past the last equal item */ |
|
119 } |
|
120 if(insertionPoint<j) { |
|
121 char *dest=array+insertionPoint*itemSize; |
|
122 uprv_memcpy(pv, item, itemSize); /* v=array[j] */ |
|
123 uprv_memmove(dest+itemSize, dest, (j-insertionPoint)*itemSize); |
|
124 uprv_memcpy(dest, pv, itemSize); /* array[insertionPoint]=v */ |
|
125 } |
|
126 } |
|
127 } |
|
128 |
|
129 static void |
|
130 insertionSort(char *array, int32_t length, int32_t itemSize, |
|
131 UComparator *cmp, const void *context, UErrorCode *pErrorCode) { |
|
132 UAlignedMemory v[STACK_ITEM_SIZE/sizeof(UAlignedMemory)+1]; |
|
133 void *pv; |
|
134 |
|
135 /* allocate an intermediate item variable (v) */ |
|
136 if(itemSize<=STACK_ITEM_SIZE) { |
|
137 pv=v; |
|
138 } else { |
|
139 pv=uprv_malloc(itemSize); |
|
140 if(pv==NULL) { |
|
141 *pErrorCode=U_MEMORY_ALLOCATION_ERROR; |
|
142 return; |
|
143 } |
|
144 } |
|
145 |
|
146 doInsertionSort(array, length, itemSize, cmp, context, pv); |
|
147 |
|
148 if(pv!=v) { |
|
149 uprv_free(pv); |
|
150 } |
|
151 } |
|
152 |
|
153 /* QuickSort ---------------------------------------------------------------- */ |
|
154 |
|
155 /* |
|
156 * This implementation is semi-recursive: |
|
157 * It recurses for the smaller sub-array to shorten the recursion depth, |
|
158 * and loops for the larger sub-array. |
|
159 * |
|
160 * Loosely after QuickSort algorithms in |
|
161 * Niklaus Wirth |
|
162 * Algorithmen und Datenstrukturen mit Modula-2 |
|
163 * B.G. Teubner Stuttgart |
|
164 * 4. Auflage 1986 |
|
165 * ISBN 3-519-02260-5 |
|
166 */ |
|
167 static void |
|
168 subQuickSort(char *array, int32_t start, int32_t limit, int32_t itemSize, |
|
169 UComparator *cmp, const void *context, |
|
170 void *px, void *pw) { |
|
171 int32_t left, right; |
|
172 |
|
173 /* start and left are inclusive, limit and right are exclusive */ |
|
174 do { |
|
175 if((start+MIN_QSORT)>=limit) { |
|
176 doInsertionSort(array+start*itemSize, limit-start, itemSize, cmp, context, px); |
|
177 break; |
|
178 } |
|
179 |
|
180 left=start; |
|
181 right=limit; |
|
182 |
|
183 /* x=array[middle] */ |
|
184 uprv_memcpy(px, array+((start+limit)/2)*itemSize, itemSize); |
|
185 |
|
186 do { |
|
187 while(/* array[left]<x */ |
|
188 cmp(context, array+left*itemSize, px)<0 |
|
189 ) { |
|
190 ++left; |
|
191 } |
|
192 while(/* x<array[right-1] */ |
|
193 cmp(context, px, array+(right-1)*itemSize)<0 |
|
194 ) { |
|
195 --right; |
|
196 } |
|
197 |
|
198 /* swap array[left] and array[right-1] via w; ++left; --right */ |
|
199 if(left<right) { |
|
200 --right; |
|
201 |
|
202 if(left<right) { |
|
203 uprv_memcpy(pw, array+left*itemSize, itemSize); |
|
204 uprv_memcpy(array+left*itemSize, array+right*itemSize, itemSize); |
|
205 uprv_memcpy(array+right*itemSize, pw, itemSize); |
|
206 } |
|
207 |
|
208 ++left; |
|
209 } |
|
210 } while(left<right); |
|
211 |
|
212 /* sort sub-arrays */ |
|
213 if((right-start)<(limit-left)) { |
|
214 /* sort [start..right[ */ |
|
215 if(start<(right-1)) { |
|
216 subQuickSort(array, start, right, itemSize, cmp, context, px, pw); |
|
217 } |
|
218 |
|
219 /* sort [left..limit[ */ |
|
220 start=left; |
|
221 } else { |
|
222 /* sort [left..limit[ */ |
|
223 if(left<(limit-1)) { |
|
224 subQuickSort(array, left, limit, itemSize, cmp, context, px, pw); |
|
225 } |
|
226 |
|
227 /* sort [start..right[ */ |
|
228 limit=right; |
|
229 } |
|
230 } while(start<(limit-1)); |
|
231 } |
|
232 |
|
233 static void |
|
234 quickSort(char *array, int32_t length, int32_t itemSize, |
|
235 UComparator *cmp, const void *context, UErrorCode *pErrorCode) { |
|
236 UAlignedMemory xw[(2*STACK_ITEM_SIZE)/sizeof(UAlignedMemory)+1]; |
|
237 void *p; |
|
238 |
|
239 /* allocate two intermediate item variables (x and w) */ |
|
240 if(itemSize<=STACK_ITEM_SIZE) { |
|
241 p=xw; |
|
242 } else { |
|
243 p=uprv_malloc(2*itemSize); |
|
244 if(p==NULL) { |
|
245 *pErrorCode=U_MEMORY_ALLOCATION_ERROR; |
|
246 return; |
|
247 } |
|
248 } |
|
249 |
|
250 subQuickSort(array, 0, length, itemSize, |
|
251 cmp, context, p, (char *)p+itemSize); |
|
252 |
|
253 if(p!=xw) { |
|
254 uprv_free(p); |
|
255 } |
|
256 } |
|
257 |
|
258 /* uprv_sortArray() API ----------------------------------------------------- */ |
|
259 |
|
260 /* |
|
261 * Check arguments, select an appropriate implementation, |
|
262 * cast the array to char * so that array+i*itemSize works. |
|
263 */ |
|
264 U_CAPI void U_EXPORT2 |
|
265 uprv_sortArray(void *array, int32_t length, int32_t itemSize, |
|
266 UComparator *cmp, const void *context, |
|
267 UBool sortStable, UErrorCode *pErrorCode) { |
|
268 if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { |
|
269 return; |
|
270 } |
|
271 if((length>0 && array==NULL) || length<0 || itemSize<=0 || cmp==NULL) { |
|
272 *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; |
|
273 return; |
|
274 } |
|
275 |
|
276 if(length<=1) { |
|
277 return; |
|
278 } else if(length<MIN_QSORT || sortStable) { |
|
279 insertionSort((char *)array, length, itemSize, cmp, context, pErrorCode); |
|
280 } else { |
|
281 quickSort((char *)array, length, itemSize, cmp, context, pErrorCode); |
|
282 } |
|
283 } |