darkfi_sdk/hex.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2024 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{util::Itertools, ContractError, GenericResult};
/// Creates a hex formatted string of the data
#[inline]
pub fn hex_from_iter<I: Iterator<Item = u8>>(iter: I) -> String {
let mut repr = String::new();
for b in iter {
repr += &format!("{:02x}", b);
}
repr
}
/// Decode hex string into bytes
pub fn decode_hex(hex: &str) -> HexDecodeIter {
HexDecodeIter { hex, curr: 0 }
}
pub struct HexDecodeIter<'a> {
hex: &'a str,
curr: usize,
}
impl Iterator for HexDecodeIter<'_> {
type Item = GenericResult<u8>;
// FromIterator auto converts [Result<u8>, ...] into Result<[u8, ...]>
// https://stackoverflow.com/a/26370894
fn next(&mut self) -> Option<Self::Item> {
// Stop iteration
if self.curr == self.hex.len() {
return None
}
// End of next 2 chars is past the end of the hex string
if self.curr + 2 > self.hex.len() {
return Some(Err(ContractError::HexFmtErr))
}
// Decode the next 2 chars
let Ok(byte) = u8::from_str_radix(&self.hex[self.curr..self.curr + 2], 16) else {
return Some(Err(ContractError::HexFmtErr))
};
self.curr += 2;
Some(Ok(byte))
}
}
pub fn decode_hex_arr<const N: usize>(hex: &str) -> GenericResult<[u8; N]> {
let decoded: Vec<_> = decode_hex(hex).try_collect()?;
let bytes: [u8; N] = decoded.try_into().map_err(|_| ContractError::HexFmtErr)?;
Ok(bytes)
}
pub trait AsHex {
fn hex(&self) -> String;
}
impl<T: AsRef<[u8]>> AsHex for T {
/// Creates a hex formatted string of the data (big endian)
fn hex(&self) -> String {
hex_from_iter(self.as_ref().iter().cloned())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hex_encode_decode() {
let decoded = decode_hex("0a00").collect::<GenericResult<Vec<_>>>().unwrap();
assert_eq!(decoded, vec![10, 0]);
assert!(decode_hex("0x").collect::<GenericResult<Vec<_>>>().is_err());
assert!(decode_hex("0a1").collect::<GenericResult<Vec<_>>>().is_err());
assert_eq!(hex_from_iter([10u8, 0].into_iter()), "0a00");
}
}