Skip to main content

reth_era/common/
compression.rs

1//! Snappy-framed compression helpers and a generic Snappy + RLP codec.
2//!
3//! These utilities are format-agnostic and shared across the e2store-based file formats.
4
5use crate::e2s::error::E2sError;
6use alloy_rlp::{Decodable, Encodable};
7use snap::{read::FrameDecoder, write::FrameEncoder};
8use std::{
9    io::{Read, Write},
10    marker::PhantomData,
11};
12
13/// Compress raw bytes with Snappy framed encoding.
14pub fn snappy_compress(data: &[u8]) -> Result<Vec<u8>, E2sError> {
15    let mut compressed = Vec::new();
16    {
17        let mut encoder = FrameEncoder::new(&mut compressed);
18        Write::write_all(&mut encoder, data)
19            .map_err(|e| E2sError::SnappyCompression(format!("Failed to compress: {e}")))?;
20        encoder
21            .flush()
22            .map_err(|e| E2sError::SnappyCompression(format!("Failed to flush encoder: {e}")))?;
23    }
24    Ok(compressed)
25}
26
27/// Decompress Snappy framed-encoded bytes.
28pub fn snappy_decompress(data: &[u8]) -> Result<Vec<u8>, E2sError> {
29    let mut decoder = FrameDecoder::new(data);
30    let mut decompressed = Vec::new();
31    Read::read_to_end(&mut decoder, &mut decompressed)
32        .map_err(|e| E2sError::SnappyDecompression(format!("Failed to decompress: {e}")))?;
33    Ok(decompressed)
34}
35
36/// Generic codec for Snappy-framed-compressed RLP data.
37#[derive(Debug, Clone, Default)]
38pub struct SnappyRlpCodec<T> {
39    _phantom: PhantomData<T>,
40}
41
42impl<T> SnappyRlpCodec<T> {
43    /// Create a new codec for the given type.
44    pub const fn new() -> Self {
45        Self { _phantom: PhantomData }
46    }
47}
48
49impl<T: Decodable> SnappyRlpCodec<T> {
50    /// Decode compressed data into the target type.
51    ///
52    /// A record holds exactly one RLP value, so any bytes left after it are treated as corruption
53    /// and rejected rather than silently ignored.
54    pub fn decode(&self, compressed_data: &[u8]) -> Result<T, E2sError> {
55        let decompressed = snappy_decompress(compressed_data)?;
56        let mut slice = decompressed.as_slice();
57        let value = T::decode(&mut slice)
58            .map_err(|e| E2sError::Rlp(format!("Failed to decode RLP data: {e}")))?;
59        if !slice.is_empty() {
60            return Err(E2sError::Rlp(format!(
61                "Trailing bytes after RLP value: {} byte(s) remain",
62                slice.len()
63            )));
64        }
65        Ok(value)
66    }
67}
68
69impl<T: Encodable> SnappyRlpCodec<T> {
70    /// Encode data into compressed format.
71    pub fn encode(&self, data: &T) -> Result<Vec<u8>, E2sError> {
72        let mut rlp_data = Vec::new();
73        data.encode(&mut rlp_data);
74        snappy_compress(&rlp_data)
75    }
76}