odem_rs_sync/channel/
backing.rs

1//! This module provides various ring buffer implementations that can be used
2//! as backing storage for message-passing channels in discrete-event
3//! simulations.
4//!
5//! ## Buffer Types
6//!
7//! - **[`Rendezvous`]**: A zero-capacity buffer that enforces direct
8//!   synchronization between sender and receiver, requiring both parties to be
9//!   ready before a message is exchanged.
10//! - **[`ArrayBuf`]**: A fixed-size, stack-allocated circular buffer with a
11//!   predefined capacity, supporting efficient enqueue and dequeue operations.
12//! - **[`VecDeque`]** *(requires `alloc` feature)*: A dynamically growing
13//!   buffer using Rust's `VecDeque`, providing flexible storage with amortized
14//!   constant-time operations.
15//! - **[`BinaryHeap`]** *(requires `alloc` feature)*: A priority queue
16//!   implementation for message passing with ordered retrieval of elements
17//!   based on priority.
18//!
19//! [`VecDeque`]: alloc::collections::VecDeque
20//! [`BinaryHeap`]: alloc::collections::BinaryHeap
21
22use core::{fmt, marker::PhantomData, mem::MaybeUninit};
23
24use super::RingBuf;
25
26/* ******************************************************** Rendezvous Buffer */
27
28/// A buffer that provides an empty backing store for a channel.
29///
30/// This leads to a direct synchronization between sender and receiver before
31/// messages are exchanged.
32pub struct Rendezvous<T>(PhantomData<T>);
33
34impl<T> Rendezvous<T> {
35	/// Creates a new rendezvouz buffer.
36	pub const fn new() -> Self {
37		Rendezvous(PhantomData)
38	}
39}
40
41impl<T> Default for Rendezvous<T> {
42	fn default() -> Self {
43		Rendezvous::new()
44	}
45}
46
47impl<T> fmt::Debug for Rendezvous<T> {
48	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49		write!(f, "Rendezvous")
50	}
51}
52
53impl<T> RingBuf for Rendezvous<T> {
54	type Item = T;
55
56	fn enqueue(&mut self, item: T) -> Result<(), T> {
57		Err(item)
58	}
59
60	fn dequeue(&mut self) -> Option<T> {
61		None
62	}
63
64	fn clear(&mut self) {}
65
66	fn len(&self) -> usize {
67		0
68	}
69}
70
71/* ************************************************** Fixed Size Array Buffer */
72
73/// A fixed-size array buffer that can be allocated on the stack and used as a
74/// backing store for channels.
75pub struct ArrayBuf<const N: usize, T> {
76	/// Index of the first item stored.
77	off: usize,
78	/// Number of elements currently held.
79	len: usize,
80	/// Pre-allocated space for the stored items.
81	buf: [MaybeUninit<T>; N],
82}
83
84impl<const N: usize, T> ArrayBuf<N, T> {
85	/// Creates a new, empty array buffer with a user-defined - but fixed -
86	/// capacity.
87	pub const fn new() -> Self {
88		ArrayBuf {
89			off: 0,
90			len: 0,
91			buf: [const { MaybeUninit::uninit() }; N],
92		}
93	}
94}
95
96impl<const N: usize, T> Default for ArrayBuf<N, T> {
97	fn default() -> Self {
98		ArrayBuf::new()
99	}
100}
101
102impl<const N: usize, T> Drop for ArrayBuf<N, T> {
103	fn drop(&mut self) {
104		for i in self.off..self.off + self.len {
105			unsafe { self.buf.get_unchecked_mut(i % N).assume_init_drop() }
106		}
107	}
108}
109
110impl<const N: usize, T> RingBuf for ArrayBuf<N, T> {
111	type Item = T;
112
113	fn enqueue(&mut self, item: T) -> Result<(), T> {
114		// test if there is still space in the buffer
115		if self.len < N {
116			// calculate the prospective index
117			let idx = (self.off + self.len) % N;
118
119			// fill the slot with the item
120			// SAFETY: there is an empty slot due to the fulfilled condition
121			// above, and it has the calculated index due to the values of
122			// `off` and `len`
123			unsafe { self.buf.get_unchecked_mut(idx).write(item) };
124
125			// increase the number of elements
126			self.len += 1;
127
128			// return success
129			Ok(())
130		} else {
131			// not enough space, so return the item
132			Err(item)
133		}
134	}
135
136	fn dequeue(&mut self) -> Option<T> {
137		// test if there are still elements in the buffer
138		if self.len > 0 {
139			// read the first element
140			// SAFETY: the item exists due to the condition fulfilled above,
141			// it has the calculated index due to the value of `off`
142			let item = unsafe { self.buf.get_unchecked_mut(self.off).assume_init_read() };
143
144			// increase the offset and wrap it around
145			self.off = (self.off + 1) % N;
146
147			// decrease the number of stored items
148			self.len -= 1;
149
150			// return the item
151			Some(item)
152		} else {
153			// no elements: return `None`
154			None
155		}
156	}
157
158	fn clear(&mut self) {
159		while self.len > 0 {
160			// read the first element
161			// SAFETY: the item exists due to the condition fulfilled above,
162			// it has the calculated index due to the value of `off`
163			unsafe { self.buf.get_unchecked_mut(self.off).assume_init_read() };
164
165			// increase the offset and wrap it around
166			self.off = (self.off + 1) % N;
167
168			// decrease the number of stored items
169			self.len -= 1;
170		}
171	}
172
173	fn len(&self) -> usize {
174		self.len
175	}
176}
177
178/* ********************************************* Variably Sized Vector Buffer */
179
180#[cfg(feature = "alloc")]
181#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
182impl<T> RingBuf for alloc::collections::VecDeque<T> {
183	type Item = T;
184
185	fn enqueue(&mut self, item: T) -> Result<(), T> {
186		self.push_back(item);
187		Ok(())
188	}
189
190	fn dequeue(&mut self) -> Option<T> {
191		Self::pop_front(self)
192	}
193
194	fn clear(&mut self) {
195		Self::clear(self);
196	}
197
198	fn len(&self) -> usize {
199		self.len()
200	}
201}
202
203/* **************************************************** Priority Queue Buffer */
204
205#[cfg(feature = "alloc")]
206#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
207impl<T: Ord> RingBuf for alloc::collections::BinaryHeap<T> {
208	type Item = T;
209
210	fn enqueue(&mut self, item: T) -> Result<(), T> {
211		self.push(item);
212		Ok(())
213	}
214
215	fn dequeue(&mut self) -> Option<T> {
216		self.pop()
217	}
218
219	fn clear(&mut self) {
220		self.clear();
221	}
222
223	fn len(&self) -> usize {
224		self.len()
225	}
226}