structbuf/
lib.rs

1//! A capacity-limited structured data buffer.
2//!
3//! This library provides [`StructBuf`] - a capacity-limited buffer for encoding
4//! and decoding structured data. The primary use case is for safely handling
5//! small, variable-length message packets sent over the network or other
6//! transports.
7//!
8//! The encoder ensures that the message size never exceeds a pre-configured
9//! limit. The decoder ensures that malformed or malicious input does not cause
10//! the program to panic.
11//!
12//! Little-endian encoding is assumed.
13//!
14//! ## `no_std` support
15//!
16//! `structbuf` is `no_std` by default.
17//!
18//! # Example
19//!
20//! ```
21//! # use structbuf::{Pack, StructBuf, Unpack};
22//! let mut b = StructBuf::new(4);
23//! b.append().u8(1).u16(2_u16).u8(3);
24//! // b.u8(4); Would panic
25//!
26//! let mut p = b.unpack();
27//! assert_eq!(p.u8(), 1);
28//! assert_eq!(p.u16(), 2);
29//! assert_eq!(p.u8(), 3);
30//! assert!(p.is_ok());
31//!
32//! assert_eq!(p.u32(), 0);
33//! assert!(!p.is_ok());
34//! ```
35
36#![no_std]
37#![warn(missing_debug_implementations)]
38#![warn(non_ascii_idents)]
39#![warn(single_use_lifetimes)]
40#![warn(unused_extern_crates)]
41#![warn(unused_import_braces)]
42#![warn(unused_lifetimes)]
43#![warn(unused_qualifications)]
44#![warn(variant_size_differences)]
45#![warn(clippy::cargo)]
46#![warn(clippy::nursery)]
47#![warn(clippy::pedantic)]
48#![allow(clippy::inline_always)]
49// #![warn(clippy::restriction)]
50#![warn(clippy::assertions_on_result_states)]
51#![warn(clippy::clone_on_ref_ptr)]
52#![warn(clippy::dbg_macro)]
53#![warn(clippy::decimal_literal_representation)]
54#![warn(clippy::default_union_representation)]
55#![warn(clippy::deref_by_slicing)]
56#![warn(clippy::empty_drop)]
57#![warn(clippy::empty_structs_with_brackets)]
58#![warn(clippy::exhaustive_enums)]
59#![warn(clippy::exit)]
60#![warn(clippy::fn_to_numeric_cast_any)]
61#![warn(clippy::format_push_string)]
62#![warn(clippy::get_unwrap)]
63#![warn(clippy::if_then_some_else_none)]
64#![warn(clippy::lossy_float_literal)]
65#![warn(clippy::missing_enforced_import_renames)]
66#![warn(clippy::mixed_read_write_in_expression)]
67#![warn(clippy::mod_module_files)]
68#![warn(clippy::mutex_atomic)]
69#![warn(clippy::pattern_type_mismatch)]
70#![warn(clippy::print_stdout)]
71#![warn(clippy::rc_buffer)]
72#![warn(clippy::rc_mutex)]
73#![warn(clippy::rest_pat_in_fully_bound_structs)]
74#![warn(clippy::str_to_string)]
75#![warn(clippy::string_add)]
76#![warn(clippy::string_to_string)]
77#![warn(clippy::suspicious_xor_used_as_pow)]
78#![warn(clippy::todo)]
79#![warn(clippy::try_err)]
80#![warn(clippy::undocumented_unsafe_blocks)]
81#![warn(clippy::unnecessary_safety_comment)]
82#![warn(clippy::unnecessary_safety_doc)]
83#![warn(clippy::unnecessary_self_imports)]
84#![warn(clippy::unneeded_field_pattern)]
85#![warn(clippy::unseparated_literal_suffix)]
86
87use core::ops::{Deref, DerefMut};
88use core::{mem, ptr, slice};
89
90use smallvec::SmallVec;
91
92/// Inline capacity selected to keep [`StructBuf`] within one cache line in most
93/// use cases. On x64, this adds two additional machine words over the minimum
94/// [`StructBuf`] size.
95const INLINE_CAP: usize = 32;
96
97/// Trait for getting a packer for a byte buffer.
98pub trait Pack {
99    /// Returns a packer for appending values to the end of the buffer.
100    #[must_use]
101    fn append(&mut self) -> Packer;
102
103    /// Returns a packer for writing values starting at index `i`. This always
104    /// succeeds, even if `i` is out of bounds.
105    #[must_use]
106    fn at(&mut self, i: usize) -> Packer;
107}
108
109/// Trait for getting an unpacker for a byte slice.
110pub trait Unpack {
111    /// Returns an unpacker for reading values from the start of the buffer.
112    fn unpack(&self) -> Unpacker;
113}
114
115/// Blanket implementation for all `AsRef<[u8]>` types.
116impl<T: AsRef<[u8]>> Unpack for T {
117    #[inline(always)]
118    fn unpack(&self) -> Unpacker {
119        Unpacker::new(self.as_ref())
120    }
121}
122
123/// A capacity-limited buffer for encoding and decoding structured data.
124///
125/// The buffer starts out with a small internal capacity that does not require
126/// allocation. Once the internal capacity is exhausted, it performs at most one
127/// heap allocation up to the capacity limit. The relationship
128/// `len() <= capacity() <= lim()` always holds.
129///
130/// # Panics
131///
132/// It panics if any write operation exceeds the capacity limit.
133#[derive(Clone, Debug, Default)]
134#[must_use]
135pub struct StructBuf {
136    lim: usize,
137    b: SmallVec<[u8; INLINE_CAP]>,
138}
139
140impl StructBuf {
141    /// Creates a new capacity-limited buffer without allocating from the heap.
142    /// This should be used for creating outbound messages that may not require
143    /// the full capacity.
144    #[inline(always)]
145    pub const fn new(lim: usize) -> Self {
146        Self {
147            lim,
148            b: SmallVec::new_const(),
149        }
150    }
151
152    /// Creates an empty buffer that can never be written to. This is the same
153    /// as [`StructBuf::default()`], but usable in `const` context.
154    #[inline(always)]
155    pub const fn none() -> Self {
156        Self::new(0)
157    }
158
159    /// Creates a pre-allocated capacity-limited buffer. This buffer will never
160    /// reallocate and should be used for receiving messages up to the capacity
161    /// limit.
162    #[inline(always)]
163    pub fn with_capacity(cap: usize) -> Self {
164        Self {
165            lim: cap,
166            b: SmallVec::with_capacity(cap),
167        }
168    }
169
170    /// Returns the number of initialized bytes in the buffer (`<= capacity()`).
171    #[inline(always)]
172    #[must_use]
173    pub fn len(&self) -> usize {
174        self.b.len()
175    }
176
177    /// Returns the number of bytes allocated by the buffer (`<= lim()`).
178    #[inline(always)]
179    #[must_use]
180    pub fn capacity(&self) -> usize {
181        self.b.capacity().min(self.lim)
182    }
183
184    /// Returns the buffer capacity limit.
185    #[inline(always)]
186    #[must_use]
187    pub const fn lim(&self) -> usize {
188        self.lim
189    }
190
191    /// Returns the number of additional bytes that can be written to the
192    /// buffer.
193    #[inline(always)]
194    #[must_use]
195    pub fn remaining(&self) -> usize {
196        self.lim - self.b.len()
197    }
198
199    /// Returns whether the buffer is empty.
200    #[inline(always)]
201    #[must_use]
202    pub fn is_empty(&self) -> bool {
203        self.b.is_empty()
204    }
205
206    /// Returns whether the buffer contains the maximum number of bytes.
207    #[inline(always)]
208    #[must_use]
209    pub fn is_full(&self) -> bool {
210        self.remaining() == 0
211    }
212
213    /// Returns whether the buffer has a limit of 0 and can never be written to.
214    #[inline(always)]
215    #[must_use]
216    pub const fn is_none(&self) -> bool {
217        self.lim == 0
218    }
219
220    /// Removes all but the first `n` bytes from the buffer without affecting
221    /// capacity. It has no effect if `self.len() <= n`.
222    #[inline]
223    pub fn truncate(&mut self, n: usize) -> &mut Self {
224        if n < self.b.len() {
225            // SAFETY: n is a valid length and there is nothing to drop
226            unsafe { self.b.set_len(n) }
227        }
228        self
229    }
230
231    /// Clears the buffer, resetting its length to 0 without affecting capacity.
232    #[inline(always)]
233    pub fn clear(&mut self) -> &mut Self {
234        self.truncate(0)
235    }
236
237    /// Returns the buffer, leaving `self` empty and unwritable.
238    #[inline(always)]
239    pub fn take(&mut self) -> Self {
240        mem::replace(self, Self::none())
241    }
242
243    /// Sets the buffer length.
244    ///
245    /// # Safety
246    ///
247    /// Caller must ensure that the buffer contains `n` initialized bytes, which
248    /// must be `<= capacity()`.
249    #[inline]
250    pub unsafe fn set_len(&mut self, n: usize) -> &mut Self {
251        debug_assert!(n <= self.capacity());
252        self.b.set_len(n);
253        self
254    }
255
256    /// Sets the buffer limit. Any existing data past the limit is truncated.
257    #[inline]
258    pub fn set_lim(&mut self, n: usize) -> &mut Self {
259        self.lim = n;
260        self.truncate(n)
261    }
262
263    /// Returns whether `n` bytes can be written to the buffer at index `i`.
264    #[inline]
265    #[must_use]
266    pub const fn can_put_at(&self, i: usize, n: usize) -> bool {
267        let (sum, overflow) = i.overflowing_add(n);
268        sum <= self.lim && !overflow
269    }
270
271    /// Writes slice `v` at index `i`. Any existing data at `i` is overwritten.
272    /// If `len() < i`, the buffer is padded with `i - len()` zeros.
273    ///
274    /// # Panics
275    ///
276    /// Panics if `lim() < i + v.len()`.
277    ///
278    /// # Examples
279    ///
280    /// ```
281    /// # use structbuf::StructBuf;
282    /// let mut b = StructBuf::new(4);
283    /// b.put_at(1, [1]);
284    /// assert_eq!(b.as_ref(), &[0, 1]);
285    ///
286    /// b.put_at(4, []);
287    /// assert_eq!(b.as_ref(), &[0, 1, 0, 0]);
288    /// ```
289    #[inline(always)]
290    pub fn put_at<T: AsRef<[u8]>>(&mut self, i: usize, v: T) {
291        assert!(self.try_put_at(i, v), "buffer limit exceeded");
292    }
293
294    /// Writes slice `v` at index `i` if `i + v.len() <= lim()`. Any existing
295    /// data at `i` is overwritten. If `len() < i`, the buffer is padded with
296    /// `i - len()` zeros. Returns whether `v` was written.
297    #[inline]
298    pub fn try_put_at<T: AsRef<[u8]>>(&mut self, i: usize, v: T) -> bool {
299        let v = v.as_ref();
300        let (j, overflow) = i.overflowing_add(v.len());
301        let ok = !overflow && j <= self.lim;
302        if ok {
303            // SAFETY: i + v.len() == j <= self.lim
304            unsafe { self.put_at_unchecked(i, j, v) };
305        }
306        ok
307    }
308
309    /// Writes slice `v` at index `i`.
310    ///
311    /// # Safety
312    ///
313    /// Caller must ensure that `i + v.len() == j <= self.lim`.
314    unsafe fn put_at_unchecked(&mut self, i: usize, j: usize, v: &[u8]) {
315        if self.b.capacity() < j {
316            self.b.grow(self.lim); // TODO: Limit growth for a large lim?
317        }
318        let pad = i.saturating_sub(self.b.len());
319        let dst = self.b.as_mut_ptr();
320        if pad > 0 {
321            // SAFETY: `len() + pad == i <= j` and dst is valid for at least j
322            // bytes.
323            unsafe { dst.add(self.b.len()).write_bytes(0, pad) };
324        }
325        // SAFETY: `&mut self` prevents v from referencing the buffer
326        unsafe { dst.add(i).copy_from_nonoverlapping(v.as_ptr(), v.len()) };
327        if j > self.b.len() {
328            // SAFETY: self.b contains j initialized bytes
329            unsafe { self.b.set_len(j) };
330        }
331    }
332}
333
334impl Pack for StructBuf {
335    #[inline(always)]
336    fn append(&mut self) -> Packer {
337        self.at(self.b.len())
338    }
339
340    #[inline(always)]
341    fn at(&mut self, i: usize) -> Packer {
342        Packer { i, b: self }
343    }
344}
345
346impl AsRef<[u8]> for StructBuf {
347    #[inline(always)]
348    fn as_ref(&self) -> &[u8] {
349        &self.b
350    }
351}
352
353impl AsMut<[u8]> for StructBuf {
354    #[inline(always)]
355    fn as_mut(&mut self) -> &mut [u8] {
356        &mut self.b
357    }
358}
359
360impl Deref for StructBuf {
361    type Target = [u8];
362
363    #[inline(always)]
364    fn deref(&self) -> &Self::Target {
365        &self.b
366    }
367}
368
369impl DerefMut for StructBuf {
370    #[inline(always)]
371    fn deref_mut(&mut self) -> &mut Self::Target {
372        &mut self.b
373    }
374}
375
376/// Packer of POD values into a [`StructBuf`].
377///
378/// The packer maintains an index (`0 <= i <= lim()`) where new values are
379/// written, which is incremented after each write. Write operations panic if
380/// the buffer capacity limit is exceeded.
381///
382/// The packer intentionally does not expose the underlying [`StructBuf`] to
383/// guarantee append-only operation.
384///
385/// Little-endian encoding is assumed.
386#[derive(Debug)]
387pub struct Packer<'a> {
388    i: usize,
389    b: &'a mut StructBuf, // TODO: Make generic?
390}
391
392impl Packer<'_> {
393    /// Returns the current index.
394    #[inline(always)]
395    #[must_use]
396    pub const fn position(&self) -> usize {
397        self.i
398    }
399
400    /// Returns the number of additional bytes that can be written.
401    #[inline]
402    #[must_use]
403    pub fn remaining(&self) -> usize {
404        self.b.lim.saturating_sub(self.i)
405    }
406
407    /// Advances the current index by `n` bytes.
408    #[inline(always)]
409    pub fn skip(&mut self, n: usize) -> &mut Self {
410        self.i += n;
411        self
412    }
413
414    /// Writes a `bool` as a `u8` at the current index.
415    #[inline(always)]
416    pub fn bool<T: Into<bool>>(&mut self, v: T) -> &mut Self {
417        self.u8(v.into())
418    }
419
420    /// Writes a `u8` at the current index.
421    #[inline(always)]
422    pub fn u8<T: Into<u8>>(&mut self, v: T) -> &mut Self {
423        self.put([v.into()])
424    }
425
426    /// Writes a `u16` at the current index.
427    #[inline(always)]
428    pub fn u16<T: Into<u16>>(&mut self, v: T) -> &mut Self {
429        self.put(v.into().to_le_bytes())
430    }
431
432    /// Writes a `u32` as a `u24` at the current index.
433    ///
434    /// # Panics
435    ///
436    /// Panics if `v` cannot be represented in 24 bits.
437    #[inline(always)]
438    pub fn u24<T: Into<u32>>(&mut self, v: T) -> &mut Self {
439        let v = v.into().to_le_bytes();
440        assert_eq!(v[3], 0);
441        self.put(&v[..3])
442    }
443
444    /// Writes a `u32` at the current index.
445    #[inline(always)]
446    pub fn u32<T: Into<u32>>(&mut self, v: T) -> &mut Self {
447        self.put(v.into().to_le_bytes())
448    }
449
450    /// Writes a `u64` at the current index.
451    #[inline(always)]
452    pub fn u64<T: Into<u64>>(&mut self, v: T) -> &mut Self {
453        self.put(v.into().to_le_bytes())
454    }
455
456    /// Writes a `u128` at the current index.
457    #[inline(always)]
458    pub fn u128<T: Into<u128>>(&mut self, v: T) -> &mut Self {
459        self.put(v.into().to_le_bytes())
460    }
461
462    /// Writes an `i8` at the current index.
463    #[inline(always)]
464    pub fn i8<T: Into<i8>>(&mut self, v: T) -> &mut Self {
465        #[allow(clippy::cast_sign_loss)]
466        self.put([v.into() as u8])
467    }
468
469    /// Writes an `i16` at the current index.
470    #[inline(always)]
471    pub fn i16<T: Into<i16>>(&mut self, v: T) -> &mut Self {
472        self.put(v.into().to_le_bytes())
473    }
474
475    /// Writes an `i32` at the current index.
476    #[inline(always)]
477    pub fn i32<T: Into<i32>>(&mut self, v: T) -> &mut Self {
478        self.put(v.into().to_le_bytes())
479    }
480
481    /// Writes an `i64` at the current index.
482    #[inline(always)]
483    pub fn i64<T: Into<i64>>(&mut self, v: T) -> &mut Self {
484        self.put(v.into().to_le_bytes())
485    }
486
487    /// Writes an `i128` at the current index.
488    #[inline(always)]
489    pub fn i128<T: Into<i128>>(&mut self, v: T) -> &mut Self {
490        self.put(v.into().to_le_bytes())
491    }
492
493    /// Returns whether `n` bytes can be written at the current index.
494    #[inline(always)]
495    #[must_use]
496    pub const fn can_put(&self, n: usize) -> bool {
497        self.b.can_put_at(self.i, n)
498    }
499
500    /// Writes `v` at the current index.
501    #[inline]
502    pub fn put<T: AsRef<[u8]>>(&mut self, v: T) -> &mut Self {
503        let v = v.as_ref();
504        self.b.put_at(self.i, v);
505        self.i += v.len();
506        self
507    }
508}
509
510impl AsRef<[u8]> for Packer<'_> {
511    #[inline]
512    fn as_ref(&self) -> &[u8] {
513        // SAFETY: Index is guaranteed to be valid
514        unsafe { self.b.get_unchecked(self.b.len().min(self.i)..) }
515    }
516}
517
518impl AsMut<[u8]> for Packer<'_> {
519    #[inline]
520    fn as_mut(&mut self) -> &mut [u8] {
521        let i = self.b.len().min(self.i);
522        // SAFETY: Index is guaranteed to be valid
523        unsafe { self.b.get_unchecked_mut(i..) }
524    }
525}
526
527/// Allows terminating packer method calls with `.into()`.
528impl From<&mut Packer<'_>> for () {
529    #[inline(always)]
530    fn from(_: &mut Packer) -> Self {}
531}
532
533/// Unpacker of POD values from a byte slice.
534///
535/// Any reads past the end of the slice return default values rather than
536/// panicking. The caller must check the error status at the end to determine
537/// whether all returned values were valid.
538///
539/// Little-endian encoding is assumed.
540#[allow(single_use_lifetimes)]
541#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
542#[must_use]
543#[repr(transparent)]
544pub struct Unpacker<'a>(&'a [u8]);
545
546impl<'a> Unpacker<'a> {
547    /// Creates a new unpacker.
548    #[inline(always)]
549    pub const fn new(b: &'a [u8]) -> Self {
550        Self(b)
551    }
552
553    /// Creates an unpacker in an error state.
554    #[inline(always)]
555    pub const fn invalid() -> Self {
556        Self(Self::err())
557    }
558
559    /// Returns the remaining byte slice, if any. The caller should use
560    /// [`Self::is_ok()`] to check whether the returned slice is valid.
561    #[inline(always)]
562    #[must_use]
563    pub const fn into_inner(self) -> &'a [u8] {
564        self.0
565    }
566
567    /// Returns the remaining number of bytes.
568    #[inline(always)]
569    #[must_use]
570    pub const fn len(&self) -> usize {
571        self.0.len()
572    }
573
574    /// Returns whether the byte slice is empty.
575    #[inline(always)]
576    #[must_use]
577    pub const fn is_empty(&self) -> bool {
578        self.0.is_empty()
579    }
580
581    /// Returns whether all reads were within the bounds of the original byte
582    /// slice.
583    #[inline(always)]
584    #[must_use]
585    pub fn is_ok(&self) -> bool {
586        !ptr::eq(self.0, Self::err())
587    }
588
589    /// Returns the remaining byte slice in a new unpacker, leaving `self`
590    /// empty. This is primarily useful in combination with [`map()`].
591    ///
592    /// [`map()`]: Self::map()
593    #[inline]
594    pub fn take(&mut self) -> Self {
595        // SAFETY: `len()..` range is always valid for get()
596        let empty = unsafe { self.0.get_unchecked(self.0.len()..) };
597        Self(mem::replace(&mut self.0, empty))
598    }
599
600    /// Returns [`Some`] output of `f`, or `None` if `f` fails to consume the
601    /// entire slice without reading past the end.
602    #[inline]
603    #[must_use]
604    pub fn map<T>(mut self, f: impl FnOnce(&mut Self) -> T) -> Option<T> {
605        let v = f(&mut self);
606        (self.is_ok() && self.0.is_empty()).then_some(v)
607    }
608
609    /// Returns the output of `f`, or `default` if `f` fails to consume the
610    /// entire slice without reading past the end.
611    #[inline(always)]
612    #[must_use]
613    pub fn map_or<T>(self, default: T, f: impl FnOnce(&mut Self) -> T) -> T {
614        self.map(f).unwrap_or(default)
615    }
616
617    /// Returns the output of `f`, or the output of `default()` if `f` fails to
618    /// consume the entire slice without reading past the end.
619    #[inline(always)]
620    pub fn map_or_else<T>(self, default: impl FnOnce() -> T, f: impl FnOnce(&mut Self) -> T) -> T {
621        self.map(f).unwrap_or_else(default)
622    }
623
624    /// Splits the remaining byte slice at `i`, and returns two new unpackers,
625    /// both of which will be in an error state if `len() < i`.
626    #[inline]
627    pub const fn split_at(&self, i: usize) -> (Self, Self) {
628        let Some(rem) = self.0.len().checked_sub(i) else {
629            return (Self(Self::err()), Self(Self::err()));
630        };
631        let p = self.0.as_ptr();
632        // SAFETY: 0 <= i <= len() and i + rem == len()
633        unsafe {
634            (
635                Self(slice::from_raw_parts(p, i)),
636                Self(slice::from_raw_parts(p.add(i), rem)),
637            )
638        }
639    }
640
641    /// Advances `self` by `n` bytes, returning a new unpacker for the skipped
642    /// bytes or [`None`] if there is an insufficient number of bytes remaining,
643    /// in which case any remaining bytes are discarded.
644    ///
645    /// # Examples
646    ///
647    /// ```
648    /// # use structbuf::{StructBuf, Unpack};
649    /// # let mut b = StructBuf::new(4);
650    /// # let mut p = b.unpack();
651    /// if let Some(mut hdr) = p.skip(2) {
652    ///     let _ = hdr.u16();
653    ///     assert!(hdr.is_ok() && p.is_ok());
654    /// } else {
655    ///     assert!(!p.is_ok());
656    /// }
657    /// ```
658    #[inline]
659    pub fn skip(&mut self, n: usize) -> Option<Self> {
660        let (a, b) = self.split_at(n);
661        self.0 = b.0;
662        a.is_ok().then_some(a)
663    }
664
665    /// Returns the next `u8` as a `bool` where any non-zero value is converted
666    /// to `true`.
667    #[inline(always)]
668    #[must_use]
669    pub fn bool(&mut self) -> bool {
670        self.u8() != 0
671    }
672
673    /// Returns the next `u8`.
674    #[inline(always)]
675    #[must_use]
676    pub fn u8(&mut self) -> u8 {
677        // SAFETY: All bit patterns are valid
678        unsafe { self.read() }
679    }
680
681    /// Returns the next `u16`.
682    #[inline(always)]
683    #[must_use]
684    pub fn u16(&mut self) -> u16 {
685        // SAFETY: All bit patterns are valid
686        u16::from_le(unsafe { self.read() })
687    }
688
689    /// Returns the next `u32`.
690    #[inline(always)]
691    #[must_use]
692    pub fn u32(&mut self) -> u32 {
693        // SAFETY: All bit patterns are valid
694        u32::from_le(unsafe { self.read() })
695    }
696
697    /// Returns the next `u64`.
698    #[inline(always)]
699    #[must_use]
700    pub fn u64(&mut self) -> u64 {
701        // SAFETY: All bit patterns are valid
702        u64::from_le(unsafe { self.read() })
703    }
704
705    /// Returns the next `u128`.
706    #[inline(always)]
707    #[must_use]
708    pub fn u128(&mut self) -> u128 {
709        // SAFETY: All bit patterns are valid
710        u128::from_le(unsafe { self.read() })
711    }
712
713    /// Returns the next `i8`.
714    #[inline(always)]
715    #[must_use]
716    pub fn i8(&mut self) -> i8 {
717        // SAFETY: All bit patterns are valid
718        unsafe { self.read() }
719    }
720
721    /// Returns the next `i16`.
722    #[inline(always)]
723    #[must_use]
724    pub fn i16(&mut self) -> i16 {
725        // SAFETY: All bit patterns are valid
726        i16::from_le(unsafe { self.read() })
727    }
728
729    /// Returns the next `i32`.
730    #[inline(always)]
731    #[must_use]
732    pub fn i32(&mut self) -> i32 {
733        // SAFETY: All bit patterns are valid
734        i32::from_le(unsafe { self.read() })
735    }
736
737    /// Returns the next `i64`.
738    #[inline(always)]
739    #[must_use]
740    pub fn i64(&mut self) -> i64 {
741        // SAFETY: All bit patterns are valid
742        i64::from_le(unsafe { self.read() })
743    }
744
745    /// Returns the next `i128`.
746    #[inline(always)]
747    #[must_use]
748    pub fn i128(&mut self) -> i128 {
749        // SAFETY: All bit patterns are valid
750        i128::from_le(unsafe { self.read() })
751    }
752
753    /// Returns the next `[u8; N]` array.
754    #[inline(always)]
755    #[must_use]
756    pub fn bytes<const N: usize>(&mut self) -> [u8; N] {
757        if let Some(rem) = self.0.len().checked_sub(N) {
758            // SAFETY: 0 <= N <= len() and the result has an alignment of 1
759            unsafe {
760                let p = self.0.as_ptr();
761                self.0 = slice::from_raw_parts(p.add(N), rem);
762                *p.cast()
763            }
764        } else {
765            self.0 = Self::err();
766            [0; N]
767        }
768    }
769
770    /// Returns the next `T`, or `T::default()` if there is an insufficient
771    /// number of bytes remaining, in which case any remaining bytes are
772    /// discarded.
773    ///
774    /// # Safety
775    ///
776    /// `T` must be able to hold the resulting bit pattern.
777    #[inline]
778    #[must_use]
779    pub unsafe fn read<T: Default>(&mut self) -> T {
780        if let Some(rem) = self.0.len().checked_sub(mem::size_of::<T>()) {
781            // 0 <= size_of::<T>() <= len()
782            let p = self.0.as_ptr().cast::<T>();
783            self.0 = slice::from_raw_parts(p.add(1).cast(), rem);
784            p.read_unaligned()
785        } else {
786            self.0 = Self::err();
787            T::default()
788        }
789    }
790
791    /// Returns a sentinel byte slice indicating that the original slice was too
792    /// short.
793    #[inline(always)]
794    #[must_use]
795    const fn err() -> &'static [u8] {
796        // Can't be a const: https://github.com/rust-lang/rust/issues/105536
797        // SAFETY: A dangling pointer is valid for a zero-length slice
798        unsafe { slice::from_raw_parts(ptr::NonNull::dangling().as_ptr(), 0) }
799    }
800}
801
802impl<'a> AsRef<[u8]> for Unpacker<'a> {
803    #[inline(always)]
804    #[must_use]
805    fn as_ref(&self) -> &'a [u8] {
806        self.0
807    }
808}
809
810#[cfg(test)]
811mod tests {
812    use super::*;
813
814    #[test]
815    fn packer() {
816        let mut b = StructBuf::new(4);
817        assert_eq!(b.len(), 0);
818        assert_eq!(b.capacity(), 4);
819        assert_eq!(b.lim(), 4);
820
821        b.append().u8(1);
822        assert_eq!(b.len(), 1);
823        assert_eq!(b.as_ref(), &[1]);
824
825        b.append().u8(2).u16(0x0403_u16);
826        assert_eq!(b.len(), 4);
827        assert_eq!(b.as_ref(), &[1, 2, 3, 4]);
828    }
829
830    #[test]
831    #[should_panic]
832    fn packer_limit() {
833        let mut b = StructBuf::new(4);
834        b.append().u8(1).u16(2_u16);
835        b.append().u16(3_u16);
836    }
837
838    #[test]
839    fn packer_overwrite() {
840        let mut b = StructBuf::new(4);
841        b.append().put([1, 2, 3, 4]);
842        assert_eq!(b.as_ref(), &[1, 2, 3, 4]);
843        b.at(1).u16(0x0203_u16);
844        assert_eq!(b.as_ref(), &[1, 3, 2, 4]);
845    }
846
847    #[test]
848    fn packer_pad() {
849        let mut b = StructBuf::with_capacity(INLINE_CAP + 1);
850        // SAFETY: b is valid for b.capacity() bytes
851        unsafe { b.as_mut_ptr().write_bytes(0xFF, b.capacity()) };
852        b.at(b.capacity() - 1).u8(1);
853        assert!(&b[..INLINE_CAP].iter().all(|&v| v == 0));
854        assert_eq!(b[INLINE_CAP], 1);
855
856        b.clear();
857        b.put_at(4, []);
858        assert_eq!(b.as_ref(), &[0, 0, 0, 0]);
859    }
860
861    #[test]
862    fn unpacker() {
863        let mut p = Unpacker::new(&[1, 2, 3]);
864        assert_eq!(p.u8(), 1);
865        assert!(p.is_ok());
866        assert_eq!(p.u16(), 0x0302);
867        assert!(p.is_ok());
868        assert_eq!(p.u8(), 0);
869        assert!(!p.is_ok());
870
871        let mut p = Unpacker::new(&[1]);
872        assert_eq!(p.u16(), 0);
873        assert!(!p.is_ok());
874        assert_eq!(p.u32(), 0);
875
876        let mut p = Unpacker::new(&[1, 2, 3]);
877        assert_eq!(p.bytes::<2>(), [1, 2]);
878        assert_eq!(p.bytes::<3>(), [0, 0, 0]);
879    }
880
881    #[test]
882    fn unpacker_take() {
883        let mut p = Unpacker::new(&[1, 2, 3]);
884        assert_eq!(p.u8(), 1);
885
886        let mut v = p.take();
887        assert!(p.is_ok());
888        assert!(p.is_empty());
889        assert_eq!((v.u8(), v.u8()), (2, 3));
890        assert!(v.is_ok());
891
892        assert_eq!(p.u64(), 0);
893        assert!(!p.is_ok());
894        let v = p.take();
895        assert!(!v.is_ok());
896    }
897
898    #[test]
899    fn unpacker_skip() {
900        let mut p = Unpacker::new(&[1, 2, 3]);
901        let mut v = p.skip(2).unwrap();
902
903        assert_eq!((v.u8(), v.u8()), (1, 2));
904        assert_eq!(p.u8(), 3);
905
906        assert!(p.skip(0).unwrap().is_ok());
907        assert!(p.is_ok());
908        assert!(p.skip(1).is_none());
909        assert!(!p.is_ok());
910    }
911}