amplify_num/
hex.rs

1// Rust language amplification library providing multiple generic trait
2// implementations, type wrappers, derive macros and other language enhancements
3//
4// Taken from bitcoin_hashes crate
5// Written in 2018 by
6//   Andrew Poelstra <apoelstra@wpsoftware.net>
7//
8// To the extent possible under law, the author(s) have dedicated all
9// copyright and related and neighboring rights to this software to
10// the public domain worldwide. This software is distributed without
11// any warranty.
12//
13// You should have received a copy of the MIT License
14// along with this software.
15// If not, see <https://opensource.org/licenses/MIT>.
16
17//! # Hex encoding and decoding
18
19#[cfg(feature = "alloc")]
20use alloc::{format, string::String, vec::Vec};
21use core::{fmt, str};
22
23/// Hex decoding error
24#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum Error {
26    /// non-hexadecimal character
27    InvalidChar(u8),
28    /// purported hex string had odd length
29    OddLengthString(usize),
30    /// tried to parse fixed-length hash from a string with the wrong type
31    /// (expected, got)
32    InvalidLength(usize, usize),
33}
34
35impl fmt::Display for Error {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        match *self {
38            Error::InvalidChar(ch) => write!(f, "invalid hex character {ch}"),
39            Error::OddLengthString(ell) => write!(f, "odd hex string length {ell}"),
40            Error::InvalidLength(ell, ell2) => {
41                write!(f, "bad hex string length {ell2} (expected {ell})")
42            }
43        }
44    }
45}
46
47#[cfg(feature = "std")]
48impl std::error::Error for Error {}
49
50/// Trait for objects that can be serialized as hex strings
51#[cfg(any(test, feature = "std", feature = "alloc"))]
52pub trait ToHex {
53    /// Hex representation of the object
54    fn to_hex(&self) -> String;
55}
56
57/// Trait for objects that can be deserialized from hex strings
58pub trait FromHex: Sized {
59    /// Produce an object from a byte iterator
60    fn from_byte_iter<I>(iter: I) -> Result<Self, Error>
61    where I: Iterator<Item = Result<u8, Error>> + ExactSizeIterator + DoubleEndedIterator;
62
63    /// Produce an object from a hex string
64    fn from_hex(s: &str) -> Result<Self, Error> { Self::from_byte_iter(HexIterator::new(s)?) }
65}
66
67#[cfg(any(test, feature = "std", feature = "alloc"))]
68impl<T: fmt::LowerHex> ToHex for T {
69    /// Outputs the hash in hexadecimal form
70    fn to_hex(&self) -> String { format!("{self:x}") }
71}
72
73/// Iterator over a hex-encoded string slice which decodes hex and yields bytes.
74pub struct HexIterator<'a> {
75    /// The `Bytes` iterator whose next two bytes will be decoded to yield
76    /// the next byte.
77    iter: str::Bytes<'a>,
78}
79
80impl<'a> HexIterator<'a> {
81    /// Constructs a new `HexIterator` from a string slice. If the string is of
82    /// odd length it returns an error.
83    pub fn new(s: &'a str) -> Result<HexIterator<'a>, Error> {
84        if s.len() % 2 != 0 {
85            Err(Error::OddLengthString(s.len()))
86        } else {
87            Ok(HexIterator { iter: s.bytes() })
88        }
89    }
90}
91
92fn chars_to_hex(hi: u8, lo: u8) -> Result<u8, Error> {
93    let hih = (hi as char).to_digit(16).ok_or(Error::InvalidChar(hi))?;
94    let loh = (lo as char).to_digit(16).ok_or(Error::InvalidChar(lo))?;
95
96    let ret = (hih << 4) + loh;
97    Ok(ret as u8)
98}
99
100impl<'a> Iterator for HexIterator<'a> {
101    type Item = Result<u8, Error>;
102
103    fn next(&mut self) -> Option<Result<u8, Error>> {
104        let hi = self.iter.next()?;
105        let lo = self.iter.next().unwrap();
106        Some(chars_to_hex(hi, lo))
107    }
108
109    fn size_hint(&self) -> (usize, Option<usize>) {
110        let (min, max) = self.iter.size_hint();
111        (min / 2, max.map(|x| x / 2))
112    }
113}
114
115impl<'a> DoubleEndedIterator for HexIterator<'a> {
116    fn next_back(&mut self) -> Option<Result<u8, Error>> {
117        let lo = self.iter.next_back()?;
118        let hi = self.iter.next_back().unwrap();
119        Some(chars_to_hex(hi, lo))
120    }
121}
122
123impl<'a> ExactSizeIterator for HexIterator<'a> {}
124
125/// Output hex into an object implementing `fmt::Write`, which is usually more
126/// efficient than going through a `String` using `ToHex`.
127pub fn format_hex(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
128    let prec = f.precision().unwrap_or(2 * data.len());
129    let width = f.width().unwrap_or(2 * data.len());
130    for _ in (2 * data.len())..width {
131        f.write_str("0")?;
132    }
133    for ch in data.iter().take(prec / 2) {
134        write!(f, "{:02x}", *ch)?;
135    }
136    if prec < 2 * data.len() && prec % 2 == 1 {
137        write!(f, "{:x}", data[prec / 2] / 16)?;
138    }
139    Ok(())
140}
141
142/// Output hex in reverse order; used for Sha256dHash whose standard hex
143/// encoding has the bytes reversed.
144pub fn format_hex_reverse(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
145    let prec = f.precision().unwrap_or(2 * data.len());
146    let width = f.width().unwrap_or(2 * data.len());
147    for _ in (2 * data.len())..width {
148        f.write_str("0")?;
149    }
150    for ch in data.iter().rev().take(prec / 2) {
151        write!(f, "{:02x}", *ch)?;
152    }
153    if prec < 2 * data.len() && prec % 2 == 1 {
154        write!(f, "{:x}", data[data.len() - 1 - prec / 2] / 16)?;
155    }
156    Ok(())
157}
158
159#[cfg(any(test, feature = "std", feature = "alloc"))]
160impl ToHex for [u8] {
161    fn to_hex(&self) -> String {
162        use core::fmt::Write;
163        let mut ret = String::with_capacity(2 * self.len());
164        for ch in self {
165            write!(ret, "{ch:02x}").expect("writing to string");
166        }
167        ret
168    }
169}
170
171#[cfg(any(test, feature = "std", feature = "alloc"))]
172impl FromHex for Vec<u8> {
173    fn from_byte_iter<I>(iter: I) -> Result<Self, Error>
174    where I: Iterator<Item = Result<u8, Error>> + ExactSizeIterator + DoubleEndedIterator {
175        iter.collect()
176    }
177}
178
179macro_rules! impl_fromhex_array {
180    ($len:expr) => {
181        impl FromHex for [u8; $len] {
182            fn from_byte_iter<I>(iter: I) -> Result<Self, Error>
183            where I: Iterator<Item = Result<u8, Error>> + ExactSizeIterator + DoubleEndedIterator
184            {
185                if iter.len() == $len {
186                    let mut ret = [0; $len];
187                    for (n, byte) in iter.enumerate() {
188                        ret[n] = byte?;
189                    }
190                    Ok(ret)
191                } else {
192                    Err(Error::InvalidLength(2 * $len, 2 * iter.len()))
193                }
194            }
195        }
196    };
197}
198
199impl_fromhex_array!(2);
200impl_fromhex_array!(4);
201impl_fromhex_array!(6);
202impl_fromhex_array!(8);
203impl_fromhex_array!(10);
204impl_fromhex_array!(12);
205impl_fromhex_array!(14);
206impl_fromhex_array!(16);
207impl_fromhex_array!(20);
208impl_fromhex_array!(24);
209impl_fromhex_array!(28);
210impl_fromhex_array!(32);
211impl_fromhex_array!(33);
212impl_fromhex_array!(64);
213impl_fromhex_array!(65);
214impl_fromhex_array!(128);
215impl_fromhex_array!(256);
216impl_fromhex_array!(384);
217impl_fromhex_array!(512);
218
219#[cfg(test)]
220mod tests {
221    use core::fmt;
222
223    use super::*;
224
225    #[test]
226    fn hex_roundtrip() {
227        let expected = "0123456789abcdef";
228        let expected_up = "0123456789ABCDEF";
229
230        let parse: Vec<u8> = FromHex::from_hex(expected).expect("parse lowercase string");
231        assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
232        let ser = parse.to_hex();
233        assert_eq!(ser, expected);
234
235        let parse: Vec<u8> = FromHex::from_hex(expected_up).expect("parse uppercase string");
236        assert_eq!(parse, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
237        let ser = parse.to_hex();
238        assert_eq!(ser, expected);
239
240        let parse: [u8; 8] = FromHex::from_hex(expected_up).expect("parse uppercase string");
241        assert_eq!(parse, [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
242        let ser = parse.to_hex();
243        assert_eq!(ser, expected);
244    }
245
246    #[test]
247    fn hex_truncate() {
248        struct HexBytes(Vec<u8>);
249        impl fmt::LowerHex for HexBytes {
250            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { format_hex(&self.0, f) }
251        }
252
253        let bytes = HexBytes(vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
254
255        assert_eq!(format!("{:x}", bytes), "0102030405060708090a");
256
257        for i in 0..20 {
258            assert_eq!(format!("{:.prec$x}", bytes, prec = i), &"0102030405060708090a"[0..i]);
259        }
260
261        assert_eq!(format!("{:25x}", bytes), "000000102030405060708090a");
262        assert_eq!(format!("{:26x}", bytes), "0000000102030405060708090a");
263    }
264
265    #[test]
266    fn hex_truncate_rev() {
267        struct HexBytes(Vec<u8>);
268        impl fmt::LowerHex for HexBytes {
269            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { format_hex_reverse(&self.0, f) }
270        }
271
272        let bytes = HexBytes(vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
273
274        assert_eq!(format!("{:x}", bytes), "0a090807060504030201");
275
276        for i in 0..20 {
277            assert_eq!(format!("{:.prec$x}", bytes, prec = i), &"0a090807060504030201"[0..i]);
278        }
279
280        assert_eq!(format!("{:25x}", bytes), "000000a090807060504030201");
281        assert_eq!(format!("{:26x}", bytes), "0000000a090807060504030201");
282    }
283
284    #[test]
285    fn hex_error() {
286        let oddlen = "0123456789abcdef0";
287        let badchar1 = "Z123456789abcdef";
288        let badchar2 = "012Y456789abcdeb";
289        let badchar3 = "«23456789abcdef";
290
291        assert_eq!(Vec::<u8>::from_hex(oddlen), Err(Error::OddLengthString(17)));
292        assert_eq!(<[u8; 4]>::from_hex(oddlen), Err(Error::OddLengthString(17)));
293        assert_eq!(<[u8; 8]>::from_hex(oddlen), Err(Error::OddLengthString(17)));
294        assert_eq!(Vec::<u8>::from_hex(badchar1), Err(Error::InvalidChar(b'Z')));
295        assert_eq!(Vec::<u8>::from_hex(badchar2), Err(Error::InvalidChar(b'Y')));
296        assert_eq!(Vec::<u8>::from_hex(badchar3), Err(Error::InvalidChar(194)));
297    }
298}