Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
23 | h266 | 1 | |
2 | /*-------------------------------------------------------------*/ |
||
3 | /*--- Block sorting machinery ---*/ |
||
4 | /*--- blocksort.c ---*/ |
||
5 | /*-------------------------------------------------------------*/ |
||
6 | |||
7 | /* ------------------------------------------------------------------ |
||
8 | This file is part of bzip2/libbzip2, a program and library for |
||
9 | lossless, block-sorting data compression. |
||
10 | |||
11 | bzip2/libbzip2 version 1.0.4 of 20 December 2006 |
||
12 | Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org> |
||
13 | |||
14 | Please read the WARNING, DISCLAIMER and PATENTS sections in the |
||
15 | README file. |
||
16 | |||
17 | This program is released under the terms of the license contained |
||
18 | in the file LICENSE. |
||
19 | ------------------------------------------------------------------ */ |
||
20 | |||
21 | |||
22 | #include "bzlib_private.h" |
||
23 | |||
24 | /*---------------------------------------------*/ |
||
25 | /*--- Fallback O(N log(N)^2) sorting ---*/ |
||
26 | /*--- algorithm, for repetitive blocks ---*/ |
||
27 | /*---------------------------------------------*/ |
||
28 | |||
29 | /*---------------------------------------------*/ |
||
30 | static |
||
31 | __inline__ |
||
32 | void fallbackSimpleSort ( UInt32* fmap, |
||
33 | UInt32* eclass, |
||
34 | Int32 lo, |
||
35 | Int32 hi ) |
||
36 | { |
||
37 | Int32 i, j, tmp; |
||
38 | UInt32 ec_tmp; |
||
39 | |||
40 | if (lo == hi) return; |
||
41 | |||
42 | if (hi - lo > 3) { |
||
43 | for ( i = hi-4; i >= lo; i-- ) { |
||
44 | tmp = fmap[i]; |
||
45 | ec_tmp = eclass[tmp]; |
||
46 | for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 ) |
||
47 | fmap[j-4] = fmap[j]; |
||
48 | fmap[j-4] = tmp; |
||
49 | } |
||
50 | } |
||
51 | |||
52 | for ( i = hi-1; i >= lo; i-- ) { |
||
53 | tmp = fmap[i]; |
||
54 | ec_tmp = eclass[tmp]; |
||
55 | for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ ) |
||
56 | fmap[j-1] = fmap[j]; |
||
57 | fmap[j-1] = tmp; |
||
58 | } |
||
59 | } |
||
60 | |||
61 | |||
62 | /*---------------------------------------------*/ |
||
63 | #define fswap(zz1, zz2) \ |
||
64 | { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } |
||
65 | |||
66 | #define fvswap(zzp1, zzp2, zzn) \ |
||
67 | { \ |
||
68 | Int32 yyp1 = (zzp1); \ |
||
69 | Int32 yyp2 = (zzp2); \ |
||
70 | Int32 yyn = (zzn); \ |
||
71 | while (yyn > 0) { \ |
||
72 | fswap(fmap[yyp1], fmap[yyp2]); \ |
||
73 | yyp1++; yyp2++; yyn--; \ |
||
74 | } \ |
||
75 | } |
||
76 | |||
77 | |||
78 | #define fmin(a,b) ((a) < (b)) ? (a) : (b) |
||
79 | |||
80 | #define fpush(lz,hz) { stackLo[sp] = lz; \ |
||
81 | stackHi[sp] = hz; \ |
||
82 | sp++; } |
||
83 | |||
84 | #define fpop(lz,hz) { sp--; \ |
||
85 | lz = stackLo[sp]; \ |
||
86 | hz = stackHi[sp]; } |
||
87 | |||
88 | #define FALLBACK_QSORT_SMALL_THRESH 10 |
||
89 | #define FALLBACK_QSORT_STACK_SIZE 100 |
||
90 | |||
91 | |||
92 | static |
||
93 | void fallbackQSort3 ( UInt32* fmap, |
||
94 | UInt32* eclass, |
||
95 | Int32 loSt, |
||
96 | Int32 hiSt ) |
||
97 | { |
||
98 | Int32 unLo, unHi, ltLo, gtHi, n, m; |
||
99 | Int32 sp, lo, hi; |
||
100 | UInt32 med, r, r3; |
||
101 | Int32 stackLo[FALLBACK_QSORT_STACK_SIZE]; |
||
102 | Int32 stackHi[FALLBACK_QSORT_STACK_SIZE]; |
||
103 | |||
104 | r = 0; |
||
105 | |||
106 | sp = 0; |
||
107 | fpush ( loSt, hiSt ); |
||
108 | |||
109 | while (sp > 0) { |
||
110 | |||
111 | AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 ); |
||
112 | |||
113 | fpop ( lo, hi ); |
||
114 | if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) { |
||
115 | fallbackSimpleSort ( fmap, eclass, lo, hi ); |
||
116 | continue; |
||
117 | } |
||
118 | |||
119 | /* Random partitioning. Median of 3 sometimes fails to |
||
120 | avoid bad cases. Median of 9 seems to help but |
||
121 | looks rather expensive. This too seems to work but |
||
122 | is cheaper. Guidance for the magic constants |
||
123 | 7621 and 32768 is taken from Sedgewick's algorithms |
||
124 | book, chapter 35. |
||
125 | */ |
||
126 | r = ((r * 7621) + 1) % 32768; |
||
127 | r3 = r % 3; |
||
128 | if (r3 == 0) med = eclass[fmap[lo]]; else |
||
129 | if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else |
||
130 | med = eclass[fmap[hi]]; |
||
131 | |||
132 | unLo = ltLo = lo; |
||
133 | unHi = gtHi = hi; |
||
134 | |||
135 | while (1) { |
||
136 | while (1) { |
||
137 | if (unLo > unHi) break; |
||
138 | n = (Int32)eclass[fmap[unLo]] - (Int32)med; |
||
139 | if (n == 0) { |
||
140 | fswap(fmap[unLo], fmap[ltLo]); |
||
141 | ltLo++; unLo++; |
||
142 | continue; |
||
143 | }; |
||
144 | if (n > 0) break; |
||
145 | unLo++; |
||
146 | } |
||
147 | while (1) { |
||
148 | if (unLo > unHi) break; |
||
149 | n = (Int32)eclass[fmap[unHi]] - (Int32)med; |
||
150 | if (n == 0) { |
||
151 | fswap(fmap[unHi], fmap[gtHi]); |
||
152 | gtHi--; unHi--; |
||
153 | continue; |
||
154 | }; |
||
155 | if (n < 0) break; |
||
156 | unHi--; |
||
157 | } |
||
158 | if (unLo > unHi) break; |
||
159 | fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--; |
||
160 | } |
||
161 | |||
162 | AssertD ( unHi == unLo-1, "fallbackQSort3(2)" ); |
||
163 | |||
164 | if (gtHi < ltLo) continue; |
||
165 | |||
166 | n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n); |
||
167 | m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m); |
||
168 | |||
169 | n = lo + unLo - ltLo - 1; |
||
170 | m = hi - (gtHi - unHi) + 1; |
||
171 | |||
172 | if (n - lo > hi - m) { |
||
173 | fpush ( lo, n ); |
||
174 | fpush ( m, hi ); |
||
175 | } else { |
||
176 | fpush ( m, hi ); |
||
177 | fpush ( lo, n ); |
||
178 | } |
||
179 | } |
||
180 | } |
||
181 | |||
182 | #undef fmin |
||
183 | #undef fpush |
||
184 | #undef fpop |
||
185 | #undef fswap |
||
186 | #undef fvswap |
||
187 | #undef FALLBACK_QSORT_SMALL_THRESH |
||
188 | #undef FALLBACK_QSORT_STACK_SIZE |
||
189 | |||
190 | |||
191 | /*---------------------------------------------*/ |
||
192 | /* Pre: |
||
193 | nblock > 0 |
||
194 | eclass exists for [0 .. nblock-1] |
||
195 | ((UChar*)eclass) [0 .. nblock-1] holds block |
||
196 | ptr exists for [0 .. nblock-1] |
||
197 | |||
198 | Post: |
||
199 | ((UChar*)eclass) [0 .. nblock-1] holds block |
||
200 | All other areas of eclass destroyed |
||
201 | fmap [0 .. nblock-1] holds sorted order |
||
202 | bhtab [ 0 .. 2+(nblock/32) ] destroyed |
||
203 | */ |
||
204 | |||
205 | #define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31)) |
||
206 | #define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31)) |
||
207 | #define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31))) |
||
208 | #define WORD_BH(zz) bhtab[(zz) >> 5] |
||
209 | #define UNALIGNED_BH(zz) ((zz) & 0x01f) |
||
210 | |||
211 | static |
||
212 | void fallbackSort ( UInt32* fmap, |
||
213 | UInt32* eclass, |
||
214 | UInt32* bhtab, |
||
215 | Int32 nblock, |
||
216 | Int32 verb ) |
||
217 | { |
||
218 | Int32 ftab[257]; |
||
219 | Int32 ftabCopy[256]; |
||
220 | Int32 H, i, j, k, l, r, cc, cc1; |
||
221 | Int32 nNotDone; |
||
222 | Int32 nBhtab; |
||
223 | UChar* eclass8 = (UChar*)eclass; |
||
224 | |||
225 | /*-- |
||
226 | Initial 1-char radix sort to generate |
||
227 | initial fmap and initial BH bits. |
||
228 | --*/ |
||
229 | if (verb >= 4) |
||
230 | VPrintf0 ( " bucket sorting ...\n" ); |
||
231 | for (i = 0; i < 257; i++) ftab[i] = 0; |
||
232 | for (i = 0; i < nblock; i++) ftab[eclass8[i]]++; |
||
233 | for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i]; |
||
234 | for (i = 1; i < 257; i++) ftab[i] += ftab[i-1]; |
||
235 | |||
236 | for (i = 0; i < nblock; i++) { |
||
237 | j = eclass8[i]; |
||
238 | k = ftab[j] - 1; |
||
239 | ftab[j] = k; |
||
240 | fmap[k] = i; |
||
241 | } |
||
242 | |||
243 | nBhtab = 2 + (nblock / 32); |
||
244 | for (i = 0; i < nBhtab; i++) bhtab[i] = 0; |
||
245 | for (i = 0; i < 256; i++) SET_BH(ftab[i]); |
||
246 | |||
247 | /*-- |
||
248 | Inductively refine the buckets. Kind-of an |
||
249 | "exponential radix sort" (!), inspired by the |
||
250 | Manber-Myers suffix array construction algorithm. |
||
251 | --*/ |
||
252 | |||
253 | /*-- set sentinel bits for block-end detection --*/ |
||
254 | for (i = 0; i < 32; i++) { |
||
255 | SET_BH(nblock + 2*i); |
||
256 | CLEAR_BH(nblock + 2*i + 1); |
||
257 | } |
||
258 | |||
259 | /*-- the log(N) loop --*/ |
||
260 | H = 1; |
||
261 | while (1) { |
||
262 | |||
263 | if (verb >= 4) |
||
264 | VPrintf1 ( " depth %6d has ", H ); |
||
265 | |||
266 | j = 0; |
||
267 | for (i = 0; i < nblock; i++) { |
||
268 | if (ISSET_BH(i)) j = i; |
||
269 | k = fmap[i] - H; if (k < 0) k += nblock; |
||
270 | eclass[k] = j; |
||
271 | } |
||
272 | |||
273 | nNotDone = 0; |
||
274 | r = -1; |
||
275 | while (1) { |
||
276 | |||
277 | /*-- find the next non-singleton bucket --*/ |
||
278 | k = r + 1; |
||
279 | while (ISSET_BH(k) && UNALIGNED_BH(k)) k++; |
||
280 | if (ISSET_BH(k)) { |
||
281 | while (WORD_BH(k) == 0xffffffff) k += 32; |
||
282 | while (ISSET_BH(k)) k++; |
||
283 | } |
||
284 | l = k - 1; |
||
285 | if (l >= nblock) break; |
||
286 | while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++; |
||
287 | if (!ISSET_BH(k)) { |
||
288 | while (WORD_BH(k) == 0x00000000) k += 32; |
||
289 | while (!ISSET_BH(k)) k++; |
||
290 | } |
||
291 | r = k - 1; |
||
292 | if (r >= nblock) break; |
||
293 | |||
294 | /*-- now [l, r] bracket current bucket --*/ |
||
295 | if (r > l) { |
||
296 | nNotDone += (r - l + 1); |
||
297 | fallbackQSort3 ( fmap, eclass, l, r ); |
||
298 | |||
299 | /*-- scan bucket and generate header bits-- */ |
||
300 | cc = -1; |
||
301 | for (i = l; i <= r; i++) { |
||
302 | cc1 = eclass[fmap[i]]; |
||
303 | if (cc != cc1) { SET_BH(i); cc = cc1; }; |
||
304 | } |
||
305 | } |
||
306 | } |
||
307 | |||
308 | if (verb >= 4) |
||
309 | VPrintf1 ( "%6d unresolved strings\n", nNotDone ); |
||
310 | |||
311 | H *= 2; |
||
312 | if (H > nblock || nNotDone == 0) break; |
||
313 | } |
||
314 | |||
315 | /*-- |
||
316 | Reconstruct the original block in |
||
317 | eclass8 [0 .. nblock-1], since the |
||
318 | previous phase destroyed it. |
||
319 | --*/ |
||
320 | if (verb >= 4) |
||
321 | VPrintf0 ( " reconstructing block ...\n" ); |
||
322 | j = 0; |
||
323 | for (i = 0; i < nblock; i++) { |
||
324 | while (ftabCopy[j] == 0) j++; |
||
325 | ftabCopy[j]--; |
||
326 | eclass8[fmap[i]] = (UChar)j; |
||
327 | } |
||
328 | AssertH ( j < 256, 1005 ); |
||
329 | } |
||
330 | |||
331 | #undef SET_BH |
||
332 | #undef CLEAR_BH |
||
333 | #undef ISSET_BH |
||
334 | #undef WORD_BH |
||
335 | #undef UNALIGNED_BH |
||
336 | |||
337 | |||
338 | /*---------------------------------------------*/ |
||
339 | /*--- The main, O(N^2 log(N)) sorting ---*/ |
||
340 | /*--- algorithm. Faster for "normal" ---*/ |
||
341 | /*--- non-repetitive blocks. ---*/ |
||
342 | /*---------------------------------------------*/ |
||
343 | |||
344 | /*---------------------------------------------*/ |
||
345 | static |
||
346 | __inline__ |
||
347 | Bool mainGtU ( UInt32 i1, |
||
348 | UInt32 i2, |
||
349 | UChar* block, |
||
350 | UInt16* quadrant, |
||
351 | UInt32 nblock, |
||
352 | Int32* budget ) |
||
353 | { |
||
354 | Int32 k; |
||
355 | UChar c1, c2; |
||
356 | UInt16 s1, s2; |
||
357 | |||
358 | AssertD ( i1 != i2, "mainGtU" ); |
||
359 | /* 1 */ |
||
360 | c1 = block[i1]; c2 = block[i2]; |
||
361 | if (c1 != c2) return (c1 > c2); |
||
362 | i1++; i2++; |
||
363 | /* 2 */ |
||
364 | c1 = block[i1]; c2 = block[i2]; |
||
365 | if (c1 != c2) return (c1 > c2); |
||
366 | i1++; i2++; |
||
367 | /* 3 */ |
||
368 | c1 = block[i1]; c2 = block[i2]; |
||
369 | if (c1 != c2) return (c1 > c2); |
||
370 | i1++; i2++; |
||
371 | /* 4 */ |
||
372 | c1 = block[i1]; c2 = block[i2]; |
||
373 | if (c1 != c2) return (c1 > c2); |
||
374 | i1++; i2++; |
||
375 | /* 5 */ |
||
376 | c1 = block[i1]; c2 = block[i2]; |
||
377 | if (c1 != c2) return (c1 > c2); |
||
378 | i1++; i2++; |
||
379 | /* 6 */ |
||
380 | c1 = block[i1]; c2 = block[i2]; |
||
381 | if (c1 != c2) return (c1 > c2); |
||
382 | i1++; i2++; |
||
383 | /* 7 */ |
||
384 | c1 = block[i1]; c2 = block[i2]; |
||
385 | if (c1 != c2) return (c1 > c2); |
||
386 | i1++; i2++; |
||
387 | /* 8 */ |
||
388 | c1 = block[i1]; c2 = block[i2]; |
||
389 | if (c1 != c2) return (c1 > c2); |
||
390 | i1++; i2++; |
||
391 | /* 9 */ |
||
392 | c1 = block[i1]; c2 = block[i2]; |
||
393 | if (c1 != c2) return (c1 > c2); |
||
394 | i1++; i2++; |
||
395 | /* 10 */ |
||
396 | c1 = block[i1]; c2 = block[i2]; |
||
397 | if (c1 != c2) return (c1 > c2); |
||
398 | i1++; i2++; |
||
399 | /* 11 */ |
||
400 | c1 = block[i1]; c2 = block[i2]; |
||
401 | if (c1 != c2) return (c1 > c2); |
||
402 | i1++; i2++; |
||
403 | /* 12 */ |
||
404 | c1 = block[i1]; c2 = block[i2]; |
||
405 | if (c1 != c2) return (c1 > c2); |
||
406 | i1++; i2++; |
||
407 | |||
408 | k = nblock + 8; |
||
409 | |||
410 | do { |
||
411 | /* 1 */ |
||
412 | c1 = block[i1]; c2 = block[i2]; |
||
413 | if (c1 != c2) return (c1 > c2); |
||
414 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
415 | if (s1 != s2) return (s1 > s2); |
||
416 | i1++; i2++; |
||
417 | /* 2 */ |
||
418 | c1 = block[i1]; c2 = block[i2]; |
||
419 | if (c1 != c2) return (c1 > c2); |
||
420 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
421 | if (s1 != s2) return (s1 > s2); |
||
422 | i1++; i2++; |
||
423 | /* 3 */ |
||
424 | c1 = block[i1]; c2 = block[i2]; |
||
425 | if (c1 != c2) return (c1 > c2); |
||
426 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
427 | if (s1 != s2) return (s1 > s2); |
||
428 | i1++; i2++; |
||
429 | /* 4 */ |
||
430 | c1 = block[i1]; c2 = block[i2]; |
||
431 | if (c1 != c2) return (c1 > c2); |
||
432 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
433 | if (s1 != s2) return (s1 > s2); |
||
434 | i1++; i2++; |
||
435 | /* 5 */ |
||
436 | c1 = block[i1]; c2 = block[i2]; |
||
437 | if (c1 != c2) return (c1 > c2); |
||
438 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
439 | if (s1 != s2) return (s1 > s2); |
||
440 | i1++; i2++; |
||
441 | /* 6 */ |
||
442 | c1 = block[i1]; c2 = block[i2]; |
||
443 | if (c1 != c2) return (c1 > c2); |
||
444 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
445 | if (s1 != s2) return (s1 > s2); |
||
446 | i1++; i2++; |
||
447 | /* 7 */ |
||
448 | c1 = block[i1]; c2 = block[i2]; |
||
449 | if (c1 != c2) return (c1 > c2); |
||
450 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
451 | if (s1 != s2) return (s1 > s2); |
||
452 | i1++; i2++; |
||
453 | /* 8 */ |
||
454 | c1 = block[i1]; c2 = block[i2]; |
||
455 | if (c1 != c2) return (c1 > c2); |
||
456 | s1 = quadrant[i1]; s2 = quadrant[i2]; |
||
457 | if (s1 != s2) return (s1 > s2); |
||
458 | i1++; i2++; |
||
459 | |||
460 | if (i1 >= nblock) i1 -= nblock; |
||
461 | if (i2 >= nblock) i2 -= nblock; |
||
462 | |||
463 | k -= 8; |
||
464 | (*budget)--; |
||
465 | } |
||
466 | while (k >= 0); |
||
467 | |||
468 | return False; |
||
469 | } |
||
470 | |||
471 | |||
472 | /*---------------------------------------------*/ |
||
473 | /*-- |
||
474 | Knuth's increments seem to work better |
||
475 | than Incerpi-Sedgewick here. Possibly |
||
476 | because the number of elems to sort is |
||
477 | usually small, typically <= 20. |
||
478 | --*/ |
||
479 | static |
||
480 | Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280, |
||
481 | 9841, 29524, 88573, 265720, |
||
482 | 797161, 2391484 }; |
||
483 | |||
484 | static |
||
485 | void mainSimpleSort ( UInt32* ptr, |
||
486 | UChar* block, |
||
487 | UInt16* quadrant, |
||
488 | Int32 nblock, |
||
489 | Int32 lo, |
||
490 | Int32 hi, |
||
491 | Int32 d, |
||
492 | Int32* budget ) |
||
493 | { |
||
494 | Int32 i, j, h, bigN, hp; |
||
495 | UInt32 v; |
||
496 | |||
497 | bigN = hi - lo + 1; |
||
498 | if (bigN < 2) return; |
||
499 | |||
500 | hp = 0; |
||
501 | while (incs[hp] < bigN) hp++; |
||
502 | hp--; |
||
503 | |||
504 | for (; hp >= 0; hp--) { |
||
505 | h = incs[hp]; |
||
506 | |||
507 | i = lo + h; |
||
508 | while (True) { |
||
509 | |||
510 | /*-- copy 1 --*/ |
||
511 | if (i > hi) break; |
||
512 | v = ptr[i]; |
||
513 | j = i; |
||
514 | while ( mainGtU ( |
||
515 | ptr[j-h]+d, v+d, block, quadrant, nblock, budget |
||
516 | ) ) { |
||
517 | ptr[j] = ptr[j-h]; |
||
518 | j = j - h; |
||
519 | if (j <= (lo + h - 1)) break; |
||
520 | } |
||
521 | ptr[j] = v; |
||
522 | i++; |
||
523 | |||
524 | /*-- copy 2 --*/ |
||
525 | if (i > hi) break; |
||
526 | v = ptr[i]; |
||
527 | j = i; |
||
528 | while ( mainGtU ( |
||
529 | ptr[j-h]+d, v+d, block, quadrant, nblock, budget |
||
530 | ) ) { |
||
531 | ptr[j] = ptr[j-h]; |
||
532 | j = j - h; |
||
533 | if (j <= (lo + h - 1)) break; |
||
534 | } |
||
535 | ptr[j] = v; |
||
536 | i++; |
||
537 | |||
538 | /*-- copy 3 --*/ |
||
539 | if (i > hi) break; |
||
540 | v = ptr[i]; |
||
541 | j = i; |
||
542 | while ( mainGtU ( |
||
543 | ptr[j-h]+d, v+d, block, quadrant, nblock, budget |
||
544 | ) ) { |
||
545 | ptr[j] = ptr[j-h]; |
||
546 | j = j - h; |
||
547 | if (j <= (lo + h - 1)) break; |
||
548 | } |
||
549 | ptr[j] = v; |
||
550 | i++; |
||
551 | |||
552 | if (*budget < 0) return; |
||
553 | } |
||
554 | } |
||
555 | } |
||
556 | |||
557 | |||
558 | /*---------------------------------------------*/ |
||
559 | /*-- |
||
560 | The following is an implementation of |
||
561 | an elegant 3-way quicksort for strings, |
||
562 | described in a paper "Fast Algorithms for |
||
563 | Sorting and Searching Strings", by Robert |
||
564 | Sedgewick and Jon L. Bentley. |
||
565 | --*/ |
||
566 | |||
567 | #define mswap(zz1, zz2) \ |
||
568 | { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; } |
||
569 | |||
570 | #define mvswap(zzp1, zzp2, zzn) \ |
||
571 | { \ |
||
572 | Int32 yyp1 = (zzp1); \ |
||
573 | Int32 yyp2 = (zzp2); \ |
||
574 | Int32 yyn = (zzn); \ |
||
575 | while (yyn > 0) { \ |
||
576 | mswap(ptr[yyp1], ptr[yyp2]); \ |
||
577 | yyp1++; yyp2++; yyn--; \ |
||
578 | } \ |
||
579 | } |
||
580 | |||
581 | static |
||
582 | __inline__ |
||
583 | UChar mmed3 ( UChar a, UChar b, UChar c ) |
||
584 | { |
||
585 | UChar t; |
||
586 | if (a > b) { t = a; a = b; b = t; }; |
||
587 | if (b > c) { |
||
588 | b = c; |
||
589 | if (a > b) b = a; |
||
590 | } |
||
591 | return b; |
||
592 | } |
||
593 | |||
594 | #define mmin(a,b) ((a) < (b)) ? (a) : (b) |
||
595 | |||
596 | #define mpush(lz,hz,dz) { stackLo[sp] = lz; \ |
||
597 | stackHi[sp] = hz; \ |
||
598 | stackD [sp] = dz; \ |
||
599 | sp++; } |
||
600 | |||
601 | #define mpop(lz,hz,dz) { sp--; \ |
||
602 | lz = stackLo[sp]; \ |
||
603 | hz = stackHi[sp]; \ |
||
604 | dz = stackD [sp]; } |
||
605 | |||
606 | |||
607 | #define mnextsize(az) (nextHi[az]-nextLo[az]) |
||
608 | |||
609 | #define mnextswap(az,bz) \ |
||
610 | { Int32 tz; \ |
||
611 | tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \ |
||
612 | tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \ |
||
613 | tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; } |
||
614 | |||
615 | |||
616 | #define MAIN_QSORT_SMALL_THRESH 20 |
||
617 | #define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT) |
||
618 | #define MAIN_QSORT_STACK_SIZE 100 |
||
619 | |||
620 | static |
||
621 | void mainQSort3 ( UInt32* ptr, |
||
622 | UChar* block, |
||
623 | UInt16* quadrant, |
||
624 | Int32 nblock, |
||
625 | Int32 loSt, |
||
626 | Int32 hiSt, |
||
627 | Int32 dSt, |
||
628 | Int32* budget ) |
||
629 | { |
||
630 | Int32 unLo, unHi, ltLo, gtHi, n, m, med; |
||
631 | Int32 sp, lo, hi, d; |
||
632 | |||
633 | Int32 stackLo[MAIN_QSORT_STACK_SIZE]; |
||
634 | Int32 stackHi[MAIN_QSORT_STACK_SIZE]; |
||
635 | Int32 stackD [MAIN_QSORT_STACK_SIZE]; |
||
636 | |||
637 | Int32 nextLo[3]; |
||
638 | Int32 nextHi[3]; |
||
639 | Int32 nextD [3]; |
||
640 | |||
641 | sp = 0; |
||
642 | mpush ( loSt, hiSt, dSt ); |
||
643 | |||
644 | while (sp > 0) { |
||
645 | |||
646 | AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 ); |
||
647 | |||
648 | mpop ( lo, hi, d ); |
||
649 | if (hi - lo < MAIN_QSORT_SMALL_THRESH || |
||
650 | d > MAIN_QSORT_DEPTH_THRESH) { |
||
651 | mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget ); |
||
652 | if (*budget < 0) return; |
||
653 | continue; |
||
654 | } |
||
655 | |||
656 | med = (Int32) |
||
657 | mmed3 ( block[ptr[ lo ]+d], |
||
658 | block[ptr[ hi ]+d], |
||
659 | block[ptr[ (lo+hi)>>1 ]+d] ); |
||
660 | |||
661 | unLo = ltLo = lo; |
||
662 | unHi = gtHi = hi; |
||
663 | |||
664 | while (True) { |
||
665 | while (True) { |
||
666 | if (unLo > unHi) break; |
||
667 | n = ((Int32)block[ptr[unLo]+d]) - med; |
||
668 | if (n == 0) { |
||
669 | mswap(ptr[unLo], ptr[ltLo]); |
||
670 | ltLo++; unLo++; continue; |
||
671 | }; |
||
672 | if (n > 0) break; |
||
673 | unLo++; |
||
674 | } |
||
675 | while (True) { |
||
676 | if (unLo > unHi) break; |
||
677 | n = ((Int32)block[ptr[unHi]+d]) - med; |
||
678 | if (n == 0) { |
||
679 | mswap(ptr[unHi], ptr[gtHi]); |
||
680 | gtHi--; unHi--; continue; |
||
681 | }; |
||
682 | if (n < 0) break; |
||
683 | unHi--; |
||
684 | } |
||
685 | if (unLo > unHi) break; |
||
686 | mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--; |
||
687 | } |
||
688 | |||
689 | AssertD ( unHi == unLo-1, "mainQSort3(2)" ); |
||
690 | |||
691 | if (gtHi < ltLo) { |
||
692 | mpush(lo, hi, d+1 ); |
||
693 | continue; |
||
694 | } |
||
695 | |||
696 | n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n); |
||
697 | m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m); |
||
698 | |||
699 | n = lo + unLo - ltLo - 1; |
||
700 | m = hi - (gtHi - unHi) + 1; |
||
701 | |||
702 | nextLo[0] = lo; nextHi[0] = n; nextD[0] = d; |
||
703 | nextLo[1] = m; nextHi[1] = hi; nextD[1] = d; |
||
704 | nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1; |
||
705 | |||
706 | if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); |
||
707 | if (mnextsize(1) < mnextsize(2)) mnextswap(1,2); |
||
708 | if (mnextsize(0) < mnextsize(1)) mnextswap(0,1); |
||
709 | |||
710 | AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" ); |
||
711 | AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" ); |
||
712 | |||
713 | mpush (nextLo[0], nextHi[0], nextD[0]); |
||
714 | mpush (nextLo[1], nextHi[1], nextD[1]); |
||
715 | mpush (nextLo[2], nextHi[2], nextD[2]); |
||
716 | } |
||
717 | } |
||
718 | |||
719 | #undef mswap |
||
720 | #undef mvswap |
||
721 | #undef mpush |
||
722 | #undef mpop |
||
723 | #undef mmin |
||
724 | #undef mnextsize |
||
725 | #undef mnextswap |
||
726 | #undef MAIN_QSORT_SMALL_THRESH |
||
727 | #undef MAIN_QSORT_DEPTH_THRESH |
||
728 | #undef MAIN_QSORT_STACK_SIZE |
||
729 | |||
730 | |||
731 | /*---------------------------------------------*/ |
||
732 | /* Pre: |
||
733 | nblock > N_OVERSHOOT |
||
734 | block32 exists for [0 .. nblock-1 +N_OVERSHOOT] |
||
735 | ((UChar*)block32) [0 .. nblock-1] holds block |
||
736 | ptr exists for [0 .. nblock-1] |
||
737 | |||
738 | Post: |
||
739 | ((UChar*)block32) [0 .. nblock-1] holds block |
||
740 | All other areas of block32 destroyed |
||
741 | ftab [0 .. 65536 ] destroyed |
||
742 | ptr [0 .. nblock-1] holds sorted order |
||
743 | if (*budget < 0), sorting was abandoned |
||
744 | */ |
||
745 | |||
746 | #define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8]) |
||
747 | #define SETMASK (1 << 21) |
||
748 | #define CLEARMASK (~(SETMASK)) |
||
749 | |||
750 | static |
||
751 | void mainSort ( UInt32* ptr, |
||
752 | UChar* block, |
||
753 | UInt16* quadrant, |
||
754 | UInt32* ftab, |
||
755 | Int32 nblock, |
||
756 | Int32 verb, |
||
757 | Int32* budget ) |
||
758 | { |
||
759 | Int32 i, j, k, ss, sb; |
||
760 | Int32 runningOrder[256]; |
||
761 | Bool bigDone[256]; |
||
762 | Int32 copyStart[256]; |
||
763 | Int32 copyEnd [256]; |
||
764 | UChar c1; |
||
765 | Int32 numQSorted; |
||
766 | UInt16 s; |
||
767 | if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" ); |
||
768 | |||
769 | /*-- set up the 2-byte frequency table --*/ |
||
770 | for (i = 65536; i >= 0; i--) ftab[i] = 0; |
||
771 | |||
772 | j = block[0] << 8; |
||
773 | i = nblock-1; |
||
774 | for (; i >= 3; i -= 4) { |
||
775 | quadrant[i] = 0; |
||
776 | j = (j >> 8) | ( ((UInt16)block[i]) << 8); |
||
777 | ftab[j]++; |
||
778 | quadrant[i-1] = 0; |
||
779 | j = (j >> 8) | ( ((UInt16)block[i-1]) << 8); |
||
780 | ftab[j]++; |
||
781 | quadrant[i-2] = 0; |
||
782 | j = (j >> 8) | ( ((UInt16)block[i-2]) << 8); |
||
783 | ftab[j]++; |
||
784 | quadrant[i-3] = 0; |
||
785 | j = (j >> 8) | ( ((UInt16)block[i-3]) << 8); |
||
786 | ftab[j]++; |
||
787 | } |
||
788 | for (; i >= 0; i--) { |
||
789 | quadrant[i] = 0; |
||
790 | j = (j >> 8) | ( ((UInt16)block[i]) << 8); |
||
791 | ftab[j]++; |
||
792 | } |
||
793 | |||
794 | /*-- (emphasises close relationship of block & quadrant) --*/ |
||
795 | for (i = 0; i < BZ_N_OVERSHOOT; i++) { |
||
796 | block [nblock+i] = block[i]; |
||
797 | quadrant[nblock+i] = 0; |
||
798 | } |
||
799 | |||
800 | if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" ); |
||
801 | |||
802 | /*-- Complete the initial radix sort --*/ |
||
803 | for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1]; |
||
804 | |||
805 | s = block[0] << 8; |
||
806 | i = nblock-1; |
||
807 | for (; i >= 3; i -= 4) { |
||
808 | s = (s >> 8) | (block[i] << 8); |
||
809 | j = ftab[s] -1; |
||
810 | ftab[s] = j; |
||
811 | ptr[j] = i; |
||
812 | s = (s >> 8) | (block[i-1] << 8); |
||
813 | j = ftab[s] -1; |
||
814 | ftab[s] = j; |
||
815 | ptr[j] = i-1; |
||
816 | s = (s >> 8) | (block[i-2] << 8); |
||
817 | j = ftab[s] -1; |
||
818 | ftab[s] = j; |
||
819 | ptr[j] = i-2; |
||
820 | s = (s >> 8) | (block[i-3] << 8); |
||
821 | j = ftab[s] -1; |
||
822 | ftab[s] = j; |
||
823 | ptr[j] = i-3; |
||
824 | } |
||
825 | for (; i >= 0; i--) { |
||
826 | s = (s >> 8) | (block[i] << 8); |
||
827 | j = ftab[s] -1; |
||
828 | ftab[s] = j; |
||
829 | ptr[j] = i; |
||
830 | } |
||
831 | |||
832 | /*-- |
||
833 | Now ftab contains the first loc of every small bucket. |
||
834 | Calculate the running order, from smallest to largest |
||
835 | big bucket. |
||
836 | --*/ |
||
837 | for (i = 0; i <= 255; i++) { |
||
838 | bigDone [i] = False; |
||
839 | runningOrder[i] = i; |
||
840 | } |
||
841 | |||
842 | { |
||
843 | Int32 vv; |
||
844 | Int32 h = 1; |
||
845 | do h = 3 * h + 1; while (h <= 256); |
||
846 | do { |
||
847 | h = h / 3; |
||
848 | for (i = h; i <= 255; i++) { |
||
849 | vv = runningOrder[i]; |
||
850 | j = i; |
||
851 | while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) { |
||
852 | runningOrder[j] = runningOrder[j-h]; |
||
853 | j = j - h; |
||
854 | if (j <= (h - 1)) goto zero; |
||
855 | } |
||
856 | zero: |
||
857 | runningOrder[j] = vv; |
||
858 | } |
||
859 | } while (h != 1); |
||
860 | } |
||
861 | |||
862 | /*-- |
||
863 | The main sorting loop. |
||
864 | --*/ |
||
865 | |||
866 | numQSorted = 0; |
||
867 | |||
868 | for (i = 0; i <= 255; i++) { |
||
869 | |||
870 | /*-- |
||
871 | Process big buckets, starting with the least full. |
||
872 | Basically this is a 3-step process in which we call |
||
873 | mainQSort3 to sort the small buckets [ss, j], but |
||
874 | also make a big effort to avoid the calls if we can. |
||
875 | --*/ |
||
876 | ss = runningOrder[i]; |
||
877 | |||
878 | /*-- |
||
879 | Step 1: |
||
880 | Complete the big bucket [ss] by quicksorting |
||
881 | any unsorted small buckets [ss, j], for j != ss. |
||
882 | Hopefully previous pointer-scanning phases have already |
||
883 | completed many of the small buckets [ss, j], so |
||
884 | we don't have to sort them at all. |
||
885 | --*/ |
||
886 | for (j = 0; j <= 255; j++) { |
||
887 | if (j != ss) { |
||
888 | sb = (ss << 8) + j; |
||
889 | if ( ! (ftab[sb] & SETMASK) ) { |
||
890 | Int32 lo = ftab[sb] & CLEARMASK; |
||
891 | Int32 hi = (ftab[sb+1] & CLEARMASK) - 1; |
||
892 | if (hi > lo) { |
||
893 | if (verb >= 4) |
||
894 | VPrintf4 ( " qsort [0x%x, 0x%x] " |
||
895 | "done %d this %d\n", |
||
896 | ss, j, numQSorted, hi - lo + 1 ); |
||
897 | mainQSort3 ( |
||
898 | ptr, block, quadrant, nblock, |
||
899 | lo, hi, BZ_N_RADIX, budget |
||
900 | ); |
||
901 | numQSorted += (hi - lo + 1); |
||
902 | if (*budget < 0) return; |
||
903 | } |
||
904 | } |
||
905 | ftab[sb] |= SETMASK; |
||
906 | } |
||
907 | } |
||
908 | |||
909 | AssertH ( !bigDone[ss], 1006 ); |
||
910 | |||
911 | /*-- |
||
912 | Step 2: |
||
913 | Now scan this big bucket [ss] so as to synthesise the |
||
914 | sorted order for small buckets [t, ss] for all t, |
||
915 | including, magically, the bucket [ss,ss] too. |
||
916 | This will avoid doing Real Work in subsequent Step 1's. |
||
917 | --*/ |
||
918 | { |
||
919 | for (j = 0; j <= 255; j++) { |
||
920 | copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK; |
||
921 | copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1; |
||
922 | } |
||
923 | for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) { |
||
924 | k = ptr[j]-1; if (k < 0) k += nblock; |
||
925 | c1 = block[k]; |
||
926 | if (!bigDone[c1]) |
||
927 | ptr[ copyStart[c1]++ ] = k; |
||
928 | } |
||
929 | for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) { |
||
930 | k = ptr[j]-1; if (k < 0) k += nblock; |
||
931 | c1 = block[k]; |
||
932 | if (!bigDone[c1]) |
||
933 | ptr[ copyEnd[c1]-- ] = k; |
||
934 | } |
||
935 | } |
||
936 | |||
937 | AssertH ( (copyStart[ss]-1 == copyEnd[ss]) |
||
938 | || |
||
939 | /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1. |
||
940 | Necessity for this case is demonstrated by compressing |
||
941 | a sequence of approximately 48.5 million of character |
||
942 | 251; 1.0.0/1.0.1 will then die here. */ |
||
943 | (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), |
||
944 | 1007 ) |
||
945 | |||
946 | for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK; |
||
947 | |||
948 | /*-- |
||
949 | Step 3: |
||
950 | The [ss] big bucket is now done. Record this fact, |
||
951 | and update the quadrant descriptors. Remember to |
||
952 | update quadrants in the overshoot area too, if |
||
953 | necessary. The "if (i < 255)" test merely skips |
||
954 | this updating for the last bucket processed, since |
||
955 | updating for the last bucket is pointless. |
||
956 | |||
957 | The quadrant array provides a way to incrementally |
||
958 | cache sort orderings, as they appear, so as to |
||
959 | make subsequent comparisons in fullGtU() complete |
||
960 | faster. For repetitive blocks this makes a big |
||
961 | difference (but not big enough to be able to avoid |
||
962 | the fallback sorting mechanism, exponential radix sort). |
||
963 | |||
964 | The precise meaning is: at all times: |
||
965 | |||
966 | for 0 <= i < nblock and 0 <= j <= nblock |
||
967 | |||
968 | if block[i] != block[j], |
||
969 | |||
970 | then the relative values of quadrant[i] and |
||
971 | quadrant[j] are meaningless. |
||
972 | |||
973 | else { |
||
974 | if quadrant[i] < quadrant[j] |
||
975 | then the string starting at i lexicographically |
||
976 | precedes the string starting at j |
||
977 | |||
978 | else if quadrant[i] > quadrant[j] |
||
979 | then the string starting at j lexicographically |
||
980 | precedes the string starting at i |
||
981 | |||
982 | else |
||
983 | the relative ordering of the strings starting |
||
984 | at i and j has not yet been determined. |
||
985 | } |
||
986 | --*/ |
||
987 | bigDone[ss] = True; |
||
988 | |||
989 | if (i < 255) { |
||
990 | Int32 bbStart = ftab[ss << 8] & CLEARMASK; |
||
991 | Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart; |
||
992 | Int32 shifts = 0; |
||
993 | |||
994 | while ((bbSize >> shifts) > 65534) shifts++; |
||
995 | |||
996 | for (j = bbSize-1; j >= 0; j--) { |
||
997 | Int32 a2update = ptr[bbStart + j]; |
||
998 | UInt16 qVal = (UInt16)(j >> shifts); |
||
999 | quadrant[a2update] = qVal; |
||
1000 | if (a2update < BZ_N_OVERSHOOT) |
||
1001 | quadrant[a2update + nblock] = qVal; |
||
1002 | } |
||
1003 | AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 ); |
||
1004 | } |
||
1005 | |||
1006 | } |
||
1007 | |||
1008 | if (verb >= 4) |
||
1009 | VPrintf3 ( " %d pointers, %d sorted, %d scanned\n", |
||
1010 | nblock, numQSorted, nblock - numQSorted ); |
||
1011 | } |
||
1012 | |||
1013 | #undef BIGFREQ |
||
1014 | #undef SETMASK |
||
1015 | #undef CLEARMASK |
||
1016 | |||
1017 | |||
1018 | /*---------------------------------------------*/ |
||
1019 | /* Pre: |
||
1020 | nblock > 0 |
||
1021 | arr2 exists for [0 .. nblock-1 +N_OVERSHOOT] |
||
1022 | ((UChar*)arr2) [0 .. nblock-1] holds block |
||
1023 | arr1 exists for [0 .. nblock-1] |
||
1024 | |||
1025 | Post: |
||
1026 | ((UChar*)arr2) [0 .. nblock-1] holds block |
||
1027 | All other areas of block destroyed |
||
1028 | ftab [ 0 .. 65536 ] destroyed |
||
1029 | arr1 [0 .. nblock-1] holds sorted order |
||
1030 | */ |
||
1031 | void BZ2_blockSort ( EState* s ) |
||
1032 | { |
||
1033 | UInt32* ptr = s->ptr; |
||
1034 | UChar* block = s->block; |
||
1035 | UInt32* ftab = s->ftab; |
||
1036 | Int32 nblock = s->nblock; |
||
1037 | Int32 verb = s->verbosity; |
||
1038 | Int32 wfact = s->workFactor; |
||
1039 | UInt16* quadrant; |
||
1040 | Int32 budget; |
||
1041 | Int32 budgetInit; |
||
1042 | Int32 i; |
||
1043 | |||
1044 | if (nblock < 10000) { |
||
1045 | fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); |
||
1046 | } else { |
||
1047 | /* Calculate the location for quadrant, remembering to get |
||
1048 | the alignment right. Assumes that &(block[0]) is at least |
||
1049 | 2-byte aligned -- this should be ok since block is really |
||
1050 | the first section of arr2. |
||
1051 | */ |
||
1052 | i = nblock+BZ_N_OVERSHOOT; |
||
1053 | if (i & 1) i++; |
||
1054 | quadrant = (UInt16*)(&(block[i])); |
||
1055 | |||
1056 | /* (wfact-1) / 3 puts the default-factor-30 |
||
1057 | transition point at very roughly the same place as |
||
1058 | with v0.1 and v0.9.0. |
||
1059 | Not that it particularly matters any more, since the |
||
1060 | resulting compressed stream is now the same regardless |
||
1061 | of whether or not we use the main sort or fallback sort. |
||
1062 | */ |
||
1063 | if (wfact < 1 ) wfact = 1; |
||
1064 | if (wfact > 100) wfact = 100; |
||
1065 | budgetInit = nblock * ((wfact-1) / 3); |
||
1066 | budget = budgetInit; |
||
1067 | |||
1068 | mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget ); |
||
1069 | if (verb >= 3) |
||
1070 | VPrintf3 ( " %d work, %d block, ratio %5.2f\n", |
||
1071 | budgetInit - budget, |
||
1072 | nblock, |
||
1073 | (float)(budgetInit - budget) / |
||
1074 | (float)(nblock==0 ? 1 : nblock) ); |
||
1075 | if (budget < 0) { |
||
1076 | if (verb >= 2) |
||
1077 | VPrintf0 ( " too repetitive; using fallback" |
||
1078 | " sorting algorithm\n" ); |
||
1079 | fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb ); |
||
1080 | } |
||
1081 | } |
||
1082 | |||
1083 | s->origPtr = -1; |
||
1084 | for (i = 0; i < s->nblock; i++) |
||
1085 | if (ptr[i] == 0) |
||
1086 | { s->origPtr = i; break; }; |
||
1087 | |||
1088 | AssertH( s->origPtr != -1, 1003 ); |
||
1089 | } |
||
1090 | |||
1091 | |||
1092 | /*-------------------------------------------------------------*/ |
||
1093 | /*--- end blocksort.c ---*/ |
||
1094 | /*-------------------------------------------------------------*/ |