Skip to main content

stealth_lib/encoding/
hex_utils.rs

1//! Hexadecimal encoding and decoding utilities.
2//!
3//! This module provides convenient functions for working with hexadecimal
4//! representations of binary data.
5
6use crate::error::{Error, Result};
7
8#[cfg(not(feature = "std"))]
9extern crate alloc;
10#[cfg(not(feature = "std"))]
11use alloc::{string::String, vec::Vec};
12
13/// Encodes bytes as a hexadecimal string.
14///
15/// # Arguments
16///
17/// * `bytes` - The bytes to encode
18///
19/// # Returns
20///
21/// A lowercase hexadecimal string representation.
22///
23/// # Example
24///
25/// ```
26/// use stealth_lib::encoding::encode_hex;
27///
28/// let hex = encode_hex(&[0xde, 0xad, 0xbe, 0xef]);
29/// assert_eq!(hex, "deadbeef");
30/// ```
31pub fn encode_hex(bytes: &[u8]) -> String {
32    hex::encode(bytes)
33}
34
35/// Decodes a hexadecimal string to bytes.
36///
37/// # Arguments
38///
39/// * `hex_str` - The hexadecimal string to decode (with or without "0x" prefix)
40///
41/// # Returns
42///
43/// The decoded bytes, or an error if the input is invalid.
44///
45/// # Errors
46///
47/// Returns [`Error::ParseError`] if the string contains invalid hex characters
48/// or has an odd length.
49///
50/// # Example
51///
52/// ```
53/// use stealth_lib::encoding::decode_hex;
54///
55/// let bytes = decode_hex("deadbeef").unwrap();
56/// assert_eq!(bytes, vec![0xde, 0xad, 0xbe, 0xef]);
57///
58/// // Also works with 0x prefix
59/// let bytes = decode_hex("0xdeadbeef").unwrap();
60/// assert_eq!(bytes, vec![0xde, 0xad, 0xbe, 0xef]);
61/// ```
62pub fn decode_hex(hex_str: &str) -> Result<Vec<u8>> {
63    let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
64    hex::decode(hex_str).map_err(|e| Error::ParseError(e.to_string()))
65}
66
67/// Converts bytes to a u128 value.
68///
69/// # Arguments
70///
71/// * `bytes` - Exactly 16 bytes to convert
72///
73/// # Returns
74///
75/// The u128 value, or an error if the input length is wrong.
76///
77/// # Errors
78///
79/// Returns [`Error::InvalidLength`] if bytes is not exactly 16 bytes.
80///
81/// # Example
82///
83/// ```
84/// use stealth_lib::encoding::hex_utils::bytes_to_u128;
85///
86/// let bytes = [0u8; 16];
87/// let value = bytes_to_u128(&bytes).unwrap();
88/// assert_eq!(value, 0);
89/// ```
90pub fn bytes_to_u128(bytes: &[u8]) -> Result<u128> {
91    if bytes.len() != 16 {
92        return Err(Error::InvalidLength {
93            expected: 16,
94            actual: bytes.len(),
95        });
96    }
97
98    let mut array = [0u8; 16];
99    array.copy_from_slice(bytes);
100    Ok(u128::from_be_bytes(array))
101}
102
103/// Converts a u128 value to bytes.
104///
105/// # Arguments
106///
107/// * `value` - The u128 value to convert
108///
109/// # Returns
110///
111/// A 16-byte array in big-endian order.
112///
113/// # Example
114///
115/// ```
116/// use stealth_lib::encoding::hex_utils::u128_to_bytes;
117///
118/// let bytes = u128_to_bytes(0x0102030405060708090a0b0c0d0e0f10);
119/// assert_eq!(bytes[0], 0x01);
120/// assert_eq!(bytes[15], 0x10);
121/// ```
122pub fn u128_to_bytes(value: u128) -> [u8; 16] {
123    value.to_be_bytes()
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn test_encode_hex() {
132        assert_eq!(encode_hex(&[]), "");
133        assert_eq!(encode_hex(&[0x00]), "00");
134        assert_eq!(encode_hex(&[0xff]), "ff");
135        assert_eq!(encode_hex(&[0xde, 0xad, 0xbe, 0xef]), "deadbeef");
136    }
137
138    #[test]
139    fn test_decode_hex() {
140        assert_eq!(decode_hex("").unwrap(), vec![]);
141        assert_eq!(decode_hex("00").unwrap(), vec![0x00]);
142        assert_eq!(decode_hex("ff").unwrap(), vec![0xff]);
143        assert_eq!(decode_hex("deadbeef").unwrap(), vec![0xde, 0xad, 0xbe, 0xef]);
144    }
145
146    #[test]
147    fn test_decode_hex_with_prefix() {
148        assert_eq!(decode_hex("0x").unwrap(), vec![]);
149        assert_eq!(decode_hex("0xdeadbeef").unwrap(), vec![0xde, 0xad, 0xbe, 0xef]);
150    }
151
152    #[test]
153    fn test_decode_hex_invalid() {
154        assert!(decode_hex("gg").is_err());
155        assert!(decode_hex("0").is_err()); // Odd length
156    }
157
158    #[test]
159    fn test_bytes_to_u128() {
160        let bytes = [0u8; 16];
161        assert_eq!(bytes_to_u128(&bytes).unwrap(), 0);
162
163        let bytes = [0xff; 16];
164        assert_eq!(bytes_to_u128(&bytes).unwrap(), u128::MAX);
165    }
166
167    #[test]
168    fn test_bytes_to_u128_wrong_length() {
169        assert!(bytes_to_u128(&[0u8; 15]).is_err());
170        assert!(bytes_to_u128(&[0u8; 17]).is_err());
171    }
172
173    #[test]
174    fn test_u128_to_bytes_roundtrip() {
175        for value in [0u128, 1, u128::MAX, 12345678901234567890] {
176            let bytes = u128_to_bytes(value);
177            let recovered = bytes_to_u128(&bytes).unwrap();
178            assert_eq!(value, recovered);
179        }
180    }
181}