use crate::{
Geometry, Pipeline, Publish, Publisher, RegionMemory, Signal, SlotReader, SlotWriter, State,
};
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockError {
Lane,
Depth,
Buffer,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[must_use]
pub struct Exchange {
pub delivered: bool,
pub published: bool,
}
pub struct BlockLane<M, S> {
memory: M,
work: S,
publisher: Publisher,
lane: usize,
pipeline: Pipeline,
sequence: u32,
block: u64,
generation: u64,
}
impl<M, S> fmt::Debug for BlockLane<M, S> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("BlockLane")
.field("geometry", &self.publisher.geometry())
.field("lane", &self.lane)
.field("depth", &self.pipeline.depth())
.field("next_block", &self.block)
.field("last_sequence", &self.sequence)
.finish()
}
}
impl<M: RegionMemory, S: Signal> BlockLane<M, S> {
pub fn new(
memory: M,
work: S,
publisher: Publisher,
lane: usize,
depth: u32,
) -> Result<Self, BlockError> {
let geometry = publisher.geometry();
if lane >= geometry.lanes as usize {
return Err(BlockError::Lane);
}
if !(1..geometry.slots).contains(&depth) {
return Err(BlockError::Depth);
}
let sequence = publisher.published(memory.region(), lane);
Ok(Self {
memory,
work,
publisher,
lane,
pipeline: Pipeline::with_depth(depth),
sequence,
block: 0,
generation: 0,
})
}
pub fn geometry(&self) -> Geometry {
self.publisher.geometry()
}
pub fn lane(&self) -> usize {
self.lane
}
pub fn depth(&self) -> u32 {
self.pipeline.depth()
}
pub fn max_depth(&self) -> u32 {
self.geometry().slots - 1
}
pub fn next_block(&self) -> u64 {
self.block
}
pub fn memory(&self) -> &M {
&self.memory
}
pub fn state(&self) -> Option<State> {
self.publisher.state(self.memory.region())
}
pub fn restart(&mut self, depth: u32) -> Result<(), BlockError> {
if !(1..=self.max_depth()).contains(&depth) {
return Err(BlockError::Depth);
}
self.pipeline.set_depth(depth);
self.block = 0;
self.generation = self.generation.wrapping_add(1);
Ok(())
}
pub fn skip(&mut self) {
self.block = self.block.wrapping_add(1);
}
pub fn process<W, R>(&mut self, write: W, read: R) -> Exchange
where
W: FnOnce(&mut SlotWriter<'_>),
R: FnOnce(SlotReader<'_>),
{
let view = self.memory.region();
let delivered = self
.pipeline
.output_sequence(
self.block,
self.generation,
self.publisher.completed(view, self.lane),
)
.is_some_and(|sequence| self.publisher.try_read(view, self.lane, sequence, read));
let sequence = self.sequence.wrapping_add(1);
let published =
if self
.publisher
.try_publish(view, self.lane, sequence, Some(&self.work), write)
== Publish::Published
{
self.pipeline
.published(sequence, self.block, self.generation);
self.sequence = sequence;
true
} else {
false
};
self.block = self.block.wrapping_add(1);
Exchange {
delivered,
published,
}
}
pub fn process_planes(
&mut self,
input: &[f32],
output: &mut [f32],
) -> Result<Exchange, BlockError> {
let geometry = self.geometry();
let frames = geometry.max_frames as usize;
if input.len() != geometry.input_channels as usize * frames
|| output.len() != geometry.output_channels as usize * frames
{
return Err(BlockError::Buffer);
}
Ok(self.process(
|slot| {
for (plane, samples) in input.chunks_exact(frames).enumerate() {
let _ = slot.write_plane(plane, samples);
}
for plane in geometry.input_channels as usize..geometry.planes() {
let _ = slot.clear_plane(plane);
}
},
|slot| {
for (plane, samples) in output.chunks_exact_mut(frames).enumerate() {
let _ = slot.read_plane(plane, samples);
}
},
))
}
pub fn process_planes_f64(
&mut self,
input: &[f64],
output: &mut [f64],
) -> Result<Exchange, BlockError> {
let geometry = self.geometry();
let frames = geometry.max_frames as usize;
if input.len() != geometry.input_channels as usize * frames
|| output.len() != geometry.output_channels as usize * frames
{
return Err(BlockError::Buffer);
}
Ok(self.process(
|slot| {
for (plane, samples) in input.chunks_exact(frames).enumerate() {
let _ = slot.write_plane_f64(plane, samples);
}
for plane in geometry.input_channels as usize..geometry.planes() {
let _ = slot.clear_plane(plane);
}
},
|slot| {
for (plane, samples) in output.chunks_exact_mut(frames).enumerate() {
let _ = slot.read_plane_f64(plane, samples);
}
},
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Consumer, InProcessEvent, Producer, RegionBuf, SampleFormat};
fn setup_at(
slots: u32,
depth: u32,
origin: u32,
) -> (BlockLane<RegionBuf, InProcessEvent>, InProcessEvent) {
let geometry = Geometry {
sample_format: SampleFormat::F32,
lanes: 1,
slots,
input_channels: 1,
output_channels: 2,
max_frames: 4,
sample_rate: 48_000,
latency_bound_frames: 0,
};
let memory = RegionBuf::new(geometry).unwrap();
let work = InProcessEvent::new();
let ready = InProcessEvent::new();
let mut producer = Producer::new(memory.region(), [Some(&work), None], &ready);
producer.announce_at(geometry, 1, origin).unwrap();
let publisher = producer.publisher().unwrap();
Consumer::new(memory.region(), &ready, [None, None])
.validate_and_ready(2)
.unwrap();
(
BlockLane::new(memory, work.clone(), publisher, 0, depth).unwrap(),
ready,
)
}
fn setup(slots: u32, depth: u32) -> (BlockLane<RegionBuf, InProcessEvent>, InProcessEvent) {
setup_at(slots, depth, 0)
}
fn consume(lane: &BlockLane<RegionBuf, InProcessEvent>) -> Option<Vec<f32>> {
let ready = InProcessEvent::new();
let mut consumer = Consumer::new(lane.memory.region(), &ready, [None, None]);
consumer.geometry = Some(lane.geometry());
consumer.layout = crate::layout(lane.geometry());
let sequence = consumer.next_published(0)?;
let mut samples = vec![0.0; lane.geometry().samples_per_slot()];
let mut seen = vec![0.0; samples.len()];
consumer.slot_mut(0, sequence, &mut samples, |values| {
seen.copy_from_slice(values);
for value in values {
*value += 10.0;
}
});
consumer.complete(0, sequence);
Some(seen)
}
#[test]
fn depths_deliver_exact_earlier_block() {
for depth in [1, 7] {
let (mut lane, _) = setup(8, depth);
let mut output = vec![-1.0; 8];
for block in 0..12 {
let input = vec![block as f32; 4];
let exchange = lane.process_planes(&input, &mut output).unwrap();
assert_eq!(exchange.delivered, block >= depth as usize);
if block >= depth as usize {
assert_eq!(output[0], (block - depth as usize) as f32 + 10.0);
}
consume(&lane);
}
}
}
#[test]
fn late_restart_skip_and_dropped_publish() {
let (mut lane, _) = setup(4, 1);
let mut output = vec![99.0; 8];
let _ = lane.process_planes(&[1.0; 4], &mut output).unwrap();
assert!(
!lane
.process_planes(&[2.0; 4], &mut output)
.unwrap()
.delivered
);
consume(&lane);
assert!(
!lane
.process_planes(&[3.0; 4], &mut output)
.unwrap()
.delivered
);
consume(&lane);
consume(&lane);
assert!(
lane.process_planes(&[4.0; 4], &mut output)
.unwrap()
.delivered
);
lane.restart(3).unwrap();
for _ in 0..3 {
assert!(
!lane
.process_planes(&[0.0; 4], &mut output)
.unwrap()
.delivered
);
consume(&lane);
}
lane.skip();
assert_eq!(lane.next_block(), 4);
}
#[test]
fn origin_wraps_at_minimum_and_maximum_depth() {
for depth in [1, 7] {
let (mut lane, _) = setup_at(8, depth, u32::MAX - 3);
let mut output = vec![-1.0; 8];
for block in 0..3 * depth + 8 {
let input = vec![block as f32; 4];
let exchange = lane.process_planes(&input, &mut output).unwrap();
assert_eq!(exchange.delivered, block >= depth);
if block >= depth {
assert_eq!(output[0], (block - depth) as f32 + 10.0);
}
consume(&lane);
}
}
}
#[test]
fn validates_buffers_clears_planes_and_wraps() {
let (mut lane, _) = setup_at(2, 1, u32::MAX - 1);
let mut output = vec![0.0; 8];
assert_eq!(
lane.process_planes(&[], &mut output),
Err(BlockError::Buffer)
);
let _ = lane.process(
|slot| {
slot.write_plane(1, &[9.0; 4]);
},
|_| {},
);
consume(&lane);
let _ = lane.process_planes(&[1.0; 4], &mut output).unwrap();
let seen = consume(&lane).unwrap();
assert_eq!(&seen[4..], &[0.0; 4]);
assert!(lane.process(|_| {}, |_| {}).published);
consume(&lane);
assert!(lane.process(|_| {}, |_| {}).published);
}
}