Skip to main content

oxigdal_embedded/
buffer.rs

1//! Fixed-size buffer implementations for embedded systems
2
3use crate::error::{EmbeddedError, Result};
4use core::mem::MaybeUninit;
5
6/// Fixed-size buffer with compile-time capacity
7pub struct FixedBuffer<T, const N: usize> {
8    data: [MaybeUninit<T>; N],
9    len: usize,
10}
11
12impl<T, const N: usize> FixedBuffer<T, N> {
13    /// Create a new empty buffer
14    pub const fn new() -> Self {
15        Self {
16            // SAFETY: MaybeUninit array doesn't require initialization
17            data: unsafe { MaybeUninit::uninit().assume_init() },
18            len: 0,
19        }
20    }
21
22    /// Get the capacity of the buffer
23    pub const fn capacity(&self) -> usize {
24        N
25    }
26
27    /// Get the current length
28    pub const fn len(&self) -> usize {
29        self.len
30    }
31
32    /// Check if buffer is empty
33    pub const fn is_empty(&self) -> bool {
34        self.len == 0
35    }
36
37    /// Check if buffer is full
38    pub const fn is_full(&self) -> bool {
39        self.len >= N
40    }
41
42    /// Push an item to the buffer
43    ///
44    /// # Errors
45    ///
46    /// Returns error if buffer is full
47    pub fn push(&mut self, item: T) -> Result<()> {
48        if self.is_full() {
49            return Err(EmbeddedError::BufferTooSmall {
50                required: 1,
51                available: 0,
52            });
53        }
54
55        self.data[self.len].write(item);
56        self.len += 1;
57        Ok(())
58    }
59
60    /// Pop an item from the buffer
61    ///
62    /// # Errors
63    ///
64    /// Returns error if buffer is empty
65    pub fn pop(&mut self) -> Result<T> {
66        if self.is_empty() {
67            return Err(EmbeddedError::InvalidParameter);
68        }
69
70        self.len -= 1;
71        // SAFETY: We just verified len > 0
72        let item = unsafe { self.data[self.len].assume_init_read() };
73        Ok(item)
74    }
75
76    /// Get a reference to an item at index
77    ///
78    /// # Errors
79    ///
80    /// Returns error if index is out of bounds
81    pub fn get(&self, index: usize) -> Result<&T> {
82        if index >= self.len {
83            return Err(EmbeddedError::OutOfBounds {
84                index,
85                max: self.len.saturating_sub(1),
86            });
87        }
88
89        // SAFETY: We verified index is within bounds and initialized
90        let item = unsafe { self.data[index].assume_init_ref() };
91        Ok(item)
92    }
93
94    /// Get a mutable reference to an item at index
95    ///
96    /// # Errors
97    ///
98    /// Returns error if index is out of bounds
99    pub fn get_mut(&mut self, index: usize) -> Result<&mut T> {
100        if index >= self.len {
101            return Err(EmbeddedError::OutOfBounds {
102                index,
103                max: self.len.saturating_sub(1),
104            });
105        }
106
107        // SAFETY: We verified index is within bounds and initialized
108        let item = unsafe { self.data[index].assume_init_mut() };
109        Ok(item)
110    }
111
112    /// Clear the buffer
113    pub fn clear(&mut self) {
114        // Drop all initialized elements
115        for i in 0..self.len {
116            // SAFETY: Elements up to len are initialized
117            unsafe {
118                self.data[i].assume_init_drop();
119            }
120        }
121        self.len = 0;
122    }
123
124    /// Get a slice of the buffer contents
125    pub fn as_slice(&self) -> &[T] {
126        // SAFETY: Elements up to len are initialized
127        unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.len) }
128    }
129
130    /// Get a mutable slice of the buffer contents
131    pub fn as_mut_slice(&mut self) -> &mut [T] {
132        // SAFETY: Elements up to len are initialized
133        unsafe { core::slice::from_raw_parts_mut(self.data.as_mut_ptr().cast(), self.len) }
134    }
135
136    /// Extend buffer from slice
137    ///
138    /// # Errors
139    ///
140    /// Returns error if not enough space
141    pub fn extend_from_slice(&mut self, items: &[T]) -> Result<()>
142    where
143        T: Copy,
144    {
145        if self.len + items.len() > N {
146            return Err(EmbeddedError::BufferTooSmall {
147                required: items.len(),
148                available: N - self.len,
149            });
150        }
151
152        for item in items {
153            self.data[self.len].write(*item);
154            self.len += 1;
155        }
156
157        Ok(())
158    }
159}
160
161impl<T, const N: usize> Default for FixedBuffer<T, N> {
162    fn default() -> Self {
163        Self::new()
164    }
165}
166
167impl<T, const N: usize> Drop for FixedBuffer<T, N> {
168    fn drop(&mut self) {
169        self.clear();
170    }
171}
172
173/// Ring buffer for streaming data
174pub struct RingBuffer<T: Copy, const N: usize> {
175    data: [T; N],
176    read_pos: usize,
177    write_pos: usize,
178    full: bool,
179}
180
181impl<T: Copy, const N: usize> RingBuffer<T, N> {
182    /// Create a new ring buffer with default-initialized data
183    pub fn new(default: T) -> Self {
184        Self {
185            data: [default; N],
186            read_pos: 0,
187            write_pos: 0,
188            full: false,
189        }
190    }
191
192    /// Get the capacity
193    pub const fn capacity(&self) -> usize {
194        N
195    }
196
197    /// Get the number of items in the buffer
198    pub fn len(&self) -> usize {
199        if self.full {
200            N
201        } else if self.write_pos >= self.read_pos {
202            self.write_pos - self.read_pos
203        } else {
204            N - self.read_pos + self.write_pos
205        }
206    }
207
208    /// Check if buffer is empty
209    pub fn is_empty(&self) -> bool {
210        !self.full && self.read_pos == self.write_pos
211    }
212
213    /// Check if buffer is full
214    pub const fn is_full(&self) -> bool {
215        self.full
216    }
217
218    /// Write an item to the buffer
219    ///
220    /// # Errors
221    ///
222    /// Returns error if buffer is full
223    pub fn write(&mut self, item: T) -> Result<()> {
224        if self.full {
225            return Err(EmbeddedError::BufferTooSmall {
226                required: 1,
227                available: 0,
228            });
229        }
230
231        self.data[self.write_pos] = item;
232        self.write_pos = (self.write_pos + 1) % N;
233
234        if self.write_pos == self.read_pos {
235            self.full = true;
236        }
237
238        Ok(())
239    }
240
241    /// Read an item from the buffer
242    ///
243    /// # Errors
244    ///
245    /// Returns error if buffer is empty
246    pub fn read(&mut self) -> Result<T> {
247        if self.is_empty() {
248            return Err(EmbeddedError::InvalidParameter);
249        }
250
251        let item = self.data[self.read_pos];
252        self.read_pos = (self.read_pos + 1) % N;
253        self.full = false;
254
255        Ok(item)
256    }
257
258    /// Peek at the next item without removing it
259    pub fn peek(&self) -> Result<T> {
260        if self.is_empty() {
261            return Err(EmbeddedError::InvalidParameter);
262        }
263
264        Ok(self.data[self.read_pos])
265    }
266
267    /// Clear the buffer
268    pub fn clear(&mut self) {
269        self.read_pos = 0;
270        self.write_pos = 0;
271        self.full = false;
272    }
273}
274
275/// Aligned buffer for DMA or hardware access
276#[repr(C, align(64))]
277pub struct AlignedBuffer<const N: usize> {
278    data: [u8; N],
279}
280
281impl<const N: usize> AlignedBuffer<N> {
282    /// Create a new aligned buffer
283    pub const fn new() -> Self {
284        Self { data: [0u8; N] }
285    }
286
287    /// Get a slice of the buffer
288    pub fn as_slice(&self) -> &[u8] {
289        &self.data
290    }
291
292    /// Get a mutable slice of the buffer
293    pub fn as_mut_slice(&mut self) -> &mut [u8] {
294        &mut self.data
295    }
296
297    /// Get the buffer pointer
298    pub fn as_ptr(&self) -> *const u8 {
299        self.data.as_ptr()
300    }
301
302    /// Get the mutable buffer pointer
303    pub fn as_mut_ptr(&mut self) -> *mut u8 {
304        self.data.as_mut_ptr()
305    }
306
307    /// Get the alignment of the buffer
308    pub const fn alignment(&self) -> usize {
309        64
310    }
311
312    /// Verify the buffer is properly aligned
313    pub fn verify_alignment(&self) -> bool {
314        self.as_ptr() as usize % 64 == 0
315    }
316}
317
318impl<const N: usize> Default for AlignedBuffer<N> {
319    fn default() -> Self {
320        Self::new()
321    }
322}
323
324#[cfg(test)]
325mod tests {
326    use super::*;
327
328    #[test]
329    fn test_fixed_buffer() {
330        let mut buffer = FixedBuffer::<u32, 8>::new();
331        assert_eq!(buffer.len(), 0);
332        assert!(buffer.is_empty());
333
334        buffer.push(1).expect("push failed");
335        buffer.push(2).expect("push failed");
336        assert_eq!(buffer.len(), 2);
337
338        assert_eq!(*buffer.get(0).expect("get failed"), 1);
339        assert_eq!(*buffer.get(1).expect("get failed"), 2);
340
341        let item = buffer.pop().expect("pop failed");
342        assert_eq!(item, 2);
343        assert_eq!(buffer.len(), 1);
344    }
345
346    #[test]
347    fn test_fixed_buffer_overflow() {
348        let mut buffer = FixedBuffer::<u32, 2>::new();
349        buffer.push(1).expect("push failed");
350        buffer.push(2).expect("push failed");
351
352        let result = buffer.push(3);
353        assert!(matches!(result, Err(EmbeddedError::BufferTooSmall { .. })));
354    }
355
356    #[test]
357    fn test_ring_buffer() {
358        let mut buffer = RingBuffer::<u32, 4>::new(0);
359        assert!(buffer.is_empty());
360
361        buffer.write(1).expect("write failed");
362        buffer.write(2).expect("write failed");
363        assert_eq!(buffer.len(), 2);
364
365        let item = buffer.read().expect("read failed");
366        assert_eq!(item, 1);
367
368        buffer.write(3).expect("write failed");
369        buffer.write(4).expect("write failed");
370        buffer.write(5).expect("write failed");
371
372        assert!(buffer.is_full());
373        assert!(buffer.write(6).is_err());
374    }
375
376    #[test]
377    fn test_aligned_buffer() {
378        let buffer = AlignedBuffer::<256>::new();
379        assert_eq!(buffer.alignment(), 64);
380        assert!(buffer.verify_alignment());
381    }
382}