1#![forbid(unsafe_code)]
2
3use std::array;
43use std::fmt;
44use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
45use std::sync::{Arc, Condvar, Mutex};
46use std::time::{Duration, Instant};
47
48mod block;
49pub use block::{BlockError, BlockLane, Exchange};
50pub mod events;
51#[cfg(test)]
52mod serial_tests;
53use events::{CONTEXT_WORDS, EVENT_WORDS, METADATA_WORDS, PACKET_WORDS};
54pub use events::{Event, EventBuffer, EventData, MAX_EVENTS, MAX_SYSEX_BYTES, Transport};
55#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
56#[repr(u32)]
57pub enum SampleFormat {
58 #[default]
59 F32 = 0,
60 F64 = 1,
61}
62impl SampleFormat {
63 pub fn words(self) -> usize {
64 if self == Self::F64 { 2 } else { 1 }
65 }
66}
67
68pub const MAGIC: u32 = u32::from_le_bytes(*b"SSRR");
69pub const LAYOUT_VERSION: u32 = 4;
70pub const HEADER_BYTES: usize = 512;
71pub const CACHE_LINE: usize = 64;
72pub const MAX_CHANNELS: u32 = 64;
73pub const MAX_FRAMES: u32 = 65_536;
74pub const MAX_SLOTS: u32 = 16;
75pub const MAX_REGION_BYTES: usize = 128 * 1024 * 1024;
76const COUNTER_BYTES: usize = 2 * CACHE_LINE;
77const HEADER_WORDS: usize = HEADER_BYTES / 4;
78const COUNTER_WORDS: usize = COUNTER_BYTES / 8;
79
80const MAGIC_I: usize = 0;
81const VERSION_I: usize = 1;
82const TOTAL_I: usize = 2;
83const LANES_I: usize = 3;
84const INPUTS_I: usize = 4;
85const OUTPUTS_I: usize = 5;
86const FRAMES_I: usize = 6;
87const RATE_I: usize = 7;
88const LATENCY_I: usize = 8;
89const STATE_I: usize = 9;
90const FAULT_I: usize = 10;
91const PRODUCER_PID_I: usize = 11;
92const CONSUMER_PID_I: usize = 12;
93const FORMAT_I: usize = 13;
94const SLOTS_I: usize = 14;
95const LANE_GEOMETRY_I: usize = 16;
96const SEQUENCE_I: usize = 32;
97const COMPLETED_I: usize = 48;
98
99#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100#[repr(u32)]
101pub enum State {
102 Empty = 0,
103 Announced = 1,
104 Ready = 2,
105 Closing = 3,
106 Fault = 4,
107}
108
109impl State {
110 fn from_wire(value: u32) -> Option<Self> {
111 Some(match value {
112 0 => Self::Empty,
113 1 => Self::Announced,
114 2 => Self::Ready,
115 3 => Self::Closing,
116 4 => Self::Fault,
117 _ => return None,
118 })
119 }
120}
121
122#[derive(Clone, Copy, Debug, Eq, PartialEq)]
123#[repr(u32)]
124pub enum Fault {
125 None = 0,
126 LayoutMismatch = 1,
127 EngineFailed = 2,
128 HostShuttingDown = 3,
129}
130
131#[derive(Clone, Copy, Debug, Eq, PartialEq)]
132pub struct Geometry {
133 pub sample_format: SampleFormat,
134 pub lanes: u32,
135 pub slots: u32,
136 pub input_channels: u32,
137 pub output_channels: u32,
138 pub max_frames: u32,
139 pub sample_rate: u32,
140 pub latency_bound_frames: u32,
141}
142
143impl Geometry {
144 pub fn validate(self) -> Result<Self, GeometryError> {
145 if !(1..=2).contains(&self.lanes) {
146 return Err(GeometryError::LaneCount);
147 }
148 if !matches!(self.slots, 2 | 4 | 8 | 16) {
149 return Err(GeometryError::Slots);
150 }
151 if self.input_channels > MAX_CHANNELS || self.output_channels > MAX_CHANNELS {
152 return Err(GeometryError::Channels);
153 }
154 if !(1..=MAX_FRAMES).contains(&self.max_frames) {
155 return Err(GeometryError::Frames);
156 }
157 if !(1_000..=1_000_000).contains(&self.sample_rate) {
158 return Err(GeometryError::SampleRate);
159 }
160 if layout(self).is_none() {
161 return Err(GeometryError::RegionTooLarge);
162 }
163 Ok(self)
164 }
165
166 pub fn planes(self) -> usize {
167 self.input_channels.max(self.output_channels) as usize
168 }
169
170 pub fn samples_per_slot(self) -> usize {
171 self.planes() * self.max_frames as usize
172 }
173
174 pub fn total_bytes(self) -> Result<usize, GeometryError> {
175 self.validate()?;
176 Ok(layout(self)
177 .expect("validated geometry has a layout")
178 .total_bytes)
179 }
180}
181
182#[derive(Clone, Copy, Debug, Eq, PartialEq)]
183pub enum GeometryError {
184 LaneCount,
185 Slots,
186 Channels,
187 Frames,
188 SampleRate,
189 RegionTooLarge,
190}
191
192#[derive(Clone, Copy, Debug)]
193struct Layout {
194 slot_bytes: usize,
195 first_slot_offsets: [usize; 2],
196 slots: u32,
197 prefix_bytes: usize,
198 total_bytes: usize,
199}
200
201impl Layout {
202 fn slot_offset(self, lane: usize, sequence: u32) -> usize {
203 self.first_slot_offsets[lane] + (sequence & (self.slots - 1)) as usize * self.slot_bytes
204 }
205}
206
207fn round_up(value: usize, alignment: usize) -> Option<usize> {
208 value
209 .checked_add(alignment - 1)
210 .map(|v| v / alignment * alignment)
211}
212
213fn layout(geometry: Geometry) -> Option<Layout> {
214 let audio_words = geometry
215 .samples_per_slot()
216 .checked_mul(geometry.sample_format.words())?;
217 let slot_bytes = round_up(
218 audio_words.checked_add(METADATA_WORDS)?.checked_mul(4)?,
219 CACHE_LINE,
220 )?;
221 let mut offset = HEADER_BYTES;
222 let mut first_slot_offsets = [0; 2];
223 for first_slot_offset in first_slot_offsets.iter_mut().take(geometry.lanes as usize) {
224 *first_slot_offset = offset;
225 offset = offset.checked_add(slot_bytes.checked_mul(geometry.slots as usize)?)?;
226 }
227 let prefix_bytes = round_up(offset, CACHE_LINE)?;
228 let total_bytes = prefix_bytes.checked_add(COUNTER_BYTES)?;
229 (total_bytes < MAX_REGION_BYTES).then_some(Layout {
230 slot_bytes,
231 first_slot_offsets,
232 slots: geometry.slots,
233 prefix_bytes,
234 total_bytes,
235 })
236}
237
238pub trait Signal: Send + Sync {
239 fn signal(&self);
240}
241
242impl<T: Signal + ?Sized> Signal for Arc<T> {
243 fn signal(&self) {
244 T::signal(self);
245 }
246}
247
248impl<T: Signal + ?Sized> Signal for Box<T> {
249 fn signal(&self) {
250 T::signal(self);
251 }
252}
253
254pub trait Wait: Send + Sync {
255 fn wait(&self, timeout: Duration) -> Waited;
256}
257
258#[derive(Clone, Copy, Debug, Eq, PartialEq)]
259pub enum Waited {
260 Signalled,
261 TimedOut,
262 PeerGone,
263}
264
265#[derive(Debug)]
266struct EventInner {
267 generation: AtomicU64,
268 gone: AtomicBool,
269 observed: Mutex<u64>,
270 changed: Condvar,
271}
272
273#[derive(Clone, Debug)]
277pub struct InProcessEvent(Arc<EventInner>);
278
279impl InProcessEvent {
280 pub fn new() -> Self {
281 Self(Arc::new(EventInner {
282 generation: AtomicU64::new(0),
283 gone: AtomicBool::new(false),
284 observed: Mutex::new(0),
285 changed: Condvar::new(),
286 }))
287 }
288
289 pub fn peer_gone(&self) {
290 self.0.gone.store(true, Ordering::Release);
291 self.0.changed.notify_all();
292 }
293}
294
295impl Default for InProcessEvent {
296 fn default() -> Self {
297 Self::new()
298 }
299}
300
301impl Signal for InProcessEvent {
302 fn signal(&self) {
303 self.0.generation.fetch_add(1, Ordering::Release);
304 self.0.changed.notify_one();
305 }
306}
307
308impl Wait for InProcessEvent {
309 fn wait(&self, timeout: Duration) -> Waited {
310 if self.0.gone.load(Ordering::Acquire) {
311 return Waited::PeerGone;
312 }
313 let mut observed = self.0.observed.lock().unwrap_or_else(|e| e.into_inner());
314 let current = self.0.generation.load(Ordering::Acquire);
315 if current != *observed {
316 *observed = current;
317 return Waited::Signalled;
318 }
319 let (mut observed, result) = self
320 .0
321 .changed
322 .wait_timeout_while(observed, timeout, |seen| {
323 !self.0.gone.load(Ordering::Acquire)
324 && self.0.generation.load(Ordering::Acquire) == *seen
325 })
326 .unwrap_or_else(|e| e.into_inner());
327 if self.0.gone.load(Ordering::Acquire) {
328 Waited::PeerGone
329 } else {
330 let current = self.0.generation.load(Ordering::Acquire);
331 if current != *observed {
332 *observed = current;
333 Waited::Signalled
334 } else if result.timed_out() {
335 Waited::TimedOut
336 } else {
337 Waited::Signalled
338 }
339 }
340 }
341}
342
343#[derive(Clone, Copy, Debug)]
346pub struct Region<'a> {
347 words32: &'a [AtomicU32],
348 words64: &'a [AtomicU64],
349}
350
351impl<'a> Region<'a> {
352 pub fn new(words32: &'a [AtomicU32], words64: &'a [AtomicU64]) -> Self {
353 Self { words32, words64 }
354 }
355
356 pub fn state(self) -> Option<State> {
357 State::from_wire(self.load32(STATE_I, Ordering::Acquire))
358 }
359
360 pub fn fault_code(self) -> u32 {
361 self.load32(FAULT_I, Ordering::Acquire)
362 }
363
364 pub fn heartbeat(self) -> u64 {
365 self.words64
366 .first()
367 .map_or(0, |v| v.load(Ordering::Acquire))
368 }
369
370 pub fn last_error(self) -> u64 {
371 self.words64
372 .get(CACHE_LINE / 8)
373 .map_or(0, |v| v.load(Ordering::Acquire))
374 }
375
376 fn load32(self, index: usize, ordering: Ordering) -> u32 {
377 self.words32.get(index).map_or(0, |v| v.load(ordering))
378 }
379
380 fn store32(self, index: usize, value: u32, ordering: Ordering) {
381 if let Some(word) = self.words32.get(index) {
382 word.store(value, ordering);
383 }
384 }
385}
386
387#[derive(Debug)]
388pub struct RegionBuf {
389 words32: Box<[AtomicU32]>,
390 words64: Box<[AtomicU64]>,
391}
392
393impl RegionBuf {
394 pub fn new(geometry: Geometry) -> Result<Self, GeometryError> {
395 let geometry = geometry.validate()?;
396 let layout = layout(geometry).expect("validated geometry has a layout");
397 Ok(Self {
398 words32: (0..layout.prefix_bytes / 4)
399 .map(|_| AtomicU32::new(0))
400 .collect(),
401 words64: (0..COUNTER_WORDS).map(|_| AtomicU64::new(0)).collect(),
402 })
403 }
404
405 pub fn region(&self) -> Region<'_> {
406 Region::new(&self.words32, &self.words64)
407 }
408}
409
410pub trait RegionMemory: Send + Sync {
415 fn region(&self) -> Region<'_>;
416}
417
418impl RegionMemory for RegionBuf {
419 fn region(&self) -> Region<'_> {
420 self.region()
421 }
422}
423
424impl<T: RegionMemory + ?Sized> RegionMemory for Arc<T> {
425 fn region(&self) -> Region<'_> {
426 T::region(self)
427 }
428}
429
430impl<T: RegionMemory + ?Sized> RegionMemory for Box<T> {
431 fn region(&self) -> Region<'_> {
432 T::region(self)
433 }
434}
435
436#[derive(Clone, Copy, Debug, Eq, PartialEq)]
437pub enum HeaderError {
438 TooShort,
439 Magic,
440 Version,
441 State,
442 Geometry(GeometryError),
443 TotalBytes,
444 LaneGeometry,
445}
446
447#[derive(Clone, Copy, Debug, Eq, PartialEq)]
448pub enum Publish {
449 Published,
450 Dropped,
451}
452
453#[derive(Clone, Copy, Debug)]
458pub struct Publisher {
459 geometry: Geometry,
460 layout: Layout,
461}
462
463impl Publisher {
464 pub fn geometry(&self) -> Geometry {
465 self.geometry
466 }
467
468 pub fn state(&self, region: Region<'_>) -> Option<State> {
469 region.state()
470 }
471
472 pub fn published(&self, region: Region<'_>, lane: usize) -> u32 {
475 if lane >= self.geometry.lanes as usize {
476 return 0;
477 }
478 u32::from_le(region.load32(SEQUENCE_I + lane, Ordering::Acquire))
479 }
480
481 pub fn completed(&self, region: Region<'_>, lane: usize) -> u32 {
484 if lane >= self.geometry.lanes as usize {
485 return 0;
486 }
487 u32::from_le(region.load32(COMPLETED_I + lane, Ordering::Acquire))
488 }
489
490 pub fn try_publish<F>(
495 &self,
496 region: Region<'_>,
497 lane: usize,
498 sequence: u32,
499 work: Option<&dyn Signal>,
500 write: F,
501 ) -> Publish
502 where
503 F: FnOnce(&mut SlotWriter<'_>),
504 {
505 if region.state() != Some(State::Ready) || lane >= self.geometry.lanes as usize {
506 return Publish::Dropped;
507 }
508 let completed = self.completed(region, lane);
509 if !sequence_at_least(completed, sequence.wrapping_sub(self.geometry.slots)) {
510 return Publish::Dropped;
511 }
512 let offset = self.layout.slot_offset(lane, sequence) / 4;
513 let Some(samples) = region.words32.get(
514 offset..offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words(),
515 ) else {
516 return Publish::Dropped;
517 };
518 let meta_offset =
519 offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words();
520 let metadata = ®ion.words32[meta_offset..meta_offset + METADATA_WORDS];
521 let mut writer = SlotWriter {
522 samples,
523 metadata,
524 format: self.geometry.sample_format,
525 frames: self.geometry.max_frames as usize,
526 planes: self.geometry.planes(),
527 };
528 writer.set_transport(Transport::default());
529 writer.set_events(&[]);
530 metadata[CONTEXT_WORDS + PACKET_WORDS].store(0, Ordering::Relaxed);
531 metadata[CONTEXT_WORDS + PACKET_WORDS + 1].store(0, Ordering::Relaxed);
532 write(&mut writer);
533 region.store32(SEQUENCE_I + lane, sequence.to_le(), Ordering::Release);
534 if let Some(signal) = work {
535 signal.signal();
536 }
537 Publish::Published
538 }
539
540 pub fn try_read<F>(&self, region: Region<'_>, lane: usize, sequence: u32, read: F) -> bool
542 where
543 F: FnOnce(SlotReader<'_>),
544 {
545 if lane >= self.geometry.lanes as usize
546 || !sequence_at_least(self.completed(region, lane), sequence)
547 {
548 return false;
549 }
550 let published = self.published(region, lane);
551 if published.wrapping_sub(sequence) >= self.geometry.slots {
552 return false;
553 }
554 let offset = self.layout.slot_offset(lane, sequence) / 4;
555 let Some(samples) = region.words32.get(
556 offset..offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words(),
557 ) else {
558 return false;
559 };
560 let meta_offset =
561 offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words();
562 read(SlotReader {
563 samples,
564 metadata: ®ion.words32[meta_offset..meta_offset + METADATA_WORDS],
565 format: self.geometry.sample_format,
566 frames: self.geometry.max_frames as usize,
567 planes: self.geometry.planes(),
568 });
569 true
570 }
571}
572
573pub struct Producer<'a> {
574 region: Region<'a>,
575 geometry: Option<Geometry>,
576 layout: Option<Layout>,
577 work: [Option<&'a dyn Signal>; 2],
578 ready: &'a dyn Wait,
579}
580
581impl fmt::Debug for Producer<'_> {
582 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
583 formatter
584 .debug_struct("Producer")
585 .field("region", &self.region)
586 .field("geometry", &self.geometry)
587 .field("layout", &self.layout)
588 .finish_non_exhaustive()
589 }
590}
591
592impl<'a> Producer<'a> {
593 pub fn new(region: Region<'a>, work: [Option<&'a dyn Signal>; 2], ready: &'a dyn Wait) -> Self {
594 Self {
595 region,
596 geometry: None,
597 layout: None,
598 work,
599 ready,
600 }
601 }
602
603 pub fn announce(&mut self, geometry: Geometry, producer_pid: u32) -> Result<(), GeometryError> {
604 self.announce_at(geometry, producer_pid, 0)
605 }
606
607 pub fn announce_at(
609 &mut self,
610 geometry: Geometry,
611 producer_pid: u32,
612 origin: u32,
613 ) -> Result<(), GeometryError> {
614 let geometry = geometry.validate()?;
615 let layout = layout(geometry).expect("validated geometry has a layout");
616 if self.region.words32.len() * 4 < layout.prefix_bytes
617 || self.region.words64.len() < COUNTER_WORDS
618 {
619 return Err(GeometryError::RegionTooLarge);
620 }
621 for word in self.region.words32.iter().take(HEADER_WORDS) {
622 word.store(0, Ordering::Relaxed);
623 }
624 for word in self.region.words64 {
625 word.store(0, Ordering::Relaxed);
626 }
627 let fields = [
628 (MAGIC_I, MAGIC),
629 (VERSION_I, LAYOUT_VERSION),
630 (TOTAL_I, layout.total_bytes as u32),
631 (LANES_I, geometry.lanes),
632 (INPUTS_I, geometry.input_channels),
633 (OUTPUTS_I, geometry.output_channels),
634 (FRAMES_I, geometry.max_frames),
635 (RATE_I, geometry.sample_rate),
636 (FORMAT_I, geometry.sample_format as u32),
637 (SLOTS_I, geometry.slots),
638 (LATENCY_I, geometry.latency_bound_frames),
639 (PRODUCER_PID_I, producer_pid),
640 ];
641 for (index, value) in fields {
642 self.region.store32(index, value.to_le(), Ordering::Relaxed);
643 }
644 for lane in 0..geometry.lanes as usize {
645 self.region
646 .store32(SEQUENCE_I + lane, origin.to_le(), Ordering::Relaxed);
647 self.region
648 .store32(COMPLETED_I + lane, origin.to_le(), Ordering::Relaxed);
649 let base = LANE_GEOMETRY_I + lane * 3;
650 self.region.store32(
651 base,
652 (layout.first_slot_offsets[lane] as u32).to_le(),
653 Ordering::Relaxed,
654 );
655 self.region.store32(
656 base + 1,
657 (layout.slot_bytes as u32).to_le(),
658 Ordering::Relaxed,
659 );
660 self.region
661 .store32(base + 2, geometry.slots.to_le(), Ordering::Relaxed);
662 }
663 self.region
664 .store32(FAULT_I, Fault::None as u32, Ordering::Release);
665 self.region
666 .store32(STATE_I, State::Announced as u32, Ordering::Release);
667 self.geometry = Some(geometry);
668 self.layout = Some(layout);
669 Ok(())
670 }
671
672 pub fn wait_ready(&self, timeout: Duration) -> Waited {
673 let deadline = Instant::now() + timeout;
674 loop {
675 match self.region.state() {
676 Some(State::Ready) => return Waited::Signalled,
677 Some(State::Fault | State::Closing) | None => return Waited::PeerGone,
678 _ => {}
679 }
680 let now = Instant::now();
681 if now >= deadline {
682 return Waited::TimedOut;
683 }
684 match self.ready.wait(deadline.saturating_duration_since(now)) {
685 Waited::Signalled => {}
686 other => return other,
687 }
688 }
689 }
690
691 pub fn publisher(&self) -> Option<Publisher> {
693 Some(Publisher {
694 geometry: self.geometry?,
695 layout: self.layout?,
696 })
697 }
698
699 pub fn completed(&self, lane: usize) -> u32 {
700 self.publisher()
701 .map_or(0, |publisher| publisher.completed(self.region, lane))
702 }
703
704 pub fn try_publish<F>(&self, lane: usize, sequence: u32, write: F) -> Publish
705 where
706 F: FnOnce(&mut SlotWriter<'_>),
707 {
708 let Some(publisher) = self.publisher() else {
709 return Publish::Dropped;
710 };
711 publisher.try_publish(self.region, lane, sequence, self.work[lane], write)
712 }
713
714 pub fn try_read<F>(&self, lane: usize, sequence: u32, read: F) -> bool
715 where
716 F: FnOnce(SlotReader<'_>),
717 {
718 self.publisher()
719 .is_some_and(|publisher| publisher.try_read(self.region, lane, sequence, read))
720 }
721
722 pub fn close(&self) {
723 self.region
724 .store32(STATE_I, State::Closing as u32, Ordering::Release);
725 for signal in self.work.iter().flatten() {
726 signal.signal();
727 }
728 }
729}
730
731#[derive(Debug)]
732pub struct SlotWriter<'a> {
733 metadata: &'a [AtomicU32],
734 format: SampleFormat,
735 samples: &'a [AtomicU32],
736 frames: usize,
737 planes: usize,
738}
739
740impl SlotWriter<'_> {
741 pub fn frames(&self) -> usize {
742 self.frames
743 }
744
745 pub fn planes(&self) -> usize {
746 self.planes
747 }
748
749 pub fn write(&mut self, plane: usize, frame: usize, sample: f32) {
750 self.write_f64(plane, frame, f64::from(sample));
751 }
752
753 pub fn write_f64(&mut self, plane: usize, frame: usize, sample: f64) {
754 let index = (plane * self.frames + frame) * self.format.words();
755 if self.format == SampleFormat::F64 {
756 self.samples[index].store((sample.to_bits() as u32).to_le(), Ordering::Relaxed);
757 self.samples[index + 1]
758 .store(((sample.to_bits() >> 32) as u32).to_le(), Ordering::Relaxed);
759 } else {
760 self.samples[index].store((sample as f32).to_bits().to_le(), Ordering::Relaxed);
761 }
762 }
763
764 pub fn write_plane(&mut self, plane: usize, samples: &[f32]) -> bool {
765 if plane >= self.planes || samples.len() != self.frames {
766 return false;
767 }
768 let words = self.format.words();
769 let destination =
770 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
771 if self.format == SampleFormat::F64 {
772 for (to, from) in destination.as_chunks::<2>().0.iter().zip(samples) {
773 let bits = f64::from(*from).to_bits();
774 to[0].store((bits as u32).to_le(), Ordering::Relaxed);
775 to[1].store(((bits >> 32) as u32).to_le(), Ordering::Relaxed);
776 }
777 } else {
778 for (to, from) in destination.iter().zip(samples) {
779 to.store(from.to_bits().to_le(), Ordering::Relaxed);
780 }
781 }
782 true
783 }
784
785 pub fn write_plane_f64(&mut self, plane: usize, samples: &[f64]) -> bool {
786 if plane >= self.planes || samples.len() != self.frames {
787 return false;
788 }
789 let words = self.format.words();
790 let destination =
791 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
792 if self.format == SampleFormat::F64 {
793 for (to, from) in destination.as_chunks::<2>().0.iter().zip(samples) {
794 let bits = from.to_bits();
795 to[0].store((bits as u32).to_le(), Ordering::Relaxed);
796 to[1].store(((bits >> 32) as u32).to_le(), Ordering::Relaxed);
797 }
798 } else {
799 for (to, from) in destination.iter().zip(samples) {
800 to.store((*from as f32).to_bits().to_le(), Ordering::Relaxed);
801 }
802 }
803 true
804 }
805
806 pub fn clear_plane(&mut self, plane: usize) -> bool {
807 if plane >= self.planes {
808 return false;
809 }
810 let words = self.format.words();
811 let destination =
812 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
813 for word in destination {
814 word.store(0, Ordering::Relaxed);
815 }
816 true
817 }
818
819 pub fn set_transport(&mut self, transport: Transport) {
820 for (to, from) in self.metadata.iter().zip(transport.words()) {
821 to.store(from.to_le(), Ordering::Relaxed);
822 }
823 }
824 pub fn set_events(&mut self, events: &[Event]) -> bool {
825 if events.len() > MAX_EVENTS || events.iter().any(|e| !e.valid(self.frames as u32)) {
826 return false;
827 }
828 store_packet(
829 &self.metadata[CONTEXT_WORDS..CONTEXT_WORDS + PACKET_WORDS],
830 events,
831 0,
832 );
833 true
834 }
835 pub fn copy_from(&mut self, samples: &[f32]) -> bool {
836 if self.format != SampleFormat::F32 || samples.len() != self.samples.len() {
837 return false;
838 }
839 for (destination, sample) in self.samples.iter().zip(samples) {
840 destination.store(sample.to_bits(), Ordering::Relaxed);
841 }
842 true
843 }
844}
845
846#[derive(Clone, Copy, Debug)]
847pub struct SlotReader<'a> {
848 metadata: &'a [AtomicU32],
849 format: SampleFormat,
850 samples: &'a [AtomicU32],
851 frames: usize,
852 planes: usize,
853}
854
855impl SlotReader<'_> {
856 pub fn frames(self) -> usize {
857 self.frames
858 }
859
860 pub fn planes(self) -> usize {
861 self.planes
862 }
863
864 pub fn read(self, plane: usize, frame: usize) -> f32 {
865 self.read_f64(plane, frame) as f32
866 }
867
868 pub fn read_f64(self, plane: usize, frame: usize) -> f64 {
869 let index = (plane * self.frames + frame) * self.format.words();
870 let lo = u32::from_le(self.samples[index].load(Ordering::Relaxed));
871 if self.format == SampleFormat::F64 {
872 let hi = u32::from_le(self.samples[index + 1].load(Ordering::Relaxed));
873 f64::from_bits(u64::from(lo) | (u64::from(hi) << 32))
874 } else {
875 f64::from(f32::from_bits(lo))
876 }
877 }
878
879 pub fn read_plane(self, plane: usize, samples: &mut [f32]) -> bool {
880 if plane >= self.planes || samples.len() != self.frames {
881 return false;
882 }
883 let words = self.format.words();
884 let source = &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
885 if self.format == SampleFormat::F64 {
886 for (to, from) in samples.iter_mut().zip(source.as_chunks::<2>().0.iter()) {
887 let lo = u32::from_le(from[0].load(Ordering::Relaxed));
888 let hi = u32::from_le(from[1].load(Ordering::Relaxed));
889 *to = f64::from_bits(u64::from(lo) | (u64::from(hi) << 32)) as f32;
890 }
891 } else {
892 for (to, from) in samples.iter_mut().zip(source) {
893 *to = f32::from_bits(u32::from_le(from.load(Ordering::Relaxed)));
894 }
895 }
896 true
897 }
898
899 pub fn read_plane_f64(self, plane: usize, samples: &mut [f64]) -> bool {
900 if plane >= self.planes || samples.len() != self.frames {
901 return false;
902 }
903 let words = self.format.words();
904 let source = &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
905 if self.format == SampleFormat::F64 {
906 for (to, from) in samples.iter_mut().zip(source.as_chunks::<2>().0.iter()) {
907 let lo = u32::from_le(from[0].load(Ordering::Relaxed));
908 let hi = u32::from_le(from[1].load(Ordering::Relaxed));
909 *to = f64::from_bits(u64::from(lo) | (u64::from(hi) << 32));
910 }
911 } else {
912 for (to, from) in samples.iter_mut().zip(source) {
913 *to = f64::from(f32::from_bits(u32::from_le(from.load(Ordering::Relaxed))));
914 }
915 }
916 true
917 }
918
919 pub fn output_events(self, events: &mut EventBuffer) -> bool {
920 load_packet(
921 &self.metadata[CONTEXT_WORDS + PACKET_WORDS..],
922 self.frames as u32,
923 events,
924 )
925 }
926 pub fn copy_to(self, samples: &mut [f32]) -> bool {
927 if self.format != SampleFormat::F32 || samples.len() != self.samples.len() {
928 return false;
929 }
930 for (destination, sample) in samples.iter_mut().zip(self.samples) {
931 *destination = f32::from_bits(sample.load(Ordering::Relaxed));
932 }
933 true
934 }
935}
936
937fn store_packet(words: &[AtomicU32], events: &[Event], dropped: u32) {
938 words[0].store((events.len() as u32).to_le(), Ordering::Relaxed);
939 words[1].store(dropped.to_le(), Ordering::Relaxed);
940 for (target, event) in words[2..].as_chunks::<EVENT_WORDS>().0.iter().zip(events) {
941 for (to, from) in target.iter().zip(event.words()) {
942 to.store(from.to_le(), Ordering::Relaxed);
943 }
944 }
945}
946fn load_packet(words: &[AtomicU32], frames: u32, events: &mut EventBuffer) -> bool {
947 events.clear();
948 let count = u32::from_le(words[0].load(Ordering::Relaxed)) as usize;
949 if count > MAX_EVENTS {
950 return false;
951 }
952 events.dropped = u32::from_le(words[1].load(Ordering::Relaxed));
953 for source in words[2..].as_chunks::<EVENT_WORDS>().0.iter().take(count) {
954 let mut values = [0; EVENT_WORDS];
955 for (to, from) in values.iter_mut().zip(source) {
956 *to = u32::from_le(from.load(Ordering::Relaxed));
957 }
958 let Some(event) = Event::from_words(&values, frames) else {
959 events.clear();
960 return false;
961 };
962 events.push(event);
963 }
964 true
965}
966
967pub struct Consumer<'a> {
968 region: Region<'a>,
969 geometry: Option<Geometry>,
970 layout: Option<Layout>,
971 ready: &'a dyn Signal,
972 done: [Option<&'a dyn Signal>; 2],
973}
974
975impl fmt::Debug for Consumer<'_> {
976 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
977 formatter
978 .debug_struct("Consumer")
979 .field("region", &self.region)
980 .field("geometry", &self.geometry)
981 .field("layout", &self.layout)
982 .finish_non_exhaustive()
983 }
984}
985
986impl<'a> Consumer<'a> {
987 pub fn new(
988 region: Region<'a>,
989 ready: &'a dyn Signal,
990 done: [Option<&'a dyn Signal>; 2],
991 ) -> Self {
992 Self {
993 region,
994 geometry: None,
995 layout: None,
996 ready,
997 done,
998 }
999 }
1000
1001 pub fn validate_and_ready(&mut self, consumer_pid: u32) -> Result<Geometry, HeaderError> {
1002 let geometry = validate_header(self.region)?;
1003 let layout = layout(geometry).expect("validated geometry has a layout");
1004 self.geometry = Some(geometry);
1005 self.layout = Some(layout);
1006 self.region
1007 .store32(CONSUMER_PID_I, consumer_pid.to_le(), Ordering::Relaxed);
1008 self.region
1009 .store32(FAULT_I, Fault::None as u32, Ordering::Relaxed);
1010 self.region
1011 .store32(STATE_I, State::Ready as u32, Ordering::Release);
1012 self.ready.signal();
1013 Ok(geometry)
1014 }
1015
1016 pub fn fault(&self, code: u32) {
1017 self.region.store32(FAULT_I, code, Ordering::Relaxed);
1018 self.region
1019 .store32(STATE_I, State::Fault as u32, Ordering::Release);
1020 self.ready.signal();
1021 for signal in self.done.iter().flatten() {
1022 signal.signal();
1023 }
1024 }
1025
1026 pub fn next_published(&self, lane: usize) -> Option<u32> {
1027 let geometry = self.geometry?;
1028 if lane >= geometry.lanes as usize || self.region.state() != Some(State::Ready) {
1029 return None;
1030 }
1031 let published = u32::from_le(self.region.load32(SEQUENCE_I + lane, Ordering::Acquire));
1032 let completed = u32::from_le(self.region.load32(COMPLETED_I + lane, Ordering::Acquire));
1033 let next = completed.wrapping_add(1);
1034 (published != completed && sequence_at_least(published, next)).then_some(next)
1035 }
1036
1037 pub fn slot_mut<F>(&self, lane: usize, sequence: u32, buffer: &mut [f32], process: F) -> bool
1040 where
1041 F: FnOnce(&mut [f32]),
1042 {
1043 let Some((geometry, layout)) = self.geometry.zip(self.layout) else {
1044 return false;
1045 };
1046 if geometry.sample_format != SampleFormat::F32
1047 || lane >= geometry.lanes as usize
1048 || buffer.len() != geometry.samples_per_slot()
1049 {
1050 return false;
1051 }
1052 let offset = layout.slot_offset(lane, sequence) / 4;
1053 let samples = &self.region.words32[offset..offset + buffer.len()];
1054 for (destination, sample) in buffer.iter_mut().zip(samples) {
1055 *destination = f32::from_bits(sample.load(Ordering::Relaxed));
1056 }
1057 process(buffer);
1058 for (destination, sample) in samples.iter().zip(buffer) {
1059 destination.store(sample.to_bits(), Ordering::Relaxed);
1060 }
1061 true
1062 }
1063
1064 pub fn slot_mut_f64<F>(
1065 &self,
1066 lane: usize,
1067 sequence: u32,
1068 buffer: &mut [f64],
1069 process: F,
1070 ) -> bool
1071 where
1072 F: FnOnce(&mut [f64]),
1073 {
1074 let Some((geometry, layout)) = self.geometry.zip(self.layout) else {
1075 return false;
1076 };
1077 if geometry.sample_format != SampleFormat::F64
1078 || lane >= geometry.lanes as usize
1079 || buffer.len() != geometry.samples_per_slot()
1080 {
1081 return false;
1082 }
1083 let offset = layout.slot_offset(lane, sequence) / 4;
1084 let samples = &self.region.words32[offset..offset + buffer.len() * 2];
1085 for (to, from) in buffer.iter_mut().zip(samples.as_chunks::<2>().0.iter()) {
1086 *to = f64::from_bits(
1087 u64::from(u32::from_le(from[0].load(Ordering::Relaxed)))
1088 | (u64::from(u32::from_le(from[1].load(Ordering::Relaxed))) << 32),
1089 );
1090 }
1091 process(buffer);
1092 for (to, from) in samples.as_chunks::<2>().0.iter().zip(buffer) {
1093 to[0].store((from.to_bits() as u32).to_le(), Ordering::Relaxed);
1094 to[1].store(((from.to_bits() >> 32) as u32).to_le(), Ordering::Relaxed);
1095 }
1096 true
1097 }
1098 fn metadata(&self, lane: usize, sequence: u32) -> Option<&[AtomicU32]> {
1099 let geometry = self.geometry?;
1100 let layout = self.layout?;
1101 if lane >= geometry.lanes as usize {
1102 return None;
1103 }
1104 let offset = layout.slot_offset(lane, sequence) / 4
1105 + geometry.samples_per_slot() * geometry.sample_format.words();
1106 self.region.words32.get(offset..offset + METADATA_WORDS)
1107 }
1108 pub fn input_packet(
1109 &self,
1110 lane: usize,
1111 sequence: u32,
1112 context: &mut Transport,
1113 events: &mut EventBuffer,
1114 ) -> bool {
1115 let Some(words) = self.metadata(lane, sequence) else {
1116 return false;
1117 };
1118 let mut raw = [0; CONTEXT_WORDS];
1119 for (to, from) in raw.iter_mut().zip(words) {
1120 *to = u32::from_le(from.load(Ordering::Relaxed));
1121 }
1122 let Some(value) = Transport::from_words(&raw) else {
1123 return false;
1124 };
1125 *context = value;
1126 load_packet(
1127 &words[CONTEXT_WORDS..CONTEXT_WORDS + PACKET_WORDS],
1128 self.geometry.expect("validated").max_frames,
1129 events,
1130 )
1131 }
1132 pub fn output_packet(&self, lane: usize, sequence: u32, events: &EventBuffer) -> bool {
1133 let Some(words) = self.metadata(lane, sequence) else {
1134 return false;
1135 };
1136 if events
1137 .as_slice()
1138 .iter()
1139 .any(|e| !e.valid(self.geometry.expect("validated").max_frames))
1140 {
1141 return false;
1142 }
1143 store_packet(
1144 &words[CONTEXT_WORDS + PACKET_WORDS..],
1145 events.as_slice(),
1146 events.dropped,
1147 );
1148 true
1149 }
1150
1151 pub fn complete(&self, lane: usize, sequence: u32) {
1152 self.region
1153 .store32(COMPLETED_I + lane, sequence.to_le(), Ordering::Release);
1154 if let Some(signal) = self.done[lane] {
1155 signal.signal();
1156 }
1157 }
1158
1159 pub fn beat(&self) -> u64 {
1160 self.region
1161 .words64
1162 .first()
1163 .map_or(0, |v| v.fetch_add(1, Ordering::Release).wrapping_add(1))
1164 }
1165
1166 pub fn set_last_error(&self, error: u64) {
1167 if let Some(word) = self.region.words64.get(CACHE_LINE / 8) {
1168 word.store(error, Ordering::Release);
1169 }
1170 }
1171}
1172
1173fn validate_header(region: Region<'_>) -> Result<Geometry, HeaderError> {
1174 if region.words32.len() < HEADER_WORDS || region.words64.len() < COUNTER_WORDS {
1175 return Err(HeaderError::TooShort);
1176 }
1177 if u32::from_le(region.load32(MAGIC_I, Ordering::Relaxed)) != MAGIC {
1178 return Err(HeaderError::Magic);
1179 }
1180 if u32::from_le(region.load32(VERSION_I, Ordering::Relaxed)) != LAYOUT_VERSION {
1181 return Err(HeaderError::Version);
1182 }
1183 if region.state() != Some(State::Announced) {
1184 return Err(HeaderError::State);
1185 }
1186 let geometry = Geometry {
1187 sample_format: match u32::from_le(region.load32(FORMAT_I, Ordering::Relaxed)) {
1188 0 => SampleFormat::F32,
1189 1 => SampleFormat::F64,
1190 _ => return Err(HeaderError::Version),
1191 },
1192 lanes: u32::from_le(region.load32(LANES_I, Ordering::Relaxed)),
1193 slots: u32::from_le(region.load32(SLOTS_I, Ordering::Relaxed)),
1194 input_channels: u32::from_le(region.load32(INPUTS_I, Ordering::Relaxed)),
1195 output_channels: u32::from_le(region.load32(OUTPUTS_I, Ordering::Relaxed)),
1196 max_frames: u32::from_le(region.load32(FRAMES_I, Ordering::Relaxed)),
1197 sample_rate: u32::from_le(region.load32(RATE_I, Ordering::Relaxed)),
1198 latency_bound_frames: u32::from_le(region.load32(LATENCY_I, Ordering::Relaxed)),
1199 }
1200 .validate()
1201 .map_err(HeaderError::Geometry)?;
1202 let expected = layout(geometry).expect("validated geometry has a layout");
1203 if u32::from_le(region.load32(TOTAL_I, Ordering::Relaxed)) as usize != expected.total_bytes
1204 || region.words32.len() * 4 < expected.prefix_bytes
1205 {
1206 return Err(HeaderError::TotalBytes);
1207 }
1208 for lane in 0..geometry.lanes as usize {
1209 let base = LANE_GEOMETRY_I + lane * 3;
1210 let actual = [
1211 u32::from_le(region.load32(base, Ordering::Relaxed)) as usize,
1212 u32::from_le(region.load32(base + 1, Ordering::Relaxed)) as usize,
1213 u32::from_le(region.load32(base + 2, Ordering::Relaxed)) as usize,
1214 ];
1215 if actual
1216 != [
1217 expected.first_slot_offsets[lane],
1218 expected.slot_bytes,
1219 expected.slots as usize,
1220 ]
1221 {
1222 return Err(HeaderError::LaneGeometry);
1223 }
1224 }
1225 Ok(geometry)
1226}
1227
1228pub fn sequence_at_least(value: u32, reference: u32) -> bool {
1233 value.wrapping_sub(reference) as i32 >= 0
1234}
1235
1236#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1237struct PipelineEntry {
1238 sequence: u32,
1239 app_block: u64,
1240 generation: u64,
1241 valid: bool,
1242}
1243
1244#[derive(Debug)]
1249pub struct Pipeline {
1250 entries: [PipelineEntry; MAX_SLOTS as usize],
1251 depth: u32,
1252}
1253
1254impl Default for Pipeline {
1255 fn default() -> Self {
1256 Self::with_depth(1)
1257 }
1258}
1259
1260impl Pipeline {
1261 pub fn new() -> Self {
1262 Self::default()
1263 }
1264
1265 pub fn with_depth(depth: u32) -> Self {
1266 assert!((1..=MAX_SLOTS).contains(&depth));
1267 Self {
1268 entries: array::from_fn(|_| PipelineEntry::default()),
1269 depth,
1270 }
1271 }
1272
1273 pub fn depth(&self) -> u32 {
1274 self.depth
1275 }
1276
1277 pub fn set_depth(&mut self, depth: u32) {
1278 assert!((1..=MAX_SLOTS).contains(&depth));
1279 self.depth = depth;
1280 self.clear();
1281 }
1282
1283 pub fn published(&mut self, sequence: u32, app_block: u64, generation: u64) {
1284 self.entries[sequence as usize % MAX_SLOTS as usize] = PipelineEntry {
1285 sequence,
1286 app_block,
1287 generation,
1288 valid: true,
1289 };
1290 }
1291
1292 pub fn output_sequence(
1295 &mut self,
1296 app_block: u64,
1297 generation: u64,
1298 completed: u32,
1299 ) -> Option<u32> {
1300 let wanted = app_block.checked_sub(u64::from(self.depth))?;
1301 let found = self.entries.iter_mut().find(|entry| {
1302 entry.valid && entry.app_block == wanted && entry.generation == generation
1303 })?;
1304 if sequence_at_least(completed, found.sequence) {
1305 found.valid = false;
1306 Some(found.sequence)
1307 } else {
1308 found.valid = false;
1310 None
1311 }
1312 }
1313
1314 pub fn clear(&mut self) {
1315 self.entries = array::from_fn(|_| PipelineEntry::default());
1316 }
1317}