1use bytes::Bytes;
7
8use crate::{ChunkManifest, Codec, CodecError, CodecKind};
9
10pub struct Passthrough;
11
12#[async_trait::async_trait]
13impl Codec for Passthrough {
14 fn kind(&self) -> CodecKind {
15 CodecKind::Passthrough
16 }
17
18 async fn compress(&self, input: Bytes) -> Result<(Bytes, ChunkManifest), CodecError> {
19 let len = input.len() as u64;
20 let crc = crc32c::crc32c(&input);
21 Ok((
22 input,
23 ChunkManifest {
24 codec: CodecKind::Passthrough,
25 original_size: len,
26 compressed_size: len,
27 crc32c: crc,
28 },
29 ))
30 }
31
32 async fn decompress(
33 &self,
34 input: Bytes,
35 manifest: &ChunkManifest,
36 ) -> Result<Bytes, CodecError> {
37 if manifest.codec != CodecKind::Passthrough {
38 return Err(CodecError::CodecMismatch {
39 expected: CodecKind::Passthrough,
40 got: manifest.codec,
41 });
42 }
43 let crc = crc32c::crc32c(&input);
44 if crc != manifest.crc32c {
45 return Err(CodecError::CrcMismatch {
46 expected: manifest.crc32c,
47 got: crc,
48 });
49 }
50 if input.len() as u64 != manifest.compressed_size {
51 return Err(CodecError::SizeMismatch {
52 expected: manifest.compressed_size,
53 got: input.len() as u64,
54 });
55 }
56 Ok(input)
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[tokio::test]
65 async fn roundtrip_preserves_bytes() {
66 let codec = Passthrough;
67 let input = Bytes::from_static(b"hello squished s3");
68 let (compressed, manifest) = codec.compress(input.clone()).await.unwrap();
69 assert_eq!(compressed, input);
70 assert_eq!(manifest.codec, CodecKind::Passthrough);
71 assert_eq!(manifest.original_size, input.len() as u64);
72 assert_eq!(manifest.compressed_size, input.len() as u64);
73 assert_ne!(manifest.crc32c, 0, "crc32c must not be the stub zero");
74 let decompressed = codec.decompress(compressed, &manifest).await.unwrap();
75 assert_eq!(decompressed, input);
76 }
77
78 #[tokio::test]
79 async fn detects_corrupted_payload() {
80 let codec = Passthrough;
81 let original = Bytes::from_static(b"hello squished s3");
82 let (_, manifest) = codec.compress(original.clone()).await.unwrap();
83 let corrupted = Bytes::from_static(b"hello SQUISHED s3");
84 let err = codec.decompress(corrupted, &manifest).await.unwrap_err();
85 assert!(matches!(err, CodecError::CrcMismatch { .. }));
86 }
87
88 #[tokio::test]
89 async fn rejects_codec_mismatch() {
90 let codec = Passthrough;
91 let original = Bytes::from_static(b"hello");
92 let (_, mut manifest) = codec.compress(original.clone()).await.unwrap();
93 manifest.codec = CodecKind::CpuZstd;
94 let err = codec.decompress(original, &manifest).await.unwrap_err();
95 assert!(matches!(err, CodecError::CodecMismatch { .. }));
96 }
97}