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::alloc::Vec;
12use crate::writer::Writer;
13use crate::Context;
14
15/// An error raised when we are at capacity.
16#[non_exhaustive]
17pub(crate) struct CapacityError;
18
19/// A fixed-size bytes storage which keeps track of how much has been
20/// initialized.
21pub struct FixedBytes<const N: usize> {
22    /// Data storage.
23    data: [MaybeUninit<u8>; N],
24    /// How many bytes have been initialized.
25    init: usize,
26}
27
28impl<const N: usize> FixedBytes<N> {
29    /// Construct a new fixed bytes array storage.
30    #[inline]
31    pub const fn new() -> Self {
32        Self {
33            // SAFETY: MaybeUnint::uninit_array is not stable.
34            data: unsafe { MaybeUninit::<[MaybeUninit<u8>; N]>::uninit().assume_init() },
35            init: 0,
36        }
37    }
38
39    /// Construct a fixed bytes while asserting that the given runtime capacity isn't violated.
40    pub fn with_capacity(capacity: usize) -> Self {
41        assert!(
42            capacity < N,
43            "Requested capacity {capacity} is larger than {N}"
44        );
45        Self::new()
46    }
47
48    /// Get the length of the collection.
49    #[inline]
50    pub const fn len(&self) -> usize {
51        self.init
52    }
53
54    /// Test if the current container is empty.
55    #[inline]
56    pub const fn is_empty(&self) -> bool {
57        self.init == 0
58    }
59
60    /// Clear the [FixedBytes] container.
61    #[inline]
62    pub fn clear(&mut self) {
63        self.init = 0;
64    }
65
66    /// Get the remaining capacity of the [FixedBytes].
67    #[inline]
68    pub const fn remaining(&self) -> usize {
69        N.saturating_sub(self.init)
70    }
71
72    /// Coerce into the underlying bytes if all of them have been initialized.
73    #[inline]
74    pub fn into_bytes(self) -> Option<[u8; N]> {
75        if self.init == N {
76            // SAFETY: All of the bytes in the sequence have been initialized
77            // and can be safety transmuted.
78            //
79            // Method of transmuting comes from the implementation of
80            // `MaybeUninit::array_assume_init` which is not yet stable.
81            unsafe { Some((&self.data as *const _ as *const [u8; N]).read()) }
82        } else {
83            None
84        }
85    }
86
87    /// Coerce into the slice of initialized memory which is present.
88    #[inline]
89    pub fn as_slice(&self) -> &[u8] {
90        if self.init == 0 {
91            return &[];
92        }
93
94        // SAFETY: We've asserted that `initialized` accounts for the number of
95        // bytes that have been initialized.
96        unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast(), self.init) }
97    }
98
99    /// Coerce into the mutable slice of initialized memory which is present.
100    #[inline]
101    pub fn as_mut_slice(&mut self) -> &mut [u8] {
102        if self.init == 0 {
103            return &mut [];
104        }
105
106        // SAFETY: We've asserted that `initialized` accounts for the number of
107        // bytes that have been initialized.
108        unsafe { core::slice::from_raw_parts_mut(self.data.as_mut_ptr().cast(), self.init) }
109    }
110
111    /// Try and push a single byte.
112    #[inline]
113    pub fn push(&mut self, value: u8) -> bool {
114        if N.saturating_sub(self.init) == 0 {
115            return false;
116        }
117
118        unsafe {
119            self.data
120                .as_mut_ptr()
121                .cast::<u8>()
122                .add(self.init)
123                .write(value)
124        }
125
126        self.init += 1;
127        true
128    }
129
130    /// Try and extend from the given slice.
131    #[inline]
132    pub fn extend_from_slice(&mut self, source: &[u8]) -> bool {
133        if source.len() > N.saturating_sub(self.init) {
134            return false;
135        }
136
137        unsafe {
138            let dst = (self.data.as_mut_ptr() as *mut u8).add(self.init);
139            ptr::copy_nonoverlapping(source.as_ptr(), dst, source.len());
140        }
141
142        self.init = self.init.wrapping_add(source.len());
143        true
144    }
145
146    /// Try and extend from the given slice.
147    #[inline]
148    pub fn write_bytes<C>(&mut self, cx: &C, source: &[u8]) -> Result<(), C::Error>
149    where
150        C: ?Sized + Context,
151    {
152        if !self.extend_from_slice(source) {
153            return Err(cx.message(FixedBytesOverflow {
154                at: self.init,
155                additional: source.len(),
156                capacity: N,
157            }));
158        }
159
160        Ok(())
161    }
162}
163
164impl<const N: usize> Deref for FixedBytes<N> {
165    type Target = [u8];
166
167    #[inline]
168    fn deref(&self) -> &Self::Target {
169        self.as_slice()
170    }
171}
172
173impl<const N: usize> DerefMut for FixedBytes<N> {
174    #[inline]
175    fn deref_mut(&mut self) -> &mut Self::Target {
176        self.as_mut_slice()
177    }
178}
179
180impl<const N: usize> Default for FixedBytes<N> {
181    #[inline]
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187impl<const N: usize> Writer for FixedBytes<N> {
188    type Mut<'this> = &'this mut Self where Self: 'this;
189
190    #[inline]
191    fn borrow_mut(&mut self) -> Self::Mut<'_> {
192        self
193    }
194
195    #[inline]
196    fn extend<C>(&mut self, cx: &C, buffer: Vec<'_, u8, C::Allocator>) -> Result<(), C::Error>
197    where
198        C: ?Sized + Context,
199    {
200        // SAFETY: the buffer never outlives this function call.
201        self.write_bytes(cx, buffer.as_slice())
202    }
203
204    #[inline]
205    fn write_bytes<C>(&mut self, cx: &C, bytes: &[u8]) -> Result<(), C::Error>
206    where
207        C: ?Sized + Context,
208    {
209        FixedBytes::write_bytes(self, cx, bytes)?;
210        cx.advance(bytes.len());
211        Ok(())
212    }
213}
214
215/// Capacity error raised by trying to write to a [FixedBytes] with no remaining
216/// capacity.
217#[derive(Debug)]
218#[allow(missing_docs)]
219#[non_exhaustive]
220pub(crate) struct FixedBytesOverflow {
221    at: usize,
222    additional: usize,
223    capacity: usize,
224}
225
226impl fmt::Display for FixedBytesOverflow {
227    #[inline]
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        let FixedBytesOverflow {
230            at,
231            additional,
232            capacity,
233        } = self;
234
235        write!(
236            f,
237            "Tried to write {additional} bytes at {at} with capacity {capacity}"
238        )
239    }
240}