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 fn from(_: SizeError<S, D>) -> Self {
107 DecodeErr::Length
108 }
109}
110
111impl<A, S, V> From<ConvertError<A, S, V>> for DecodeErr {
112 fn from(err: ConvertError<A, S, V>) -> Self {
113 match err {
114 ConvertError::Alignment(_) => panic!("All zerocopy transmutations must be unaligned"),
115 ConvertError::Size(_) => DecodeErr::Length,
116 ConvertError::Validity(_) => DecodeErr::Validity,
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use assert_matches::assert_matches;
124 use itertools::Itertools;
125 use quickcheck::TestResult;
126 use quickcheck_macros::quickcheck;
127
128 use crate::{
129 Encodable, Splinter, SplinterRef,
130 codec::{
131 DecodeErr,
132 footer::{Footer, SPLINTER_V2_MAGIC},
133 partition_ref::PartitionRef,
134 },
135 level::{Block, Level, Low},
136 partition_kind::PartitionKind,
137 testutil::{
138 LevelSetGen, mkpartition, mkpartition_buf, mksplinter_buf, mksplinter_manual,
139 test_partition_read,
140 },
141 traits::{Optimizable, TruncateFrom},
142 };
143
144 #[test]
145 fn test_encode_decode_direct() {
146 let mut setgen = LevelSetGen::<Low>::new(0xDEADBEEF);
147 let kinds = [
148 PartitionKind::Bitmap,
149 PartitionKind::Vec,
150 PartitionKind::Run,
151 PartitionKind::Tree,
152 ];
153 let sets = &[
154 vec![0],
155 vec![0, 1],
156 vec![0, u16::MAX],
157 vec![u16::MAX],
158 setgen.random(8),
159 setgen.random(4096),
160 setgen.runs(4096, 0.01),
161 setgen.runs(4096, 0.2),
162 setgen.runs(4096, 0.5),
163 setgen.runs(4096, 0.9),
164 (0..Low::MAX_LEN)
165 .map(|v| <Low as Level>::Value::truncate_from(v))
166 .collect_vec(),
167 ];
168
169 for kind in kinds {
170 for (i, set) in sets.iter().enumerate() {
171 println!("Testing partition kind: {kind:?} with set {i}");
172
173 let partition = mkpartition::<Low>(kind, &set);
174 let buf = partition.encode_to_bytes();
175 assert_eq!(
176 partition.encoded_size(),
177 buf.len(),
178 "encoded_size doesn't match actual size"
179 );
180
181 let partition_ref = PartitionRef::<'_, Low>::from_suffix(&buf).unwrap();
182
183 assert_eq!(partition_ref.kind(), kind);
184 test_partition_read(&partition_ref, &set);
185 }
186 }
187 }
188
189 #[quickcheck]
190 fn test_encode_decode_quickcheck(values: Vec<u32>) -> TestResult {
191 let expected = values.iter().copied().sorted().dedup().collect_vec();
192 let mut splinter = Splinter::from_iter(values);
193 splinter.optimize();
194 let buf = splinter.encode_to_bytes();
195 assert_eq!(
196 buf.len(),
197 splinter.encoded_size(),
198 "encoded_size doesn't match actual size"
199 );
200 let splinter_ref = SplinterRef::from_bytes(buf).unwrap();
201
202 test_partition_read(&splinter_ref, &expected);
203
204 TestResult::passed()
205 }
206
207 #[test]
208 fn test_length_corruption() {
209 for i in 0..Footer::SIZE {
210 let truncated = [0].repeat(i);
211 assert_matches!(
212 SplinterRef::from_bytes(truncated),
213 Err(DecodeErr::Length),
214 "Failed for truncated buffer of size {}",
215 i
216 );
217 }
218 }
219
220 #[test]
221 fn test_corrupted_root_partition_kind() {
222 let mut buf = mksplinter_buf(&[1, 2, 3]);
223
224 let footer_offset = buf.len() - Footer::SIZE;
226 let partitions = &mut buf[0..footer_offset];
227 partitions[partitions.len() - 1] = 10;
228 let corrupted = mksplinter_manual(partitions);
229
230 assert_matches!(SplinterRef::from_bytes(corrupted), Err(DecodeErr::Validity));
231 }
232
233 #[test]
234 fn test_corrupted_magic() {
235 let mut buf = mksplinter_buf(&[1, 2, 3]);
236
237 let magic_offset = buf.len() - SPLINTER_V2_MAGIC.len();
238 buf[magic_offset..].copy_from_slice(&[0].repeat(4));
239
240 assert_matches!(SplinterRef::from_bytes(buf), Err(DecodeErr::Magic));
241 }
242
243 #[test]
244 fn test_corrupted_data() {
245 let mut buf = mksplinter_buf(&[1, 2, 3]);
246 buf[0] = 123;
247 assert_matches!(SplinterRef::from_bytes(buf), Err(DecodeErr::Checksum));
248 }
249
250 #[test]
251 fn test_corrupted_checksum() {
252 let mut buf = mksplinter_buf(&[1, 2, 3]);
253 let checksum_offset = buf.len() - Footer::SIZE;
254 buf[checksum_offset] = 123;
255 assert_matches!(SplinterRef::from_bytes(buf), Err(DecodeErr::Checksum));
256 }
257
258 #[test]
259 fn test_corrupted_vec_partition() {
260 let mut buf = mkpartition_buf::<Block>(PartitionKind::Vec, &[1, 2, 3]);
261
262 assert_eq!(buf.as_ref(), &[0x01, 0x02, 0x03, 0x02, 0x03]);
264
265 buf[3] = 5;
267
268 assert_matches!(
269 PartitionRef::<Block>::from_suffix(&buf),
270 Err(DecodeErr::Length)
271 );
272 }
273
274 #[test]
275 fn test_corrupted_run_partition() {
276 let mut buf = mkpartition_buf::<Block>(PartitionKind::Run, &[1, 2, 3]);
277
278 assert_eq!(buf.as_ref(), &[0x01, 0x03, 0x00, 0x04]);
280
281 buf[2] = 5;
283
284 assert_matches!(
285 PartitionRef::<Block>::from_suffix(&buf),
286 Err(DecodeErr::Length)
287 );
288 }
289
290 #[test]
291 fn test_corrupted_tree_partition() {
292 let mut buf = mkpartition_buf::<Low>(PartitionKind::Tree, &[1, 2]);
293
294 assert_eq!(
295 buf.as_ref(),
296 &[
297 0x01, 0x02, 0x01, 0x03,
300 0x00, 0x00, 0x00, 0x00, 0x05
303 ]
304 );
305
306 buf[7] = 5;
308
309 assert_matches!(
310 PartitionRef::<Block>::from_suffix(&buf),
311 Err(DecodeErr::Length)
312 );
313 }
314
315 #[test]
316 fn test_vec_byteorder() {
317 let buf = mkpartition_buf::<Low>(PartitionKind::Vec, &[0x01_00, 0x02_00]);
318 assert_eq!(
319 buf.as_ref(),
320 &[
321 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x03, ]
326 );
327 }
328
329 #[test]
330 fn test_run_byteorder() {
331 let buf = mkpartition_buf::<Low>(PartitionKind::Run, &[0x01_00, 0x02_00]);
332 assert_eq!(
333 buf.as_ref(),
334 &[
335 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x01, 0x04, ]
340 );
341 }
342
343 #[test]
344 fn test_detect_splinter_v1() {
345 let empty_splinter_v1 = b"\xda\xae\x12\xdf\0\0\0\0";
346 assert_matches!(
347 SplinterRef::from_bytes(empty_splinter_v1.as_slice()),
348 Err(DecodeErr::SplinterV1)
349 );
350 }
351}