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}