1use std::{
6 io::{Read, Write},
7 marker::PhantomData,
8};
9
10use anyhow::{Result, anyhow, bail};
11use byteorder::ReadBytesExt;
12use integer_encoding::{VarInt, VarIntReader};
13use num_enum::{IntoPrimitive, TryFromPrimitive};
14use serde::{Serialize, de::DeserializeOwned};
15use tracing::error;
16
17pub const MAX_VARINT_LENGTH: usize = 10;
18pub const BLOB_ENCODING_BYTES: usize = 1;
19const MAX_PREALLOCATION: usize = 1 << 20;
20const MAX_BLOB_LENGTH: u64 = 1 << 32;
24
25#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
26#[repr(u8)]
27pub enum BlobEncoding {
28 Bcs = 1,
29}
30
31pub struct Blob {
32 pub data: Vec<u8>,
33 pub encoding: BlobEncoding,
34}
35
36impl Blob {
37 pub fn encode<T: Serialize>(value: &T, encoding: BlobEncoding) -> Result<Self> {
38 let value_buf = bcs::to_bytes(value)?;
39 let (data, encoding) = match encoding {
40 BlobEncoding::Bcs => (value_buf, encoding),
41 };
42 Ok(Blob { data, encoding })
43 }
44 pub fn decode<T: DeserializeOwned>(self) -> Result<T> {
45 let data = match &self.encoding {
46 BlobEncoding::Bcs => self.data,
47 };
48 let res = bcs::from_bytes(&data)?;
49 Ok(res)
50 }
51 pub fn read<R: Read>(rbuf: &mut R) -> Result<Blob> {
63 let len = rbuf.read_varint::<u64>()?;
64 if len == 0 {
65 bail!("Invalid object length of 0 in file");
66 }
67 if len > MAX_BLOB_LENGTH {
68 bail!("blob length {len} is above the maximum of {MAX_BLOB_LENGTH} bytes");
69 }
70 let encoding = rbuf.read_u8()?;
71 let mut data = Vec::with_capacity(len.min(MAX_PREALLOCATION as u64) as usize);
74 rbuf.take(len).read_to_end(&mut data)?;
75 if data.len() as u64 != len {
76 bail!(
77 "blob length {len} is beyond the remaining input of {} bytes",
78 data.len()
79 );
80 }
81 let blob = Blob {
82 data,
83 encoding: BlobEncoding::try_from(encoding)?,
84 };
85 Ok(blob)
86 }
87 pub fn write<W: Write>(&self, wbuf: &mut W) -> Result<usize> {
88 let mut buf = [0u8; MAX_VARINT_LENGTH];
89 let mut counter = 0;
90 let n = (self.data.len() as u64).encode_var(&mut buf);
91 wbuf.write_all(&buf[0..n])?;
92 counter += n;
93 buf[0] = self.encoding.into();
94 wbuf.write_all(&buf[0..BLOB_ENCODING_BYTES])?;
95 counter += 1;
96 wbuf.write_all(&self.data)?;
97 counter += self.data.len();
98 Ok(counter)
99 }
100 pub fn size(&self) -> usize {
101 let mut blob_size = self.data.len().required_space();
102 blob_size += BLOB_ENCODING_BYTES;
103 blob_size += self.data.len();
104 blob_size
105 }
106 pub fn to_bytes(&self) -> Vec<u8> {
107 [vec![self.encoding.into()], self.data.clone()].concat()
108 }
109 pub fn from_bytes<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
110 let (encoding, data) = bytes.split_first().ok_or(anyhow!("empty bytes"))?;
111 Blob {
112 data: data.to_vec(),
113 encoding: BlobEncoding::try_from(*encoding)?,
114 }
115 .decode()
116 }
117}
118
119pub struct BlobIter<T> {
122 reader: Box<dyn Read>,
123 _phantom: PhantomData<T>,
124}
125
126impl<T: DeserializeOwned> BlobIter<T> {
127 pub fn new(reader: Box<dyn Read>) -> Self {
128 Self {
129 reader,
130 _phantom: PhantomData,
131 }
132 }
133 fn next_blob(&mut self) -> Result<Option<T>> {
135 let mut first = [0u8; 1];
138 if self.reader.read(&mut first)? == 0 {
139 return Ok(None);
140 }
141 let blob = Blob::read(&mut first.as_slice().chain(&mut self.reader))?;
142 blob.decode().map(Some)
143 }
144}
145
146impl<T: DeserializeOwned> Iterator for BlobIter<T> {
147 type Item = T;
148 fn next(&mut self) -> Option<Self::Item> {
149 self.next_blob().unwrap_or_else(|err| {
150 error!("failed to read a blob, ignoring the rest of the file: {err:#}");
151 None
152 })
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use std::io::Cursor;
159
160 use super::*;
161 use crate::{FileCompression, StorageFormat, make_iterator};
162
163 const MAGIC: u32 = 0x0000BEEF;
164
165 fn header(len: u64) -> Vec<u8> {
168 let mut bytes = len.encode_var_vec();
169 bytes.push(BlobEncoding::Bcs.into());
170 bytes
171 }
172
173 #[test]
174 fn write_read() {
175 for value in [vec![1u8, 2, 3], vec![7u8; 3 * MAX_PREALLOCATION]] {
177 let blob = Blob::encode(&value, BlobEncoding::Bcs).unwrap();
178 let mut bytes = Vec::new();
179 blob.write(&mut bytes).unwrap();
180 let read = Blob::read(&mut Cursor::new(bytes)).unwrap();
181 assert_eq!(read.data, blob.data);
182 assert_eq!(read.decode::<Vec<u8>>().unwrap(), value);
183 }
184 }
185
186 #[test]
187 fn rejects_length_beyond_input() {
188 for len in [MAX_BLOB_LENGTH, 2_313_050_709, 4] {
190 let mut bytes = header(len);
191 bytes.extend_from_slice(&[0u8; 3]);
192 let err = Blob::read(&mut Cursor::new(bytes)).err().unwrap();
193 assert!(
194 err.to_string()
195 .contains("is beyond the remaining input of 3 bytes"),
196 "{err}"
197 );
198 }
199 }
200
201 #[test]
204 fn rejects_length_above_maximum() {
205 for compression in [FileCompression::None, FileCompression::Zstd] {
206 for len in [MAX_BLOB_LENGTH + 1, u64::MAX] {
207 let mut blob = header(len);
208 blob.extend_from_slice(&[0u8; 3]);
209 let mut file = MAGIC.to_be_bytes().to_vec();
210 file.push(StorageFormat::Blob.into());
211 file.push(compression.into());
212 match compression {
213 FileCompression::None => file.extend(blob),
214 FileCompression::Zstd => {
215 FileCompression::zstd_compress(&mut Cursor::new(blob), &mut file).unwrap()
216 }
217 }
218
219 let (mut reader, _) = crate::read(MAGIC, Cursor::new(file)).unwrap();
220 let err = Blob::read(&mut reader).err().unwrap();
221 assert!(
222 err.to_string().contains(&format!(
223 "blob length {len} is above the maximum of {MAX_BLOB_LENGTH} bytes"
224 )),
225 "{err}"
226 );
227 }
228 }
229 }
230
231 #[test]
232 fn blob_iter_reads_until_end_of_input() {
233 for count in [0u64, 1, 3] {
234 let mut blobs = Vec::new();
235 for value in 0..count {
236 Blob::encode(&value, BlobEncoding::Bcs)
237 .unwrap()
238 .write(&mut blobs)
239 .unwrap();
240 }
241 let mut file = MAGIC.to_be_bytes().to_vec();
242 file.push(StorageFormat::Blob.into());
243 file.push(FileCompression::None.into());
244 file.extend(blobs);
245
246 let values: Vec<u64> = make_iterator(MAGIC, Cursor::new(file)).unwrap().collect();
247 assert_eq!(values, (0..count).collect::<Vec<_>>());
248 }
249 }
250
251 #[test]
252 fn blob_iter_stops_at_length_beyond_input() {
253 for compression in [FileCompression::None, FileCompression::Zstd] {
254 let mut blobs = Vec::new();
255 Blob::encode(&7u64, BlobEncoding::Bcs)
256 .unwrap()
257 .write(&mut blobs)
258 .unwrap();
259 blobs.extend(header(4));
260 blobs.extend_from_slice(&[0u8; 3]);
261
262 let mut file = MAGIC.to_be_bytes().to_vec();
263 file.push(StorageFormat::Blob.into());
264 file.push(compression.into());
265 match compression {
266 FileCompression::None => file.extend(blobs),
267 FileCompression::Zstd => {
268 FileCompression::zstd_compress(&mut Cursor::new(blobs), &mut file).unwrap()
269 }
270 }
271
272 let values: Vec<u64> = make_iterator(MAGIC, Cursor::new(file)).unwrap().collect();
273 assert_eq!(values, vec![7]);
274 }
275 }
276}