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