use alloc::vec::Vec;
use crate::{
Error, ErrorKind, Level, Limits, Progress, Status,
checksum::Crc32,
deflate::{Deflater, Flush, Inflater},
zlib::Sink,
};
const ID1: u8 = 0x1F;
const ID2: u8 = 0x8B;
const FTEXT: u8 = 1;
const FHCRC: u8 = 2;
const FEXTRA: u8 = 4;
const FNAME: u8 = 8;
const FCOMMENT: u8 = 16;
const OS_UNKNOWN: u8 = 255;
#[must_use]
pub fn compress(data: &[u8]) -> Vec<u8> {
compress_with(data, Level::DEFAULT)
}
#[must_use]
pub fn compress_with(data: &[u8], level: Level) -> Vec<u8> {
let mut encoder = Encoder::new(level);
let mut out = Vec::with_capacity(data.len() / 2 + 64);
let _ = encoder.compress_vec(data, &mut out, Flush::Finish);
out
}
pub fn decompress(data: &[u8]) -> Result<Vec<u8>, Error> {
decompress_with(data, Limits::new())
}
pub fn decompress_with(data: &[u8], limits: Limits) -> Result<Vec<u8>, Error> {
let mut decoder = Decoder::one_shot(limits);
let mut out = Vec::new();
let reserve = data.len().saturating_mul(4).clamp(64, 1 << 20);
let cap = usize::try_from(limits.max_output()).unwrap_or(usize::MAX);
if out.try_reserve(reserve.min(cap)).is_err() {
return Err(Error::config(ErrorKind::AllocationFailed));
}
let p = decoder.finish_vec(data, &mut out)?;
if p.consumed != data.len() {
return Err(Error::new(ErrorKind::TrailingData, p.consumed as u64));
}
Ok(out)
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct GzipHeader {
mtime: u32,
os: u8,
extra_flags: u8,
text: bool,
name: Option<Vec<u8>>,
comment: Option<Vec<u8>>,
extra: Option<Vec<u8>>,
}
impl GzipHeader {
#[must_use]
pub fn new() -> Self {
Self {
os: OS_UNKNOWN,
..Self::default()
}
}
#[must_use]
pub fn with_name(mut self, name: &[u8]) -> Self {
self.name = Some(name.iter().copied().take_while(|&b| b != 0).collect());
self
}
#[must_use]
pub fn with_comment(mut self, comment: &[u8]) -> Self {
self.comment = Some(comment.iter().copied().take_while(|&b| b != 0).collect());
self
}
#[must_use]
pub fn with_extra(mut self, extra: &[u8]) -> Self {
self.extra = Some(extra[..extra.len().min(0xFFFF)].to_vec());
self
}
#[must_use]
pub fn with_mtime(mut self, mtime: u32) -> Self {
self.mtime = mtime;
self
}
#[must_use]
pub fn with_os(mut self, os: u8) -> Self {
self.os = os;
self
}
#[must_use]
pub fn with_text(mut self, text: bool) -> Self {
self.text = text;
self
}
#[must_use]
pub fn name(&self) -> Option<&[u8]> {
self.name.as_deref()
}
#[must_use]
pub fn comment(&self) -> Option<&[u8]> {
self.comment.as_deref()
}
#[must_use]
pub fn extra(&self) -> Option<&[u8]> {
self.extra.as_deref()
}
#[must_use]
pub fn mtime(&self) -> u32 {
self.mtime
}
#[must_use]
pub fn os(&self) -> u8 {
self.os
}
#[must_use]
pub fn extra_flags(&self) -> u8 {
self.extra_flags
}
#[must_use]
pub fn text(&self) -> bool {
self.text
}
fn encode(&self, xfl: u8) -> Vec<u8> {
let mut flg = 0;
if self.text {
flg |= FTEXT;
}
if self.extra.is_some() {
flg |= FEXTRA;
}
if self.name.is_some() {
flg |= FNAME;
}
if self.comment.is_some() {
flg |= FCOMMENT;
}
let mut h = Vec::with_capacity(10);
h.extend_from_slice(&[ID1, ID2, 8, flg]);
h.extend_from_slice(&self.mtime.to_le_bytes());
h.extend_from_slice(&[xfl, self.os]);
if let Some(extra) = &self.extra {
h.extend_from_slice(&(extra.len() as u16).to_le_bytes());
h.extend_from_slice(extra);
}
for field in [&self.name, &self.comment].into_iter().flatten() {
h.extend_from_slice(field);
h.push(0);
}
h
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum State {
Fixed,
ExtraLength,
Extra,
Name,
Comment,
HeaderCrc,
Body,
Trailer,
MemberEnd,
Done,
}
#[derive(Debug)]
pub struct Decoder {
inflater: Inflater,
limits: Limits,
state: State,
multi: bool,
allow_trailing: bool,
keep_header: bool,
header: Option<GzipHeader>,
fixed: [u8; 10],
index: usize,
extra_left: usize,
header_crc: Crc32,
header_bytes: usize,
field: Vec<u8>,
crc: Crc32,
member_len: u32,
trailer: [u8; 8],
members: u32,
out_before: u64,
consumed_total: u64,
failed: Option<Error>,
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
impl Decoder {
#[must_use]
pub fn new() -> Self {
Self::from_inflater(Inflater::new(), Limits::new())
}
pub(crate) fn one_shot(limits: Limits) -> Self {
Self::from_inflater(Inflater::without_history(limits), limits)
}
fn from_inflater(inflater: Inflater, limits: Limits) -> Self {
Self {
inflater,
limits,
state: State::Fixed,
multi: true,
allow_trailing: false,
keep_header: false,
header: None,
fixed: [0; 10],
index: 0,
extra_left: 0,
header_crc: Crc32::new(),
header_bytes: 0,
field: Vec::new(),
crc: Crc32::new(),
member_len: 0,
trailer: [0; 8],
members: 0,
out_before: 0,
consumed_total: 0,
failed: None,
}
}
#[must_use]
pub fn with_limits(mut self, limits: Limits) -> Self {
self.inflater = self.inflater.with_limits(limits);
self.limits = limits;
self
}
#[must_use]
pub fn with_single_member(mut self) -> Self {
self.multi = false;
self
}
#[must_use]
pub fn with_trailing_data(mut self, allow: bool) -> Self {
self.allow_trailing = allow;
self
}
#[must_use]
pub fn with_keep_header(mut self, keep: bool) -> Self {
self.keep_header = keep;
self
}
#[must_use]
pub fn header(&self) -> Option<&GzipHeader> {
self.header.as_ref()
}
#[must_use]
pub fn is_done(&self) -> bool {
self.state == State::Done
}
#[must_use]
pub fn members(&self) -> u32 {
self.members
}
#[must_use]
pub fn total_in(&self) -> u64 {
self.consumed_total
}
#[must_use]
pub fn total_out(&self) -> u64 {
self.out_before + self.inflater.total_out()
}
#[must_use]
pub fn memory_in_use(&self) -> usize {
self.inflater.memory_in_use() + self.field.capacity()
}
pub fn reset(&mut self) {
self.inflater.reset();
self.inflater.set_max_output(self.limits.max_output());
self.state = State::Fixed;
self.index = 0;
self.header = None;
self.field.clear();
self.crc = Crc32::new();
self.header_crc = Crc32::new();
self.header_bytes = 0;
self.members = 0;
self.out_before = 0;
self.consumed_total = 0;
self.failed = None;
}
pub fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<Progress, Error> {
self.run(input, Sink::Slice(output), false)
}
pub fn finish(&mut self, input: &[u8], output: &mut [u8]) -> Result<Progress, Error> {
self.run(input, Sink::Slice(output), true)
}
pub fn decompress_vec(&mut self, input: &[u8], out: &mut Vec<u8>) -> Result<Progress, Error> {
self.run(input, Sink::Vec(out), false)
}
pub fn finish_vec(&mut self, input: &[u8], out: &mut Vec<u8>) -> Result<Progress, Error> {
self.run(input, Sink::Vec(out), true)
}
fn fail(&mut self, kind: ErrorKind, at: usize) -> Error {
let e = Error::new(kind, self.consumed_total + at as u64);
self.failed = Some(e);
e
}
fn header_byte(&mut self, b: u8) -> Result<(), ErrorKind> {
self.header_crc.update(&[b]);
self.header_bytes += 1;
if self.header_bytes > self.limits.max_header() {
return Err(ErrorKind::HeaderTooLarge);
}
if self.keep_header && self.members == 0 {
if self.field.try_reserve(1).is_err() {
return Err(ErrorKind::AllocationFailed);
}
self.field.push(b);
}
Ok(())
}
fn keep_field(&mut self, which: State) {
if !(self.keep_header && self.members == 0) {
return;
}
let value = core::mem::take(&mut self.field);
if let Some(h) = &mut self.header {
match which {
State::Extra => h.extra = Some(value),
State::Name => h.name = Some(value),
_ => h.comment = Some(value),
}
}
}
fn next_header_state(&self, from: State) -> State {
let flg = self.fixed[3];
let order = [
(State::ExtraLength, FEXTRA),
(State::Name, FNAME),
(State::Comment, FCOMMENT),
(State::HeaderCrc, FHCRC),
];
let start = match from {
State::Fixed => 0,
State::Extra | State::ExtraLength => 1,
State::Name => 2,
State::Comment => 3,
_ => 4,
};
for &(state, bit) in &order[start..] {
if flg & bit != 0 {
return state;
}
}
State::Body
}
fn run(&mut self, input: &[u8], mut sink: Sink<'_>, last: bool) -> Result<Progress, Error> {
if let Some(e) = self.failed {
return Err(e);
}
let mut ip = 0;
let mut produced = 0;
let status = loop {
match self.state {
State::Fixed => {
while self.index < 10 && ip < input.len() {
self.fixed[self.index] = input[ip];
self.index += 1;
ip += 1;
if self.index == 2 && self.fixed[..2] != [ID1, ID2] {
return Err(self.fail(ErrorKind::InvalidHeader, ip));
}
}
if self.index < 10 {
break Status::NeedsInput;
}
let f = self.fixed;
if f[2] != 8 || f[3] & 0xE0 != 0 {
return Err(self.fail(ErrorKind::InvalidHeader, ip));
}
self.header_crc = Crc32::new();
self.header_crc.update(&f);
self.header_bytes = 0;
if self.keep_header && self.members == 0 {
self.header = Some(GzipHeader {
mtime: u32::from_le_bytes([f[4], f[5], f[6], f[7]]),
extra_flags: f[8],
os: f[9],
text: f[3] & FTEXT != 0,
name: None,
comment: None,
extra: None,
});
}
self.index = 0;
self.state = self.next_header_state(State::Fixed);
}
State::ExtraLength => {
while self.index < 2 && ip < input.len() {
self.trailer[self.index] = input[ip];
self.index += 1;
ip += 1;
}
if self.index < 2 {
break Status::NeedsInput;
}
let xlen = [self.trailer[0], self.trailer[1]];
self.header_crc.update(&xlen);
self.extra_left = usize::from(u16::from_le_bytes(xlen));
self.index = 0;
self.state = State::Extra;
}
State::Extra => {
while self.extra_left > 0 && ip < input.len() {
let b = input[ip];
ip += 1;
self.extra_left -= 1;
if let Err(kind) = self.header_byte(b) {
return Err(self.fail(kind, ip));
}
}
if self.extra_left > 0 {
break Status::NeedsInput;
}
self.keep_field(State::Extra);
self.state = self.next_header_state(State::Extra);
}
State::Name | State::Comment => {
let which = self.state;
let mut ended = false;
while ip < input.len() {
let b = input[ip];
ip += 1;
if b == 0 {
self.header_crc.update(&[0]);
ended = true;
break;
}
if let Err(kind) = self.header_byte(b) {
return Err(self.fail(kind, ip));
}
}
if !ended {
break Status::NeedsInput;
}
self.keep_field(which);
self.state = self.next_header_state(which);
}
State::HeaderCrc => {
while self.index < 2 && ip < input.len() {
self.trailer[self.index] = input[ip];
self.index += 1;
ip += 1;
}
if self.index < 2 {
break Status::NeedsInput;
}
self.index = 0;
let want = u16::from_le_bytes([self.trailer[0], self.trailer[1]]);
if want != (self.header_crc.finish() & 0xFFFF) as u16 {
return Err(self.fail(ErrorKind::ChecksumMismatch, ip));
}
self.state = State::Body;
}
State::Body => {
let r = match &mut sink {
Sink::Slice(out) => self
.inflater
.decompress(&input[ip..], &mut out[produced..])
.inspect(|p| {
self.crc.update(&out[produced..produced + p.produced]);
}),
Sink::Vec(out) => {
let at = out.len();
self.inflater
.decompress_vec(&input[ip..], out)
.inspect(|_| {
self.crc.update(&out[at..]);
})
}
};
let p = match r {
Ok(p) => p,
Err(e) => {
self.failed = Some(e);
return Err(e);
}
};
ip += p.consumed;
produced += p.produced;
self.member_len = self.member_len.wrapping_add(p.produced as u32);
match p.status {
Status::Done => {
self.index = 0;
self.state = State::Trailer;
}
Status::NeedsOutput => break Status::NeedsOutput,
_ => break Status::NeedsInput,
}
}
State::Trailer => {
while self.index < 8 && ip < input.len() {
self.trailer[self.index] = input[ip];
self.index += 1;
ip += 1;
}
if self.index < 8 {
break Status::NeedsInput;
}
let t = self.trailer;
if u32::from_le_bytes([t[0], t[1], t[2], t[3]]) != self.crc.finish() {
return Err(self.fail(ErrorKind::ChecksumMismatch, ip));
}
if u32::from_le_bytes([t[4], t[5], t[6], t[7]]) != self.member_len {
return Err(self.fail(ErrorKind::LengthMismatch, ip));
}
self.members += 1;
self.state = if self.multi {
State::MemberEnd
} else {
State::Done
};
}
State::MemberEnd => {
if ip == input.len() {
if last {
self.state = State::Done;
break Status::Done;
}
break Status::NeedsInput;
}
if input[ip] != ID1 || (ip + 1 < input.len() && input[ip + 1] != ID2) {
if self.allow_trailing {
self.state = State::Done;
break Status::Done;
}
return Err(self.fail(ErrorKind::TrailingData, ip));
}
if self.members >= self.limits.max_members() {
return Err(self.fail(ErrorKind::TooManyMembers, ip));
}
self.out_before += self.inflater.total_out();
self.inflater.reset();
self.inflater
.set_max_output(self.limits.max_output().saturating_sub(self.out_before));
self.crc = Crc32::new();
self.member_len = 0;
self.index = 0;
self.state = State::Fixed;
}
State::Done => break Status::Done,
}
};
self.consumed_total += ip as u64;
if status == Status::NeedsInput && last {
return Err(self.fail(ErrorKind::UnexpectedEnd, 0));
}
Ok(Progress::new(ip, produced, status))
}
}
#[derive(Debug)]
pub struct Encoder {
deflater: Deflater,
header: GzipHeader,
crc: Crc32,
pending: Vec<u8>,
pending_pos: usize,
header_written: bool,
trailer_written: bool,
}
impl Default for Encoder {
fn default() -> Self {
Self::new(Level::DEFAULT)
}
}
impl Encoder {
#[must_use]
pub fn new(level: Level) -> Self {
Self {
deflater: Deflater::new(level),
header: GzipHeader::new(),
crc: Crc32::new(),
pending: Vec::new(),
pending_pos: 0,
header_written: false,
trailer_written: false,
}
}
#[must_use]
pub fn with_header(mut self, header: GzipHeader) -> Self {
self.header = header;
self
}
pub fn with_window_bits(mut self, bits: u8) -> Result<Self, Error> {
self.deflater = self.deflater.with_window_bits(bits)?;
Ok(self)
}
pub fn reset(&mut self) {
self.deflater.reset();
self.crc = Crc32::new();
self.pending.clear();
self.pending_pos = 0;
self.header_written = false;
self.trailer_written = false;
}
#[must_use]
pub fn total_in(&self) -> u64 {
self.deflater.total_in()
}
#[must_use]
pub fn memory_in_use(&self) -> usize {
self.deflater.memory_in_use() + self.pending.capacity()
}
fn drain(&mut self, output: &mut [u8], produced: &mut usize) -> usize {
let n = (self.pending.len() - self.pending_pos).min(output.len() - *produced);
output[*produced..*produced + n]
.copy_from_slice(&self.pending[self.pending_pos..self.pending_pos + n]);
self.pending_pos += n;
*produced += n;
self.pending.len() - self.pending_pos
}
pub fn compress(
&mut self,
input: &[u8],
output: &mut [u8],
flush: Flush,
) -> Result<Progress, Error> {
let mut produced = 0;
if !self.header_written {
let xfl = match self.deflater.level().get() {
9 => 2,
1 => 4,
_ => 0,
};
self.pending = self.header.encode(xfl);
self.pending_pos = 0;
self.header_written = true;
}
if self.drain(output, &mut produced) > 0 {
return Ok(Progress::new(0, produced, Status::NeedsOutput));
}
if self.trailer_written {
if !input.is_empty() {
return Err(Error::config(ErrorKind::InvalidState));
}
return Ok(Progress::new(0, produced, Status::Done));
}
let p = self
.deflater
.compress(input, &mut output[produced..], flush)?;
self.crc.update(&input[..p.consumed]);
produced += p.produced;
if p.status == Status::Done && flush == Flush::Finish {
self.pending.clear();
self.pending
.extend_from_slice(&self.crc.finish().to_le_bytes());
self.pending
.extend_from_slice(&(self.crc.len() as u32).to_le_bytes());
self.pending_pos = 0;
self.trailer_written = true;
let left = self.drain(output, &mut produced);
let status = if left == 0 {
Status::Done
} else {
Status::NeedsOutput
};
return Ok(Progress::new(p.consumed, produced, status));
}
Ok(Progress::new(p.consumed, produced, p.status))
}
pub fn compress_vec(
&mut self,
input: &[u8],
out: &mut Vec<u8>,
flush: Flush,
) -> Result<Progress, Error> {
let start = out.len();
let mut consumed = 0;
loop {
let have = out.len();
let room = (input.len() - consumed) / 2 + 1024;
if out.capacity() - have < room && out.try_reserve(room).is_err() {
return Err(Error::config(ErrorKind::AllocationFailed));
}
let spare = out.capacity() - have;
out.resize(have + spare, 0);
match self.compress(&input[consumed..], &mut out[have..], flush) {
Ok(p) => {
out.truncate(have + p.produced);
consumed += p.consumed;
if p.status != Status::NeedsOutput {
return Ok(Progress::new(consumed, out.len() - start, p.status));
}
}
Err(e) => {
out.truncate(have);
return Err(e);
}
}
}
}
}