yevm-misc 0.1.0

Utilities (keccak256, hex, HTTP client, byte buffer) shared across the YEVM crates.
Documentation
use std::fmt;

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Hex<const N: usize>([u8; N]);

impl<const N: usize> Hex<N> {
    pub const N: usize = N;
    pub const ZERO: Self = Self::zero();
    pub const ONE: Self = Self::one();
    pub const MAX: Self = Self::max();

    pub const fn new(bytes: [u8; N]) -> Self {
        Self(bytes)
    }

    pub const fn zero() -> Self {
        Self([0; N])
    }

    pub const fn one() -> Self {
        let mut buf = [0; N];
        buf[N - 1] = 1;
        Self(buf)
    }

    pub const fn max() -> Self {
        Self([0xff; N])
    }

    pub fn is_zero(&self) -> bool {
        self == &Self::ZERO
    }

    pub fn as_usize(&self) -> usize {
        const K: usize = size_of::<usize>();
        let mut b = [0u8; K];
        b.copy_from_slice(self.to::<K>().as_ref());
        usize::from_be_bytes(b)
    }

    pub fn as_usize_checked(&self) -> Option<usize> {
        const K: usize = size_of::<usize>();
        if self.0[..N - K].iter().any(|&b| b != 0) {
            return None;
        }
        Some(self.as_usize())
    }

    pub fn as_u128(&self) -> u128 {
        const K: usize = size_of::<u128>();
        let mut b = [0u8; K];
        b.copy_from_slice(self.to::<K>().as_ref());
        u128::from_be_bytes(b)
    }

    pub fn as_u64(&self) -> u64 {
        const K: usize = size_of::<u64>();
        let mut b = [0u8; K];
        b.copy_from_slice(self.to::<K>().as_ref());
        u64::from_be_bytes(b)
    }

    pub fn as_u32(&self) -> u32 {
        const K: usize = size_of::<u32>();
        let mut b = [0u8; K];
        b.copy_from_slice(self.to::<K>().as_ref());
        u32::from_be_bytes(b)
    }

    pub fn as_u16(&self) -> u16 {
        const K: usize = size_of::<u16>();
        let mut b = [0u8; K];
        b.copy_from_slice(self.to::<K>().as_ref());
        u16::from_be_bytes(b)
    }

    pub fn as_u8(&self) -> u8 {
        self.0[N - 1]
    }
}

impl<const N: usize> AsRef<[u8]> for Hex<N> {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl<const N: usize> From<&[u8]> for Hex<N> {
    fn from(value: &[u8]) -> Self {
        assert!(value.len() <= N, "data loss detected");
        let mut buffer = [0u8; N];
        let take = value.len().min(N);
        let skip = N - take;
        buffer[skip..].copy_from_slice(&value[..take]);
        Self(buffer)
    }
}

impl<const N: usize> From<usize> for Hex<N> {
    fn from(value: usize) -> Self {
        Self::from(&value.to_be_bytes()[..])
    }
}

impl<const N: usize> From<u128> for Hex<N> {
    fn from(value: u128) -> Self {
        Self::from(&value.to_be_bytes()[..])
    }
}

impl<const N: usize> From<u64> for Hex<N> {
    fn from(value: u64) -> Self {
        Self::from(&value.to_be_bytes()[..])
    }
}

impl<const N: usize> From<u32> for Hex<N> {
    fn from(value: u32) -> Self {
        Self::from(&value.to_be_bytes()[..])
    }
}

impl<const N: usize> From<u16> for Hex<N> {
    fn from(value: u16) -> Self {
        Self::from(&value.to_be_bytes()[..])
    }
}

impl<const N: usize> From<u8> for Hex<N> {
    fn from(value: u8) -> Self {
        Self::from(&[value][..])
    }
}

impl<const N: usize> From<i32> for Hex<N> {
    fn from(value: i32) -> Self {
        Self::from(value.max(0) as u32)
    }
}

impl<const N: usize> From<i64> for Hex<N> {
    fn from(value: i64) -> Self {
        Self::from(value.max(0) as u64)
    }
}

impl<const N: usize> Hex<N> {
    pub fn to<const M: usize>(self) -> Hex<M> {
        let mut buffer = [0; M];
        if M > N {
            buffer[(M - N)..].copy_from_slice(&self.0);
        } else {
            buffer[..].copy_from_slice(&self.0[(N - M)..]);
        }
        Hex(buffer)
    }
}

impl<const N: usize> Default for Hex<N> {
    fn default() -> Self {
        Self([0; N])
    }
}

impl<const N: usize> fmt::Display for Hex<N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("0x")?;
        for b in &self.0 {
            write!(f, "{:02x}", b)?;
        }
        Ok(())
    }
}

