ckb_fixed_hash_core/
std_str.rs

1use crate::{error::FromStrError, H160, H256, H512, H520};
2
3pub(crate) const DICT_HEX_ERROR: u8 = u8::MAX;
4pub(crate) static DICT_HEX_LO: [u8; 256] = {
5    const ____: u8 = DICT_HEX_ERROR;
6    [
7        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
8        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
9        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
10        ____, ____, ____, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, ____, ____,
11        ____, ____, ____, ____, ____, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, ____, ____, ____, ____,
12        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
13        ____, ____, ____, ____, ____, ____, ____, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, ____, ____,
14        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
15        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
16        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
17        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
18        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
19        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
20        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
21        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
22        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
23        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
24        ____,
25    ]
26};
27pub(crate) static DICT_HEX_HI: [u8; 256] = {
28    const ____: u8 = DICT_HEX_ERROR;
29    [
30        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
31        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
32        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
33        ____, ____, ____, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, ____, ____,
34        ____, ____, ____, ____, ____, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, ____, ____, ____, ____,
35        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
36        ____, ____, ____, ____, ____, ____, ____, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, ____, ____,
37        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
38        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
39        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
40        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
41        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
42        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
43        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
44        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
45        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
46        ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____,
47        ____,
48    ]
49};
50
51macro_rules! impl_std_str_fromstr {
52    ($name:ident, $bytes_size:expr) => {
53        impl ::std::str::FromStr for $name {
54            type Err = FromStrError;
55            fn from_str(input: &str) -> Result<Self, Self::Err> {
56                let len = input.as_bytes().len();
57                if len != $bytes_size * 2 {
58                    return Err(FromStrError::InvalidLength(len));
59                }
60                let mut ret = Self::default();
61                for (idx, chr) in input.bytes().enumerate() {
62                    let val = if idx % 2 == 0 {
63                        DICT_HEX_HI[usize::from(chr)]
64                    } else {
65                        DICT_HEX_LO[usize::from(chr)]
66                    };
67                    if val == DICT_HEX_ERROR {
68                        return Err(FromStrError::InvalidCharacter { chr, idx });
69                    }
70                    ret.0[idx / 2] |= val;
71                }
72                Ok(ret)
73            }
74        }
75    };
76}
77
78macro_rules! impl_from_trimmed_str {
79    ($name:ident, $bytes_size:expr, $use_stmt:expr, $bytes_size_stmt:expr) => {
80        impl $name {
81            /// To convert a trimmed hexadecimal string into `Self`.
82            ///
83            /// If the beginning of a hexadecimal string are one or more zeros, then these zeros
84            /// should be omitted.
85            ///
86            /// There should be only one zero at the beginning of a hexadecimal string at most.
87            ///
88            /// For example, if `x` is `H16` (a 16 bits binary data):
89            /// - when `x = [0, 0]`, the trimmed hexadecimal string should be "0" or "".
90            /// - when `x = [0, 1]`, the trimmed hexadecimal string should be "1".
91            /// - when `x = [1, 0]`, the trimmed hexadecimal string should be "100".
92            ///
93            /// ```rust
94            #[doc = $use_stmt]
95            #[doc = $bytes_size_stmt]
96            ///
97            /// let mut inner = [0u8; BYTES_SIZE];
98            ///
99            /// {
100            ///     let actual = Hash(inner.clone());
101            ///     let expected1 = Hash::from_trimmed_str("").unwrap();
102            ///     let expected2 = Hash::from_trimmed_str("0").unwrap();
103            ///     assert_eq!(actual, expected1);
104            ///     assert_eq!(actual, expected2);
105            /// }
106            ///
107            /// {
108            ///     inner[BYTES_SIZE - 1] = 1;
109            ///     let actual = Hash(inner);
110            ///     let expected = Hash::from_trimmed_str("1").unwrap();
111            ///     assert_eq!(actual, expected);
112            /// }
113            ///
114            /// {
115            ///     assert!(Hash::from_trimmed_str("00").is_err());
116            ///     assert!(Hash::from_trimmed_str("000").is_err());
117            ///     assert!(Hash::from_trimmed_str("0000").is_err());
118            ///     assert!(Hash::from_trimmed_str("01").is_err());
119            ///     assert!(Hash::from_trimmed_str("001").is_err());
120            ///     assert!(Hash::from_trimmed_str("0001").is_err());
121            /// }
122            /// ```
123            pub fn from_trimmed_str(input: &str) -> Result<Self, FromStrError> {
124                let bytes = input.as_bytes();
125                let len = bytes.len();
126                if len > $bytes_size * 2 {
127                    Err(FromStrError::InvalidLength(len))
128                } else if len == 0 {
129                    Ok(Self::default())
130                } else if bytes[0] == b'0' {
131                    if len == 1 {
132                        Ok(Self::default())
133                    } else {
134                        Err(FromStrError::InvalidCharacter { chr: b'0', idx: 0 })
135                    }
136                } else {
137                    let mut ret = Self::default();
138                    let mut idx = 0;
139                    let mut unit_idx = ($bytes_size * 2 - len) / 2;
140                    let mut high = len % 2 == 0;
141                    for chr in input.bytes() {
142                        let val = if high {
143                            DICT_HEX_HI[usize::from(chr)]
144                        } else {
145                            DICT_HEX_LO[usize::from(chr)]
146                        };
147                        if val == DICT_HEX_ERROR {
148                            return Err(FromStrError::InvalidCharacter { chr, idx });
149                        }
150                        idx += 1;
151                        ret.0[unit_idx] |= val;
152                        if high {
153                            high = false;
154                        } else {
155                            high = true;
156                            unit_idx += 1;
157                        }
158                    }
159                    Ok(ret)
160                }
161            }
162        }
163    };
164    ($name:ident, $bytes_size:expr) => {
165        impl_from_trimmed_str!(
166            $name,
167            $bytes_size,
168            concat!("use ckb_fixed_hash_core::", stringify!($name), " as Hash;"),
169            concat!("const BYTES_SIZE: usize = ", stringify!($bytes_size), ";")
170        );
171    };
172}
173
174impl_std_str_fromstr!(H160, 20);
175impl_std_str_fromstr!(H256, 32);
176impl_std_str_fromstr!(H512, 64);
177impl_std_str_fromstr!(H520, 65);
178
179impl_from_trimmed_str!(H160, 20);
180impl_from_trimmed_str!(H256, 32);
181impl_from_trimmed_str!(H512, 64);
182impl_from_trimmed_str!(H520, 65);