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