ant_node/client/
data_types.rs1use bytes::Bytes;
8#[must_use]
10pub fn compute_address(content: &[u8]) -> XorName {
11 *blake3::hash(content).as_bytes()
12}
13
14#[must_use]
18pub fn xor_distance(a: &XorName, b: &XorName) -> XorName {
19 std::array::from_fn(|i| a[i] ^ b[i])
20}
21
22#[must_use]
26pub fn peer_id_to_xor_name(peer_id: &str) -> Option<XorName> {
27 let bytes = hex::decode(peer_id).ok()?;
28 if bytes.len() != 32 {
29 return None;
30 }
31 let mut name = [0u8; 32];
32 name.copy_from_slice(&bytes);
33 Some(name)
34}
35
36pub type XorName = [u8; 32];
41
42#[derive(Debug, Clone)]
49pub struct DataChunk {
50 pub address: XorName,
52 pub content: Bytes,
54}
55
56impl DataChunk {
57 #[must_use]
62 pub fn new(address: XorName, content: Bytes) -> Self {
63 Self { address, content }
64 }
65
66 #[must_use]
68 pub fn from_content(content: Bytes) -> Self {
69 let address = compute_address(&content);
70 Self { address, content }
71 }
72
73 #[must_use]
75 pub fn size(&self) -> usize {
76 self.content.len()
77 }
78
79 #[must_use]
81 pub fn verify(&self) -> bool {
82 self.address == compute_address(&self.content)
83 }
84}
85
86#[derive(Debug, Default, Clone)]
88pub struct ChunkStats {
89 pub chunks_stored: u64,
91 pub chunks_retrieved: u64,
93 pub cache_hits: u64,
95 pub misses: u64,
97 pub bytes_stored: u64,
99 pub bytes_retrieved: u64,
101}
102
103#[cfg(test)]
104#[allow(clippy::unwrap_used, clippy::expect_used)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_data_chunk_creation() {
110 let address = [0xAB; 32];
111 let content = Bytes::from("test data");
112 let chunk = DataChunk::new(address, content.clone());
113
114 assert_eq!(chunk.address, address);
115 assert_eq!(chunk.content, content);
116 assert_eq!(chunk.size(), 9);
117 }
118
119 #[test]
120 fn test_chunk_from_content() {
121 let content = Bytes::from("hello world");
122 let chunk = DataChunk::from_content(content.clone());
123
124 let expected: [u8; 32] = [
126 0xd7, 0x49, 0x81, 0xef, 0xa7, 0x0a, 0x0c, 0x88, 0x0b, 0x8d, 0x8c, 0x19, 0x85, 0xd0,
127 0x75, 0xdb, 0xcb, 0xf6, 0x79, 0xb9, 0x9a, 0x5f, 0x99, 0x14, 0xe5, 0xaa, 0xf9, 0x6b,
128 0x83, 0x1a, 0x9e, 0x24,
129 ];
130
131 assert_eq!(chunk.address, expected);
132 assert_eq!(chunk.content, content);
133 assert!(chunk.verify());
134 }
135
136 #[test]
137 fn test_xor_distance_identity() {
138 let a = [0xAB; 32];
139 assert_eq!(xor_distance(&a, &a), [0u8; 32]);
140 }
141
142 #[test]
143 fn test_xor_distance_symmetry() {
144 let a = [0x01; 32];
145 let b = [0xFF; 32];
146 assert_eq!(xor_distance(&a, &b), xor_distance(&b, &a));
147 }
148
149 #[test]
150 fn test_xor_distance_known_values() {
151 let a = [0x00; 32];
152 let b = [0xFF; 32];
153 assert_eq!(xor_distance(&a, &b), [0xFF; 32]);
154
155 let mut c = [0x00; 32];
156 c[0] = 0x80;
157 let mut expected = [0x00; 32];
158 expected[0] = 0x80;
159 assert_eq!(xor_distance(&a, &c), expected);
160 }
161
162 #[test]
163 fn test_peer_id_to_xor_name_valid() {
164 let hex_str = "ab".repeat(32);
165 let result = peer_id_to_xor_name(&hex_str);
166 assert_eq!(result, Some([0xAB; 32]));
167 }
168
169 #[test]
170 fn test_peer_id_to_xor_name_invalid_hex() {
171 assert_eq!(peer_id_to_xor_name("not_hex_at_all!"), None);
172 }
173
174 #[test]
175 fn test_peer_id_to_xor_name_wrong_length() {
176 let short = "ab".repeat(16);
178 assert_eq!(peer_id_to_xor_name(&short), None);
179
180 let long = "ab".repeat(33);
182 assert_eq!(peer_id_to_xor_name(&long), None);
183 }
184
185 #[test]
186 fn test_chunk_verify() {
187 let content = Bytes::from("test");
189 let valid = DataChunk::from_content(content);
190 assert!(valid.verify());
191
192 let invalid = DataChunk::new([0; 32], Bytes::from("test"));
194 assert!(!invalid.verify());
195 }
196}