1use crate::Hash;
3use bytes::Bytes;
4use iroh_io::{AsyncSliceReader, AsyncSliceReaderExt};
5use std::{fmt::Debug, io};
6
7#[derive(Debug, Clone, derive_more::Into)]
9pub struct HashSeq(Bytes);
10
11impl FromIterator<Hash> for HashSeq {
12 fn from_iter<T: IntoIterator<Item = Hash>>(iter: T) -> Self {
13 let iter = iter.into_iter();
14 let (lower, _upper) = iter.size_hint();
15 let mut bytes = Vec::with_capacity(lower * 32);
16 for hash in iter {
17 bytes.extend_from_slice(hash.as_ref());
18 }
19 Self(bytes.into())
20 }
21}
22
23impl TryFrom<Bytes> for HashSeq {
24 type Error = anyhow::Error;
25
26 fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
27 Self::new(bytes).ok_or_else(|| anyhow::anyhow!("invalid hash sequence"))
28 }
29}
30
31impl IntoIterator for HashSeq {
32 type Item = Hash;
33 type IntoIter = HashSeqIter;
34
35 fn into_iter(self) -> Self::IntoIter {
36 HashSeqIter(self)
37 }
38}
39
40#[derive(Debug, Clone)]
44pub struct HashSeqStream(HashSeq);
45
46impl HashSeqStream {
47 #[allow(clippy::should_implement_trait, clippy::unused_async)]
49 pub async fn next(&mut self) -> io::Result<Option<Hash>> {
50 Ok(self.0.pop_front())
51 }
52
53 #[allow(clippy::unused_async)]
55 pub async fn skip(&mut self, n: u64) -> io::Result<()> {
56 let ok = self.0.drop_front(n as usize);
57 if !ok {
58 Err(io::Error::new(
59 io::ErrorKind::UnexpectedEof,
60 "end of sequence",
61 ))
62 } else {
63 Ok(())
64 }
65 }
66}
67
68impl HashSeq {
69 pub fn new(bytes: Bytes) -> Option<Self> {
71 if bytes.len() % 32 == 0 {
72 Some(Self(bytes))
73 } else {
74 None
75 }
76 }
77
78 fn drop_front(&mut self, n: usize) -> bool {
79 let start = n * 32;
80 if start > self.0.len() {
81 false
82 } else {
83 self.0 = self.0.slice(start..);
84 true
85 }
86 }
87
88 pub fn iter(&self) -> impl Iterator<Item = Hash> + '_ {
90 self.0.chunks_exact(32).map(|chunk| {
91 let hash: [u8; 32] = chunk.try_into().unwrap();
92 hash.into()
93 })
94 }
95
96 pub fn len(&self) -> usize {
98 self.0.len() / 32
99 }
100
101 pub fn is_empty(&self) -> bool {
103 self.0.is_empty()
104 }
105
106 pub fn get(&self, index: usize) -> Option<Hash> {
108 if index < self.len() {
109 let hash: [u8; 32] = self.0[index * 32..(index + 1) * 32].try_into().unwrap();
110 Some(hash.into())
111 } else {
112 None
113 }
114 }
115
116 pub fn pop_front(&mut self) -> Option<Hash> {
118 if self.is_empty() {
119 None
120 } else {
121 let hash = self.get(0).unwrap();
122 self.0 = self.0.slice(32..);
123 Some(hash)
124 }
125 }
126
127 pub fn into_inner(self) -> Bytes {
129 self.0
130 }
131}
132
133#[derive(Debug, Clone)]
135pub struct HashSeqIter(HashSeq);
136
137impl Iterator for HashSeqIter {
138 type Item = Hash;
139
140 fn next(&mut self) -> Option<Self::Item> {
141 self.0.pop_front()
142 }
143}
144
145pub async fn parse_hash_seq<'a, R: AsyncSliceReader + 'a>(
147 mut reader: R,
148) -> anyhow::Result<(HashSeqStream, u64)> {
149 let bytes = reader.read_to_end().await?;
150 let hashes = HashSeq::try_from(bytes)?;
151 let num_hashes = hashes.len() as u64;
152 let stream = HashSeqStream(hashes);
153 Ok((stream, num_hashes))
154}