Skip to main content

crypto_bigint/limb/
encoding.rs

1//! Limb encoding
2
3use super::{Limb, Word};
4use crate::{Encoding, bitlen};
5
6impl Limb {
7    /// Decode from big endian bytes.
8    #[inline]
9    #[must_use]
10    pub const fn from_be_bytes(bytes: [u8; Self::BYTES]) -> Self {
11        Limb(Word::from_be_bytes(bytes))
12    }
13
14    /// Decode from little endian bytes.
15    #[inline]
16    #[must_use]
17    pub const fn from_le_bytes(bytes: [u8; Self::BYTES]) -> Self {
18        Limb(Word::from_le_bytes(bytes))
19    }
20
21    /// Decode limb from a big endian byte slice, which may be shorter than [`Limb::BYTES`].
22    ///
23    /// # Panics
24    /// If the slice larger than [`Limb::BYTES`].
25    #[must_use]
26    #[track_caller]
27    pub const fn from_be_slice(bytes: &[u8]) -> Self {
28        let offset = Self::BYTES
29            .checked_sub(bytes.len())
30            .expect("The given slice is larger than Limb::BYTES");
31
32        let mut repr = [0u8; Self::BYTES];
33        let mut i = 0;
34        while i < bytes.len() {
35            repr[offset + i] = bytes[i];
36            i += 1;
37        }
38        Self::from_be_bytes(repr)
39    }
40
41    /// Decode limb from a little endian byte slice, which may be shorter than [`Limb::BYTES`].
42    ///
43    /// # Panics
44    /// If the slice larger than [`Limb::BYTES`].
45    #[must_use]
46    #[track_caller]
47    pub const fn from_le_slice(bytes: &[u8]) -> Self {
48        assert!(
49            bytes.len() <= Self::BYTES,
50            "The given slice is larger than Limb::BYTES"
51        );
52
53        let mut repr = [0u8; Self::BYTES];
54        let mut i = 0;
55        while i < bytes.len() {
56            repr[i] = bytes[i];
57            i += 1;
58        }
59        Self::from_le_bytes(repr)
60    }
61
62    /// Decode limb from the provided big endian bytes, zero padding if necessary, and
63    /// truncating to the least significant bytes in the event the given amount of data exceeds
64    /// `bits_precision`.
65    ///
66    /// # Panics
67    /// If `bits_precision > Self::BITS`.
68    #[inline]
69    #[must_use]
70    #[track_caller]
71    pub const fn from_be_slice_truncated(mut bytes: &[u8], bits_precision: u32) -> Self {
72        assert!(bits_precision <= Self::BITS);
73        let bytes_precision = bitlen::to_bytes(bits_precision);
74        if bytes.len() > bytes_precision {
75            bytes = bytes
76                .split_at(bytes.len().saturating_sub(bytes_precision))
77                .1;
78        }
79
80        let mut ret = Self::from_be_slice(bytes);
81        ret.mask_to_precision(bits_precision);
82        ret
83    }
84
85    /// Decode limb from the provided little endian bytes, zero padding if necessary, and
86    /// truncating to the least significant bytes in the event the given amount of data exceeds
87    /// `bits_precision`.
88    ///
89    /// # Panics
90    /// If `bits_precision > Self::BITS`.
91    #[inline]
92    #[must_use]
93    #[track_caller]
94    pub const fn from_le_slice_truncated(mut bytes: &[u8], bits_precision: u32) -> Self {
95        assert!(bits_precision <= Self::BITS);
96        let bytes_precision = bitlen::to_bytes(bits_precision);
97        if bytes.len() > bytes_precision {
98            bytes = bytes.split_at(bytes_precision).0;
99        }
100
101        let mut ret = Self::from_le_slice(bytes);
102        ret.mask_to_precision(bits_precision);
103        ret
104    }
105
106    /// Encode as big endian bytes.
107    #[inline]
108    #[must_use]
109    pub const fn to_be_bytes(&self) -> [u8; Self::BYTES] {
110        self.0.to_be_bytes()
111    }
112
113    /// Encode as little endian bytes.
114    #[inline]
115    #[must_use]
116    pub const fn to_le_bytes(&self) -> [u8; Self::BYTES] {
117        self.0.to_le_bytes()
118    }
119
120    /// Mask to the given number of bits precision.
121    #[inline(always)]
122    pub(crate) const fn mask_to_precision(&mut self, bits_precision: u32) {
123        debug_assert!(bits_precision <= Self::BITS);
124        self.0 &= Word::MAX >> (Limb::BITS.saturating_sub(bits_precision));
125    }
126}
127
128impl Encoding for Limb {
129    type Repr = [u8; Self::BYTES];
130
131    #[inline]
132    fn from_be_bytes(bytes: Self::Repr) -> Self {
133        Self::from_be_bytes(bytes)
134    }
135
136    #[inline]
137    fn from_le_bytes(bytes: Self::Repr) -> Self {
138        Self::from_le_bytes(bytes)
139    }
140
141    #[inline]
142    fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
143        Self::from_be_slice_truncated(bytes, bits_precision)
144    }
145
146    #[inline]
147    fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
148        Self::from_le_slice_truncated(bytes, bits_precision)
149    }
150
151    #[inline]
152    fn to_be_bytes(&self) -> Self::Repr {
153        self.to_be_bytes()
154    }
155
156    #[inline]
157    fn to_le_bytes(&self) -> Self::Repr {
158        self.to_le_bytes()
159    }
160}
161
162#[cfg(test)]
163mod test {
164    use super::*;
165
166    cpubits::cpubits! {
167        32 => { const LIMB: Limb = Limb(0x7654_3210); }
168        64 => { const LIMB: Limb = Limb(0xFEDCBA9876543210); }
169    }
170
171    #[test]
172    fn from_be_slice_truncated() {
173        // Enough capacity
174        let limb = Limb::from_be_slice_truncated(&[1], 32);
175        assert_eq!(limb, Limb::ONE);
176
177        // Result needs masking
178        let limb = Limb::from_be_slice_truncated(&[3], 1);
179        assert_eq!(limb, Limb::ONE);
180    }
181
182    #[test]
183    #[should_panic]
184    fn from_be_slice_truncated_oversized() {
185        let _ = Limb::from_be_slice_truncated(b"", 65);
186    }
187
188    #[test]
189    fn from_le_slice_truncated() {
190        // Enough capacity
191        let limb = Limb::from_le_slice_truncated(&[1], 32);
192        assert_eq!(limb, Limb::ONE);
193
194        // Result needs masking
195        let limb = Limb::from_le_slice_truncated(&[1], 1);
196        assert_eq!(limb, Limb::ONE);
197    }
198
199    #[test]
200    #[should_panic]
201    fn from_le_slice_truncated_oversized() {
202        let _ = Limb::from_le_slice_truncated(b"", 65);
203    }
204
205    #[test]
206    fn roundtrip() {
207        assert_eq!(LIMB, Limb::from_be_bytes(LIMB.to_be_bytes()));
208        assert_eq!(LIMB, Limb::from_le_bytes(LIMB.to_le_bytes()));
209    }
210
211    #[test]
212    fn reverse() {
213        let mut bytes = LIMB.to_be_bytes();
214        bytes.reverse();
215        assert_eq!(LIMB, Limb::from_le_bytes(bytes));
216
217        let mut bytes = LIMB.to_le_bytes();
218        bytes.reverse();
219        assert_eq!(LIMB, Limb::from_be_bytes(bytes));
220    }
221}