Skip to main content

primitive_buffer/
lib.rs

1use std::ops::Deref;
2use std::mem::MaybeUninit;
3use std::fmt::Debug;
4
5/// A stack-based buffer with fixed capacity for primitives.
6/// 
7/// Uses uninitialized memory to avoid the cost of filling the buffer with default values.
8/// Only the first `len` elements are guaranteed to be initialized.
9/// # Examples
10/// ```
11/// # use primitive_buffer::Buffer;
12/// let mut buffer: Buffer<u8, 8> = Buffer::new();
13/// 
14/// buffer.push(1);
15/// buffer.push(2);
16/// 
17/// assert_eq!(buffer.len(), 2);
18/// 
19/// assert_eq!(buffer.pop(), Some(2));
20/// assert_eq!(buffer.len(), 1);
21/// 
22/// buffer.clear();
23/// 
24/// assert!(buffer.is_empty());
25/// ```
26pub struct Buffer<T: Copy, const N: usize> {
27    buffer: [MaybeUninit<T>; N],
28    len: usize
29}
30
31impl<T: Copy, const N: usize> Buffer<T, N> {
32    /// Creates a new empty buffer on the stack with a fixed capacity.
33    /// 
34    /// # Examples
35    /// ```
36    /// # use primitive_buffer::Buffer;
37    /// let mut buf: Buffer<u8, 4> = Buffer::new();
38    /// assert!(buf.is_empty());
39    /// ```
40    /// 
41    /// # Complexity
42    /// `O(1)`
43    #[inline(always)]
44    pub fn new() -> Self {
45        Self { buffer: [MaybeUninit::uninit(); N], len: 0 }
46    }
47    
48    /// Appends an element to the back of the buffer.
49    /// 
50    /// # Examples
51    /// ```
52    /// # use primitive_buffer::Buffer;
53    /// let mut buf: Buffer<u8, 2> = Buffer::new();
54    /// buf.push(10);
55    /// buf.push(20);
56    /// assert_eq!(buf.as_slice(), &[10, 20]);
57    /// ```
58    /// 
59    /// # Panics
60    /// Panics when trying to add an element with full capacity.
61    /// 
62    /// # Complexity
63    /// `O(1)`
64    #[inline(always)]
65    pub fn push(&mut self, item: T) {
66        if self.len >= N {
67            panic!("buffer overflow: capacity {} reached", N)
68        }
69
70        self.buffer[self.len].write(item);
71        self.len += 1;
72    }
73    
74    /// Appends an element to the back of the buffer without checking capacity.
75    /// 
76    /// # Examples
77    /// ```
78    /// # use primitive_buffer::Buffer;
79    /// let mut buf: Buffer<u8, 2> = Buffer::new();
80    /// unsafe {
81    ///     buf.push_unchecked(42);
82    /// }
83    /// assert_eq!(buf.len(), 1);
84    /// ```
85    /// 
86    /// # Safety
87    /// The caller must ensure that the buffer is not full. Calling this method
88    /// when `len() == N` is undefined behavior.
89    /// 
90    /// # Complexity
91    /// `O(1)`
92    #[inline(always)]
93    pub unsafe fn push_unchecked(&mut self, item: T) {
94        debug_assert!(self.len < N, "buffer overflow: capacity {} reached", N);
95        
96        unsafe {
97            self.buffer.get_unchecked_mut(self.len)
98        }.write(item);
99
100        self.len += 1;
101    }
102
103    /// Returns the last element, if there is one, and removes it from the buffer.
104    /// 
105    /// # Examples
106    /// ```
107    /// # use primitive_buffer::Buffer;
108    /// let mut buf: Buffer<u8, 2> = Buffer::new();
109    /// buf.push(10);
110    /// assert_eq!(buf.pop(), Some(10));
111    /// assert_eq!(buf.pop(), None);
112    /// ```
113    /// 
114    /// # Complexity
115    /// `O(1)`
116    #[inline(always)]
117    pub fn pop(&mut self) -> Option<T> {
118        if self.len > 0 {
119            self.len -= 1;
120            Some(unsafe {
121                self.buffer[self.len].assume_init()
122            })
123        } else {
124            None
125        }
126    }
127
128    /// Removes and returns the last element from the buffer without checking if it is empty.
129    /// 
130    /// # Examples
131    /// ```
132    /// # use primitive_buffer::Buffer;
133    /// let mut buf: Buffer<u8, 2> = Buffer::new();
134    /// buf.push(5);
135    /// unsafe {
136    ///     assert_eq!(buf.pop_unchecked(), 5);
137    /// }
138    /// ```
139    /// 
140    /// # Safety
141    /// The caller must ensure that the buffer is not empty. Calling this method
142    /// when `is_empty()` is true is undefined behavior.
143    /// 
144    /// # Complexity
145    /// `O(1)`
146    #[inline(always)]
147    pub unsafe fn pop_unchecked(&mut self) -> T {
148        debug_assert!(self.len > 0, "buffer is empty");
149        
150        self.len -= 1;
151        unsafe { self.buffer.get_unchecked(self.len).assume_init() }
152    }
153    
154    /// Clears the buffer.
155    /// 
156    /// # Examples
157    /// ```
158    /// # use primitive_buffer::Buffer;
159    /// let mut buf: Buffer<u8, 3> = Buffer::new();
160    /// buf.push(1);
161    /// buf.clear();
162    /// assert!(buf.is_empty());
163    /// ```
164    /// 
165    /// # Complexity
166    /// `O(1)`
167    #[inline(always)]
168    pub fn clear(&mut self) {
169        self.len = 0;
170    }
171
172    /// Returns the buffer as a slice.
173    /// 
174    /// # Examples
175    /// ```
176    /// # use primitive_buffer::Buffer;
177    /// let mut buf: Buffer<u8, 3> = Buffer::new();
178    /// buf.push(1);
179    /// buf.push(2);
180    /// assert_eq!(buf.as_slice(), &[1, 2]);
181    /// ```
182    /// 
183    /// # Complexity
184    /// `O(1)`
185    #[inline(always)]
186    pub fn as_slice(&self) -> &[T] {
187        unsafe {
188            let arr_ptr: *const T = self.buffer.as_ptr() as *const T;
189            std::slice::from_raw_parts(arr_ptr, self.len)
190        }
191    }
192    
193    /// Checks if the buffer is empty.
194    /// 
195    /// # Examples
196    /// ```
197    /// # use primitive_buffer::Buffer;
198    /// let mut buf: Buffer<u8, 2> = Buffer::new();
199    /// assert!(buf.is_empty());
200    /// buf.push(1);
201    /// assert!(!buf.is_empty());
202    /// ```
203    /// 
204    /// # Complexity
205    /// `O(1)`
206    #[inline(always)]
207    pub fn is_empty(&self) -> bool {
208        self.len == 0
209    }
210
211    /// Returns `true` if the buffer is full.
212    /// 
213    /// # Examples
214    /// ```
215    /// # use primitive_buffer::Buffer;
216    /// let mut buf: Buffer<u8, 1> = Buffer::new();
217    /// assert!(!buf.is_full());
218    /// buf.push(10);
219    /// assert!(buf.is_full());
220    /// ```
221    /// 
222    /// # Complexity
223    /// `O(1)`
224    #[inline(always)]
225    pub fn is_full(&self) -> bool {
226        self.len == N
227    }
228
229    /// Returns the length of the buffer.
230    /// 
231    /// # Examples
232    /// ```
233    /// # use primitive_buffer::Buffer;
234    /// let mut buf: Buffer<u8, 5> = Buffer::new();
235    /// assert_eq!(buf.len(), 0);
236    /// buf.push(10);
237    /// assert_eq!(buf.len(), 1);
238    /// ```
239    /// 
240    /// # Complexity
241    /// `O(1)`
242    #[inline(always)]
243    pub fn len(&self) -> usize {
244        self.len
245    }
246
247    /// Returns the total capacity of the buffer.
248    /// 
249    /// # Examples
250    /// ```
251    /// # use primitive_buffer::Buffer;
252    /// let buf: Buffer<u8, 5> = Buffer::new();
253    /// assert_eq!(buf.capacity(), 5);
254    /// ```
255    /// 
256    /// # Complexity
257    /// `O(1)`
258    #[inline(always)]
259    pub fn capacity(&self) -> usize {
260        N
261    }
262}
263
264impl<T: Copy, const N: usize> Deref for Buffer<T, N> {
265    type Target = [T];
266    
267    fn deref(&self) -> &Self::Target {
268        self.as_slice()
269    }
270}
271
272impl<T: Copy + Debug, const N: usize> Debug for Buffer<T, N> {
273    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274        f.debug_list().entries(self.as_slice()).finish()
275    }
276}
277
278/// Creates a [`Buffer`] containing the given arguments.
279///
280/// `buf!` allows shorthand initialization of a buffer. It supports two syntax 
281/// variants:
282/// 
283/// 1. Specifying the capacity explicitly as a macro argument.
284/// 2. Allowing the capacity to be inferred by the compiler.
285///
286/// # Examples
287///
288/// Creating a buffer with an explicit fixed capacity via the macro argument:
289///
290/// ```
291/// # use primitive_buffer::{Buffer, buf};
292/// let b = buf![10; 1, 2];
293/// ```
294///
295/// Creating a buffer where the capacity and type are inferred from the context:
296///
297/// ```
298/// # use primitive_buffer::{Buffer, buf};
299/// let b: Buffer<i32, 5> = buf![1, 2, 3];
300/// assert_eq!(b.len(), 3);
301/// ```
302///
303/// # Panics
304///
305/// This macro will panic at runtime if the number of passed elements exceeds the 
306/// allocated capacity.
307#[macro_export]
308macro_rules! buf {
309    ($cap:expr; $($element:expr),*) => {
310        {
311            let mut buffer = Buffer::<_, $cap>::new();
312            
313            $(
314                buffer.push($element);
315            )*
316            
317            buffer
318        }
319    };
320
321    ($($element:expr),*) => {
322        {
323            let mut buffer = Buffer::new();
324            
325            $(
326                buffer.push($element);
327            )*
328            
329            buffer
330        }
331    };
332}