use std::cmp::Ordering;
use dig_nat::PeerId;
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Key([u8; 32]);
impl Key {
pub const fn from_bytes(bytes: [u8; 32]) -> Self {
Key(bytes)
}
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub fn from_peer_id(peer_id: &PeerId) -> Self {
Key(*peer_id.as_bytes())
}
pub fn distance(&self, other: &Key) -> Distance {
let mut out = [0u8; 32];
for (i, o) in out.iter_mut().enumerate() {
*o = self.0[i] ^ other.0[i];
}
Distance(out)
}
pub fn to_hex(&self) -> String {
to_hex(&self.0)
}
}
impl std::fmt::Debug for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Key({})", self.to_hex())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Distance([u8; 32]);
impl Distance {
pub const ZERO: Distance = Distance([0u8; 32]);
pub const fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
pub fn bucket_index(&self) -> Option<usize> {
for (i, byte) in self.0.iter().enumerate() {
if *byte != 0 {
let bit_in_byte = byte.leading_zeros() as usize; let leading_zero_bits = i * 8 + bit_in_byte;
return Some(255 - leading_zero_bits);
}
}
None
}
pub fn to_hex(&self) -> String {
to_hex(&self.0)
}
}
impl PartialOrd for Distance {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Distance {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl std::fmt::Debug for Distance {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Distance({})", self.to_hex())
}
}
fn to_hex(bytes: &[u8; 32]) -> String {
let mut s = String::with_capacity(64);
for b in bytes {
s.push(char::from_digit((b >> 4) as u32, 16).unwrap());
s.push(char::from_digit((b & 0x0f) as u32, 16).unwrap());
}
s
}
#[cfg(test)]
mod tests {
use super::*;
fn key(byte0: u8, rest: u8) -> Key {
let mut b = [rest; 32];
b[0] = byte0;
Key::from_bytes(b)
}
#[test]
fn xor_distance_is_symmetric() {
let a = key(0x12, 0x34);
let b = key(0xAB, 0xCD);
assert_eq!(a.distance(&b), b.distance(&a));
}
#[test]
fn distance_to_self_is_zero() {
let a = key(0x99, 0x01);
assert_eq!(a.distance(&a), Distance::ZERO);
assert_eq!(a.distance(&a).bucket_index(), None);
}
#[test]
fn xor_is_the_distance() {
let a = Key::from_bytes([0x00; 32]);
let mut bb = [0u8; 32];
bb[31] = 0x01;
let b = Key::from_bytes(bb);
assert_eq!(a.distance(&b).as_bytes()[31], 0x01);
assert_eq!(a.distance(&b).bucket_index(), Some(0));
}
#[test]
fn bucket_index_tracks_most_significant_set_bit() {
let zero = Key::from_bytes([0u8; 32]);
let mut hi = [0u8; 32];
hi[0] = 0x80;
assert_eq!(
zero.distance(&Key::from_bytes(hi)).bucket_index(),
Some(255)
);
let mut lo = [0u8; 32];
lo[31] = 0x01;
assert_eq!(zero.distance(&Key::from_bytes(lo)).bucket_index(), Some(0));
let mut b = [0u8; 32];
b[0] = 0x40;
assert_eq!(zero.distance(&Key::from_bytes(b)).bucket_index(), Some(254));
let mut c = [0u8; 32];
c[0] = 0x01;
assert_eq!(zero.distance(&Key::from_bytes(c)).bucket_index(), Some(248));
}
#[test]
fn closer_keys_have_smaller_distance() {
let target = Key::from_bytes([0u8; 32]);
let near = key(0x00, 0x01); let far = key(0xFF, 0x00); assert!(target.distance(&near) < target.distance(&far));
}
#[test]
fn from_peer_id_is_verbatim() {
let mut raw = [0u8; 32];
raw[0] = 0xDE;
raw[31] = 0xAD;
let pid = PeerId::from_bytes(raw);
assert_eq!(Key::from_peer_id(&pid).as_bytes(), &raw);
}
#[test]
fn hex_round_trips_length() {
let k = key(0x0a, 0xf0);
assert_eq!(k.to_hex().len(), 64);
assert!(k.to_hex().starts_with("0a"));
}
#[test]
fn distance_ordering_is_big_endian() {
let mut a = [0u8; 32];
a[0] = 0x01;
let mut b = [0u8; 32];
b[31] = 0xFF;
assert!(Distance(b) < Distance(a));
}
}