use std::{cmp::Ordering, str::FromStr}; use serde::{Deserialize, Deserializer, Serialize}; #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Id20(pub [u8; 20]); impl FromStr for Id20 { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let mut out = [0u8; 20]; if s.len() != 40 { anyhow::bail!("expected a hex string of length 40") }; hex::decode_to_slice(s, &mut out)?; Ok(Id20(out)) } } impl std::fmt::Debug for Id20 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "<")?; for byte in self.0 { write!(f, "{:02x?}", byte)?; } write!(f, ">")?; Ok(()) } } impl Serialize for Id20 { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_bytes(&self.0) } } impl<'de> Deserialize<'de> for Id20 { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = Id20; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "a 20 byte slice") } fn visit_bytes(self, v: &[u8]) -> Result where E: serde::de::Error, { if v.len() != 20 { return Err(E::invalid_length(20, &self)); } let mut buf = [0u8; 20]; buf.copy_from_slice(&v); Ok(Id20(buf)) } } deserializer.deserialize_bytes(Visitor {}) } } impl Id20 { pub fn distance(&self, other: &Id20) -> Id20 { let mut xor = [0u8; 20]; for (idx, (s, o)) in self .0 .iter() .copied() .zip(other.0.iter().copied()) .enumerate() { xor[idx] = s ^ o; } Id20(xor) } pub fn set_bit(&mut self, bit: u8, value: bool) { let n = &mut self.0[(bit / 8) as usize]; if value { *n |= 1 << (7 - bit % 8) } else { let mask = !(1 << (7 - bit % 8)); *n &= mask; } } pub fn set_bits_range(&mut self, r: std::ops::Range, value: bool) { for bit in r { self.set_bit(bit, value) } } } impl Ord for Id20 { fn cmp(&self, other: &Id20) -> Ordering { for (s, o) in self.0.iter().copied().zip(other.0.iter().copied()) { match s.cmp(&o) { Ordering::Less => return Ordering::Less, Ordering::Equal => continue, Ordering::Greater => return Ordering::Greater, } } Ordering::Equal } } impl PartialOrd for Id20 { fn partial_cmp(&self, other: &Id20) -> Option { Some(self.cmp(other)) } } #[cfg(test)] mod tests { use super::Id20; #[test] fn test_set_bit_range() { let mut id = Id20([0u8; 20]); id.set_bits_range(9..17, true); assert_eq!( id, Id20([0, 127, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) ) } }