iroh_blobs/
hashseq.rs

1//! Helpers for blobs that contain a sequence of hashes.
2use std::fmt::Debug;
3
4use bytes::Bytes;
5use n0_error::{anyerr, AnyError};
6
7use crate::Hash;
8
9/// A sequence of links, backed by a [`Bytes`] object.
10#[derive(Clone, derive_more::Into)]
11pub struct HashSeq(Bytes);
12
13impl Debug for HashSeq {
14    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        f.debug_list().entries(self.iter()).finish()
16    }
17}
18
19impl<'a> FromIterator<&'a Hash> for HashSeq {
20    fn from_iter<T: IntoIterator<Item = &'a Hash>>(iter: T) -> Self {
21        iter.into_iter().copied().collect()
22    }
23}
24
25impl FromIterator<Hash> for HashSeq {
26    fn from_iter<T: IntoIterator<Item = Hash>>(iter: T) -> Self {
27        let iter = iter.into_iter();
28        let (lower, _upper) = iter.size_hint();
29        let mut bytes = Vec::with_capacity(lower * 32);
30        for hash in iter {
31            bytes.extend_from_slice(hash.as_ref());
32        }
33        Self(bytes.into())
34    }
35}
36
37impl TryFrom<Bytes> for HashSeq {
38    type Error = AnyError;
39
40    fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
41        Self::new(bytes).ok_or_else(|| anyerr!("invalid hash sequence"))
42    }
43}
44
45impl IntoIterator for HashSeq {
46    type Item = Hash;
47    type IntoIter = HashSeqIter;
48
49    fn into_iter(self) -> Self::IntoIter {
50        HashSeqIter(self)
51    }
52}
53
54impl HashSeq {
55    /// Create a new sequence of hashes.
56    pub fn new(bytes: Bytes) -> Option<Self> {
57        if bytes.len() % 32 == 0 {
58            Some(Self(bytes))
59        } else {
60            None
61        }
62    }
63
64    /// Iterate over the hashes in this sequence.
65    pub fn iter(&self) -> impl Iterator<Item = Hash> + '_ {
66        self.0.chunks_exact(32).map(|chunk| {
67            let hash: [u8; 32] = chunk.try_into().unwrap();
68            hash.into()
69        })
70    }
71
72    /// Get the number of hashes in this sequence.
73    pub fn len(&self) -> usize {
74        self.0.len() / 32
75    }
76
77    /// Check if this sequence is empty.
78    pub fn is_empty(&self) -> bool {
79        self.0.is_empty()
80    }
81
82    /// Get the hash at the given index.
83    pub fn get(&self, index: usize) -> Option<Hash> {
84        if index < self.len() {
85            let hash: [u8; 32] = self.0[index * 32..(index + 1) * 32].try_into().unwrap();
86            Some(hash.into())
87        } else {
88            None
89        }
90    }
91
92    /// Get and remove the first hash in this sequence.
93    pub fn pop_front(&mut self) -> Option<Hash> {
94        if self.is_empty() {
95            None
96        } else {
97            let hash = self.get(0).unwrap();
98            self.0 = self.0.slice(32..);
99            Some(hash)
100        }
101    }
102
103    /// Get the underlying bytes.
104    pub fn into_inner(self) -> Bytes {
105        self.0
106    }
107}
108
109/// Iterator over the hashes in a [`HashSeq`].
110#[derive(Debug, Clone)]
111pub struct HashSeqIter(HashSeq);
112
113impl Iterator for HashSeqIter {
114    type Item = Hash;
115
116    fn next(&mut self) -> Option<Self::Item> {
117        self.0.pop_front()
118    }
119}