use crate::xxhash::Xxh32State;
use core::fmt;
pub const LZ4F_VERSION: u32 = 100;
pub const LZ4F_BLOCKUNCOMPRESSED_FLAG: u32 = 0x8000_0000;
pub const BH_SIZE: usize = 4;
pub const BF_SIZE: usize = 4;
pub const MIN_FH_SIZE: usize = 7;
pub const MAX_FH_SIZE: usize = 19;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum BlockSizeId {
#[default]
Default = 0,
Max64Kb = 4,
Max256Kb = 5,
Max1Mb = 6,
Max4Mb = 7,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum BlockMode {
#[default]
Linked = 0,
Independent = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum ContentChecksum {
#[default]
Disabled = 0,
Enabled = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum BlockChecksum {
#[default]
Disabled = 0,
Enabled = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum FrameType {
#[default]
Frame = 0,
SkippableFrame = 1,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FrameInfo {
pub block_size_id: BlockSizeId,
pub block_mode: BlockMode,
pub content_checksum_flag: ContentChecksum,
pub frame_type: FrameType,
pub content_size: u64,
pub dict_id: u32,
pub block_checksum_flag: BlockChecksum,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Preferences {
pub frame_info: FrameInfo,
pub compression_level: i32,
pub auto_flush: bool,
pub favor_dec_speed: bool,
}
#[derive(Clone, Copy, Default)]
pub struct CustomMem {
pub alloc_fn: Option<fn(opaque: *mut (), size: usize) -> *mut ()>,
pub calloc_fn: Option<fn(opaque: *mut (), size: usize) -> *mut ()>,
pub free_fn: Option<fn(opaque: *mut (), ptr: *mut ())>,
pub opaque: *mut (),
}
unsafe impl Send for CustomMem {}
unsafe impl Sync for CustomMem {}
impl fmt::Debug for CustomMem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CustomMem")
.field("alloc_fn", &self.alloc_fn.map(|_| "<fn>"))
.field("calloc_fn", &self.calloc_fn.map(|_| "<fn>"))
.field("free_fn", &self.free_fn.map(|_| "<fn>"))
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum BlockCompressMode {
#[default]
Compressed = 0,
Uncompressed = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u16)]
pub enum CtxType {
#[default]
None = 0,
Fast = 1,
Hc = 2,
}
pub struct Lz4FCCtx {
pub cmem: CustomMem,
pub prefs: Preferences,
pub version: u32,
pub c_stage: u32,
pub max_block_size: usize,
pub max_buffer_size: usize,
pub tmp_buf: Vec<u8>,
pub tmp_in_offset: usize,
pub tmp_in_size: usize,
pub total_in_size: u64,
pub xxh: Xxh32State,
pub lz4_ctx: Option<Vec<u8>>,
pub lz4_ctx_alloc: u16,
pub lz4_ctx_type: CtxType,
pub block_compress_mode: BlockCompressMode,
pub cdict_ptr: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(u32)]
pub enum DecompressStage {
#[default]
GetFrameHeader = 0,
StoreFrameHeader = 1,
Init = 2,
GetBlockHeader = 3,
StoreBlockHeader = 4,
CopyDirect = 5,
GetBlockChecksum = 6,
GetCBlock = 7,
StoreCBlock = 8,
FlushOut = 9,
GetSuffix = 10,
StoreSuffix = 11,
GetSFrameSize = 12,
StoreSFrameSize = 13,
SkipSkippable = 14,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Lz4FError {
OkNoError,
Generic,
MaxBlockSizeInvalid,
BlockModeInvalid,
ParameterInvalid,
CompressionLevelInvalid,
HeaderVersionWrong,
BlockChecksumInvalid,
ReservedFlagSet,
AllocationFailed,
SrcSizeTooLarge,
DstMaxSizeTooSmall,
FrameHeaderIncomplete,
FrameTypeUnknown,
FrameSizeWrong,
SrcPtrWrong,
DecompressionFailed,
HeaderChecksumInvalid,
ContentChecksumInvalid,
FrameDecodingAlreadyStarted,
CompressionStateUninitialized,
ParameterNull,
IoWrite,
IoRead,
}
impl Lz4FError {
pub fn error_name(&self) -> &'static str {
match self {
Lz4FError::OkNoError => "OK_NoError",
Lz4FError::Generic => "ERROR_GENERIC",
Lz4FError::MaxBlockSizeInvalid => "ERROR_maxBlockSize_invalid",
Lz4FError::BlockModeInvalid => "ERROR_blockMode_invalid",
Lz4FError::ParameterInvalid => "ERROR_parameter_invalid",
Lz4FError::CompressionLevelInvalid => "ERROR_compressionLevel_invalid",
Lz4FError::HeaderVersionWrong => "ERROR_headerVersion_wrong",
Lz4FError::BlockChecksumInvalid => "ERROR_blockChecksum_invalid",
Lz4FError::ReservedFlagSet => "ERROR_reservedFlag_set",
Lz4FError::AllocationFailed => "ERROR_allocation_failed",
Lz4FError::SrcSizeTooLarge => "ERROR_srcSize_tooLarge",
Lz4FError::DstMaxSizeTooSmall => "ERROR_dstMaxSize_tooSmall",
Lz4FError::FrameHeaderIncomplete => "ERROR_frameHeader_incomplete",
Lz4FError::FrameTypeUnknown => "ERROR_frameType_unknown",
Lz4FError::FrameSizeWrong => "ERROR_frameSize_wrong",
Lz4FError::SrcPtrWrong => "ERROR_srcPtr_wrong",
Lz4FError::DecompressionFailed => "ERROR_decompressionFailed",
Lz4FError::HeaderChecksumInvalid => "ERROR_headerChecksum_invalid",
Lz4FError::ContentChecksumInvalid => "ERROR_contentChecksum_invalid",
Lz4FError::FrameDecodingAlreadyStarted => "ERROR_frameDecoding_alreadyStarted",
Lz4FError::CompressionStateUninitialized => "ERROR_compressionState_uninitialized",
Lz4FError::ParameterNull => "ERROR_parameter_null",
Lz4FError::IoWrite => "ERROR_io_write",
Lz4FError::IoRead => "ERROR_io_read",
}
}
pub fn from_index(idx: usize) -> Option<Self> {
match idx {
0 => Some(Lz4FError::OkNoError),
1 => Some(Lz4FError::Generic),
2 => Some(Lz4FError::MaxBlockSizeInvalid),
3 => Some(Lz4FError::BlockModeInvalid),
4 => Some(Lz4FError::ParameterInvalid),
5 => Some(Lz4FError::CompressionLevelInvalid),
6 => Some(Lz4FError::HeaderVersionWrong),
7 => Some(Lz4FError::BlockChecksumInvalid),
8 => Some(Lz4FError::ReservedFlagSet),
9 => Some(Lz4FError::AllocationFailed),
10 => Some(Lz4FError::SrcSizeTooLarge),
11 => Some(Lz4FError::DstMaxSizeTooSmall),
12 => Some(Lz4FError::FrameHeaderIncomplete),
13 => Some(Lz4FError::FrameTypeUnknown),
14 => Some(Lz4FError::FrameSizeWrong),
15 => Some(Lz4FError::SrcPtrWrong),
16 => Some(Lz4FError::DecompressionFailed),
17 => Some(Lz4FError::HeaderChecksumInvalid),
18 => Some(Lz4FError::ContentChecksumInvalid),
19 => Some(Lz4FError::FrameDecodingAlreadyStarted),
20 => Some(Lz4FError::CompressionStateUninitialized),
21 => Some(Lz4FError::ParameterNull),
22 => Some(Lz4FError::IoWrite),
23 => Some(Lz4FError::IoRead),
_ => None,
}
}
pub fn from_raw(code: usize) -> Option<Self> {
if !lz4f_is_error(code) {
return None;
}
let idx = code.wrapping_neg();
Self::from_index(idx).or(Some(Lz4FError::Generic))
}
#[inline]
pub fn is_error(&self) -> bool {
!matches!(self, Lz4FError::OkNoError)
}
}
impl fmt::Display for Lz4FError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.error_name())
}
}
impl std::error::Error for Lz4FError {}
#[inline]
pub fn lz4f_is_error(code: usize) -> bool {
code > usize::MAX - 23
}
pub fn lz4f_get_error_name(code: usize) -> &'static str {
if !lz4f_is_error(code) {
return "Unspecified error code";
}
let idx = code.wrapping_neg();
Lz4FError::from_index(idx)
.map(|e| e.error_name())
.unwrap_or("Unspecified error code")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_variant_count() {
for i in 0..24usize {
assert!(
Lz4FError::from_index(i).is_some(),
"missing variant for index {i}"
);
}
assert!(Lz4FError::from_index(24).is_none());
}
#[test]
fn error_name_strings() {
assert_eq!(Lz4FError::OkNoError.error_name(), "OK_NoError");
assert_eq!(Lz4FError::Generic.error_name(), "ERROR_GENERIC");
assert_eq!(
Lz4FError::MaxBlockSizeInvalid.error_name(),
"ERROR_maxBlockSize_invalid"
);
assert_eq!(
Lz4FError::BlockModeInvalid.error_name(),
"ERROR_blockMode_invalid"
);
assert_eq!(
Lz4FError::ParameterInvalid.error_name(),
"ERROR_parameter_invalid"
);
assert_eq!(
Lz4FError::CompressionLevelInvalid.error_name(),
"ERROR_compressionLevel_invalid"
);
assert_eq!(
Lz4FError::HeaderVersionWrong.error_name(),
"ERROR_headerVersion_wrong"
);
assert_eq!(
Lz4FError::BlockChecksumInvalid.error_name(),
"ERROR_blockChecksum_invalid"
);
assert_eq!(
Lz4FError::ReservedFlagSet.error_name(),
"ERROR_reservedFlag_set"
);
assert_eq!(
Lz4FError::AllocationFailed.error_name(),
"ERROR_allocation_failed"
);
assert_eq!(
Lz4FError::SrcSizeTooLarge.error_name(),
"ERROR_srcSize_tooLarge"
);
assert_eq!(
Lz4FError::DstMaxSizeTooSmall.error_name(),
"ERROR_dstMaxSize_tooSmall"
);
assert_eq!(
Lz4FError::FrameHeaderIncomplete.error_name(),
"ERROR_frameHeader_incomplete"
);
assert_eq!(
Lz4FError::FrameTypeUnknown.error_name(),
"ERROR_frameType_unknown"
);
assert_eq!(
Lz4FError::FrameSizeWrong.error_name(),
"ERROR_frameSize_wrong"
);
assert_eq!(Lz4FError::SrcPtrWrong.error_name(), "ERROR_srcPtr_wrong");
assert_eq!(
Lz4FError::DecompressionFailed.error_name(),
"ERROR_decompressionFailed"
);
assert_eq!(
Lz4FError::HeaderChecksumInvalid.error_name(),
"ERROR_headerChecksum_invalid"
);
assert_eq!(
Lz4FError::ContentChecksumInvalid.error_name(),
"ERROR_contentChecksum_invalid"
);
assert_eq!(
Lz4FError::FrameDecodingAlreadyStarted.error_name(),
"ERROR_frameDecoding_alreadyStarted"
);
assert_eq!(
Lz4FError::CompressionStateUninitialized.error_name(),
"ERROR_compressionState_uninitialized"
);
assert_eq!(
Lz4FError::ParameterNull.error_name(),
"ERROR_parameter_null"
);
assert_eq!(Lz4FError::IoWrite.error_name(), "ERROR_io_write");
assert_eq!(Lz4FError::IoRead.error_name(), "ERROR_io_read");
}
#[test]
fn is_error_boundary() {
assert!(!lz4f_is_error(usize::MAX - 23));
assert!(lz4f_is_error(usize::MAX - 22));
assert!(lz4f_is_error(usize::MAX)); }
#[test]
fn get_error_name_parity() {
assert_eq!(lz4f_get_error_name(0), "Unspecified error code");
assert_eq!(lz4f_get_error_name(42), "Unspecified error code");
assert_eq!(lz4f_get_error_name(usize::MAX), "ERROR_GENERIC");
assert_eq!(lz4f_get_error_name(usize::MAX - 22), "ERROR_io_read");
}
#[test]
fn decompress_stage_discriminants() {
assert_eq!(DecompressStage::GetFrameHeader as u32, 0);
assert_eq!(DecompressStage::StoreFrameHeader as u32, 1);
assert_eq!(DecompressStage::Init as u32, 2);
assert_eq!(DecompressStage::GetBlockHeader as u32, 3);
assert_eq!(DecompressStage::StoreBlockHeader as u32, 4);
assert_eq!(DecompressStage::CopyDirect as u32, 5);
assert_eq!(DecompressStage::GetBlockChecksum as u32, 6);
assert_eq!(DecompressStage::GetCBlock as u32, 7);
assert_eq!(DecompressStage::StoreCBlock as u32, 8);
assert_eq!(DecompressStage::FlushOut as u32, 9);
assert_eq!(DecompressStage::GetSuffix as u32, 10);
assert_eq!(DecompressStage::StoreSuffix as u32, 11);
assert_eq!(DecompressStage::GetSFrameSize as u32, 12);
assert_eq!(DecompressStage::StoreSFrameSize as u32, 13);
assert_eq!(DecompressStage::SkipSkippable as u32, 14);
}
}