libbz2_rs_sys/
blocksort.rs

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