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