Skip to main content

fixed_bigint/heapless/
bytes.rs

1//! Byte serialization for `HeaplessBigInt`.
2//!
3//! The written byte count is `self.len * word_size` — public shape,
4//! derived from the public `len`. Callers own the output buffer; the
5//! methods panic if it is too small (checked at runtime, no `Result`).
6//!
7//! Layout matches `FixedUInt`'s slice-based conventions: BE writes the
8//! high-order word's high byte at index 0, LE writes the low-order
9//! word's low byte at index 0.
10//!
11//! Decoding delegates to `FixedUInt`'s `impl_from_{be,le}_bytes_slice`
12//! (the byte→limb scatter is identical, and those are the helpers
13//! `panic-free-audit` already validates), so `HeaplessBigInt` only owns the
14//! `len` computation and the oversize guard.
15
16use super::HeaplessBigInt;
17use crate::MachineWord;
18use crate::fixeduint::{impl_from_be_bytes_slice, impl_from_le_bytes_slice};
19use const_num_traits::{ByteSliceError, ByteSliceErrorKind, FromByteSlice, Personality};
20use core::marker::PhantomData;
21
22impl<T: MachineWord, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P> {
23    /// Serialize into `out` as big-endian bytes. Writes `self.len *
24    /// size_of::<T>()` bytes into `out[..byte_count]` and returns that
25    /// slice. Panics if `out.len() < byte_count`.
26    pub fn to_be_bytes<'a>(&self, out: &'a mut [u8]) -> &'a [u8] {
27        let word_size = core::mem::size_of::<T>();
28        let byte_count = self.len as usize * word_size;
29        assert!(
30            out.len() >= byte_count,
31            "HeaplessBigInt::to_be_bytes: out.len() < required ({byte_count} bytes)"
32        );
33        // `by_ref().zip` over a flat `iter_mut()` rather than
34        // `chunks_exact_mut(word_size)`, whose `size()` division by the
35        // chunk size retains a div-by-zero panic guard at MSRV/`-Oz`.
36        let mut dst = out[..byte_count].iter_mut();
37        for word in self.limbs[..self.len as usize].iter().rev() {
38            let word_bytes = word.to_be_bytes();
39            for (&src, slot) in word_bytes.as_ref().iter().zip(dst.by_ref()) {
40                *slot = src;
41            }
42        }
43        &out[..byte_count]
44    }
45
46    /// Serialize into `out` as little-endian bytes. Same size + panic
47    /// contract as [`to_be_bytes`](Self::to_be_bytes).
48    pub fn to_le_bytes<'a>(&self, out: &'a mut [u8]) -> &'a [u8] {
49        let word_size = core::mem::size_of::<T>();
50        let byte_count = self.len as usize * word_size;
51        assert!(
52            out.len() >= byte_count,
53            "HeaplessBigInt::to_le_bytes: out.len() < required ({byte_count} bytes)"
54        );
55        // See `to_be_bytes` for why this avoids `chunks_exact_mut`.
56        let mut dst = out[..byte_count].iter_mut();
57        for word in self.limbs[..self.len as usize].iter() {
58            let word_bytes = word.to_le_bytes();
59            for (&src, slot) in word_bytes.as_ref().iter().zip(dst.by_ref()) {
60                *slot = src;
61            }
62        }
63        &out[..byte_count]
64    }
65
66    /// Deserialize a big-endian byte slice. Output `len =
67    /// ceil(bytes.len() / word_size)`, capped at `CAP`. A partial top
68    /// word (input length not a multiple of `word_size`) leaves the
69    /// missing high bytes zero — matches the BE convention. Panics if
70    /// `bytes.len() > CAP * word_size`.
71    pub fn from_be_bytes(bytes: &[u8]) -> Self {
72        let word_size = core::mem::size_of::<T>();
73        let max_bytes = CAP * word_size;
74        assert!(
75            bytes.len() <= max_bytes,
76            "HeaplessBigInt::from_be_bytes: input {} bytes > CAP * word_size ({max_bytes})",
77            bytes.len()
78        );
79        let out_len = bytes.len().div_ceil(word_size);
80        // The oversize case is rejected above, so the helper fills the full
81        // `[T; CAP]` without truncating.
82        Self {
83            limbs: impl_from_be_bytes_slice::<T, CAP>(bytes),
84            len: out_len as u16,
85            _p: PhantomData,
86        }
87    }
88
89    /// Deserialize a little-endian byte slice. Same size contract as
90    /// [`from_be_bytes`](Self::from_be_bytes).
91    pub fn from_le_bytes(bytes: &[u8]) -> Self {
92        let word_size = core::mem::size_of::<T>();
93        let max_bytes = CAP * word_size;
94        assert!(
95            bytes.len() <= max_bytes,
96            "HeaplessBigInt::from_le_bytes: input {} bytes > CAP * word_size ({max_bytes})",
97            bytes.len()
98        );
99        let out_len = bytes.len().div_ceil(word_size);
100        Self {
101            limbs: impl_from_le_bytes_slice::<T, CAP>(bytes),
102            len: out_len as u16,
103            _p: PhantomData,
104        }
105    }
106}
107
108// ── const_num_traits::FromByteSlice ──
109//
110// Result-returning slice parse: empty → `Empty`, wider than the
111// container → `Overflow`, shorter → zero-extended. The inherent
112// `from_be_bytes`/`from_le_bytes` already zero-extend; the length
113// guard converts their panic-on-oversize into the `Overflow` error.
114
115impl<T: MachineWord, const CAP: usize, P: Personality> HeaplessBigInt<T, CAP, P> {
116    #[inline]
117    fn check_slice_len(len: usize) -> Result<(), ByteSliceError> {
118        if len == 0 {
119            return Err(ByteSliceError {
120                kind: ByteSliceErrorKind::Empty,
121            });
122        }
123        if len > CAP * core::mem::size_of::<T>() {
124            return Err(ByteSliceError {
125                kind: ByteSliceErrorKind::Overflow,
126            });
127        }
128        Ok(())
129    }
130}
131
132impl<T: MachineWord, const CAP: usize, P: Personality> FromByteSlice for HeaplessBigInt<T, CAP, P> {
133    fn from_be_slice(bytes: &[u8]) -> Result<Self, ByteSliceError> {
134        Self::check_slice_len(bytes.len())?;
135        Ok(Self::from_be_bytes(bytes))
136    }
137
138    fn from_le_slice(bytes: &[u8]) -> Result<Self, ByteSliceError> {
139        Self::check_slice_len(bytes.len())?;
140        Ok(Self::from_le_bytes(bytes))
141    }
142}