use bytes::Bytes;
#[must_use]
pub fn compute_address(content: &[u8]) -> XorName {
*blake3::hash(content).as_bytes()
}
#[must_use]
pub fn xor_distance(a: &XorName, b: &XorName) -> XorName {
std::array::from_fn(|i| a[i] ^ b[i])
}
#[must_use]
pub fn peer_id_to_xor_name(peer_id: &str) -> Option<XorName> {
let bytes = hex::decode(peer_id).ok()?;
if bytes.len() != 32 {
return None;
}
let mut name = [0u8; 32];
name.copy_from_slice(&bytes);
Some(name)
}
pub type XorName = [u8; 32];
#[derive(Debug, Clone)]
pub struct DataChunk {
pub address: XorName,
pub content: Bytes,
}
impl DataChunk {
#[must_use]
pub fn new(address: XorName, content: Bytes) -> Self {
Self { address, content }
}
#[must_use]
pub fn from_content(content: Bytes) -> Self {
let address = compute_address(&content);
Self { address, content }
}
#[must_use]
pub fn size(&self) -> usize {
self.content.len()
}
#[must_use]
pub fn verify(&self) -> bool {
self.address == compute_address(&self.content)
}
}
#[derive(Debug, Default, Clone)]
pub struct ChunkStats {
pub chunks_stored: u64,
pub chunks_retrieved: u64,
pub cache_hits: u64,
pub misses: u64,
pub bytes_stored: u64,
pub bytes_retrieved: u64,
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn test_data_chunk_creation() {
let address = [0xAB; 32];
let content = Bytes::from("test data");
let chunk = DataChunk::new(address, content.clone());
assert_eq!(chunk.address, address);
assert_eq!(chunk.content, content);
assert_eq!(chunk.size(), 9);
}
#[test]
fn test_chunk_from_content() {
let content = Bytes::from("hello world");
let chunk = DataChunk::from_content(content.clone());
let expected: [u8; 32] = [
0xd7, 0x49, 0x81, 0xef, 0xa7, 0x0a, 0x0c, 0x88, 0x0b, 0x8d, 0x8c, 0x19, 0x85, 0xd0,
0x75, 0xdb, 0xcb, 0xf6, 0x79, 0xb9, 0x9a, 0x5f, 0x99, 0x14, 0xe5, 0xaa, 0xf9, 0x6b,
0x83, 0x1a, 0x9e, 0x24,
];
assert_eq!(chunk.address, expected);
assert_eq!(chunk.content, content);
assert!(chunk.verify());
}
#[test]
fn test_xor_distance_identity() {
let a = [0xAB; 32];
assert_eq!(xor_distance(&a, &a), [0u8; 32]);
}
#[test]
fn test_xor_distance_symmetry() {
let a = [0x01; 32];
let b = [0xFF; 32];
assert_eq!(xor_distance(&a, &b), xor_distance(&b, &a));
}
#[test]
fn test_xor_distance_known_values() {
let a = [0x00; 32];
let b = [0xFF; 32];
assert_eq!(xor_distance(&a, &b), [0xFF; 32]);
let mut c = [0x00; 32];
c[0] = 0x80;
let mut expected = [0x00; 32];
expected[0] = 0x80;
assert_eq!(xor_distance(&a, &c), expected);
}
#[test]
fn test_peer_id_to_xor_name_valid() {
let hex_str = "ab".repeat(32);
let result = peer_id_to_xor_name(&hex_str);
assert_eq!(result, Some([0xAB; 32]));
}
#[test]
fn test_peer_id_to_xor_name_invalid_hex() {
assert_eq!(peer_id_to_xor_name("not_hex_at_all!"), None);
}
#[test]
fn test_peer_id_to_xor_name_wrong_length() {
let short = "ab".repeat(16);
assert_eq!(peer_id_to_xor_name(&short), None);
let long = "ab".repeat(33);
assert_eq!(peer_id_to_xor_name(&long), None);
}
#[test]
fn test_chunk_verify() {
let content = Bytes::from("test");
let valid = DataChunk::from_content(content);
assert!(valid.verify());
let invalid = DataChunk::new([0; 32], Bytes::from("test"));
assert!(!invalid.verify());
}
}