use flashraw_core::par;
const SYNC_STATES: usize = 1 << 14;
const MIN_CHUNK_BYTES: usize = 128 << 10;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum EndCheck {
Valid,
Invalid,
Unknown,
}
pub(crate) trait SymbolStream: Sync {
type Pump<'p>
where
Self: 'p;
const WIDTH: usize;
fn cycle(&self) -> usize;
fn period(&self) -> usize;
fn total_bits(&self) -> u64;
fn span_bits(&self) -> u64 {
self.total_bits()
}
fn end_check(&self, pos: u64) -> EndCheck;
fn pump_at(&self, bit: u64) -> Self::Pump<'_>;
fn position(p: &Self::Pump<'_>) -> u64;
fn decode(&self, p: &mut Self::Pump<'_>, phase: usize, out: &mut [u16]) -> bool;
}
struct Spec {
phase: usize,
merge: Option<(usize, usize)>,
states: Vec<u64>,
values: Vec<u16>,
errors: Vec<usize>,
end: u64,
valid: usize,
limited_by_end: bool,
}
impl Spec {
fn symbols(&self, width: usize) -> usize {
self.values.len() / width
}
}
pub(crate) struct Values {
specs: Vec<Vec<Spec>>,
runs: Vec<(usize, Vec<u16>, usize, usize, usize, usize)>,
}
impl Values {
fn run_parts(&self, r: usize) -> (&[u16], &[u16]) {
let (_, bridge, k, g, from, n) = &self.runs[r];
let body = if *n == 0 { &[][..] } else { &self.specs[*k][*g].values[*from..from + n] };
(&bridge[..], body)
}
fn locate(&self, i: usize) -> (usize, usize) {
let r = self.runs.partition_point(|s| s.0 <= i).saturating_sub(1);
(r, i - self.runs[r].0)
}
pub fn get(&self, i: usize) -> u16 {
let (r, off) = self.locate(i);
let (bridge, body) = self.run_parts(r);
if off < bridge.len() { bridge[off] } else { body[off - bridge.len()] }
}
pub fn copy_to(&self, start: usize, dst: &mut [u16]) {
let (mut r, mut off) = self.locate(start);
let mut done = 0;
while done < dst.len() && r < self.runs.len() {
let (bridge, body) = self.run_parts(r);
for part in [bridge, body] {
if off >= part.len() {
off -= part.len();
continue;
}
let n = (part.len() - off).min(dst.len() - done);
dst[done..done + n].copy_from_slice(&part[off..off + n]);
done += n;
off = 0;
if done == dst.len() {
return;
}
}
r += 1;
off = 0;
}
}
}
pub(crate) fn default_chunk(bytes: usize) -> Option<usize> {
let tasks = par::threads() * 4;
if tasks <= 4 || bytes < 2 * MIN_CHUNK_BYTES {
return None;
}
Some(bytes.div_ceil(tasks).max(MIN_CHUNK_BYTES))
}
pub(crate) fn decode<S: SymbolStream>(s: &S, symbols: usize, chunk: usize) -> Option<Values> {
let chunk_bits = chunk.max(1) as u64 * 8;
let span = s.span_bits().min(s.total_bits());
let nchunks = span.div_ceil(chunk_bits).max(1) as usize;
let period = s.period().max(1);
let specs: Vec<Vec<Spec>> = par::map((0..nchunks).collect(), true, |k| {
let start = k as u64 * chunk_bits;
let limit = if k + 1 == nchunks && span == s.total_bits() {
None
} else {
Some(((k as u64 + 1) * chunk_bits).min(span))
};
let first = speculate(s, 0, start, limit, symbols, None);
let mut all = Vec::with_capacity(period);
let rest: Vec<Spec> =
(1..period).map(|g| speculate(s, g, start, limit, symbols, Some((&first.states, period)))).collect();
all.push(first);
all.extend(rest);
all
});
let runs = resolve(s, &specs, symbols)?;
Some(Values { specs, runs })
}
fn speculate<S: SymbolStream>(
s: &S,
phase: usize,
start: u64,
limit: Option<u64>,
cap: usize,
mut merge_into: Option<(&[u64], usize)>,
) -> Spec {
let w = S::WIDTH;
let cycle = s.cycle().max(1);
let total_bits = s.total_bits();
let span = limit.unwrap_or(total_bits).saturating_sub(start);
let mut values: Vec<u16> = Vec::with_capacity(((span / 6) as usize * w).min(cap * w));
let mut states = Vec::with_capacity(SYNC_STATES.min(span as usize));
let mut errors = Vec::new();
let mut pump = s.pump_at(start);
let mut pos = start;
let mut c = phase % cycle;
let mut merge = None;
let mut out = [0u16; 4];
let mut n = 0usize;
let mut valid = 0usize;
loop {
match limit {
Some(l) if pos >= l => break,
None if s.end_check(pos) != EndCheck::Valid && pos > total_bits => break,
_ => {}
}
if n > cap {
break;
}
if limit.is_none() && s.end_check(pos) == EndCheck::Valid {
valid = n;
}
if let Some((reference, p)) = merge_into {
if let Ok(j0) = reference.binary_search(&pos) {
if j0 % p == (phase + n) % p {
states.push(pos);
merge = Some((n, j0));
break;
}
}
if n >= SYNC_STATES {
merge_into = None;
}
}
if states.len() < SYNC_STATES {
states.push(pos);
}
if !s.decode(&mut pump, c, &mut out[..w]) {
errors.push(n);
}
values.extend_from_slice(&out[..w]);
pos = S::position(&pump);
n += 1;
c += 1;
if c == cycle {
c = 0;
}
}
if limit.is_some() || merge.is_some() {
valid = n;
}
Spec { phase, merge, states, values, errors, end: pos, valid, limited_by_end: limit.is_none() }
}
type Runs = Vec<(usize, Vec<u16>, usize, usize, usize, usize)>;
fn resolve<S: SymbolStream>(s: &S, specs: &[Vec<Spec>], total: usize) -> Option<Runs> {
let w = S::WIDTH;
let cycle = s.cycle().max(1);
let period = s.period().max(1);
let mut out: Runs = Vec::with_capacity(specs.len());
let mut n = 0usize;
let mut pos = 0u64;
let mut bridge: Vec<u16> = Vec::new();
let mut pump: Option<S::Pump<'_>> = None;
let mut buf = [0u16; 4];
let mut k = 0usize;
while n < total {
let Some(phases) = specs.get(k) else { break };
let last = k + 1 == specs.len() && phases[0].limited_by_end;
let hit = phases.iter().enumerate().find_map(|(g, sp)| {
let j = sp.states.binary_search(&pos).ok()?;
((sp.phase + j) % period == n % period).then_some((g, j))
});
if let Some((g, j)) = hit {
let sp = &phases[g];
if let Some((jm, j0)) = sp.merge {
if sp.errors.iter().any(|&e| e >= j && e < jm) {
return None;
}
let take = (jm - j).min(total - n);
bridge.extend_from_slice(&sp.values[j * w..(j + take) * w]);
n += take;
pos = if n == total { sp.states[j + take] } else { phases[0].states[j0] };
pump = None;
continue;
}
let avail = if last { sp.valid } else { sp.symbols(w) };
let count = avail.checked_sub(j)?.min(total - n);
if last && count < total - n {
return None; }
if sp.errors.iter().any(|&e| e >= j && e < j + count) {
return None; }
out.push((n * w - bridge.len(), std::mem::take(&mut bridge), k, g, j * w, count * w));
n += count;
if n == total {
return Some(out);
}
pos = sp.end;
pump = None;
k += 1;
continue;
}
let full = || phases.iter().filter(|sp| sp.merge.is_none());
if !last && k + 1 < specs.len() && full().all(|sp| pos >= sp.end) {
k += 1;
continue;
}
if full().all(|sp| sp.states.len() == SYNC_STATES && sp.states.last().is_some_and(|&l| pos > l)) {
return None; }
let p = pump.get_or_insert_with(|| s.pump_at(pos));
if !s.decode(p, n % cycle, &mut buf[..w]) {
return None;
}
bridge.extend_from_slice(&buf[..w]);
pos = S::position(p);
n += 1;
}
if n < total || s.end_check(pos) != EndCheck::Valid {
return None;
}
if !bridge.is_empty() {
out.push((n * w - bridge.len(), bridge, 0, 0, 0, 0));
}
Some(out)
}