use std::cmp::Ordering;
use std::fmt;
use std::ops::Deref;
use alloy_primitives::{B256, U256};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::error::Result;
use crate::{Bin, ProximityOrder};
pub const MAX_PO: u8 = 31;
pub const EXTENDED_PO: u8 = MAX_PO + 5;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct SwarmAddress(pub B256);
impl SwarmAddress {
pub const fn with_first_byte(byte: u8) -> Self {
let mut bytes = [0u8; 32];
bytes[0] = byte;
Self(B256::new(bytes))
}
pub fn new(bytes: [u8; 32]) -> Self {
Self(B256::from(bytes))
}
pub const fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
let address = B256::try_from(slice)?;
Ok(Self(address))
}
pub fn is_zero(&self) -> bool {
self.0.is_zero()
}
pub const fn zero() -> Self {
Self(B256::ZERO)
}
#[inline(always)]
#[must_use]
pub fn distance(&self, y: &Self) -> U256 {
let mut result = [0u8; 32];
for (i, (&a, &b)) in self
.0
.as_slice()
.iter()
.zip(y.0.as_slice().iter())
.enumerate()
{
result[i] = a ^ b;
}
U256::from_be_bytes(result)
}
#[inline(always)]
#[must_use]
pub fn distance_cmp(&self, x: &Self, y: &Self) -> Ordering {
let (ab, xb, yb) = (self.0.as_slice(), x.0.as_slice(), y.0.as_slice());
for i in 0..ab.len() {
let dx = xb[i] ^ ab[i];
let dy = yb[i] ^ ab[i];
if dx != dy {
return match dx < dy {
true => Ordering::Greater,
false => Ordering::Less,
};
}
}
Ordering::Equal
}
#[must_use]
pub fn closer(&self, x: &Self, y: &Self) -> bool {
self.distance_cmp(x, y) == Ordering::Greater
}
pub fn is_within_proximity(&self, other: &Self, min_proximity: ProximityOrder) -> bool {
self.proximity(other) >= min_proximity
}
#[inline(always)]
#[must_use]
pub fn proximity(&self, other: &Self) -> ProximityOrder {
ProximityOrder::new_unchecked(self.proximity_helper(other, MAX_PO.into()))
}
#[inline(always)]
#[must_use]
pub fn extended_proximity(&self, other: &Self) -> u8 {
self.proximity_helper(other, EXTENDED_PO.into())
}
#[inline(always)]
#[must_use]
pub fn xor(&self, other: &Self) -> Self {
let mut out = [0u8; 32];
for (i, (a, b)) in self.0.as_slice().iter().zip(other.0.as_slice()).enumerate() {
out[i] = a ^ b;
}
Self(B256::from(out))
}
#[inline(always)]
#[must_use]
pub fn bin(&self, anchor: &Self) -> Bin {
Bin::from(self.proximity(anchor))
}
#[inline(always)]
fn proximity_helper(&self, other: &Self, max: usize) -> u8 {
let max_bytes = max / 8;
let max_bits = max as u8;
let bytes1 = self.0.as_slice();
let bytes2 = other.0.as_slice();
for i in 0..=max_bytes {
let xor = bytes1[i] ^ bytes2[i];
if xor != 0 {
let leading_zeros = xor.leading_zeros() as u8;
let proximity = (i as u8 * 8) + leading_zeros;
return if proximity < max_bits {
proximity
} else {
max_bits
};
}
if i == max_bytes {
return max_bits; }
}
max_bits
}
}
impl Default for SwarmAddress {
fn default() -> Self {
Self(B256::ZERO)
}
}
impl fmt::Display for SwarmAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Deref for SwarmAddress {
type Target = B256;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<B256> for SwarmAddress {
fn from(value: B256) -> Self {
Self(value)
}
}
impl From<[u8; 32]> for SwarmAddress {
fn from(bytes: [u8; 32]) -> Self {
Self::new(bytes)
}
}
impl From<SwarmAddress> for B256 {
fn from(addr: SwarmAddress) -> Self {
addr.0
}
}
impl AsRef<[u8]> for SwarmAddress {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl From<SwarmAddress> for [u8; 32] {
fn from(addr: SwarmAddress) -> Self {
addr.0.into()
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for SwarmAddress {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self(B256::arbitrary(u)?))
}
}