1use std::{fmt::Debug, io};
3
4use bytes::Bytes;
5use iroh_io::{AsyncSliceReader, AsyncSliceReaderExt};
6
7use crate::Hash;
8
9#[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 = anyhow::Error;
39
40 fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
41 Self::new(bytes).ok_or_else(|| anyhow::anyhow!("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
54#[derive(Debug, Clone)]
58pub struct HashSeqStream(HashSeq);
59
60impl HashSeqStream {
61 #[allow(clippy::should_implement_trait, clippy::unused_async)]
63 pub async fn next(&mut self) -> io::Result<Option<Hash>> {
64 Ok(self.0.pop_front())
65 }
66
67 #[allow(clippy::unused_async)]
69 pub async fn skip(&mut self, n: u64) -> io::Result<()> {
70 let ok = self.0.drop_front(n as usize);
71 if !ok {
72 Err(io::Error::new(
73 io::ErrorKind::UnexpectedEof,
74 "end of sequence",
75 ))
76 } else {
77 Ok(())
78 }
79 }
80}
81
82impl HashSeq {
83 pub fn new(bytes: Bytes) -> Option<Self> {
85 if bytes.len() % 32 == 0 {
86 Some(Self(bytes))
87 } else {
88 None
89 }
90 }
91
92 fn drop_front(&mut self, n: usize) -> bool {
93 let start = n * 32;
94 if start > self.0.len() {
95 false
96 } else {
97 self.0 = self.0.slice(start..);
98 true
99 }
100 }
101
102 pub fn iter(&self) -> impl Iterator<Item = Hash> + '_ {
104 self.0.chunks_exact(32).map(|chunk| {
105 let hash: [u8; 32] = chunk.try_into().unwrap();
106 hash.into()
107 })
108 }
109
110 pub fn len(&self) -> usize {
112 self.0.len() / 32
113 }
114
115 pub fn is_empty(&self) -> bool {
117 self.0.is_empty()
118 }
119
120 pub fn get(&self, index: usize) -> Option<Hash> {
122 if index < self.len() {
123 let hash: [u8; 32] = self.0[index * 32..(index + 1) * 32].try_into().unwrap();
124 Some(hash.into())
125 } else {
126 None
127 }
128 }
129
130 pub fn pop_front(&mut self) -> Option<Hash> {
132 if self.is_empty() {
133 None
134 } else {
135 let hash = self.get(0).unwrap();
136 self.0 = self.0.slice(32..);
137 Some(hash)
138 }
139 }
140
141 pub fn into_inner(self) -> Bytes {
143 self.0
144 }
145}
146
147#[derive(Debug, Clone)]
149pub struct HashSeqIter(HashSeq);
150
151impl Iterator for HashSeqIter {
152 type Item = Hash;
153
154 fn next(&mut self) -> Option<Self::Item> {
155 self.0.pop_front()
156 }
157}
158
159pub async fn parse_hash_seq<'a, R: AsyncSliceReader + 'a>(
161 mut reader: R,
162) -> anyhow::Result<(HashSeqStream, u64)> {
163 let bytes = reader.read_to_end().await?;
164 let hashes = HashSeq::try_from(bytes)?;
165 let num_hashes = hashes.len() as u64;
166 let stream = HashSeqStream(hashes);
167 Ok((stream, num_hashes))
168}