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