Skip to main content

prefix_arena/
staging_buffer.rs

1use crate::{ArenaView, PrefixArena};
2use core::fmt;
3use core::mem::MaybeUninit;
4
5#[inline(always)]
6const unsafe fn bytes_as_uninit(slice: &[u8]) -> &[MaybeUninit<u8>] {
7    unsafe { core::mem::transmute::<&[u8], &[MaybeUninit<u8>]>(slice) }
8}
9
10#[inline(always)]
11const unsafe fn uninit_as_bytes(slice: &[MaybeUninit<u8>]) -> &[u8] {
12    unsafe { core::mem::transmute::<&[MaybeUninit<u8>], &[u8]>(slice) }
13}
14
15#[inline(always)]
16const unsafe fn uninit_as_bytes_mut(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] {
17    unsafe { core::mem::transmute::<&mut [MaybeUninit<u8>], &mut [u8]>(slice) }
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct StagingBufferError;
22
23impl fmt::Display for StagingBufferError {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        f.write_str("staging buffer capacity exceeded")
26    }
27}
28
29/// A staging buffer over the remaining bytes of a [`PrefixArena`].
30///
31/// `StagingBuffer` lets callers append bytes into temporary arena-backed
32/// storage, inspect the written prefix, and finally detach that written prefix
33/// from the underlying arena.
34pub struct StagingBuffer<'arena, 'b> {
35    inner: ArenaView<'arena, 'b>,
36    used: usize,
37}
38
39impl<'arena, 'b> StagingBuffer<'arena, 'b>
40where
41    'b: 'arena,
42{
43    /// Creates a staging buffer over the remaining space of the given arena.
44    pub const fn new(arena: &'arena mut PrefixArena<'b>) -> Self {
45        Self {
46            inner: arena.view(),
47            used: 0,
48        }
49    }
50
51    /// Appends one byte to the written prefix.
52    ///
53    /// Returns `Ok(())` on success and `Err(())` if no capacity remains.
54    pub fn push_byte(&mut self, byte: u8) -> Result<(), StagingBufferError> {
55        if self.used >= self.inner.len() {
56            return Err(StagingBufferError);
57        }
58        self.inner.as_slice_mut()[self.used].write(byte);
59        self.used += 1;
60        Ok(())
61    }
62
63    /// Appends the entire slice to the written prefix.
64    ///
65    /// Returns `Ok(())` on success and `Err(())` if the full slice does not fit.
66    pub fn extend_from_slice(&mut self, slice: &[u8]) -> Result<(), StagingBufferError> {
67        if self.used + slice.len() > self.inner.len() {
68            return Err(StagingBufferError);
69        }
70        self.inner.as_slice_mut()[self.used..self.used + slice.len()]
71            .copy_from_slice(unsafe { bytes_as_uninit(slice) });
72        self.used += slice.len();
73        Ok(())
74    }
75
76    /// Appends as much of the slice as fits and returns the number of bytes written.
77    pub fn extend_from_slice_capped(&mut self, slice: &[u8]) -> usize {
78        let to_fill = core::cmp::min(self.spare_capacity(), slice.len());
79        self.inner.as_slice_mut()[self.used..self.used + to_fill]
80            .copy_from_slice(unsafe { bytes_as_uninit(&slice[..to_fill]) });
81        self.used += to_fill;
82        to_fill
83    }
84
85    /// Returns the written prefix as an immutable slice.
86    pub fn written(&self) -> &[u8] {
87        unsafe { uninit_as_bytes(&self.inner.as_slice()[..self.used]) }
88    }
89
90    /// Returns the written prefix as a mutable slice.
91    pub fn written_mut(&mut self) -> &mut [u8] {
92        unsafe { uninit_as_bytes_mut(&mut self.inner.as_slice_mut()[..self.used]) }
93    }
94
95    /// Returns the number of bytes currently stored in the buffer.
96    pub const fn len(&self) -> usize {
97        self.used
98    }
99
100    /// Returns `true` when no bytes have been written.
101    pub const fn is_empty(&self) -> bool {
102        self.used == 0
103    }
104
105    /// Returns the total capacity of the buffer.
106    pub const fn capacity(&self) -> usize {
107        self.inner.len()
108    }
109
110    /// Returns how many bytes can still be appended without overflowing.
111    pub const fn spare_capacity(&self) -> usize {
112        self.inner.len() - self.used
113    }
114
115    /// Marks the buffer as empty without modifying the underlying bytes.
116    pub fn clear(&mut self) {
117        self.used = 0;
118    }
119
120    /// Detaches the written prefix from the underlying arena.
121    pub fn into_written_slice(self) -> &'b mut [u8] {
122        unsafe { uninit_as_bytes_mut(self.inner.take_prefix(self.used)) }
123    }
124}
125#[cfg(test)]
126pub mod tests {
127    use super::*;
128    use crate::PrefixArena as HeadArena;
129
130    #[test]
131    fn test_basic_push() {
132        let mut buffer = [0u8; 5];
133        let mut allocator = HeadArena::new(&mut buffer);
134        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
135
136        assert_eq!(borrowed_buffer.len(), 0);
137        assert_eq!(borrowed_buffer.capacity(), 5);
138        assert_eq!(borrowed_buffer.spare_capacity(), 5);
139
140        borrowed_buffer.push_byte(42).unwrap();
141        assert_eq!(borrowed_buffer.len(), 1);
142        assert_eq!(borrowed_buffer.written(), &[42]);
143        assert_eq!(borrowed_buffer.spare_capacity(), 4);
144    }
145
146    #[test]
147    fn test_push_until_full() {
148        let mut buffer = [0u8; 3];
149        let mut allocator = HeadArena::new(&mut buffer);
150        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
151
152        borrowed_buffer.push_byte(1).unwrap();
153        borrowed_buffer.push_byte(2).unwrap();
154        borrowed_buffer.push_byte(3).unwrap();
155
156        assert_eq!(borrowed_buffer.written(), &[1, 2, 3]);
157        assert_eq!(borrowed_buffer.spare_capacity(), 0);
158        assert!(borrowed_buffer.push_byte(4).is_err());
159    }
160
161    #[test]
162    fn test_clear() {
163        let mut buffer = [0u8; 5];
164        let mut allocator = HeadArena::new(&mut buffer);
165        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
166
167        borrowed_buffer.extend_from_slice(&[1, 2, 3]).unwrap();
168        assert_eq!(borrowed_buffer.len(), 3);
169
170        borrowed_buffer.clear();
171        assert_eq!(borrowed_buffer.len(), 0);
172        assert_eq!(borrowed_buffer.spare_capacity(), 5);
173        assert_eq!(borrowed_buffer.written(), &[]);
174    }
175
176    #[test]
177    fn test_as_mut_slice() {
178        let mut buffer = [0u8; 5];
179        let mut allocator = HeadArena::new(&mut buffer);
180        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
181
182        borrowed_buffer.extend_from_slice(&[1, 2, 3]).unwrap();
183
184        let mut_slice = borrowed_buffer.written_mut();
185        mut_slice[1] = 99;
186
187        assert_eq!(borrowed_buffer.written(), &[1, 99, 3]);
188    }
189
190    #[test]
191    fn test_empty_buffer() {
192        let mut buffer = [0u8; 0];
193        let mut allocator = HeadArena::new(&mut buffer);
194        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
195
196        assert_eq!(borrowed_buffer.len(), 0);
197        assert_eq!(borrowed_buffer.capacity(), 0);
198        assert_eq!(borrowed_buffer.spare_capacity(), 0);
199        assert!(borrowed_buffer.push_byte(1).is_err());
200        assert!(borrowed_buffer.extend_from_slice(&[1]).is_err());
201        assert_eq!(borrowed_buffer.written(), &[]);
202    }
203
204    #[test]
205    fn test_single_byte_buffer() {
206        let mut buffer = [0u8; 1];
207        let mut allocator = HeadArena::new(&mut buffer);
208        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
209
210        assert_eq!(borrowed_buffer.capacity(), 1);
211        assert_eq!(borrowed_buffer.spare_capacity(), 1);
212
213        borrowed_buffer.push_byte(42).unwrap();
214        assert_eq!(borrowed_buffer.written(), &[42]);
215        assert_eq!(borrowed_buffer.spare_capacity(), 0);
216
217        assert!(borrowed_buffer.push_byte(1).is_err());
218        assert!(borrowed_buffer.extend_from_slice(&[1]).is_err());
219    }
220
221    #[test]
222    fn test_clear_and_reuse() {
223        let mut buffer = [0u8; 5];
224        let mut allocator = HeadArena::new(&mut buffer);
225        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
226
227        borrowed_buffer.extend_from_slice(&[1, 2, 3]).unwrap();
228        assert_eq!(borrowed_buffer.len(), 3);
229
230        borrowed_buffer.clear();
231        borrowed_buffer.extend_from_slice(&[4, 5, 6, 7, 8]).unwrap();
232        assert_eq!(borrowed_buffer.written(), &[4, 5, 6, 7, 8]);
233        assert_eq!(borrowed_buffer.spare_capacity(), 0);
234    }
235
236    #[test]
237    fn test_extend_at_exact_capacity() {
238        let mut buffer = [0u8; 3];
239        let mut allocator = HeadArena::new(&mut buffer);
240        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
241
242        assert!(borrowed_buffer.extend_from_slice(&[1, 2, 3]).is_ok());
243        assert_eq!(borrowed_buffer.len(), 3);
244        assert_eq!(borrowed_buffer.spare_capacity(), 0);
245
246        assert!(borrowed_buffer.extend_from_slice(&[]).is_ok());
247        assert_eq!(borrowed_buffer.len(), 3);
248    }
249
250    #[test]
251    fn test_take_used() {
252        let mut buffer = [0u8; 5];
253        let mut allocator = HeadArena::new(&mut buffer);
254        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
255
256        borrowed_buffer.extend_from_slice(&[10, 20, 30]).unwrap();
257        let used = borrowed_buffer.into_written_slice();
258
259        assert_eq!(used, &[10, 20, 30]);
260        used[0] = 255;
261        assert_eq!(used, &[255, 20, 30]);
262    }
263
264    #[test]
265    fn test_extend_empty_slice() {
266        let mut buffer = [0u8; 5];
267        let mut allocator = HeadArena::new(&mut buffer);
268        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
269
270        borrowed_buffer.extend_from_slice(&[]).unwrap();
271        assert_eq!(borrowed_buffer.len(), 0);
272        assert_eq!(borrowed_buffer.written(), &[]);
273    }
274
275    #[test]
276    fn test_capacity_overflow() {
277        let mut buffer = [0u8; 2];
278        let mut allocator = HeadArena::new(&mut buffer);
279        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
280
281        assert!(borrowed_buffer.extend_from_slice(&[1, 2, 3]).is_err());
282        assert_eq!(borrowed_buffer.len(), 0);
283
284        borrowed_buffer.extend_from_slice(&[1, 2]).unwrap();
285        assert!(borrowed_buffer.push_byte(3).is_err());
286        assert_eq!(borrowed_buffer.written(), &[1, 2]);
287    }
288
289    #[test]
290    fn test_multiple_extension() {
291        let mut buffer = [0u8; 10];
292        let mut allocator = HeadArena::new(&mut buffer);
293        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
294
295        borrowed_buffer.extend_from_slice(&[1, 2, 3]).unwrap();
296        assert_eq!(borrowed_buffer.written(), &[1, 2, 3]);
297
298        borrowed_buffer.extend_from_slice(&[4, 5]).unwrap();
299        assert_eq!(borrowed_buffer.written(), &[1, 2, 3, 4, 5]);
300
301        borrowed_buffer
302            .extend_from_slice(&[6, 7, 8, 9, 10])
303            .unwrap();
304        assert_eq!(borrowed_buffer.written(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
305
306        assert!(borrowed_buffer.extend_from_slice(&[11]).is_err());
307    }
308
309    #[test]
310    fn test_append_from_slice() {
311        let mut buffer = [0u8; 5];
312        let mut allocator = HeadArena::new(&mut buffer);
313        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
314
315        // Append with data larger than remaining capacity
316        let filled = borrowed_buffer.extend_from_slice_capped(&[1, 2, 3, 4, 5, 6, 7]);
317        assert_eq!(filled, 5);
318        assert_eq!(borrowed_buffer.written(), &[1, 2, 3, 4, 5]);
319        assert_eq!(borrowed_buffer.spare_capacity(), 0);
320
321        // Try to append when buffer is full
322        let filled = borrowed_buffer.extend_from_slice_capped(&[8, 9]);
323        assert_eq!(filled, 0);
324        assert_eq!(borrowed_buffer.written(), &[1, 2, 3, 4, 5]);
325    }
326
327    #[test]
328    fn test_append_from_slice_partial() {
329        let mut buffer = [0u8; 10];
330        let mut allocator = HeadArena::new(&mut buffer);
331        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
332
333        // Add some data first
334        borrowed_buffer.extend_from_slice(&[1, 2, 3]).unwrap();
335        assert_eq!(borrowed_buffer.spare_capacity(), 7);
336
337        // Append with smaller slice
338        let filled = borrowed_buffer.extend_from_slice_capped(&[4, 5]);
339        assert_eq!(filled, 2);
340        assert_eq!(borrowed_buffer.written(), &[1, 2, 3, 4, 5]);
341        assert_eq!(borrowed_buffer.spare_capacity(), 5);
342
343        // Append remaining with exact size
344        let filled = borrowed_buffer.extend_from_slice_capped(&[6, 7, 8, 9, 10]);
345        assert_eq!(filled, 5);
346        assert_eq!(borrowed_buffer.written(), &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
347        assert_eq!(borrowed_buffer.spare_capacity(), 0);
348    }
349
350    #[test]
351    fn test_fill_remaining_empty_slice() {
352        let mut buffer = [0u8; 5];
353        let mut allocator = HeadArena::new(&mut buffer);
354        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
355
356        let filled = borrowed_buffer.extend_from_slice_capped(&[]);
357        assert_eq!(filled, 0);
358        assert_eq!(borrowed_buffer.len(), 0);
359    }
360
361    #[test]
362    fn test_as_slice_lifetime() {
363        let mut buffer = [1, 2, 3, 4, 5];
364        let mut allocator = HeadArena::new(&mut buffer);
365        let mut borrowed_buffer = StagingBuffer::new(&mut allocator);
366        borrowed_buffer.extend_from_slice(&[1, 2, 3]).unwrap();
367
368        let slice1 = borrowed_buffer.written();
369        let slice2 = borrowed_buffer.written();
370
371        assert_eq!(slice1, slice2);
372        assert_eq!(slice1, &[1, 2, 3]);
373    }
374}