diff options
Diffstat (limited to 'vendor/snap/src/frame.rs')
-rw-r--r-- | vendor/snap/src/frame.rs | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/vendor/snap/src/frame.rs b/vendor/snap/src/frame.rs new file mode 100644 index 000000000..3c56f9a88 --- /dev/null +++ b/vendor/snap/src/frame.rs @@ -0,0 +1,104 @@ +use crate::bytes; +use crate::compress::{max_compress_len, Encoder}; +use crate::crc32::CheckSummer; +use crate::error::Error; +use crate::MAX_BLOCK_SIZE; + +/// The maximum chunk of compressed bytes that can be processed at one time. +/// +/// This is computed via `max_compress_len(MAX_BLOCK_SIZE)`. +/// +/// TODO(ag): Replace with const fn once they support nominal branching. +pub const MAX_COMPRESS_BLOCK_SIZE: usize = 76490; + +/// The special magic string that starts any stream. +/// +/// This may appear more than once in a stream in order to support easy +/// concatenation of files compressed in the Snappy frame format. +pub const STREAM_IDENTIFIER: &'static [u8] = b"\xFF\x06\x00\x00sNaPpY"; + +/// The body of the special stream identifier. +pub const STREAM_BODY: &'static [u8] = b"sNaPpY"; + +/// The length of a snappy chunk type (1 byte), packet length (3 bytes) +/// and CRC field (4 bytes). This is technically the chunk header _plus_ +/// the CRC present in most chunks. +pub const CHUNK_HEADER_AND_CRC_SIZE: usize = 8; + +/// An enumeration describing each of the 4 main chunk types. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ChunkType { + Stream = 0xFF, + Compressed = 0x00, + Uncompressed = 0x01, + Padding = 0xFE, +} + +impl ChunkType { + /// Converts a byte to one of the four defined chunk types represented by + /// a single byte. If the chunk type is reserved, then it is returned as + /// an Err. + pub fn from_u8(b: u8) -> Result<ChunkType, u8> { + match b { + 0xFF => Ok(ChunkType::Stream), + 0x00 => Ok(ChunkType::Compressed), + 0x01 => Ok(ChunkType::Uncompressed), + 0xFE => Ok(ChunkType::Padding), + b => Err(b), + } + } +} + +/// Compress a single frame (or decide to pass it through uncompressed). This +/// will output a frame header in `dst_chunk_header`, and it will return a slice +/// pointing to the data to use in the frame. The `dst_chunk_header` array must +/// always have a size of 8 bytes. +/// +/// If `always_use_dst` is set to false, the return value may point into either +/// `src` (for data we couldn't compress) or into `dst` (for data we could +/// compress). If `always_use_dst` is true, the data will always be in `dst`. +/// This is a bit weird, but because of Rust's ownership rules, it's easiest +/// for a single function to always be in charge of writing to `dst`. +pub fn compress_frame<'a>( + enc: &mut Encoder, + checksummer: CheckSummer, + src: &'a [u8], + dst_chunk_header: &mut [u8], + dst: &'a mut [u8], + always_use_dst: bool, +) -> Result<&'a [u8], Error> { + // This is a purely internal function, with a bunch of preconditions. + assert!(src.len() <= MAX_BLOCK_SIZE); + assert!(dst.len() >= max_compress_len(MAX_BLOCK_SIZE)); + assert_eq!(dst_chunk_header.len(), CHUNK_HEADER_AND_CRC_SIZE); + + // Build a checksum of our _uncompressed_ data. + let checksum = checksummer.crc32c_masked(src); + + // Compress the buffer. If compression sucked, throw it out and + // write uncompressed bytes instead. Since our buffer is at most + // MAX_BLOCK_SIZE and our dst buffer has size + // max_compress_len(MAX_BLOCK_SIZE), we have enough space. + let compress_len = enc.compress(src, dst)?; + let (chunk_type, chunk_len) = + // We add 4 to the chunk_len because of the checksum. + if compress_len >= src.len() - (src.len() / 8) { + (ChunkType::Uncompressed, 4 + src.len()) + } else { + (ChunkType::Compressed, 4 + compress_len) + }; + + dst_chunk_header[0] = chunk_type as u8; + bytes::write_u24_le(chunk_len as u32, &mut dst_chunk_header[1..]); + bytes::write_u32_le(checksum, &mut dst_chunk_header[4..]); + + // Return the data to put in our frame. + if chunk_type == ChunkType::Compressed { + Ok(&dst[0..compress_len]) + } else if always_use_dst { + dst[..src.len()].copy_from_slice(src); + Ok(&dst[..src.len()]) + } else { + Ok(src) + } +} |