wnfs_hamt/hash.rs
1use crate::error::HamtError;
2use anyhow::{Result, bail};
3use std::fmt::Debug;
4use wnfs_common::{HASH_BYTE_SIZE, HashOutput, utils};
5
6//--------------------------------------------------------------------------------------------------
7// Constants
8//--------------------------------------------------------------------------------------------------
9
10/// The number of nibbles in a [`HashOutput`][HashOutput].
11///
12/// [HashOutput]: wnfs_common::HashOutput
13pub const MAX_HASH_NIBBLE_LENGTH: usize = HASH_BYTE_SIZE * 2;
14
15//--------------------------------------------------------------------------------------------------
16// Type Definition
17//--------------------------------------------------------------------------------------------------
18
19/// A common trait for the ability to generate a hash of some data.
20///
21/// # Examples
22///
23/// ```
24/// use wnfs_hamt::Hasher;
25/// use wnfs_common::HashOutput;
26///
27/// struct MyHasher;
28///
29/// impl Hasher for MyHasher {
30/// fn hash<D: AsRef<[u8]>>(data: &D) -> HashOutput {
31/// blake3::hash(data.as_ref()).into()
32/// }
33/// }
34/// ```
35pub trait Hasher {
36 /// Generates a hash of the given data.
37 fn hash<D: AsRef<[u8]>>(data: &D) -> HashOutput;
38}
39
40/// HashNibbles is a wrapper around a byte slice that provides a cursor for traversing the nibbles.
41#[derive(Clone)]
42pub struct HashNibbles<'a> {
43 pub digest: &'a HashOutput,
44 cursor: usize,
45}
46
47/// This represents the location of a intermediate or leaf node in the HAMT.
48///
49/// It is based on the hash of the key with a length info for knowing how deep
50/// to traverse the tree to find the intermediate or leaf node.
51///
52/// # Examples
53///
54/// ```
55/// use wnfs_hamt::HashPrefix;
56/// use wnfs_common::utils;
57///
58/// let hashprefix = HashPrefix::with_length(utils::to_hash_output(&[0xff, 0x22]), 4);
59///
60/// println!("{:?}", hashprefix);
61/// ```
62#[derive(Clone, Default)]
63pub struct HashPrefix {
64 pub digest: HashOutput,
65 length: u8,
66}
67
68/// An iterator over the nibbles of a HashPrefix.
69///
70/// # Examples
71///
72/// ```
73/// use wnfs_hamt::HashPrefix;
74/// use wnfs_common::utils;
75///
76/// let hashprefix = HashPrefix::with_length(utils::to_hash_output(&[0xff, 0x22]), 4);
77/// for i in hashprefix.iter() {
78/// println!("{}", i);
79/// }
80/// ```
81#[derive(Clone)]
82pub struct HashPrefixIterator<'a> {
83 pub hashprefix: &'a HashPrefix,
84 cursor: u8,
85}
86
87//--------------------------------------------------------------------------------------------------
88// Implementation
89//--------------------------------------------------------------------------------------------------
90
91impl<'a> HashNibbles<'a> {
92 /// Creates a new `HashNibbles` instance from a `[u8; 32]` hash.
93 pub fn new(digest: &'a HashOutput) -> HashNibbles<'a> {
94 Self::with_cursor(digest, 0)
95 }
96
97 /// Constructs a `HashNibbles` with custom cursor index.
98 pub fn with_cursor(digest: &'a HashOutput, cursor: usize) -> HashNibbles<'a> {
99 Self { digest, cursor }
100 }
101
102 /// Gets the next nibble from the hash.
103 pub fn try_next(&mut self) -> Result<usize> {
104 if let Some(nibble) = self.next() {
105 return Ok(nibble as usize);
106 }
107 bail!(HamtError::CursorOutOfBounds)
108 }
109
110 /// Gets the current cursor position.
111 #[inline]
112 pub fn get_cursor(&self) -> usize {
113 self.cursor
114 }
115}
116
117impl Iterator for HashNibbles<'_> {
118 type Item = u8;
119
120 fn next(&mut self) -> Option<Self::Item> {
121 if self.cursor >= MAX_HASH_NIBBLE_LENGTH {
122 return None;
123 }
124
125 let byte = self.digest[self.cursor / 2];
126 let byte = if self.cursor % 2 == 0 {
127 byte >> 4
128 } else {
129 byte & 0b0000_1111
130 };
131
132 self.cursor += 1;
133 Some(byte)
134 }
135}
136
137impl Debug for HashNibbles<'_> {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 let mut nibbles_str = String::new();
140 for nibble in HashNibbles::with_cursor(self.digest, 0) {
141 nibbles_str.push_str(&format!("{nibble:1X}"));
142 }
143
144 f.debug_struct("HashNibbles")
145 .field("hash", &nibbles_str)
146 .field("cursor", &self.cursor)
147 .finish()
148 }
149}
150
151impl Hasher for blake3::Hasher {
152 fn hash<D: AsRef<[u8]>>(data: &D) -> HashOutput {
153 blake3::hash(data.as_ref()).into()
154 }
155}
156
157impl HashPrefix {
158 /// Creates a new `HashPrefix` instance from a `[u8; 32]` hash.
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use wnfs_hamt::HashPrefix;
164 /// use wnfs_common::utils;
165 ///
166 /// let hashprefix = HashPrefix::with_length(utils::to_hash_output(&[0xff, 0x22]), 4);
167 ///
168 /// println!("{:?}", hashprefix);
169 /// ```
170 pub fn with_length(digest: HashOutput, length: u8) -> HashPrefix {
171 Self { digest, length }
172 }
173
174 /// Pushes a nibble to the end of the hash.
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// use wnfs_hamt::HashPrefix;
180 /// use wnfs_common::utils;
181 ///
182 /// let mut hashprefix = HashPrefix::default();
183 /// for i in 0..16_u8 {
184 /// hashprefix.push(i);
185 /// }
186 ///
187 /// assert_eq!(hashprefix.len(), 16);
188 /// ```
189 pub fn push(&mut self, nibble: u8) {
190 if self.length >= MAX_HASH_NIBBLE_LENGTH as u8 {
191 panic!("HashPrefix is full");
192 }
193
194 let offset = self.length as usize / 2;
195 let byte = self.digest[offset];
196 let byte = if self.length as usize % 2 == 0 {
197 nibble << 4
198 } else {
199 byte | (nibble & 0x0F)
200 };
201
202 self.digest[offset] = byte;
203 self.length += 1;
204 }
205
206 /// Gets the length of the hash.
207 ///
208 /// # Examples
209 ///
210 /// ```
211 /// use wnfs_hamt::HashPrefix;
212 /// use wnfs_common::utils;
213 ///
214 /// let mut hashprefix = HashPrefix::default();
215 /// for i in 0..16_u8 {
216 /// hashprefix.push(i);
217 /// }
218 ///
219 /// assert_eq!(hashprefix.len(), 16);
220 /// ```
221 #[inline(always)]
222 pub fn len(&self) -> usize {
223 self.length as usize
224 }
225
226 /// Checks if the hash is empty.
227 ///
228 /// # Examples
229 ///
230 /// ```
231 /// use wnfs_hamt::HashPrefix;
232 /// use wnfs_common::utils;
233 ///
234 /// let hashprefix = HashPrefix::default();
235 /// assert!(hashprefix.is_empty());
236 /// ```
237 pub fn is_empty(&self) -> bool {
238 self.length == 0
239 }
240
241 /// Get the nibble at specified offset.
242 ///
243 /// # Examples
244 ///
245 /// ```
246 /// use wnfs_hamt::HashPrefix;
247 /// use wnfs_common::utils;
248 ///
249 /// let mut hashprefix = HashPrefix::default();
250 /// for i in 0..16_u8 {
251 /// hashprefix.push(i);
252 /// }
253 ///
254 /// assert_eq!(hashprefix.get(15), Some(0x0f));
255 /// ```
256 pub fn get(&self, index: u8) -> Option<u8> {
257 if index >= self.length {
258 return None;
259 }
260
261 let byte = self.digest.get(index as usize / 2)?;
262 Some(if index % 2 == 0 {
263 byte >> 4
264 } else {
265 byte & 0x0F
266 })
267 }
268
269 /// Creates an iterator over the nibbles of the hash.
270 ///
271 /// # Examples
272 ///
273 /// ```
274 /// use wnfs_hamt::HashPrefix;
275 /// use wnfs_common::utils;
276 ///
277 /// let hashprefix = HashPrefix::with_length(utils::to_hash_output(&[0xff, 0x22]), 4);
278 /// for i in hashprefix.iter() {
279 /// println!("{}", i);
280 /// }
281 /// ```
282 pub fn iter(&self) -> HashPrefixIterator<'_> {
283 HashPrefixIterator {
284 hashprefix: self,
285 cursor: 0,
286 }
287 }
288
289 /// Checks if the HashPrefix is a prefix of some arbitrary byte slice.
290 ///
291 /// # Examples
292 ///
293 /// ```
294 /// use wnfs_hamt::HashPrefix;
295 /// use wnfs_common::utils;
296 ///
297 /// let hashprefix = HashPrefix::with_length(utils::to_hash_output(&[0xff, 0x22]), 4);
298 ///
299 /// assert!(hashprefix.is_prefix_of(&[0xff, 0x22, 0x33]));
300 /// ```
301 pub fn is_prefix_of(&self, bytes: &[u8]) -> bool {
302 self == &HashPrefix::with_length(utils::to_hash_output(bytes), self.length)
303 }
304}
305
306impl Debug for HashPrefix {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 write!(f, "0x")?;
309 for nibble in self.iter() {
310 write!(f, "{nibble:1X}")?;
311 }
312
313 Ok(())
314 }
315}
316
317impl PartialEq for HashPrefix {
318 fn eq(&self, other: &Self) -> bool {
319 self.iter().eq(other.iter())
320 }
321}
322
323impl Iterator for HashPrefixIterator<'_> {
324 type Item = u8;
325
326 fn next(&mut self) -> Option<Self::Item> {
327 if self.cursor >= self.hashprefix.length {
328 return None;
329 }
330
331 let byte = self.hashprefix.get(self.cursor)?;
332 self.cursor += 1;
333 Some(byte)
334 }
335}
336
337//--------------------------------------------------------------------------------------------------
338// Tests
339//--------------------------------------------------------------------------------------------------
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn hash_nibbles_can_cursor_over_digest() {
347 let key = {
348 let mut bytes = [0u8; HASH_BYTE_SIZE];
349 bytes[0] = 0b1000_1100;
350 bytes[1] = 0b1010_1010;
351 bytes[2] = 0b1011_1111;
352 bytes[3] = 0b1111_1101;
353 bytes
354 };
355
356 let hashnibbles = &mut HashNibbles::new(&key);
357 let expected_nibbles = [
358 0b1000, 0b1100, 0b1010, 0b1010, 0b1011, 0b1111, 0b1111, 0b1101,
359 ];
360
361 for (got, expected) in hashnibbles.zip(expected_nibbles.into_iter()) {
362 assert_eq!(expected, got);
363 }
364
365 // Exhaust the iterator.
366 let _ = hashnibbles
367 .take(MAX_HASH_NIBBLE_LENGTH - expected_nibbles.len())
368 .collect::<Vec<_>>();
369
370 assert_eq!(hashnibbles.next(), None);
371 }
372
373 #[test]
374 fn can_push_and_get_nibbles_from_hashprefix() {
375 let mut hashprefix = HashPrefix::default();
376 for i in 0..HASH_BYTE_SIZE {
377 hashprefix.push((i % 16) as u8);
378 hashprefix.push((15 - i % 16) as u8);
379 }
380
381 assert!(!hashprefix.is_empty());
382
383 for i in 0..HASH_BYTE_SIZE {
384 assert_eq!(hashprefix.get(i as u8 * 2).unwrap(), (i % 16) as u8);
385 assert_eq!(
386 hashprefix.get(i as u8 * 2 + 1).unwrap(),
387 (15 - i % 16) as u8
388 );
389 }
390 }
391}