pub trait InverseBwt: Default + Send {
fn invert(&mut self, ll: &[u8], counts: &[u32; 256], orig_ptr: u32, out: &mut Vec<u8>);
}
#[derive(Default)]
pub struct Plain {
tt: TVector,
}
impl InverseBwt for Plain {
fn invert(&mut self, ll: &[u8], counts: &[u32; 256], orig_ptr: u32, out: &mut Vec<u8>) {
build(ll, counts, &mut self.tt);
walk(&self.tt, orig_ptr, out);
}
}
#[derive(Default)]
pub struct Segmented {
tt: TVector,
seg: SegScratch,
}
impl InverseBwt for Segmented {
fn invert(&mut self, ll: &[u8], counts: &[u32; 256], orig_ptr: u32, out: &mut Vec<u8>) {
build(ll, counts, &mut self.tt);
walk_segments_flagged(&mut self.tt, orig_ptr, out, &mut self.seg);
}
}
#[derive(Default)]
pub struct TVector(Vec<u32>);
impl TVector {
pub fn new() -> Self {
TVector(Vec::new())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
pub fn build(ll: &[u8], counts: &[u32; 256], tt: &mut TVector) {
let tt = &mut tt.0;
tt.clear();
tt.extend(ll.iter().map(|&c| c as u32));
let mut ftab = [0u32; 256];
let mut cum = 0u32;
for i in 0..256 {
ftab[i] = cum;
cum += counts[i];
}
for (i, &c) in ll.iter().enumerate() {
let k = ftab[c as usize] as usize;
tt[k] = (tt[k] & 0xff) | (i as u32) << 8;
ftab[c as usize] += 1;
}
}
pub fn walk(tt: &TVector, orig_ptr: u32, out: &mut Vec<u8>) {
let t = &tt.0;
let n = t.len();
out.clear();
out.reserve(n);
let mut p = t[orig_ptr as usize];
for _ in 0..n {
p = unsafe { *t.get_unchecked((p >> 8) as usize) };
out.push(p as u8);
}
}
const LANES: usize = 12;
const SEGMENTS: usize = 256;
const MIN_SEGMENTED: usize = 1 << 16;
#[derive(Default)]
pub struct SegScratch {
starts: Vec<u32>,
bitmap: Vec<u64>,
bufs: Vec<Vec<u8>>,
next: Vec<usize>,
}
pub fn walk_fast(tt: &TVector, orig_ptr: u32, out: &mut Vec<u8>, s: &mut SegScratch) {
if tt.len() < MIN_SEGMENTED {
walk(tt, orig_ptr, out)
} else {
walk_segments(tt, orig_ptr, out, s)
}
}
pub fn walk_segments(tt: &TVector, orig_ptr: u32, out: &mut Vec<u8>, s: &mut SegScratch) {
let t = &tt.0;
let n = t.len();
out.clear();
let r0 = t[orig_ptr as usize] >> 8;
random_rows(n, SEGMENTS - 1, &mut s.starts);
s.starts.push(r0);
s.starts.sort_unstable();
s.starts.dedup();
let starts = &s.starts;
let ns = starts.len();
s.bitmap.clear();
s.bitmap.resize(n.div_ceil(64), 0);
for &r in starts {
s.bitmap[(r >> 6) as usize] |= 1 << (r & 63);
}
let cap = 2 * n / ns + 64;
s.bufs.resize_with(ns.max(s.bufs.len()), Vec::new);
for b in &mut s.bufs[..ns] {
b.clear();
b.reserve(cap);
}
s.next.clear();
s.next.resize(ns, usize::MAX);
let seg_of = |row: u32| starts.binary_search(&row).unwrap_or(usize::MAX);
let mut lane_seg = [usize::MAX; LANES];
let mut lane_row = [0u32; LANES];
let mut queued = 0;
for k in 0..LANES {
if queued < ns {
lane_seg[k] = queued;
lane_row[k] = starts[queued];
queued += 1;
}
}
let mut active = lane_seg.iter().filter(|&&x| x != usize::MAX).count();
let bm = &s.bitmap;
let bufs = &mut s.bufs;
while active > 0 {
for k in 0..LANES {
let seg = lane_seg[k];
if seg == usize::MAX {
continue;
}
let q = unsafe { *t.get_unchecked(lane_row[k] as usize) };
let row = q >> 8;
bufs[seg].push(q as u8);
lane_row[k] = row;
if unsafe { *bm.get_unchecked((row >> 6) as usize) } >> (row & 63) & 1 != 0 {
s.next[seg] = seg_of(row);
if queued < ns {
lane_seg[k] = queued;
lane_row[k] = starts[queued];
queued += 1;
} else {
lane_seg[k] = usize::MAX;
active -= 1;
}
}
}
}
out.reserve(n);
let mut seg = seg_of(r0);
while out.len() < n {
let b = &bufs[seg];
out.extend_from_slice(&b[..b.len().min(n - out.len())]);
seg = s.next[seg];
}
}
const START: u32 = 1 << 31;
const LINK: u32 = !START;
#[derive(Clone, Copy)]
struct Lane {
row: u32,
seg: usize,
p: *mut u8,
end: *mut u8,
}
pub fn walk_segments_flagged(
tt: &mut TVector,
orig_ptr: u32,
out: &mut Vec<u8>,
s: &mut SegScratch,
) {
let n = tt.0.len();
out.clear();
if n < MIN_SEGMENTED {
return walk(tt, orig_ptr, out);
}
let r0 = tt.0[orig_ptr as usize] >> 8;
random_rows(n, SEGMENTS - 1, &mut s.starts);
s.starts.push(r0);
s.starts.sort_unstable();
s.starts.dedup();
let ns = s.starts.len();
for &r in &s.starts {
tt.0[r as usize] |= START;
}
let cap = 2 * n / ns + 64;
s.bufs.resize_with(ns.max(s.bufs.len()), Vec::new);
for b in &mut s.bufs[..ns] {
b.clear();
b.reserve(cap);
}
s.next.clear();
s.next.resize(ns, usize::MAX);
let starts = &s.starts;
let seg_of = |row: u32| starts.binary_search(&row).unwrap_or(usize::MAX);
let t = tt.0.as_ptr();
let bufs = &mut s.bufs;
let next = &mut s.next;
let take = |seg: usize, bufs: &mut Vec<Vec<u8>>| -> Lane {
let b = &mut bufs[seg];
let base = b.as_mut_ptr();
let q = unsafe { *t.add(starts[seg] as usize) } & LINK;
debug_assert!(b.capacity() > 1);
unsafe { *base = q as u8 };
Lane {
row: q >> 8,
seg,
p: unsafe { base.add(1) },
end: unsafe { base.add(b.capacity()) },
}
};
let finish = |l: &Lane, bufs: &mut Vec<Vec<u8>>, next: &mut Vec<usize>| {
let b = &mut bufs[l.seg];
unsafe { b.set_len(l.p.offset_from(b.as_ptr()) as usize) };
next[l.seg] = seg_of(l.row);
};
let grow = |l: &mut Lane, bufs: &mut Vec<Vec<u8>>| {
let b = &mut bufs[l.seg];
let len = unsafe { l.p.offset_from(b.as_ptr()) } as usize;
unsafe { b.set_len(len) };
b.reserve(b.capacity());
l.p = unsafe { b.as_mut_ptr().add(len) };
l.end = unsafe { b.as_mut_ptr().add(b.capacity()) };
};
let mut queued = 0;
let mut lanes: [Lane; LANES] = std::array::from_fn(|_| Lane {
row: 0,
seg: usize::MAX,
p: std::ptr::null_mut(),
end: std::ptr::null_mut(),
});
let mut active = 0;
while active < LANES && queued < ns {
lanes[active] = take(queued, bufs);
queued += 1;
active += 1;
}
if active == LANES {
'full: loop {
for l in lanes.iter_mut() {
let q = unsafe { *t.add(l.row as usize) };
if q & START != 0 {
finish(l, bufs, next);
if queued == ns {
break 'full;
}
*l = take(queued, bufs);
queued += 1;
continue;
}
unsafe {
*l.p = q as u8;
l.p = l.p.add(1);
}
l.row = q >> 8;
if l.p == l.end {
grow(l, bufs);
}
}
}
}
let mut live: Vec<Lane> = lanes[..active]
.iter()
.copied()
.filter(|l| next_unset(l, next))
.collect();
while !live.is_empty() {
let mut i = 0;
while i < live.len() {
let l = &mut live[i];
let q = unsafe { *t.add(l.row as usize) };
if q & START != 0 {
finish(l, bufs, next);
live.swap_remove(i);
continue;
}
unsafe {
*l.p = q as u8;
l.p = l.p.add(1);
}
l.row = q >> 8;
if l.p == l.end {
grow(l, bufs);
}
i += 1;
}
}
for &r in starts {
tt.0[r as usize] &= LINK;
}
out.reserve(n);
let mut seg = seg_of(r0);
while out.len() < n {
let b = &bufs[seg];
out.extend_from_slice(&b[..b.len().min(n - out.len())]);
seg = next[seg];
}
}
fn next_unset(l: &Lane, next: &[usize]) -> bool {
l.seg != usize::MAX && next[l.seg] == usize::MAX
}
fn random_rows(n: usize, m: usize, rows: &mut Vec<u32>) {
rows.clear();
let mut x = 0x2545_f491_4f6c_dd1du64 ^ n as u64;
rows.extend((0..m).map(|_| {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
(x % n as u64) as u32
}));
}
#[cfg(test)]
mod tests {
use super::*;
fn check(ll: &[u8]) {
let mut counts = [0u32; 256];
for &c in ll {
counts[c as usize] += 1;
}
let mut tt = TVector::new();
build(ll, &counts, &mut tt);
let (mut plain, mut flagged, mut seg) = (Vec::new(), Vec::new(), SegScratch::default());
for orig_ptr in [0, ll.len() as u32 / 3, ll.len() as u32 - 1] {
walk(&tt, orig_ptr, &mut plain);
walk_segments_flagged(&mut tt, orig_ptr, &mut flagged, &mut seg);
assert!(plain == flagged, "orig_ptr {orig_ptr}");
assert!(tt.0.iter().all(|&e| e & START == 0));
}
}
#[test]
fn flagged_walk_equals_plain() {
let n = MIN_SEGMENTED + 1000;
let mut x = 0x9e37_79b9_7f4a_7c15u64;
let random: Vec<u8> = (0..n)
.map(|_| {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
(x % 7) as u8
})
.collect();
check(&random);
let periodic: Vec<u8> = (0..n).map(|i| b"abc"[i % 3]).collect();
check(&periodic);
}
}