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}