use std::cmp::Ordering;
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{Hash, Hasher};
const BASE58_ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct PeerId([u8; 64]);
impl PeerId {
pub fn from_bytes(bytes: [u8; 64]) -> Self {
PeerId(bytes)
}
pub fn from_slice(bytes: &[u8]) -> Self {
let mut arr = [0u8; 64];
let len = bytes.len().min(64);
arr[..len].copy_from_slice(&bytes[..len]);
PeerId(arr)
}
pub fn from_data<T: Hash>(data: T) -> Self {
let mut bytes = [0u8; 64];
for chunk in 0..8 {
let mut hasher = DefaultHasher::new();
chunk.hash(&mut hasher);
data.hash(&mut hasher);
let hash_value = hasher.finish();
bytes[chunk * 8..(chunk + 1) * 8].copy_from_slice(&hash_value.to_be_bytes());
}
PeerId(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
pub fn short_hex(&self) -> String {
self.0[..4].iter().map(|b| format!("{:02x}", b)).collect()
}
pub fn hex(&self) -> String {
self.0.iter().map(|b| format!("{:02x}", b)).collect()
}
pub fn base58(&self) -> String {
Self::bytes_to_base58(&self.0)
}
pub fn short_base58(&self) -> String {
let mut short_bytes = [0u8; 8];
short_bytes.copy_from_slice(&self.0[..8]);
Self::bytes_to_base58(&short_bytes)
}
fn bytes_to_base58(bytes: &[u8]) -> String {
let mut digits = vec![0u8];
for &byte in bytes {
let mut carry = byte as usize;
for digit in &mut digits {
carry += (*digit as usize) << 8;
*digit = (carry % 58) as u8;
carry /= 58;
}
while carry > 0 {
digits.push((carry % 58) as u8);
carry /= 58;
}
}
for &byte in bytes {
if byte == 0 {
digits.push(0);
} else {
break;
}
}
digits.reverse();
digits
.iter()
.map(|&d| BASE58_ALPHABET[d as usize] as char)
.collect()
}
}
impl fmt::Debug for PeerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.short_base58())
}
}
impl fmt::Display for PeerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.short_base58())
}
}
impl Ord for PeerId {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl PartialOrd for PeerId {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_bytes() {
let bytes = [42u8; 64];
let id = PeerId::from_bytes(bytes);
assert_eq!(id.as_bytes(), &bytes);
}
#[test]
fn test_from_slice_exact() {
let bytes = [1u8; 64];
let id = PeerId::from_slice(&bytes);
assert_eq!(id.as_bytes(), &bytes);
}
#[test]
fn test_from_slice_short() {
let bytes = [1u8, 2, 3];
let id = PeerId::from_slice(&bytes);
assert_eq!(id.as_bytes()[0], 1);
assert_eq!(id.as_bytes()[1], 2);
assert_eq!(id.as_bytes()[2], 3);
assert_eq!(id.as_bytes()[3], 0);
}
#[test]
fn test_from_data_deterministic() {
let id1 = PeerId::from_data("hello");
let id2 = PeerId::from_data("hello");
assert_eq!(id1, id2);
}
#[test]
fn test_from_data_different() {
let id1 = PeerId::from_data("hello");
let id2 = PeerId::from_data("world");
assert_ne!(id1, id2);
}
#[test]
fn test_from_data_fills_all_bytes() {
let id = PeerId::from_data("test-full-fill");
let bytes = id.as_bytes();
assert!(bytes[8..].iter().any(|&b| b != 0));
}
#[test]
fn test_ordering() {
let id1 = PeerId::from_slice(&[0u8]);
let id2 = PeerId::from_slice(&[1u8]);
assert!(id1 < id2);
}
#[test]
fn test_display() {
let id = PeerId::from_data("test");
let s = format!("{}", id);
assert!(!s.is_empty());
}
#[test]
fn test_hex() {
let mut bytes = [0u8; 64];
bytes[0] = 0xAB;
bytes[1] = 0xCD;
let id = PeerId::from_bytes(bytes);
assert!(id.hex().starts_with("abcd"));
assert_eq!(id.short_hex(), "abcd0000");
}
#[test]
fn test_copy() {
let id1 = PeerId::from_data("test");
let id2 = id1; assert_eq!(id1, id2);
}
#[test]
fn test_hash_as_key() {
use std::collections::HashMap;
let mut map = HashMap::new();
let id = PeerId::from_data("key");
map.insert(id, "value");
assert_eq!(map.get(&id), Some(&"value"));
}
}