crypto_bigint/uint/
encoding.rs

1//! Const-friendly decoding operations for [`UInt`]
2
3#[cfg(all(feature = "der", feature = "generic-array"))]
4mod der;
5
6#[cfg(feature = "rlp")]
7mod rlp;
8
9use super::UInt;
10use crate::{Encoding, Limb, Word};
11
12impl<const LIMBS: usize> UInt<LIMBS> {
13    /// Create a new [`UInt`] from the provided big endian bytes.
14    pub const fn from_be_slice(bytes: &[u8]) -> Self {
15        assert!(
16            bytes.len() == Limb::BYTE_SIZE * LIMBS,
17            "bytes are not the expected size"
18        );
19
20        let mut res = [Limb::ZERO; LIMBS];
21        let mut buf = [0u8; Limb::BYTE_SIZE];
22        let mut i = 0;
23
24        while i < LIMBS {
25            let mut j = 0;
26            while j < Limb::BYTE_SIZE {
27                buf[j] = bytes[i * Limb::BYTE_SIZE + j];
28                j += 1;
29            }
30            res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
31            i += 1;
32        }
33
34        UInt::new(res)
35    }
36
37    /// Create a new [`UInt`] from the provided big endian hex string.
38    pub const fn from_be_hex(hex: &str) -> Self {
39        let bytes = hex.as_bytes();
40
41        assert!(
42            bytes.len() == Limb::BYTE_SIZE * LIMBS * 2,
43            "hex string is not the expected size"
44        );
45
46        let mut res = [Limb::ZERO; LIMBS];
47        let mut buf = [0u8; Limb::BYTE_SIZE];
48        let mut i = 0;
49
50        while i < LIMBS {
51            let mut j = 0;
52            while j < Limb::BYTE_SIZE {
53                let offset = (i * Limb::BYTE_SIZE + j) * 2;
54                buf[j] = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
55                j += 1;
56            }
57            res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
58            i += 1;
59        }
60
61        UInt::new(res)
62    }
63
64    /// Create a new [`UInt`] from the provided little endian bytes.
65    pub const fn from_le_slice(bytes: &[u8]) -> Self {
66        assert!(
67            bytes.len() == Limb::BYTE_SIZE * LIMBS,
68            "bytes are not the expected size"
69        );
70
71        let mut res = [Limb::ZERO; LIMBS];
72        let mut buf = [0u8; Limb::BYTE_SIZE];
73        let mut i = 0;
74
75        while i < LIMBS {
76            let mut j = 0;
77            while j < Limb::BYTE_SIZE {
78                buf[j] = bytes[i * Limb::BYTE_SIZE + j];
79                j += 1;
80            }
81            res[i] = Limb(Word::from_le_bytes(buf));
82            i += 1;
83        }
84
85        UInt::new(res)
86    }
87
88    /// Create a new [`UInt`] from the provided little endian hex string.
89    pub const fn from_le_hex(hex: &str) -> Self {
90        let bytes = hex.as_bytes();
91
92        assert!(
93            bytes.len() == Limb::BYTE_SIZE * LIMBS * 2,
94            "bytes are not the expected size"
95        );
96
97        let mut res = [Limb::ZERO; LIMBS];
98        let mut buf = [0u8; Limb::BYTE_SIZE];
99        let mut i = 0;
100
101        while i < LIMBS {
102            let mut j = 0;
103            while j < Limb::BYTE_SIZE {
104                let offset = (i * Limb::BYTE_SIZE + j) * 2;
105                buf[j] = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
106                j += 1;
107            }
108            res[i] = Limb(Word::from_le_bytes(buf));
109            i += 1;
110        }
111
112        UInt::new(res)
113    }
114
115    /// Serialize this [`UInt`] as big-endian, writing it into the provided
116    /// byte slice.
117    #[inline]
118    #[cfg_attr(docsrs, doc(cfg(feature = "generic-array")))]
119    pub(crate) fn write_be_bytes(&self, out: &mut [u8]) {
120        debug_assert_eq!(out.len(), Limb::BYTE_SIZE * LIMBS);
121
122        for (src, dst) in self
123            .limbs
124            .iter()
125            .rev()
126            .cloned()
127            .zip(out.chunks_exact_mut(Limb::BYTE_SIZE))
128        {
129            dst.copy_from_slice(&src.to_be_bytes());
130        }
131    }
132
133    /// Serialize this [`UInt`] as little-endian, writing it into the provided
134    /// byte slice.
135    #[inline]
136    #[cfg_attr(docsrs, doc(cfg(feature = "generic-array")))]
137    pub(crate) fn write_le_bytes(&self, out: &mut [u8]) {
138        debug_assert_eq!(out.len(), Limb::BYTE_SIZE * LIMBS);
139
140        for (src, dst) in self
141            .limbs
142            .iter()
143            .cloned()
144            .zip(out.chunks_exact_mut(Limb::BYTE_SIZE))
145        {
146            dst.copy_from_slice(&src.to_le_bytes());
147        }
148    }
149}
150
151/// Decode a single byte encoded as two hexadecimal characters.
152const fn decode_hex_byte(bytes: [u8; 2]) -> u8 {
153    let mut i = 0;
154    let mut result = 0u8;
155
156    while i < 2 {
157        result <<= 4;
158        result |= match bytes[i] {
159            b @ b'0'..=b'9' => b - b'0',
160            b @ b'a'..=b'f' => 10 + b - b'a',
161            b @ b'A'..=b'F' => 10 + b - b'A',
162            b => {
163                assert!(
164                    matches!(b, b'0'..=b'9' | b'a' ..= b'f' | b'A'..=b'F'),
165                    "invalid hex byte"
166                );
167                0
168            }
169        };
170
171        i += 1;
172    }
173
174    result
175}
176
177#[cfg(test)]
178mod tests {
179    use crate::Limb;
180    use hex_literal::hex;
181
182    #[cfg(feature = "alloc")]
183    use {crate::U128, alloc::format};
184
185    #[cfg(target_pointer_width = "32")]
186    use crate::U64 as UIntEx;
187
188    #[cfg(target_pointer_width = "64")]
189    use crate::U128 as UIntEx;
190
191    #[test]
192    #[cfg(target_pointer_width = "32")]
193    fn from_be_slice() {
194        let bytes = hex!("0011223344556677");
195        let n = UIntEx::from_be_slice(&bytes);
196        assert_eq!(n.limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
197    }
198
199    #[test]
200    #[cfg(target_pointer_width = "64")]
201    fn from_be_slice() {
202        let bytes = hex!("00112233445566778899aabbccddeeff");
203        let n = UIntEx::from_be_slice(&bytes);
204        assert_eq!(
205            n.limbs(),
206            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
207        );
208    }
209
210    #[test]
211    #[cfg(target_pointer_width = "32")]
212    fn from_le_slice() {
213        let bytes = hex!("7766554433221100");
214        let n = UIntEx::from_le_slice(&bytes);
215        assert_eq!(n.limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
216    }
217
218    #[test]
219    #[cfg(target_pointer_width = "64")]
220    fn from_le_slice() {
221        let bytes = hex!("ffeeddccbbaa99887766554433221100");
222        let n = UIntEx::from_le_slice(&bytes);
223        assert_eq!(
224            n.limbs(),
225            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
226        );
227    }
228
229    #[test]
230    #[cfg(target_pointer_width = "32")]
231    fn from_be_hex() {
232        let n = UIntEx::from_be_hex("0011223344556677");
233        assert_eq!(n.limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
234    }
235
236    #[test]
237    #[cfg(target_pointer_width = "64")]
238    fn from_be_hex() {
239        let n = UIntEx::from_be_hex("00112233445566778899aabbccddeeff");
240        assert_eq!(
241            n.limbs(),
242            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
243        );
244    }
245
246    #[test]
247    #[cfg(target_pointer_width = "32")]
248    fn from_le_hex() {
249        let n = UIntEx::from_le_hex("7766554433221100");
250        assert_eq!(n.limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
251    }
252
253    #[test]
254    #[cfg(target_pointer_width = "64")]
255    fn from_le_hex() {
256        let n = UIntEx::from_le_hex("ffeeddccbbaa99887766554433221100");
257        assert_eq!(
258            n.limbs(),
259            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
260        );
261    }
262
263    #[cfg(feature = "alloc")]
264    #[test]
265    fn hex_upper() {
266        let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
267        let n = U128::from_be_hex(hex);
268        assert_eq!(hex, format!("{:X}", n));
269    }
270
271    #[cfg(feature = "alloc")]
272    #[test]
273    fn hex_lower() {
274        let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
275        let n = U128::from_be_hex(hex);
276        assert_eq!(hex, format!("{:x}", n));
277    }
278}