Skip to main content

musli/
fixed.rs

1//! Fixed containers.
2//!
3//! These can be used to store or reference a fixed amount of data, usually on
4//! the stack.
5
6use core::fmt;
7use core::mem::MaybeUninit;
8use core::ops::{Deref, DerefMut};
9use core::ptr;
10
11use crate::Context;
12use crate::alloc::Vec;
13use crate::writer::Writer;
14
15/// A fixed-size bytes storage which keeps track of how much has been
16/// initialized.
17pub struct FixedBytes<const N: usize> {
18    /// Data storage.
19    data: [MaybeUninit<u8>; N],
20    /// How many bytes have been initialized.
21    init: usize,
22}
23
24impl<const N: usize> FixedBytes<N> {
25    /// Construct a new fixed bytes array storage.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// use musli::fixed::FixedBytes;
31    ///
32    /// let mut buffer = FixedBytes::<128>::new();
33    /// assert_eq!(buffer.len(), 0);
34    /// assert!(buffer.is_empty());
35    /// ```
36    #[inline]
37    pub const fn new() -> Self {
38        Self {
39            // SAFETY: MaybeUnint::uninit_array is not stable.
40            data: unsafe { MaybeUninit::<[MaybeUninit<u8>; N]>::uninit().assume_init() },
41            init: 0,
42        }
43    }
44
45    /// Construct a fixed bytes while asserting that the given runtime capacity isn't violated.
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use musli::fixed::FixedBytes;
51    ///
52    /// let buffer = FixedBytes::<128>::with_capacity(64);
53    /// assert_eq!(buffer.len(), 0);
54    /// assert_eq!(buffer.remaining(), 128);
55    /// ```
56    ///
57    /// # Panics
58    ///
59    /// Panics if the requested capacity is larger than `N`.
60    ///
61    /// ```should_panic
62    /// use musli::fixed::FixedBytes;
63    ///
64    /// // This will panic
65    /// let _buffer = FixedBytes::<10>::with_capacity(20);
66    /// ```
67    #[inline]
68    pub fn with_capacity(capacity: usize) -> Self {
69        assert!(
70            capacity < N,
71            "Requested capacity {capacity} is larger than {N}"
72        );
73        Self::new()
74    }
75
76    /// Get the length of the collection.
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// use musli::fixed::FixedBytes;
82    ///
83    /// let mut buffer = FixedBytes::<10>::new();
84    /// assert_eq!(buffer.len(), 0);
85    /// buffer.push(42);
86    /// assert_eq!(buffer.len(), 1);
87    /// ```
88    #[inline]
89    pub const fn len(&self) -> usize {
90        self.init
91    }
92
93    /// Check if the current container is empty.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use musli::fixed::FixedBytes;
99    ///
100    /// let mut buffer = FixedBytes::<10>::new();
101    /// assert!(buffer.is_empty());
102    /// buffer.push(42);
103    /// assert!(!buffer.is_empty());
104    /// ```
105    #[inline]
106    pub const fn is_empty(&self) -> bool {
107        self.init == 0
108    }
109
110    /// Clear the [FixedBytes] container.
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// use musli::fixed::FixedBytes;
116    ///
117    /// let mut buffer = FixedBytes::<10>::new();
118    /// buffer.push(42);
119    /// assert_eq!(buffer.len(), 1);
120    ///
121    /// buffer.clear();
122    /// assert_eq!(buffer.len(), 0);
123    /// assert!(buffer.is_empty());
124    /// ```
125    #[inline]
126    pub fn clear(&mut self) {
127        self.init = 0;
128    }
129
130    /// Get the remaining capacity of the [FixedBytes].
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// use musli::fixed::FixedBytes;
136    ///
137    /// let mut buffer = FixedBytes::<10>::new();
138    /// assert_eq!(buffer.remaining(), 10);
139    ///
140    /// buffer.push(42);
141    /// assert_eq!(buffer.remaining(), 9);
142    /// ```
143    #[inline]
144    pub const fn remaining(&self) -> usize {
145        N.saturating_sub(self.init)
146    }
147
148    /// Coerce into the underlying bytes if all of them have been initialized.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use musli::fixed::FixedBytes;
154    ///
155    /// // Converting a full buffer returns the complete array
156    /// let mut buffer = FixedBytes::<3>::new();
157    /// buffer.extend_from_slice(&[1, 2, 3]);
158    ///
159    /// let bytes = buffer.into_bytes().expect("Buffer should be full");
160    /// assert_eq!(bytes, [1, 2, 3]);
161    ///
162    /// // Partial buffers cannot be converted to arrays
163    /// let partial_buffer = FixedBytes::<3>::new();
164    /// assert_eq!(partial_buffer.into_bytes(), None);
165    /// ```
166    #[inline]
167    pub fn into_bytes(self) -> Option<[u8; N]> {
168        if self.init == N {
169            // SAFETY: All of the bytes in the sequence have been initialized
170            // and can be safety transmuted.
171            //
172            // Method of transmuting comes from the implementation of
173            // `MaybeUninit::array_assume_init` which is not yet stable.
174            unsafe { Some((&self.data as *const _ as *const [u8; N]).read()) }
175        } else {
176            None
177        }
178    }
179
180    /// Coerce into the slice of initialized memory which is present.
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// use musli::fixed::FixedBytes;
186    ///
187    /// let mut buffer = FixedBytes::<10>::new();
188    /// buffer.extend_from_slice(&[1, 2, 3]);
189    ///
190    /// let slice = buffer.as_slice();
191    /// assert_eq!(slice, &[1, 2, 3]);
192    /// ```
193    #[inline]
194    pub fn as_slice(&self) -> &[u8] {
195        if self.init == 0 {
196            return &[];
197        }
198
199        // SAFETY: We've asserted that `initialized` accounts for the number of
200        // bytes that have been initialized.
201        unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.init) }
202    }
203
204    /// Coerce into the mutable slice of initialized memory which is present.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// use musli::fixed::FixedBytes;
210    ///
211    /// let mut buffer = FixedBytes::<10>::new();
212    /// buffer.extend_from_slice(&[1, 2, 3]);
213    ///
214    /// let slice = buffer.as_mut_slice();
215    /// slice[0] = 42;
216    /// assert_eq!(buffer.as_slice(), &[42, 2, 3]);
217    /// ```
218    #[inline]
219    pub fn as_mut_slice(&mut self) -> &mut [u8] {
220        if self.init == 0 {
221            return &mut [];
222        }
223
224        // SAFETY: We've asserted that `initialized` accounts for the number of
225        // bytes that have been initialized.
226        unsafe { core::slice::from_raw_parts_mut(self.data.as_mut_ptr().cast(), self.init) }
227    }
228
229    /// Try and push a single byte.
230    ///
231    /// # Examples
232    ///
233    /// ```
234    /// use musli::fixed::FixedBytes;
235    ///
236    /// let mut buffer = FixedBytes::<3>::new();
237    /// assert!(buffer.push(1));
238    /// assert!(buffer.push(2));
239    /// assert!(buffer.push(3));
240    /// // Buffer is full
241    /// assert!(!buffer.push(4));
242    ///
243    /// assert_eq!(buffer.as_slice(), &[1, 2, 3]);
244    /// ```
245    #[inline]
246    pub fn push(&mut self, value: u8) -> bool {
247        if N.saturating_sub(self.init) == 0 {
248            return false;
249        }
250
251        unsafe {
252            self.data
253                .as_mut_ptr()
254                .cast::<u8>()
255                .add(self.init)
256                .write(value)
257        }
258
259        self.init += 1;
260        true
261    }
262
263    /// Try and extend from the given slice.
264    ///
265    /// # Examples
266    ///
267    /// ```
268    /// use musli::fixed::FixedBytes;
269    ///
270    /// let mut buffer = FixedBytes::<10>::new();
271    /// assert!(buffer.extend_from_slice(&[1, 2, 3]));
272    /// assert!(buffer.extend_from_slice(&[4, 5]));
273    /// // Would exceed capacity
274    /// assert!(!buffer.extend_from_slice(&[6, 7, 8, 9, 10, 11]));
275    ///
276    /// assert_eq!(buffer.as_slice(), &[1, 2, 3, 4, 5]);
277    /// ```
278    #[inline]
279    pub fn extend_from_slice(&mut self, source: &[u8]) -> bool {
280        if source.len() > N.saturating_sub(self.init) {
281            return false;
282        }
283
284        unsafe {
285            let dst = (self.data.as_mut_ptr() as *mut u8).add(self.init);
286            ptr::copy_nonoverlapping(source.as_ptr(), dst, source.len());
287        }
288
289        self.init = self.init.wrapping_add(source.len());
290        true
291    }
292
293    /// Try and extend from the given slice.
294    /// Write bytes to the buffer using a context for error handling.
295    ///
296    /// # Examples
297    ///
298    /// ```
299    /// use musli::context;
300    /// use musli::fixed::FixedBytes;
301    ///
302    /// let cx = context::new();
303    ///
304    /// // Writing a few bytes to a buffer with capacity succeeds
305    /// let mut buffer = FixedBytes::<10>::new();
306    /// buffer.write_bytes(&cx, &[1, 2, 3]).unwrap();
307    /// assert_eq!(buffer.as_slice(), &[1, 2, 3]);
308    ///
309    /// // Writing more data than the buffer capacity fails
310    /// let mut small_buffer = FixedBytes::<2>::new();
311    /// let result = small_buffer.write_bytes(&cx, &[1, 2, 3]);
312    /// assert!(result.is_err());
313    /// ```
314    #[inline]
315    pub fn write_bytes<C>(&mut self, cx: C, source: &[u8]) -> Result<(), C::Error>
316    where
317        C: Context,
318    {
319        if !self.extend_from_slice(source) {
320            return Err(cx.message(FixedBytesOverflow {
321                at: self.init,
322                additional: source.len(),
323                capacity: N,
324            }));
325        }
326
327        Ok(())
328    }
329}
330
331impl<const N: usize> Deref for FixedBytes<N> {
332    type Target = [u8];
333
334    #[inline]
335    fn deref(&self) -> &Self::Target {
336        self.as_slice()
337    }
338}
339
340impl<const N: usize> DerefMut for FixedBytes<N> {
341    #[inline]
342    fn deref_mut(&mut self) -> &mut Self::Target {
343        self.as_mut_slice()
344    }
345}
346
347impl<const N: usize> Default for FixedBytes<N> {
348    #[inline]
349    fn default() -> Self {
350        Self::new()
351    }
352}
353
354impl<const N: usize> Writer for FixedBytes<N> {
355    type Ok = ();
356    type Mut<'this>
357        = &'this mut Self
358    where
359        Self: 'this;
360
361    #[inline]
362    fn finish<C>(&mut self, _: C) -> Result<Self::Ok, C::Error>
363    where
364        C: Context,
365    {
366        Ok(())
367    }
368
369    #[inline]
370    fn borrow_mut(&mut self) -> Self::Mut<'_> {
371        self
372    }
373
374    #[inline]
375    fn extend<C>(&mut self, cx: C, buffer: Vec<u8, C::Allocator>) -> Result<(), C::Error>
376    where
377        C: Context,
378    {
379        // SAFETY: the buffer never outlives this function call.
380        self.write_bytes(cx, buffer.as_slice())
381    }
382
383    #[inline]
384    fn write_bytes<C>(&mut self, cx: C, bytes: &[u8]) -> Result<(), C::Error>
385    where
386        C: Context,
387    {
388        FixedBytes::write_bytes(self, cx, bytes)?;
389        cx.advance(bytes.len());
390        Ok(())
391    }
392}
393
394/// Capacity error raised by trying to write to a [FixedBytes] with no remaining
395/// capacity.
396#[derive(Debug)]
397#[allow(missing_docs)]
398#[non_exhaustive]
399pub(crate) struct FixedBytesOverflow {
400    at: usize,
401    additional: usize,
402    capacity: usize,
403}
404
405impl fmt::Display for FixedBytesOverflow {
406    #[inline]
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        let FixedBytesOverflow {
409            at,
410            additional,
411            capacity,
412        } = self;
413
414        write!(
415            f,
416            "Tried to write {additional} bytes at {at} with capacity {capacity}"
417        )
418    }
419}