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];
}
let mut i = 0;
while i + 16 <= ll.len() {
let (a, b) = (read_u64(ll, i), read_u64(ll, i + 8));
if a == b && a == a.rotate_left(8) {
let c = ll[i] as usize;
let k = ftab[c] as usize;
let base = (i as u32) << 8;
for (j, e) in tt[k..k + 16].iter_mut().enumerate() {
*e = (*e & 0xff) | (base + ((j as u32) << 8));
}
ftab[c] += 16;
} else {
link(ll, tt, &mut ftab, i..i + 16);
}
i += 16;
}
link(ll, tt, &mut ftab, i..ll.len());
}
#[inline(always)]
fn link(ll: &[u8], tt: &mut [u32], ftab: &mut [u32; 256], rows: std::ops::Range<usize>) {
for i in rows {
let c = ll[i];
let k = ftab[c as usize] as usize;
tt[k] = (tt[k] & 0xff) | (i as u32) << 8;
ftab[c as usize] += 1;
}
}
#[inline(always)]
fn read_u64(b: &[u8], i: usize) -> u64 {
u64::from_ne_bytes(b[i..i + 8].try_into().unwrap())
}
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 FLAGGED_LANES: usize = 32;
const FLAGGED_SEGMENTS: usize = 512;
const PREFETCH: bool = true;
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,
) {
walk_segments_flagged_with::<FLAGGED_LANES, PREFETCH>(tt, orig_ptr, out, s, FLAGGED_SEGMENTS)
}
pub fn walk_segments_flagged_with<const L: usize, const PF: bool>(
tt: &mut TVector,
orig_ptr: u32,
out: &mut Vec<u8>,
s: &mut SegScratch,
segments: usize,
) {
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; L] = 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 < L && queued < ns {
lanes[active] = take(queued, bufs);
if PF {
prefetch(t, lanes[active].row);
}
queued += 1;
active += 1;
}
if active == L {
'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);
if PF {
prefetch(t, l.row);
}
queued += 1;
continue;
}
l.row = q >> 8;
if PF {
prefetch(t, l.row);
}
unsafe {
*l.p = q as u8;
l.p = l.p.add(1);
}
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;
}
l.row = q >> 8;
if PF {
prefetch(t, l.row);
}
unsafe {
*l.p = q as u8;
l.p = l.p.add(1);
}
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];
}
}
#[inline(always)]
fn prefetch(t: *const u32, row: u32) {
let p = t.wrapping_add(row as usize);
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::x86_64::_mm_prefetch::<{ core::arch::x86_64::_MM_HINT_T0 }>(p as *const i8)
};
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::asm!("prfm pldl1keep, [{p}]", p = in(reg) p, options(nostack, preserves_flags, readonly))
};
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
let _ = p;
}
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));
for segments in [1, 256, 1024] {
walk_segments_flagged_with::<12, false>(
&mut tt,
orig_ptr,
&mut flagged,
&mut seg,
segments,
);
assert!(plain == flagged, "12 lanes, {segments} segments");
walk_segments_flagged_with::<7, true>(
&mut tt,
orig_ptr,
&mut flagged,
&mut seg,
segments,
);
assert!(plain == flagged, "7 lanes, prefetch, {segments} segments");
assert!(tt.0.iter().all(|&e| e & START == 0));
}
}
}
#[test]
fn run_stores_equal_one_pass() {
fn one_pass(ll: &[u8], counts: &[u32; 256]) -> Vec<u32> {
let mut ftab = [0u32; 256];
let mut cum = 0;
for c in 0..256 {
ftab[c] = cum;
cum += counts[c];
}
let mut tt: Vec<u32> = ll.iter().map(|&c| c as u32).collect();
for (i, &c) in ll.iter().enumerate() {
let k = ftab[c as usize] as usize;
tt[k] |= (i as u32) << 8;
ftab[c as usize] += 1;
}
tt
}
let mut x = 0x2545_f491_4f6c_dd1du64;
let mut rnd = move || {
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x
};
let mut inputs: Vec<Vec<u8>> = (0..=40).map(|n| vec![7u8; n]).collect();
for _ in 0..40 {
let mut ll = Vec::new();
while ll.len() < 3000 {
let c = (rnd() % 5) as u8;
let run = [1, 2, 15, 16, 17, 31, 32, 33, 48, 100][(rnd() % 10) as usize];
ll.extend(std::iter::repeat_n(c, run));
}
ll.truncate(3000 - (rnd() % 16) as usize);
inputs.push(ll);
}
inputs.push((0..5000).map(|_| rnd() as u8).collect());
inputs.push(
(0..5000)
.map(|i| if i < 1003 { (i % 3) as u8 } else { 9 })
.collect(),
);
for ll in &inputs {
let mut counts = [0u32; 256];
for &c in ll {
counts[c as usize] += 1;
}
let mut tt = TVector::new();
build(ll, &counts, &mut tt);
assert!(tt.0 == one_pass(ll, &counts), "length {}", ll.len());
}
}
#[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);
}
}