1use bytes::{BufMut, Bytes, BytesMut};
2use thiserror::Error;
3use zerocopy::{ConvertError, SizeError};
4
5use crate::codec::encoder::Encoder;
6
7pub mod encoder;
8
9pub(crate) mod footer;
10pub(crate) mod partition_ref;
11pub(crate) mod runs_ref;
12pub(crate) mod tree_ref;
13
14pub trait Encodable {
16 fn encoded_size(&self) -> usize;
23
24 fn encode<B: BufMut>(&self, encoder: &mut Encoder<B>);
26
27 fn encode_to_bytes(&self) -> Bytes {
43 let size = self.encoded_size();
44 let mut encoder = Encoder::new(BytesMut::with_capacity(size));
45 self.encode(&mut encoder);
46 encoder.into_inner().freeze()
47 }
48}
49
50#[derive(Debug, Error)]
55pub enum DecodeErr {
56 #[error("not enough bytes")]
61 Length,
62
63 #[error("invalid encoding")]
69 Validity,
70
71 #[error("unknown magic value")]
77 Magic,
78
79 #[error("invalid checksum")]
84 Checksum,
85
86 #[error("buffer contains serialized Splinter V1, decode using splinter-rs:v0.3.3")]
91 SplinterV1,
92}
93
94impl DecodeErr {
95 #[inline]
96 fn ensure_bytes_available(data: &[u8], len: usize) -> Result<(), DecodeErr> {
97 if data.len() < len {
98 Err(Self::Length)
99 } else {
100 Ok(())
101 }
102 }
103}
104
105impl<S, D> From<SizeError<S, D>> for DecodeErr {
106 #[track_caller]
107 fn from(_: SizeError<S, D>) -> Self {
108 DecodeErr::Length
109 }
110}
111
112impl<A, S, V> From<ConvertError<A, S, V>> for DecodeErr {
113 #[track_caller]
114 fn from(err: ConvertError<A, S, V>) -> Self {
115 match err {
116 ConvertError::Alignment(_) => panic!("All zerocopy transmutations must be unaligned"),
117 ConvertError::Size(_) => DecodeErr::Length,
118 ConvertError::Validity(_) => DecodeErr::Validity,
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use bytes::BytesMut;
126 use itertools::Itertools;
127 use proptest::proptest;
128
129 use crate::{
130 Encodable, Splinter, SplinterRef, assert_error,
131 codec::{
132 DecodeErr,
133 encoder::Encoder,
134 footer::{Footer, SPLINTER_V2_MAGIC},
135 partition_ref::PartitionRef,
136 },
137 level::{Block, Level, Low},
138 partition_kind::PartitionKind,
139 testutil::{
140 LevelSetGen, mkpartition, mkpartition_buf, mksplinter_buf, mksplinter_manual,
141 test_partition_read,
142 },
143 traits::{Optimizable, TruncateFrom},
144 };
145
146 #[test]
147 fn test_encode_decode_direct() {
148 let mut setgen = LevelSetGen::<Low>::new(0xDEADBEEF);
149 let kinds = [
150 PartitionKind::Bitmap,
151 PartitionKind::Vec,
152 PartitionKind::Run,
153 PartitionKind::Tree,
154 ];
155 let sets = &[
156 vec![0],
157 vec![0, 1],
158 vec![0, u16::MAX],
159 vec![u16::MAX],
160 setgen.random(8),
161 setgen.random(4096),
162 setgen.runs(4096, 0.01),
163 setgen.runs(4096, 0.2),
164 setgen.runs(4096, 0.5),
165 setgen.runs(4096, 0.9),
166 (0..Low::MAX_LEN)
167 .map(|v| <Low as Level>::Value::truncate_from(v))
168 .collect_vec(),
169 ];
170
171 for kind in kinds {
172 for (i, set) in sets.iter().enumerate() {
173 println!("Testing partition kind: {kind:?} with set {i}");
174
175 let partition = mkpartition::<Low>(kind, &set);
176 let buf = partition.encode_to_bytes();
177 assert_eq!(
178 partition.encoded_size(),
179 buf.len(),
180 "encoded_size doesn't match actual size"
181 );
182
183 let partition_ref = PartitionRef::<'_, Low>::from_suffix(&buf).unwrap();
184
185 assert_eq!(partition_ref.kind(), kind);
186 test_partition_read(&partition_ref, &set);
187 }
188 }
189 }
190
191 proptest! {
192 #[test]
193 fn test_encode_decode_proptest(
194 values in proptest::collection::vec(0u32..16384, 0..1024),
195 ) {
196 let expected = values.iter().copied().sorted().dedup().collect_vec();
197 let mut splinter = Splinter::from_iter(values);
198 splinter.optimize();
199 let buf = splinter.encode_to_bytes();
200 assert_eq!(
201 buf.len(),
202 splinter.encoded_size(),
203 "encoded_size doesn't match actual size"
204 );
205 let splinter_ref = SplinterRef::from_bytes(buf).unwrap();
206
207 test_partition_read(&splinter_ref, &expected);
208 }
209 }
210
211 #[test]
212 fn test_length_corruption() {
213 for i in 0..Footer::SIZE {
214 let truncated = [0].repeat(i);
215 assert_error!(
216 SplinterRef::from_bytes(truncated),
217 DecodeErr::Length,
218 "Failed for truncated buffer of size {}",
219 i
220 );
221 }
222 }
223
224 #[test]
225 fn test_corrupted_root_partition_kind() {
226 let mut buf = mksplinter_buf(&[1, 2, 3]);
227
228 let footer_offset = buf.len() - Footer::SIZE;
230 let partitions = &mut buf[0..footer_offset];
231 partitions[partitions.len() - 1] = 10;
232 let corrupted = mksplinter_manual(partitions);
233
234 assert_error!(SplinterRef::from_bytes(corrupted), DecodeErr::Validity);
235 }
236
237 #[test]
238 fn test_corrupted_magic() {
239 let mut buf = mksplinter_buf(&[1, 2, 3]);
240
241 let magic_offset = buf.len() - SPLINTER_V2_MAGIC.len();
242 buf[magic_offset..].copy_from_slice(&[0].repeat(4));
243
244 assert_error!(SplinterRef::from_bytes(buf), DecodeErr::Magic);
245 }
246
247 #[test]
248 fn test_corrupted_data() {
249 let mut buf = mksplinter_buf(&[1, 2, 3]);
250 buf[0] = 123;
251 assert_error!(SplinterRef::from_bytes(buf), DecodeErr::Checksum);
252 }
253
254 #[test]
255 fn test_corrupted_checksum() {
256 let mut buf = mksplinter_buf(&[1, 2, 3]);
257 let checksum_offset = buf.len() - Footer::SIZE;
258 buf[checksum_offset] = 123;
259 assert_error!(SplinterRef::from_bytes(buf), DecodeErr::Checksum);
260 }
261
262 #[test]
263 fn test_corrupted_vec_partition() {
264 let mut buf = mkpartition_buf::<Block>(PartitionKind::Vec, &[1, 2, 3]);
265
266 assert_eq!(buf.as_ref(), &[0x01, 0x02, 0x03, 0x02, 0x03]);
268
269 buf[3] = 5;
271
272 assert_error!(PartitionRef::<Block>::from_suffix(&buf), DecodeErr::Length);
273 }
274
275 #[test]
276 fn test_corrupted_run_partition() {
277 let mut buf = mkpartition_buf::<Block>(PartitionKind::Run, &[1, 2, 3]);
278
279 assert_eq!(buf.as_ref(), &[0x01, 0x03, 0x00, 0x04]);
281
282 buf[2] = 5;
284
285 assert_error!(PartitionRef::<Block>::from_suffix(&buf), DecodeErr::Length);
286 }
287
288 #[test]
289 fn test_corrupted_tree_partition() {
290 let mut buf = mkpartition_buf::<Low>(PartitionKind::Tree, &[1, 2]);
291
292 assert_eq!(
293 buf.as_ref(),
294 &[
295 0x01, 0x02, 0x01, 0x03,
298 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05
301 ]
302 );
303
304 buf[9] = 5;
306
307 assert_error!(PartitionRef::<Block>::from_suffix(&buf), DecodeErr::Length);
308 }
309
310 #[test]
311 fn test_vec_byteorder() {
312 let buf = mkpartition_buf::<Low>(PartitionKind::Vec, &[0x01_00, 0x02_00]);
313 assert_eq!(
314 buf.as_ref(),
315 &[
316 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x03, ]
321 );
322 }
323
324 #[test]
325 fn test_run_byteorder() {
326 let buf = mkpartition_buf::<Low>(PartitionKind::Run, &[0x01_00, 0x02_00]);
327 assert_eq!(
328 buf.as_ref(),
329 &[
330 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, ]
335 );
336 }
337
338 #[test]
339 fn test_detect_splinter_v1() {
340 let empty_splinter_v1 = b"\xda\xae\x12\xdf\0\0\0\0";
341 assert_error!(
342 SplinterRef::from_bytes(empty_splinter_v1.as_slice()),
343 DecodeErr::SplinterV1
344 );
345 }
346
347 #[test]
348 #[should_panic(expected = "footer already present")]
349 fn test_encoder_panics_when_footer_is_written_after_splinter_blob() {
350 let mut buf = BytesMut::new();
351 let mut encoder = Encoder::new(&mut buf);
352 encoder.write_splinter(&[1, 2, 3]);
353 encoder.write_footer();
354 }
355
356 #[test]
357 #[should_panic(expected = "footer already present")]
358 fn test_encoder_panics_when_footer_is_written_twice() {
359 let mut buf = BytesMut::new();
360 let mut encoder = Encoder::new(&mut buf);
361 encoder.write_footer();
362 encoder.write_footer();
363 }
364}