#[cfg(feature = "codec")]
use bincode::{Decode, Encode};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "codec", derive(Encode, Decode))]
pub struct Id {
bytes: [u8; Self::BYTES],
}
impl Id {
pub const BYTES: usize = 32;
pub const BITS: usize = 32 * 8;
pub fn new(bytes: [u8; Self::BYTES]) -> Self {
Id { bytes }
}
pub fn bytes(&self) -> [u8; Self::BYTES] {
self.bytes
}
#[cfg(test)]
pub fn from_u16(raw: u16) -> Self {
let mut bytes = [0u8; Self::BYTES];
bytes[..2].copy_from_slice(&raw.to_le_bytes());
Self { bytes }
}
#[doc(hidden)]
pub fn rand() -> Self {
use rand::{thread_rng, Fill};
let mut rng = thread_rng();
let mut bytes = [0u8; Self::BYTES];
assert!(bytes.try_fill(&mut rng).is_ok());
Self { bytes }
}
pub fn log2_distance(&self, other: &Id) -> Option<u32> {
self.bytes
.iter()
.zip(other.bytes.iter())
.map(|(&a, &b)| a ^ b)
.enumerate()
.rev()
.find(|(_, byte)| byte != &0b0)
.map(|(i, byte)| Self::msb(byte) + ((i as u32) << 3))
}
fn msb(n: u8) -> u32 {
debug_assert_ne!(n, 0);
u8::BITS - n.leading_zeros() - 1
}
}
#[cfg(test)]
mod tests {
use rand::{thread_rng, Rng};
use super::*;
#[test]
fn id() {
const N: usize = 1000;
let mut rng = thread_rng();
for _ in 0..N {
let a = rng.gen();
let b = rng.gen();
if a == b {
continue;
}
let id_a = Id::from_u16(a);
let id_b = Id::from_u16(b);
let xor = a ^ b;
let xor_log2 = u16::BITS - xor.leading_zeros() - 1;
assert_eq!(id_a.log2_distance(&id_b), Some(xor_log2))
}
}
}