#[cfg_attr(feature = "safe-encode", forbid(unsafe_code))]
pub(crate) mod compress;
pub(crate) mod hashtable;
#[cfg(feature = "safe-decode")]
#[cfg_attr(feature = "safe-decode", forbid(unsafe_code))]
pub(crate) mod decompress_safe;
#[cfg(feature = "safe-decode")]
pub(crate) use decompress_safe as decompress;
#[cfg(not(feature = "safe-decode"))]
pub(crate) mod decompress;
pub use compress::*;
pub use decompress::*;
use core::{error::Error, fmt};
pub(crate) const WINDOW_SIZE: usize = 64 * 1024;
const MFLIMIT: usize = 12;
const LAST_LITERALS: usize = 5;
const END_OFFSET: usize = LAST_LITERALS + 1;
const LZ4_MIN_LENGTH: usize = MFLIMIT + 1;
const MAXD_LOG: usize = 16;
const MAX_DISTANCE: usize = (1 << MAXD_LOG) - 1;
#[allow(dead_code)]
const MATCH_LENGTH_MASK: u32 = (1_u32 << 4) - 1;
const MINMATCH: usize = 4;
#[allow(dead_code)]
const FASTLOOP_SAFE_DISTANCE: usize = 64;
#[allow(dead_code)]
static LZ4_64KLIMIT: usize = (64 * 1024) + (MFLIMIT - 1);
#[derive(Debug)]
#[non_exhaustive]
pub enum DecompressError {
OutputTooSmall {
expected: usize,
actual: usize,
},
LiteralOutOfBounds,
ExpectedAnotherByte,
OffsetZero,
OffsetOutOfBounds,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum CompressError {
OutputTooSmall,
}
impl fmt::Display for DecompressError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DecompressError::OutputTooSmall { expected, actual } => {
write!(
f,
"provided output is too small for the decompressed data, actual {actual}, expected \
{expected}"
)
}
DecompressError::LiteralOutOfBounds => {
f.write_str("literal is out of bounds of the input")
}
DecompressError::ExpectedAnotherByte => {
f.write_str("expected another byte, found none")
}
DecompressError::OffsetZero => f.write_str("0 is not a valid match offset"),
DecompressError::OffsetOutOfBounds => {
f.write_str("the offset to copy is not contained in the decompressed buffer")
}
}
}
}
impl fmt::Display for CompressError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
CompressError::OutputTooSmall => f.write_str(
"output is too small for the compressed data, use get_maximum_output_size to \
reserve enough space",
),
}
}
}
impl Error for DecompressError {}
impl Error for CompressError {}
#[inline]
pub fn uncompressed_size(input: &[u8]) -> Result<(usize, &[u8]), DecompressError> {
let size = input.get(..4).ok_or(DecompressError::ExpectedAnotherByte)?;
let size: &[u8; 4] = size.try_into().unwrap();
let uncompressed_size = u32::from_le_bytes(*size) as usize;
let rest = &input[4..];
Ok((uncompressed_size, rest))
}
#[test]
#[cfg(target_pointer_width = "64")] fn large_integer_roundtrip() {
let u32_max = usize::try_from(u32::MAX).unwrap();
let value = u32_max + u32_max / 2;
let mut buf = vec![0u8; value / 255 + 1];
let mut sink = crate::sink::SliceSink::new(&mut buf, 0);
self::compress::write_integer(&mut sink, value);
#[cfg(feature = "safe-decode")]
let value_decompressed = self::decompress_safe::read_integer(&buf, &mut 0).unwrap();
#[cfg(not(feature = "safe-decode"))]
let value_decompressed = {
let mut ptr_range = buf.as_ptr_range();
self::decompress::read_integer_ptr(&mut ptr_range.start, ptr_range.end).unwrap()
};
assert_eq!(value, value_decompressed);
}