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::BYTES * LIMBS,
17            "bytes are not the expected size"
18        );
19
20        let mut res = [Limb::ZERO; LIMBS];
21        let mut buf = [0u8; Limb::BYTES];
22        let mut i = 0;
23
24        while i < LIMBS {
25            let mut j = 0;
26            while j < Limb::BYTES {
27                buf[j] = bytes[i * Limb::BYTES + 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::BYTES * 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::BYTES];
48        let mut i = 0;
49        let mut err = 0;
50
51        while i < LIMBS {
52            let mut j = 0;
53            while j < Limb::BYTES {
54                let offset = (i * Limb::BYTES + j) * 2;
55                let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
56                err |= byte_err;
57                buf[j] = result;
58                j += 1;
59            }
60            res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
61            i += 1;
62        }
63
64        assert!(err == 0, "invalid hex byte");
65
66        Uint::new(res)
67    }
68
69    /// Create a new [`Uint`] from the provided little endian bytes.
70    pub const fn from_le_slice(bytes: &[u8]) -> Self {
71        assert!(
72            bytes.len() == Limb::BYTES * LIMBS,
73            "bytes are not the expected size"
74        );
75
76        let mut res = [Limb::ZERO; LIMBS];
77        let mut buf = [0u8; Limb::BYTES];
78        let mut i = 0;
79
80        while i < LIMBS {
81            let mut j = 0;
82            while j < Limb::BYTES {
83                buf[j] = bytes[i * Limb::BYTES + j];
84                j += 1;
85            }
86            res[i] = Limb(Word::from_le_bytes(buf));
87            i += 1;
88        }
89
90        Uint::new(res)
91    }
92
93    /// Create a new [`Uint`] from the provided little endian hex string.
94    pub const fn from_le_hex(hex: &str) -> Self {
95        let bytes = hex.as_bytes();
96
97        assert!(
98            bytes.len() == Limb::BYTES * LIMBS * 2,
99            "bytes are not the expected size"
100        );
101
102        let mut res = [Limb::ZERO; LIMBS];
103        let mut buf = [0u8; Limb::BYTES];
104        let mut i = 0;
105        let mut err = 0;
106
107        while i < LIMBS {
108            let mut j = 0;
109            while j < Limb::BYTES {
110                let offset = (i * Limb::BYTES + j) * 2;
111                let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
112                err |= byte_err;
113                buf[j] = result;
114                j += 1;
115            }
116            res[i] = Limb(Word::from_le_bytes(buf));
117            i += 1;
118        }
119
120        assert!(err == 0, "invalid hex byte");
121
122        Uint::new(res)
123    }
124
125    /// Serialize this [`Uint`] as big-endian, writing it into the provided
126    /// byte slice.
127    #[inline]
128    pub(crate) fn write_be_bytes(&self, out: &mut [u8]) {
129        debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
130
131        for (src, dst) in self
132            .limbs
133            .iter()
134            .rev()
135            .cloned()
136            .zip(out.chunks_exact_mut(Limb::BYTES))
137        {
138            dst.copy_from_slice(&src.to_be_bytes());
139        }
140    }
141
142    /// Serialize this [`Uint`] as little-endian, writing it into the provided
143    /// byte slice.
144    #[inline]
145    pub(crate) fn write_le_bytes(&self, out: &mut [u8]) {
146        debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
147
148        for (src, dst) in self
149            .limbs
150            .iter()
151            .cloned()
152            .zip(out.chunks_exact_mut(Limb::BYTES))
153        {
154            dst.copy_from_slice(&src.to_le_bytes());
155        }
156    }
157}
158
159/// Decode a single nibble of upper or lower hex
160#[inline(always)]
161const fn decode_nibble(src: u8) -> u16 {
162    let byte = src as i16;
163    let mut ret: i16 = -1;
164
165    // 0-9  0x30-0x39
166    // if (byte > 0x2f && byte < 0x3a) ret += byte - 0x30 + 1; // -47
167    ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
168    // A-F  0x41-0x46
169    // if (byte > 0x40 && byte < 0x47) ret += byte - 0x41 + 10 + 1; // -54
170    ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
171    // a-f  0x61-0x66
172    // if (byte > 0x60 && byte < 0x67) ret += byte - 0x61 + 10 + 1; // -86
173    ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
174
175    ret as u16
176}
177
178/// Decode a single byte encoded as two hexadecimal characters.
179/// Second element of the tuple is non-zero if the `bytes` values are not in the valid range
180/// (0-9, a-z, A-Z).
181#[inline(always)]
182const fn decode_hex_byte(bytes: [u8; 2]) -> (u8, u16) {
183    let hi = decode_nibble(bytes[0]);
184    let lo = decode_nibble(bytes[1]);
185    let byte = (hi << 4) | lo;
186    let err = byte >> 8;
187    let result = byte as u8;
188    (result, err)
189}
190
191#[cfg(test)]
192mod tests {
193    use crate::Limb;
194    use hex_literal::hex;
195
196    #[cfg(feature = "alloc")]
197    use {crate::U128, alloc::format};
198
199    #[cfg(target_pointer_width = "32")]
200    use crate::U64 as UintEx;
201
202    #[cfg(target_pointer_width = "64")]
203    use crate::U128 as UintEx;
204
205    #[test]
206    #[cfg(target_pointer_width = "32")]
207    fn from_be_slice() {
208        let bytes = hex!("0011223344556677");
209        let n = UintEx::from_be_slice(&bytes);
210        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
211    }
212
213    #[test]
214    #[cfg(target_pointer_width = "64")]
215    fn from_be_slice() {
216        let bytes = hex!("00112233445566778899aabbccddeeff");
217        let n = UintEx::from_be_slice(&bytes);
218        assert_eq!(
219            n.as_limbs(),
220            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
221        );
222    }
223
224    #[test]
225    #[cfg(target_pointer_width = "32")]
226    fn from_le_slice() {
227        let bytes = hex!("7766554433221100");
228        let n = UintEx::from_le_slice(&bytes);
229        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
230    }
231
232    #[test]
233    #[cfg(target_pointer_width = "64")]
234    fn from_le_slice() {
235        let bytes = hex!("ffeeddccbbaa99887766554433221100");
236        let n = UintEx::from_le_slice(&bytes);
237        assert_eq!(
238            n.as_limbs(),
239            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
240        );
241    }
242
243    #[test]
244    #[cfg(target_pointer_width = "32")]
245    fn from_be_hex() {
246        let n = UintEx::from_be_hex("0011223344556677");
247        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
248    }
249
250    #[test]
251    #[cfg(target_pointer_width = "64")]
252    fn from_be_hex() {
253        let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
254        assert_eq!(
255            n.as_limbs(),
256            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
257        );
258    }
259
260    #[test]
261    #[cfg(target_pointer_width = "32")]
262    fn from_le_hex() {
263        let n = UintEx::from_le_hex("7766554433221100");
264        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
265    }
266
267    #[test]
268    #[cfg(target_pointer_width = "64")]
269    fn from_le_hex() {
270        let n = UintEx::from_le_hex("ffeeddccbbaa99887766554433221100");
271        assert_eq!(
272            n.as_limbs(),
273            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
274        );
275    }
276
277    #[cfg(feature = "alloc")]
278    #[test]
279    fn hex_upper() {
280        let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
281        let n = U128::from_be_hex(hex);
282        assert_eq!(hex, format!("{:X}", n));
283    }
284
285    #[cfg(feature = "alloc")]
286    #[test]
287    fn hex_lower() {
288        let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
289        let n = U128::from_be_hex(hex);
290        assert_eq!(hex, format!("{:x}", n));
291    }
292}