impl<const N: usize> fmt::Debug for Hex<N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("0x")?;
        for b in &self.0 {
            write!(f, "{:02x}", b)?;
        }
        Ok(())
    }
}

impl<const N: usize> serde::Serialize for Hex<N> {
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        s.collect_str(self)
    }
}

impl<'de, const N: usize> serde::Deserialize<'de> for Hex<N> {
    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
        let s = String::deserialize(d)?;
        let hex = s.strip_prefix("0x").unwrap_or(&s);
        let ok = hex.as_bytes().iter().all(|&c| c.is_ascii_hexdigit());
        if !ok {
            return Err(serde::de::Error::custom("hex literal invalid"));
        }
        if hex.len() > 2 * N {
            return Err(serde::de::Error::custom("hex literal too long"));
        }
        let bytes = parse(&s);
        Ok(Self(bytes))
    }
}

pub fn parse_vec(s: &str) -> Result<Vec<u8>, &'static str> {
    let skip = if s.len() >= 2 && s.as_bytes()[0] == b'0' && s.as_bytes()[1] == b'x' {
        2
    } else {
        0
    };
    let len = s.len() - skip;
    let n = len.div_ceil(2);
    let offset = n * 2 - len;
    let mut ret = vec![0u8; n];
    for j in skip..s.len() {
        let c = s.as_bytes()[j];
        let d = match c {
            b'0'..=b'9' => c - b'0',
            b'a'..=b'f' => c - b'a' + 10,
            b'A'..=b'F' => c - b'A' + 10,
            _ => return Err("hex literal invalid"),
        };
        let k = j - skip;
        let i = offset + k;
        let b = i / 2;
        if i.is_multiple_of(2) {
            ret[b] = d << 4;
        } else {
            ret[b] |= d;
        }
    }
    Ok(ret)
}

