use super::stream::{Event, Scanner, Source};
use super::{decode_block, Error, ErrorKind, Kernels, Scratch};
use std::collections::BTreeMap;
use std::panic::{catch_unwind, AssertUnwindSafe};
use std::sync::mpsc::{channel, sync_channel, Receiver, SyncSender};
use std::sync::{Arc, Condvar, Mutex};
use std::thread::JoinHandle;
const INPUT_QUEUE: usize = 2;
const COMPACT: usize = 4 << 20;
type Job = (u64, u64, super::block::Symbols);
struct State {
ready: BTreeMap<u64, Result<Vec<u8>, Error>>,
issued: u64,
taken: u64,
end: Option<u64>,
cancelled: bool,
free_out: Vec<Vec<u8>>,
free_syms: Vec<Vec<u16>>,
}
struct Shared {
state: Mutex<State>,
cv: Condvar,
slots: u64,
}
impl Shared {
fn lock(&self) -> std::sync::MutexGuard<'_, State> {
self.state.lock().unwrap_or_else(|e| e.into_inner())
}
}
#[derive(Debug)]
pub struct Stopped;
pub struct StreamDecoder {
input: Mutex<Option<SyncSender<Vec<u8>>>>,
shared: Arc<Shared>,
handles: Mutex<Vec<JoinHandle<()>>>,
reader: Mutex<Reader>,
threads: usize,
}
#[derive(Default)]
struct Reader {
cur: Vec<u8>,
at: usize,
failed: Option<Error>,
done: bool,
}
impl StreamDecoder {
pub fn new(threads: usize, slots: usize, k: Kernels) -> StreamDecoder {
Self::with_inverse::<super::bwt::Segmented>(threads, slots, k)
}
pub fn with_inverse<I: super::bwt::InverseBwt + 'static>(
threads: usize,
slots: usize,
k: Kernels,
) -> StreamDecoder {
let threads = threads.max(1);
let slots = slots.max(threads) as u64;
let shared = Arc::new(Shared {
state: Mutex::new(State {
ready: BTreeMap::new(),
issued: 0,
taken: 0,
end: None,
cancelled: false,
free_out: Vec::new(),
free_syms: Vec::new(),
}),
cv: Condvar::new(),
slots,
});
let (in_tx, in_rx) = sync_channel::<Vec<u8>>(INPUT_QUEUE);
let (job_tx, job_rx) = channel::<Job>();
let job_rx = Arc::new(Mutex::new(job_rx));
let mut handles = Vec::with_capacity(threads + 1);
for i in 0..threads {
let (shared, job_rx) = (shared.clone(), job_rx.clone());
handles.push(
std::thread::Builder::new()
.name(format!("mibz2-{i}"))
.spawn(move || worker::<I>(&shared, &job_rx, k))
.expect("spawn a bz2 worker"),
);
}
let sh = shared.clone();
handles.push(
std::thread::Builder::new()
.name("mibz2-scan".into())
.spawn(move || scanner(&sh, in_rx, job_tx))
.expect("spawn the bz2 scanner"),
);
StreamDecoder {
input: Mutex::new(Some(in_tx)),
shared,
handles: Mutex::new(handles),
reader: Mutex::default(),
threads,
}
}
pub fn threads(&self) -> usize {
self.threads
}
pub fn write(&self, data: Vec<u8>) -> Result<(), Stopped> {
let tx = self
.input
.lock()
.unwrap_or_else(|e| e.into_inner())
.clone()
.ok_or(Stopped)?;
if data.is_empty() {
return Ok(());
}
tx.send(data).map_err(|_| Stopped)
}
pub fn close(&self) {
self.input.lock().unwrap_or_else(|e| e.into_inner()).take();
}
pub fn cancel(&self) {
self.close();
let mut st = self.shared.lock();
st.cancelled = true;
st.ready.clear();
drop(st);
self.shared.cv.notify_all();
}
pub fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
let mut r = self.reader.lock().unwrap_or_else(|e| e.into_inner());
if !r.done && r.failed.is_none() && self.shared.lock().cancelled {
r.failed = Some(Error::new(ErrorKind::Cancelled));
}
loop {
if let Some(e) = &r.failed {
return Err(e.clone());
}
if r.at < r.cur.len() {
let n = buf.len().min(r.cur.len() - r.at);
buf[..n].copy_from_slice(&r.cur[r.at..r.at + n]);
r.at += n;
return Ok(n);
}
if r.done || buf.is_empty() {
return Ok(0);
}
let mut st = self.shared.lock();
let spent = std::mem::take(&mut r.cur);
if spent.capacity() != 0 && st.free_out.len() < self.shared.slots as usize {
st.free_out.push(spent);
}
r.at = 0;
loop {
if st.cancelled {
r.failed = Some(Error::new(ErrorKind::Cancelled));
break;
}
let taken = st.taken;
if let Some(res) = st.ready.remove(&taken) {
st.taken += 1;
self.shared.cv.notify_all();
match res {
Ok(v) => r.cur = v,
Err(e) => r.failed = Some(e),
}
break;
}
if st.end == Some(st.taken) {
r.done = true;
break;
}
st = self.shared.cv.wait(st).unwrap_or_else(|e| e.into_inner());
}
}
}
pub fn join(&self) {
self.cancel();
let handles = std::mem::take(&mut *self.handles.lock().unwrap_or_else(|e| e.into_inner()));
for h in handles {
let _ = h.join();
}
}
}
impl Drop for StreamDecoder {
fn drop(&mut self) {
self.join();
}
}
struct ChannelSource<'a> {
rx: Receiver<Vec<u8>>,
buf: Vec<u8>,
base: u64,
released: u64,
eof: bool,
shared: &'a Shared,
}
impl Source for ChannelSource<'_> {
fn view(&mut self, from: u64, want: usize) -> Result<(&[u8], bool), ErrorKind> {
while !self.eof && self.base + (self.buf.len() as u64) < from + want as u64 {
match self.rx.recv() {
Ok(chunk) => {
let drop_n = (self.released - self.base) as usize;
if drop_n >= COMPACT {
self.buf.drain(..drop_n);
self.base = self.released;
}
self.buf.extend_from_slice(&chunk);
}
Err(_) => self.eof = true,
}
if self.shared.lock().cancelled {
return Err(ErrorKind::Cancelled);
}
}
let i = (from - self.base) as usize;
Ok((self.buf.get(i..).unwrap_or(&[]), self.eof))
}
fn release(&mut self, upto: u64) {
self.released = self.released.max(upto);
}
}
fn scanner(shared: &Shared, rx: Receiver<Vec<u8>>, jobs: std::sync::mpsc::Sender<Job>) {
let src = ChannelSource {
rx,
buf: Vec::new(),
base: 0,
released: 0,
eof: false,
shared,
};
let mut sc = Scanner::new(src);
let finish = |res: Option<Error>| {
let mut st = shared.lock();
let at = st.issued;
match res {
Some(e) if e.kind != ErrorKind::Cancelled => {
st.ready.insert(at, Err(e));
st.issued += 1;
st.end = Some(at + 1);
}
_ => st.end = Some(at),
}
drop(st);
shared.cv.notify_all();
};
loop {
let syms = {
let mut st = shared.lock();
while !st.cancelled && st.issued - st.taken >= shared.slots {
st = shared.cv.wait(st).unwrap_or_else(|e| e.into_inner());
}
if st.cancelled {
drop(st);
return finish(None);
}
st.free_syms.pop().unwrap_or_default()
};
let next = catch_unwind(AssertUnwindSafe(|| sc.next(syms)))
.unwrap_or_else(|p| Err(Error::new(ErrorKind::Internal(panic_message(p)))));
match next {
Ok(Event::Block {
index,
bit,
symbols,
..
}) => {
let mut st = shared.lock();
let place = st.issued;
st.issued += 1;
drop(st);
debug_assert_eq!(place, index);
if jobs.send((place, bit, symbols)).is_err() {
return finish(None);
}
}
Ok(Event::Done) => return finish(None),
Err(e) => return finish(Some(e)),
}
}
}
fn worker<I: super::bwt::InverseBwt>(shared: &Shared, jobs: &Mutex<Receiver<Job>>, k: Kernels) {
let mut scratch = Scratch::<I>::default();
loop {
let job = jobs.lock().unwrap_or_else(|e| e.into_inner()).recv();
let Ok((place, bit, symbols)) = job else {
return;
};
let mut out = {
let mut st = shared.lock();
if st.cancelled {
continue; }
st.free_out.pop().unwrap_or_default()
};
out.clear();
let res = catch_unwind(AssertUnwindSafe(|| {
decode_block(&symbols, k, &mut scratch, &mut out)
}));
let res = match res {
Ok(Ok(_)) => Ok(out),
Ok(Err(kind)) => Err(kind),
Err(p) => {
scratch = Scratch::default();
Err(ErrorKind::Internal(panic_message(p)))
}
}
.map_err(|kind| Error {
kind,
block: Some(place),
bit: Some(bit),
});
let mut st = shared.lock();
if !st.cancelled {
st.ready.insert(place, res);
}
if st.free_syms.len() < shared.slots as usize {
st.free_syms.push(symbols.syms);
}
drop(st);
shared.cv.notify_all();
}
}
fn panic_message(p: Box<dyn std::any::Any + Send>) -> String {
p.downcast_ref::<&str>()
.map(|s| s.to_string())
.or_else(|| p.downcast_ref::<String>().cloned())
.unwrap_or_else(|| "a panic".into())
}