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