1use crate::errors::*;
2use sha2::{Digest, Sha256};
3use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
4
5pub type HashLength = u16;
6pub type UncompressedLength = u64;
7pub type DataLength = u64;
8
9#[derive(Debug, PartialEq)]
10pub struct CryptoHash(pub String);
11
12impl CryptoHash {
13 pub fn as_str(&self) -> &str {
14 &self.0
15 }
16
17 #[inline]
18 fn split_marker(bytes: &[u8]) -> Result<(&[u8], &[u8])> {
19 let offset = memchr::memchr(b':', bytes).context("Failed to find hash id marker `:`")?;
20 let (hash_id, hash_data) = bytes.split_at(offset + 1);
21 Ok((hash_id, hash_data))
22 }
23
24 pub fn decode(bytes: &[u8]) -> Result<Self> {
25 let (hash_id, hash_data) = Self::split_marker(bytes)?;
27
28 let mut hash = hash_id.to_owned();
30 hash.resize(hash.len() + hash_data.len() * 2, 0u8);
31
32 hex::encode_to_slice(hash_data, &mut hash[hash_id.len()..])
34 .context("Failed to encode header hash into buffer")?;
35
36 let hash = String::from_utf8(hash).context("Decoded crypto hash is invalid utf8")?;
38 Ok(CryptoHash(hash))
39 }
40
41 pub fn encode(&self) -> Result<Vec<u8>> {
42 let (hash_id, hash_data) = Self::split_marker(self.0.as_bytes())?;
44
45 let mut hash = hash_id.to_owned();
47 hash.resize(hash.len() + hash_data.len().div_ceil(2), 0u8);
48
49 hex::decode_to_slice(hash_data, &mut hash[hash_id.len()..])
51 .context("Failed to decode header hash into buffer")?;
52
53 Ok(hash)
54 }
55
56 pub fn calculate(bytes: &[u8]) -> Self {
57 let mut hasher = Sha256::new();
58 hasher.update(bytes);
59 let result = hasher.finalize();
60 CryptoHash(format!("sha256:{result:x}"))
61 }
62}
63
64#[derive(Debug, PartialEq)]
65pub struct BlockHeader {
66 pub hash: CryptoHash,
67 pub uncompressed_length: u64,
68 pub data_length: u64,
69}
70
71impl BlockHeader {
72 pub fn new(hash: CryptoHash, uncompressed_length: usize, data_length: usize) -> Self {
73 Self {
74 hash,
75 uncompressed_length: uncompressed_length as u64,
76 data_length: data_length as u64,
77 }
78 }
79
80 async fn read_u16<R: AsyncRead + Unpin>(mut reader: R, n: &mut usize) -> Result<u16> {
81 let mut bytes = [0u8; u16::BITS as usize / 8];
82 *n += reader.read_exact(&mut bytes).await?;
83 let num = u16::from_be_bytes(bytes);
84 Ok(num)
85 }
86
87 async fn read_u64<R: AsyncRead + Unpin>(mut reader: R, n: &mut usize) -> Result<u64> {
88 let mut bytes = [0u8; u64::BITS as usize / 8];
89 *n += reader.read_exact(&mut bytes).await?;
90 let num = u64::from_be_bytes(bytes);
91 Ok(num)
92 }
93
94 pub async fn parse<R: AsyncRead + Unpin>(mut reader: R) -> Result<(Self, usize)> {
95 let mut n = 0;
96
97 let hash_length = Self::read_u16(&mut reader, &mut n)
99 .await
100 .context("Failed to read hash length")?;
101
102 let mut hash_bytes = vec![0u8; hash_length as usize];
104 n += reader
105 .read_exact(&mut hash_bytes)
106 .await
107 .context("Failed to read hash bytes")?;
108 let hash = CryptoHash::decode(&hash_bytes)?;
109
110 let uncompressed_length = Self::read_u64(&mut reader, &mut n)
112 .await
113 .context("Failed to read uncompressed length")?;
114
115 let data_length = Self::read_u64(&mut reader, &mut n)
117 .await
118 .context("Failed to read data length")?;
119
120 let header = BlockHeader {
121 hash,
122 uncompressed_length,
123 data_length,
124 };
125 trace!("Parsed block header: {header:?}");
126 Ok((header, n))
127 }
128
129 pub async fn write<W: AsyncWrite + Unpin>(&self, mut writer: W) -> Result<usize> {
130 let mut n = 0;
131
132 let encoded = self.hash.encode()?;
134 let hash_length_bytes = HashLength::to_be_bytes(encoded.len() as u16);
135 writer.write_all(&hash_length_bytes).await?;
136 n += hash_length_bytes.len();
137
138 writer.write_all(&encoded).await?;
139 n += encoded.len();
140
141 let uncompressed_length_bytes = UncompressedLength::to_be_bytes(self.uncompressed_length);
143 writer.write_all(&uncompressed_length_bytes).await?;
144 n += uncompressed_length_bytes.len();
145
146 let data_length_bytes = DataLength::to_be_bytes(self.data_length);
148 writer.write_all(&data_length_bytes).await?;
149 n += data_length_bytes.len();
150
151 Ok(n)
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[tokio::test]
160 async fn test_parse_header() {
161 let mut bytes = Vec::<u8>::new();
162 bytes.extend(39u16.to_be_bytes());
163 bytes.extend(b"sha256:");
164 bytes.extend(&[
165 0xe8, 0x47, 0x12, 0x23, 0x87, 0x09, 0x39, 0x8f, 0x6d, 0x34, 0x9d, 0xc2, 0x25, 0x0b,
166 0x0e, 0xfc, 0xa4, 0xb7, 0x2d, 0x8c, 0x2b, 0xfb, 0x7b, 0x74, 0x33, 0x9d, 0x30, 0xba,
167 0x94, 0x05, 0x6b, 0x14,
168 ]);
169 bytes.extend(1337u64.to_be_bytes());
170 bytes.extend(4u64.to_be_bytes());
171 let (header, bytes_read) = BlockHeader::parse(&bytes[..]).await.unwrap();
174 assert_eq!(
175 header,
176 BlockHeader {
177 hash: CryptoHash(
178 "sha256:e84712238709398f6d349dc2250b0efca4b72d8c2bfb7b74339d30ba94056b14"
179 .to_string()
180 ),
181 uncompressed_length: 1337,
182 data_length: 4,
183 }
184 );
185 assert_eq!(bytes_read, 57);
186 }
187
188 #[tokio::test]
189 async fn test_write_header() {
190 let header = BlockHeader {
191 hash: CryptoHash(
192 "sha256:e84712238709398f6d349dc2250b0efca4b72d8c2bfb7b74339d30ba94056b14"
193 .to_string(),
194 ),
195 uncompressed_length: 1337,
196 data_length: 4,
197 };
198 let mut buf = Vec::new();
199 header.write(&mut buf).await.unwrap();
200
201 let mut expected = Vec::<u8>::new();
202 expected.extend(39u16.to_be_bytes());
203 expected.extend(b"sha256:");
204 expected.extend(&[
205 0xe8, 0x47, 0x12, 0x23, 0x87, 0x09, 0x39, 0x8f, 0x6d, 0x34, 0x9d, 0xc2, 0x25, 0x0b,
206 0x0e, 0xfc, 0xa4, 0xb7, 0x2d, 0x8c, 0x2b, 0xfb, 0x7b, 0x74, 0x33, 0x9d, 0x30, 0xba,
207 0x94, 0x05, 0x6b, 0x14,
208 ]);
209 expected.extend(1337u64.to_be_bytes());
210 expected.extend(4u64.to_be_bytes());
211
212 assert_eq!(buf, expected);
213 }
214
215 #[test]
216 fn test_hash_decode_encode() {
217 let mut bytes = Vec::<u8>::new();
218 bytes.extend(b"sha256:");
219 bytes.extend(&[
220 0xe8, 0x47, 0x12, 0x23, 0x87, 0x09, 0x39, 0x8f, 0x6d, 0x34, 0x9d, 0xc2, 0x25, 0x0b,
221 0x0e, 0xfc, 0xa4, 0xb7, 0x2d, 0x8c, 0x2b, 0xfb, 0x7b, 0x74, 0x33, 0x9d, 0x30, 0xba,
222 0x94, 0x05, 0x6b, 0x14,
223 ]);
224
225 let hash = CryptoHash::decode(&bytes).unwrap();
226 assert_eq!(
227 hash,
228 CryptoHash(
229 "sha256:e84712238709398f6d349dc2250b0efca4b72d8c2bfb7b74339d30ba94056b14"
230 .to_string()
231 )
232 );
233
234 let encoded = hash.encode().unwrap();
235 assert_eq!(encoded, bytes);
236 }
237}