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}