Skip to content

Commit

Permalink
Add support for the delta encoding filter
Browse files Browse the repository at this point in the history
  • Loading branch information
shkoo committed Dec 19, 2024
1 parent 1a82c40 commit c4e2596
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 1 deletion.
20 changes: 20 additions & 0 deletions lzma-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub type lzma_check = __enum_ty;
pub type lzma_vli = u64;
pub type lzma_mode = __enum_ty;
pub type lzma_match_finder = __enum_ty;
pub type lzma_delta_type = __enum_ty;

pub const LZMA_OK: lzma_ret = 0;
pub const LZMA_STREAM_END: lzma_ret = 1;
Expand Down Expand Up @@ -90,6 +91,12 @@ pub const LZMA_FILTER_ARMTHUMB: lzma_vli = 0x08;
pub const LZMA_FILTER_SPARC: lzma_vli = 0x09;
pub const LZMA_FILTER_LZMA1: lzma_vli = 0x4000000000000001;
pub const LZMA_FILTER_LZMA2: lzma_vli = 0x21;
pub const LZMA_FILTER_DELTA: lzma_vli = 0x03;

pub const LZMA_DELTA_TYPE_BYTE: lzma_delta_type = 0x0;

pub const LZMA_DELTA_DIST_MIN: u32 = 1;
pub const LZMA_DELTA_DIST_MAX: u32 = 256;

#[repr(C)]
pub struct lzma_allocator {
Expand Down Expand Up @@ -186,6 +193,19 @@ pub struct lzma_options_lzma {
reserved_ptr2: *mut c_void,
}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct lzma_options_delta {
pub type_: lzma_delta_type,
pub dist: u32,
reserved_int1: u32,
reserved_int2: u32,
reserved_int3: u32,
reserved_int4: u32,
reserved_ptr1: *mut c_void,
reserved_ptr2: *mut c_void,
}

#[repr(C)]
pub struct lzma_stream_flags {
pub version: u32,
Expand Down
68 changes: 67 additions & 1 deletion src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ pub struct LzmaOptions {
raw: lzma_sys::lzma_options_lzma,
}

/// Options that can be used to configure the delta filter.
pub struct DeltaOptions {
raw: lzma_sys::lzma_options_delta,
}

/// Builder to create a multi-threaded stream encoder.
pub struct MtStreamBuilder {
raw: lzma_sys::lzma_mt,
Expand All @@ -41,6 +46,7 @@ pub struct MtStreamBuilder {
pub struct Filters {
inner: Vec<lzma_sys::lzma_filter>,
lzma_opts: LinkedList<lzma_sys::lzma_options_lzma>,
delta_opts: LinkedList<lzma_sys::lzma_options_delta>,
}

/// The `action` argument for `process`,
Expand Down Expand Up @@ -571,6 +577,22 @@ impl LzmaOptions {
}
}

impl DeltaOptions {
/// Creates a set of options for the delta filter for a distance type of bytes.
pub fn new_byte(dist: u32) -> Result<DeltaOptions, Error> {
if dist < lzma_sys::LZMA_DELTA_DIST_MIN || dist > lzma_sys::LZMA_DELTA_DIST_MAX {
Err(Error::Options)
} else {
unsafe {
let mut options = DeltaOptions { raw: mem::zeroed() };
options.raw.type_ = lzma_sys::LZMA_DELTA_TYPE_BYTE;
options.raw.dist = dist;
Ok(options)
}
}
}
}

impl Check {
/// Test if this check is supported in this build of liblzma.
pub fn is_supported(&self) -> bool {
Expand All @@ -596,6 +618,7 @@ impl Filters {
options: 0 as *mut _,
}],
lzma_opts: LinkedList::new(),
delta_opts: LinkedList::new(),
}
}

Expand Down Expand Up @@ -632,7 +655,15 @@ impl Filters {
})
}

// TODO: delta filter
/// Add a filter for delta encoding.
pub fn delta(&mut self, opts: &DeltaOptions) -> &mut Filters {
self.delta_opts.push_back(opts.raw);
let ptr = self.delta_opts.back().unwrap() as *const _ as *mut _;
self.push(lzma_sys::lzma_filter {
id: lzma_sys::LZMA_FILTER_DELTA,
options: ptr,
})
}

/// Add a filter for x86 binaries.
pub fn x86(&mut self) -> &mut Filters {
Expand Down Expand Up @@ -856,3 +887,38 @@ impl Drop for Stream {
}
}
}

#[cfg(test)]
mod tests {
use super::super::write::{XzDecoder, XzEncoder};
use super::DeltaOptions;
use super::{Check, Filters, LzmaOptions, Stream};
use std::io::prelude::*;
use std::iter::repeat;

#[test]
fn delta_bounds() {
assert!(DeltaOptions::new_byte(lzma_sys::LZMA_DELTA_DIST_MIN - 1).is_err());
assert!(DeltaOptions::new_byte(lzma_sys::LZMA_DELTA_DIST_MAX + 1).is_err());
assert!(DeltaOptions::new_byte(lzma_sys::LZMA_DELTA_DIST_MIN).is_ok());
assert!(DeltaOptions::new_byte(lzma_sys::LZMA_DELTA_DIST_MAX).is_ok());
}

#[test]
fn stream_smoke() {
let mut filters = Filters::new();
filters.delta(&DeltaOptions::new_byte(10).unwrap());
filters.lzma2(&LzmaOptions::new_preset(6).unwrap());
let stream = Stream::new_stream_encoder(&filters, Check::Crc64).unwrap();

let d = XzDecoder::new(Vec::new());
let mut c = XzEncoder::new_stream(d, stream);
c.write_all(b"12834").unwrap();
let s = repeat("12345").take(100000).collect::<String>();
c.write_all(s.as_bytes()).unwrap();
let data = c.finish().unwrap().finish().unwrap();
assert_eq!(&data[0..5], b"12834");
assert_eq!(data.len(), 500005);
assert!(format!("12834{}", s).as_bytes() == &*data);
}
}

0 comments on commit c4e2596

Please sign in to comment.