essential_types/
convert.rs

1//! Helper functions for converting between byte, word and hex string representations.
2
3use crate::{ContentAddress, Signature, Word};
4pub use hex::FromHexError;
5
6/// Convert a hex string slice to a `Vec<Word>`.
7pub fn words_from_hex_str(str: &str) -> Result<Vec<Word>, FromHexError> {
8    Ok(hex::decode(str)?
9        .chunks_exact(8)
10        .map(|chunk| word_from_bytes(chunk.try_into().expect("Word is always 8 bytes")))
11        .collect())
12}
13
14/// Convert a slice of `Word`s to a hex string.
15pub fn hex_str_from_words(words: &[Word]) -> String {
16    hex::encode(
17        words
18            .iter()
19            .flat_map(|word| bytes_from_word(*word))
20            .collect::<Vec<u8>>(),
21    )
22}
23
24/// Convert a `Word` to its bytes.
25pub fn bytes_from_word(w: Word) -> [u8; 8] {
26    w.to_be_bytes()
27}
28
29/// Convert a fixed array of bytes to a `Word`.
30pub fn word_from_bytes(bytes: [u8; 8]) -> Word {
31    Word::from_be_bytes(bytes)
32}
33
34/// Convert a slice of bytes to a `Word`.
35///
36/// Ignores any bytes beyond the first 8.
37pub fn word_from_bytes_slice(bytes: &[u8]) -> Word {
38    let mut word = [0; core::mem::size_of::<Word>()];
39    let len = bytes.len().min(word.len());
40    word[..len].copy_from_slice(&bytes[..len]);
41    word_from_bytes(word)
42}
43
44/// A common conversion for 32-byte hashes and other addresses.
45#[rustfmt::skip]
46pub fn word_4_from_u8_32(bytes: [u8; 32]) -> [Word; 4] {
47    // TODO: This should be converted to use const-slicing if Rust ever provides
48    // some kind of support for it.
49    let [
50        b0, b1, b2, b3, b4, b5, b6, b7,
51        b8, b9, b10, b11, b12, b13, b14, b15,
52        b16, b17, b18, b19, b20, b21, b22, b23,
53        b24, b25, b26, b27, b28, b29, b30, b31,
54    ] = bytes;
55    [
56        word_from_bytes([b0, b1, b2, b3, b4, b5, b6, b7]),
57        word_from_bytes([b8, b9, b10, b11, b12, b13, b14, b15]),
58        word_from_bytes([b16, b17, b18, b19, b20, b21, b22, b23]),
59        word_from_bytes([b24, b25, b26, b27, b28, b29, b30, b31]),
60    ]
61}
62
63/// A common conversion for 64-byte signatures.
64#[rustfmt::skip]
65pub fn word_8_from_u8_64(bytes: [u8; 64]) -> [Word; 8] {
66    // TODO: This should be converted to use const-slicing if Rust ever provides
67    // some kind of support for it.
68    let [
69        b0, b1, b2, b3, b4, b5, b6, b7,
70        b8, b9, b10, b11, b12, b13, b14, b15,
71        b16, b17, b18, b19, b20, b21, b22, b23,
72        b24, b25, b26, b27, b28, b29, b30, b31,
73        b32, b33, b34, b35, b36, b37, b38, b39,
74        b40, b41, b42, b43, b44, b45, b46, b47,
75        b48, b49, b50, b51, b52, b53, b54, b55,
76        b56, b57, b58, b59, b60, b61, b62, b63,
77    ] = bytes;
78    [
79        word_from_bytes([b0, b1, b2, b3, b4, b5, b6, b7]),
80        word_from_bytes([b8, b9, b10, b11, b12, b13, b14, b15]),
81        word_from_bytes([b16, b17, b18, b19, b20, b21, b22, b23]),
82        word_from_bytes([b24, b25, b26, b27, b28, b29, b30, b31]),
83        word_from_bytes([b32, b33, b34, b35, b36, b37, b38, b39]),
84        word_from_bytes([b40, b41, b42, b43, b44, b45, b46, b47]),
85        word_from_bytes([b48, b49, b50, b51, b52, b53, b54, b55]),
86        word_from_bytes([b56, b57, b58, b59, b60, b61, b62, b63]),
87    ]
88}
89
90/// A common conversion for 32-byte hashes and other addresses.
91#[rustfmt::skip]
92pub fn u8_32_from_word_4(words: [Word; 4]) -> [u8; 32] {
93    // TODO: This should be converted to use const array concatenation if Rust
94    // ever provides some kind of support for it.
95    let [w0, w1, w2, w3] = words;
96    let [b0, b1, b2, b3, b4, b5, b6, b7] = bytes_from_word(w0);
97    let [b8, b9, b10, b11, b12, b13, b14, b15] = bytes_from_word(w1);
98    let [b16, b17, b18, b19, b20, b21, b22, b23] = bytes_from_word(w2);
99    let [b24, b25, b26, b27, b28, b29, b30, b31] = bytes_from_word(w3);
100    [
101        b0, b1, b2, b3, b4, b5, b6, b7,
102        b8, b9, b10, b11, b12, b13, b14, b15,
103        b16, b17, b18, b19, b20, b21, b22, b23,
104        b24, b25, b26, b27, b28, b29, b30, b31,
105    ]
106}
107
108/// A common conversion for 64-byte signatures.
109#[rustfmt::skip]
110pub fn u8_64_from_word_8(words: [Word; 8]) -> [u8; 64] {
111    // TODO: This should be converted to use const array concatenation if Rust
112    // ever provides some kind of support for it.
113    let [w0, w1, w2, w3, w4, w5, w6, w7] = words;
114    let [b0, b1, b2, b3, b4, b5, b6, b7] = bytes_from_word(w0);
115    let [b8, b9, b10, b11, b12, b13, b14, b15] = bytes_from_word(w1);
116    let [b16, b17, b18, b19, b20, b21, b22, b23] = bytes_from_word(w2);
117    let [b24, b25, b26, b27, b28, b29, b30, b31] = bytes_from_word(w3);
118    let [b32, b33, b34, b35, b36, b37, b38, b39] = bytes_from_word(w4);
119    let [b40, b41, b42, b43, b44, b45, b46, b47] = bytes_from_word(w5);
120    let [b48, b49, b50, b51, b52, b53, b54, b55] = bytes_from_word(w6);
121    let [b56, b57, b58, b59, b60, b61, b62, b63] = bytes_from_word(w7);
122    [
123        b0, b1, b2, b3, b4, b5, b6, b7,
124        b8, b9, b10, b11, b12, b13, b14, b15,
125        b16, b17, b18, b19, b20, b21, b22, b23,
126        b24, b25, b26, b27, b28, b29, b30, b31,
127        b32, b33, b34, b35, b36, b37, b38, b39,
128        b40, b41, b42, b43, b44, b45, b46, b47,
129        b48, b49, b50, b51, b52, b53, b54, b55,
130        b56, b57, b58, b59, b60, b61, b62, b63,
131    ]
132}
133
134/// Convert a `Word` to its `bool` representation.
135///
136/// Returns `None` if the given `Word` is not `0` or `1`.
137pub fn bool_from_word(word: Word) -> Option<bool> {
138    match word {
139        0 => Some(false),
140        1 => Some(true),
141        _ => None,
142    }
143}
144
145impl From<ContentAddress> for [Word; 4] {
146    fn from(address: ContentAddress) -> Self {
147        word_4_from_u8_32(address.0)
148    }
149}
150
151impl From<ContentAddress> for [u8; 32] {
152    fn from(address: ContentAddress) -> Self {
153        address.0
154    }
155}
156
157impl From<[Word; 4]> for ContentAddress {
158    fn from(address: [Word; 4]) -> Self {
159        Self(u8_32_from_word_4(address))
160    }
161}
162
163impl From<[u8; 32]> for ContentAddress {
164    fn from(address: [u8; 32]) -> Self {
165        Self(address)
166    }
167}
168
169impl From<Signature> for [u8; 65] {
170    #[rustfmt::skip]
171    fn from(sig: Signature) -> Self {
172        let [
173            b0, b1, b2, b3, b4, b5, b6, b7,
174            b8, b9, b10, b11, b12, b13, b14, b15,
175            b16, b17, b18, b19, b20, b21, b22, b23,
176            b24, b25, b26, b27, b28, b29, b30, b31,
177            b32, b33, b34, b35, b36, b37, b38, b39,
178            b40, b41, b42, b43, b44, b45, b46, b47,
179            b48, b49, b50, b51, b52, b53, b54, b55,
180            b56, b57, b58, b59, b60, b61, b62, b63,
181        ] = sig.0;
182        [
183            b0, b1, b2, b3, b4, b5, b6, b7,
184            b8, b9, b10, b11, b12, b13, b14, b15,
185            b16, b17, b18, b19, b20, b21, b22, b23,
186            b24, b25, b26, b27, b28, b29, b30, b31,
187            b32, b33, b34, b35, b36, b37, b38, b39,
188            b40, b41, b42, b43, b44, b45, b46, b47,
189            b48, b49, b50, b51, b52, b53, b54, b55,
190            b56, b57, b58, b59, b60, b61, b62, b63,
191            sig.1,
192        ]
193    }
194}
195
196impl From<[u8; 65]> for Signature {
197    #[rustfmt::skip]
198    fn from(bytes: [u8; 65]) -> Self {
199        let [
200            b0, b1, b2, b3, b4, b5, b6, b7,
201            b8, b9, b10, b11, b12, b13, b14, b15,
202            b16, b17, b18, b19, b20, b21, b22, b23,
203            b24, b25, b26, b27, b28, b29, b30, b31,
204            b32, b33, b34, b35, b36, b37, b38, b39,
205            b40, b41, b42, b43, b44, b45, b46, b47,
206            b48, b49, b50, b51, b52, b53, b54, b55,
207            b56, b57, b58, b59, b60, b61, b62, b63,
208            id,
209        ] = bytes;
210        let sig = [
211            b0, b1, b2, b3, b4, b5, b6, b7,
212            b8, b9, b10, b11, b12, b13, b14, b15,
213            b16, b17, b18, b19, b20, b21, b22, b23,
214            b24, b25, b26, b27, b28, b29, b30, b31,
215            b32, b33, b34, b35, b36, b37, b38, b39,
216            b40, b41, b42, b43, b44, b45, b46, b47,
217            b48, b49, b50, b51, b52, b53, b54, b55,
218            b56, b57, b58, b59, b60, b61, b62, b63,
219        ];
220        Signature(sig, id)
221    }
222}
223
224#[cfg(test)]
225mod tests {
226    use super::*;
227
228    // Sample data for tests
229    const WORD_SAMPLE: Word = 0x123456789ABCDEF0;
230    const BYTES_SAMPLE: [u8; 8] = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
231    const U8_32_SAMPLE: [u8; 32] = [
232        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
233        0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
234        0x1E, 0x1F,
235    ];
236    const U8_64_SAMPLE: [u8; 64] = [
237        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
238        0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
239        0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C,
240        0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
241        0x3C, 0x3D, 0x3E, 0x3F,
242    ];
243
244    #[test]
245    fn test_bytes_from_word() {
246        assert_eq!(bytes_from_word(WORD_SAMPLE), BYTES_SAMPLE);
247    }
248
249    #[test]
250    fn test_word_from_bytes() {
251        assert_eq!(word_from_bytes(BYTES_SAMPLE), WORD_SAMPLE);
252    }
253
254    #[test]
255    fn test_word_4_from_u8_32() {
256        let expected_words = [
257            Word::from_be_bytes([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]),
258            Word::from_be_bytes([0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]),
259            Word::from_be_bytes([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]),
260            Word::from_be_bytes([0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F]),
261        ];
262        assert_eq!(word_4_from_u8_32(U8_32_SAMPLE), expected_words);
263    }
264
265    #[test]
266    fn test_u8_32_from_word_4() {
267        let words = [
268            Word::from_be_bytes([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]),
269            Word::from_be_bytes([0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]),
270            Word::from_be_bytes([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]),
271            Word::from_be_bytes([0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F]),
272        ];
273        assert_eq!(u8_32_from_word_4(words), U8_32_SAMPLE);
274    }
275
276    #[test]
277    fn test_word_8_from_u8_64() {
278        let expected_words = [
279            Word::from_be_bytes([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]),
280            Word::from_be_bytes([0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]),
281            Word::from_be_bytes([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]),
282            Word::from_be_bytes([0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F]),
283            Word::from_be_bytes([0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27]),
284            Word::from_be_bytes([0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F]),
285            Word::from_be_bytes([0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37]),
286            Word::from_be_bytes([0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F]),
287        ];
288        assert_eq!(word_8_from_u8_64(U8_64_SAMPLE), expected_words);
289    }
290
291    #[test]
292    fn test_u8_64_from_word_8() {
293        let words = [
294            Word::from_be_bytes([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]),
295            Word::from_be_bytes([0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]),
296            Word::from_be_bytes([0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17]),
297            Word::from_be_bytes([0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F]),
298            Word::from_be_bytes([0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27]),
299            Word::from_be_bytes([0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F]),
300            Word::from_be_bytes([0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37]),
301            Word::from_be_bytes([0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F]),
302        ];
303        assert_eq!(u8_64_from_word_8(words), U8_64_SAMPLE);
304    }
305}