1#[cfg_attr(feature = "safe-encode", forbid(unsafe_code))]
18pub(crate) mod compress;
19pub(crate) mod hashtable;
20
21#[cfg(feature = "safe-decode")]
22#[cfg_attr(feature = "safe-decode", forbid(unsafe_code))]
23pub(crate) mod decompress_safe;
24#[cfg(feature = "safe-decode")]
25pub(crate) use decompress_safe as decompress;
26
27#[cfg(not(feature = "safe-decode"))]
28pub(crate) mod decompress;
29
30pub use compress::*;
31pub use decompress::*;
32
33use core::{error::Error, fmt};
34
35pub(crate) const WINDOW_SIZE: usize = 64 * 1024;
36
37const MFLIMIT: usize = 12;
47
48const LAST_LITERALS: usize = 5;
51
52const END_OFFSET: usize = LAST_LITERALS + 1;
56
57const LZ4_MIN_LENGTH: usize = MFLIMIT + 1;
62
63const MAXD_LOG: usize = 16;
64const MAX_DISTANCE: usize = (1 << MAXD_LOG) - 1;
65
66#[allow(dead_code)]
67const MATCH_LENGTH_MASK: u32 = (1_u32 << 4) - 1; const MINMATCH: usize = 4;
71
72#[allow(dead_code)]
73const FASTLOOP_SAFE_DISTANCE: usize = 64;
74
75#[allow(dead_code)]
77static LZ4_64KLIMIT: usize = (64 * 1024) + (MFLIMIT - 1);
78
79#[derive(Debug)]
81#[non_exhaustive]
82pub enum DecompressError {
83 OutputTooSmall {
85 expected: usize,
87 actual: usize,
89 },
90 LiteralOutOfBounds,
92 ExpectedAnotherByte,
94 OffsetZero,
96 OffsetOutOfBounds,
98}
99
100#[derive(Debug)]
101#[non_exhaustive]
102pub enum CompressError {
104 OutputTooSmall,
106}
107
108impl fmt::Display for DecompressError {
109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 match self {
111 DecompressError::OutputTooSmall { expected, actual } => {
112 write!(
113 f,
114 "provided output is too small for the decompressed data, actual {actual}, expected \
115 {expected}"
116 )
117 }
118 DecompressError::LiteralOutOfBounds => {
119 f.write_str("literal is out of bounds of the input")
120 }
121 DecompressError::ExpectedAnotherByte => {
122 f.write_str("expected another byte, found none")
123 }
124 DecompressError::OffsetZero => f.write_str("0 is not a valid match offset"),
125 DecompressError::OffsetOutOfBounds => {
126 f.write_str("the offset to copy is not contained in the decompressed buffer")
127 }
128 }
129 }
130}
131
132impl fmt::Display for CompressError {
133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134 match self {
135 CompressError::OutputTooSmall => f.write_str(
136 "output is too small for the compressed data, use get_maximum_output_size to \
137 reserve enough space",
138 ),
139 }
140 }
141}
142
143impl Error for DecompressError {}
144
145impl Error for CompressError {}
146
147#[inline]
151pub fn uncompressed_size(input: &[u8]) -> Result<(usize, &[u8]), DecompressError> {
152 let size = input.get(..4).ok_or(DecompressError::ExpectedAnotherByte)?;
153 let size: &[u8; 4] = size.try_into().unwrap();
154 let uncompressed_size = u32::from_le_bytes(*size) as usize;
155 let rest = &input[4..];
156 Ok((uncompressed_size, rest))
157}
158
159#[test]
160#[cfg(target_pointer_width = "64")] fn large_integer_roundtrip() {
162 let u32_max = usize::try_from(u32::MAX).unwrap();
163 let value = u32_max + u32_max / 2;
164
165 let mut buf = vec![0u8; value / 255 + 1];
166 let mut sink = crate::sink::SliceSink::new(&mut buf, 0);
167 self::compress::write_integer(&mut sink, value);
168
169 #[cfg(feature = "safe-decode")]
170 let value_decompressed = self::decompress_safe::read_integer(&buf, &mut 0).unwrap();
171
172 #[cfg(not(feature = "safe-decode"))]
173 let value_decompressed = {
174 let mut ptr_range = buf.as_ptr_range();
175 self::decompress::read_integer_ptr(&mut ptr_range.start, ptr_range.end).unwrap()
176 };
177
178 assert_eq!(value, value_decompressed);
179}