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}