Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions miniz_oxide/src/inflate/core.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
//! Streaming decompression functionality.
//! Core decompression functionality.
//!
//! # Using decompress with a wrapping buffer
//!
//! [`decompress`] and [`decompress_with_limit`] can be used with a wrapping buffer.
//!
//! To decompress with a wrapping buffer you must:
//! - not pass `TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF` flag
//! - pass an output buffer with a size of a power of 2
//! - pass an output buffer with a size greater or equal to the decompression window
//! (which cannot be more than 32KiB, so 32KiB is a safe size)
//! - pass the same buffer on each call without modification
//!
//! You must process return values so that:
//! - next call pass the input buffer without the first input bytes read skipped
//! - next call pass the same output buffer
//! - next call pass an out_pos incremented by the number of bytes output (wrapping to 0 if needed)
//! - do a next call only if return status is `NeedsMoreInput` or `NeedsMoreInput`
//!
//! [`decompress`] will write to any byte after `out_pos` in the output buffer, but will not
//! wrap around. This means that all bytes after `out_pos` must be saved while the ones before
//! do not have to.
//!
//! [`decompress_with_limit`] will write to any byte after `out_pos` but not more than `out_max`
//! and will not wrap around. This means that you can use the buffer as a ring buffer for your
//! application usage, as long as you keep track of the number of disposable bytes.

use super::*;
use crate::shared::{update_adler32, HUFFMAN_LENGTH_ORDER};
Expand Down Expand Up @@ -1361,6 +1386,27 @@ pub fn decompress(
out: &mut [u8],
out_pos: usize,
flags: u32,
) -> (TINFLStatus, usize, usize) {
decompress_with_limit(r, in_buf, out, out_pos, usize::MAX, flags)
}

/// Same as [`decompress()`] with a maximum decompressed byte count.
///
/// By default [`decompress()`] decompress untill end of `out` buffer if possible.
/// `decompress_with_limit` will stop when `out_max` bytes have been decompressed,
/// or when `out` buffer is full, whichever comes first.
///
/// This is especially useful when using a wrapping output buffer. This helps keeping
/// some data that has not yet been consumed in the buffer while decompressing new bytes.
///
/// `out_max` is the maximum number of *bytes* that decompress will write
pub fn decompress_with_limit(
r: &mut DecompressorOxide,
in_buf: &[u8],
out: &mut [u8],
out_pos: usize,
out_max: usize,
flags: u32,
) -> (TINFLStatus, usize, usize) {
let out_buf_size_mask = if flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF != 0 {
usize::MAX
Expand All @@ -1383,7 +1429,7 @@ pub fn decompress(

let mut state = r.state;

let mut out_buf = OutputBuffer::from_slice_and_pos(out, out_pos);
let mut out_buf = OutputBuffer::from_slice_pos_and_max(out, out_pos, out_max);

// Make a local copy of the important variables here so we can work with them on the stack.
let mut l = LocalVars {
Expand Down Expand Up @@ -1845,7 +1891,7 @@ pub fn decompress(
let source_pos = out_buf.position()
.wrapping_sub(l.dist as usize) & out_buf_size_mask;

let out_len = out_buf.get_ref().len();
let out_len = out_buf.bytes_left();
let match_end_pos = out_buf.position() + l.counter as usize;

if match_end_pos > out_len ||
Expand Down
11 changes: 8 additions & 3 deletions miniz_oxide/src/inflate/output_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@
pub struct OutputBuffer<'a> {
slice: &'a mut [u8],
position: usize,
max: usize,
}

impl<'a> OutputBuffer<'a> {
#[inline]
pub fn from_slice_and_pos(slice: &'a mut [u8], position: usize) -> OutputBuffer<'a> {
OutputBuffer { slice, position }
pub fn from_slice_pos_and_max(slice: &'a mut [u8], position: usize, max_count: usize) -> OutputBuffer<'a> {
let mut max = position.saturating_add(max_count);
if max > slice.len() {
max = slice.len();
}
OutputBuffer { slice, position, max }
}

#[inline(always)]
Expand Down Expand Up @@ -45,7 +50,7 @@ impl<'a> OutputBuffer<'a> {

#[inline]
pub const fn bytes_left(&self) -> usize {
self.slice.len() - self.position
self.max - self.position
}

#[inline(always)]
Expand Down