pub const fn parse<const N: usize>(s: &str) -> [u8; N] {
    let skip = if s.len() >= 2 && s.as_bytes()[0] == b'0' && s.as_bytes()[1] == b'x' {
        2
    } else {
        0
    };
    if s.len() - skip > N * 2 {
        panic!("hex literal too long");
    }
    let len = s.len() - skip;
    let offset = N * 2 - len;
    let mut ret = [0u8; N];
    let mut j = skip;
    while j < s.len() {
        let c = s.as_bytes()[j];
        let d = match c {
            b'0'..=b'9' => c - b'0',
            b'a'..=b'f' => c - b'a' + 10,
            b'A'..=b'F' => c - b'A' + 10,
            _ => panic!("hex literal invalid"),
        };
        let i = offset + (j - skip);
        let b = i / 2;
        if i.is_multiple_of(2) {
            ret[b] = d << 4;
        } else {
            ret[b] |= d;
        }
        j += 1;
    }
    ret
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_empty() {
        assert_eq!(parse::<4>(""), [0x00, 0x00, 0x00, 0x00]);
    }

    #[test]
    fn test_parse_single_nibble() {
        assert_eq!(parse::<1>("f"), [0x0f]);
        assert_eq!(parse::<4>("f"), [0x00, 0x00, 0x00, 0x0f]);
    }

    #[test]
    fn test_parse_single_byte() {
        assert_eq!(parse::<1>("ff"), [0xff]);
        assert_eq!(parse::<4>("ff"), [0x00, 0x00, 0x00, 0xff]);
    }

    #[test]
    fn test_parse_exact() {
        assert_eq!(parse::<4>("deadbeef"), [0xde, 0xad, 0xbe, 0xef]);
        assert_eq!(parse::<4>("00000000"), [0x00, 0x00, 0x00, 0x00]);
        assert_eq!(parse::<4>("ffffffff"), [0xff, 0xff, 0xff, 0xff]);
    }

    #[test]
    fn test_parse_short() {
        assert_eq!(parse::<4>("beef"), [0x00, 0x00, 0xbe, 0xef]);
        assert_eq!(parse::<4>("1"), [0x00, 0x00, 0x00, 0x01]);
    }

    #[test]
    fn test_parse_with_prefix() {
        assert_eq!(parse::<4>("0xdeadbeef"), [0xde, 0xad, 0xbe, 0xef]);
        assert_eq!(parse::<1>("0xff"), [0xff]);
        assert_eq!(parse::<4>("0xbeef"), [0x00, 0x00, 0xbe, 0xef]);
        assert_eq!(parse::<4>("0x1"), [0x00, 0x00, 0x00, 0x01]);
    }

    #[test]
    #[should_panic]
    fn test_parse_too_long() {
        let _ = parse::<4>("deadbeef00");
    }

    #[test]
    #[should_panic]
    fn test_parse_too_long_with_prefix() {
        let _ = parse::<4>("0xdeadbeef00");
    }

    #[test]
    #[should_panic]
    fn test_parse_invalid_char() {
        let _ = parse::<4>("xyz0");
    }

    #[test]
    fn test_display() {
        let h = Hex::<4>::from(&[0x00, 0x00, 0xbe, 0xef][..]);
        assert_eq!(h.to_string(), "0x0000beef");
        let z = Hex::<4>::ZERO;
        assert_eq!(z.to_string(), "0x00000000");
        let one = Hex::<4>::ONE;
        assert_eq!(one.to_string(), "0x00000001");
    }

    #[test]
    fn test_debug() {
        let h = Hex::<4>::from(&[0x00, 0x00, 0xbe, 0xef][..]);
        assert_eq!(format!("{:?}", h), "0x0000beef");
    }

    #[test]
    fn test_serde_roundtrip() {
        let h = Hex::<4>::from(&[0xde, 0xad, 0xbe, 0xef][..]);
        let s = serde_json::to_string(&h).unwrap();
        let h2: Hex<4> = serde_json::from_str(&s).unwrap();
        assert_eq!(h, h2);
    }

    #[test]
    fn test_parse_vec_empty() {
        assert_eq!(parse_vec("").unwrap(), vec![] as Vec<u8>);
        assert_eq!(parse_vec("0x").unwrap(), vec![] as Vec<u8>);
    }

    #[test]
    fn test_parse_vec_no_prefix() {
        assert_eq!(parse_vec("abcd").unwrap(), vec![0xab, 0xcd]);
        assert_eq!(parse_vec("ff").unwrap(), vec![0xff]);
        assert_eq!(parse_vec("1").unwrap(), vec![0x01]);
        assert_eq!(parse_vec("deadbeef").unwrap(), vec![0xde, 0xad, 0xbe, 0xef]);
    }

    #[test]
    fn test_parse_vec_with_prefix() {
        assert_eq!(parse_vec("0xabcd").unwrap(), vec![0xab, 0xcd]);
        assert_eq!(parse_vec("0xff").unwrap(), vec![0xff]);
        assert_eq!(parse_vec("0x1").unwrap(), vec![0x01]);
        assert_eq!(
            parse_vec("0xdeadbeef").unwrap(),
            vec![0xde, 0xad, 0xbe, 0xef]
        );
    }

    #[test]
    fn test_parse_vec_odd_nibble() {
        assert_eq!(parse_vec("abc").unwrap(), vec![0x0a, 0xbc]);
        assert_eq!(parse_vec("0xabc").unwrap(), vec![0x0a, 0xbc]);
    }

    #[test]
    fn test_parse_vec_invalid_char() {
        assert!(parse_vec("0xgg").is_err());
    }
}