iroh_blobs/
hashseq.rs

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