Skip to content

Commit

Permalink
add unmanaged interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mcroomp committed Jan 29, 2025
1 parent ebb8f2c commit 013011d
Show file tree
Hide file tree
Showing 9 changed files with 592 additions and 173 deletions.
75 changes: 75 additions & 0 deletions src/hash_algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,81 @@ pub enum HashAlgorithm {
RandomVector,
Crc32cHash,
}

const HASH_ALGORITHM_NONE: u16 = 0;
const HASH_ALGORITHM_ZLIB: u16 = 1;
const HASH_ALGORITHM_MINIZ_FAST: u16 = 2;
const HASH_ALGORITHM_LIBDEFLATE4: u16 = 3;
const HASH_ALGORITHM_LIBDEFLATE4_FAST: u16 = 4;
const HASH_ALGORITHM_ZLIBNG: u16 = 5;
const HASH_ALGORITHM_RANDOMVECTOR: u16 = 6;
const HASH_ALGORITHM_CRC32C: u16 = 7;

impl HashAlgorithm {
pub fn to_u16(self) -> u16 {
match self {
HashAlgorithm::None => HASH_ALGORITHM_NONE,
HashAlgorithm::Zlib {
hash_mask,
hash_shift,
} => {
HASH_ALGORITHM_ZLIB
| ((hash_mask.trailing_ones() as u16) << 8)
| ((hash_shift as u16) << 12)
}
HashAlgorithm::MiniZFast => HASH_ALGORITHM_MINIZ_FAST,
HashAlgorithm::Libdeflate4Fast => HASH_ALGORITHM_LIBDEFLATE4_FAST,
HashAlgorithm::Libdeflate4 => HASH_ALGORITHM_LIBDEFLATE4,
HashAlgorithm::ZlibNG => HASH_ALGORITHM_ZLIBNG,
HashAlgorithm::RandomVector => HASH_ALGORITHM_RANDOMVECTOR,
HashAlgorithm::Crc32cHash => HASH_ALGORITHM_CRC32C,
}
}

pub fn from_u16(v: u16) -> Option<Self> {
match v & 0xff {
HASH_ALGORITHM_NONE => Some(HashAlgorithm::None),
HASH_ALGORITHM_ZLIB => {
let hash_mask = (1 << ((v >> 8) & 0xf)) - 1;
let hash_shift = (v >> 12) & 0xf;
Some(HashAlgorithm::Zlib {
hash_mask,
hash_shift: hash_shift.into(),
})
}
HASH_ALGORITHM_MINIZ_FAST => Some(HashAlgorithm::MiniZFast),
HASH_ALGORITHM_LIBDEFLATE4_FAST => Some(HashAlgorithm::Libdeflate4Fast),
HASH_ALGORITHM_LIBDEFLATE4 => Some(HashAlgorithm::Libdeflate4),
HASH_ALGORITHM_ZLIBNG => Some(HashAlgorithm::ZlibNG),
HASH_ALGORITHM_RANDOMVECTOR => Some(HashAlgorithm::RandomVector),
HASH_ALGORITHM_CRC32C => Some(HashAlgorithm::Crc32cHash),
_ => None,
}
}
}

#[test]
fn roundtrip_hash_algorithm_to_int() {
let test_hashes = [
HashAlgorithm::Zlib {
hash_mask: 0x7ff,
hash_shift: 3,
},
HashAlgorithm::MiniZFast,
HashAlgorithm::Libdeflate4Fast,
HashAlgorithm::Libdeflate4,
HashAlgorithm::ZlibNG,
HashAlgorithm::RandomVector,
HashAlgorithm::Crc32cHash,
];

for &hash in test_hashes.iter() {
let hash_int = hash.to_u16();
let hash2 = HashAlgorithm::from_u16(hash_int).unwrap();
assert_eq!(hash, hash2);
}
}

pub trait HashImplementation: Default + Copy + Clone {
const NUM_HASH_BYTES: usize;

Expand Down
2 changes: 1 addition & 1 deletion src/idat_parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct IdatContents {
}

impl IdatContents {
pub fn read_from_bytestream(r: &mut impl Read) -> std::io::Result<IdatContents> {
pub fn read_from_bytestream(r: &mut impl Read) -> Result<IdatContents> {
let mut chunk_sizes = Vec::new();

loop {
Expand Down
205 changes: 108 additions & 97 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,120 +34,131 @@ mod statistical_codec;
mod token_predictor;
mod tree_predictor;

pub mod unmanaged_api;

use hash_algorithm::HashAlgorithm;
pub use preflate_container::{
compress_zstd, decompress_deflate_stream, decompress_zstd, expand_zlib_chunks,
recompress_deflate_stream, recreated_zlib_chunks,
};
pub use preflate_error::PreflateError;

use std::{io::Cursor, panic::catch_unwind};

/// C ABI interface for compressing Zip file, exposed from DLL.
#[no_mangle]
pub unsafe extern "C" fn WrapperCompressZip(
input_buffer: *const u8,
input_buffer_size: u64,
output_buffer: *mut u8,
output_buffer_size: u64,
result_size: *mut u64,
) -> i32 {
match catch_unwind(|| {
let input_buffer = std::slice::from_raw_parts(input_buffer, input_buffer_size as usize);
let output_buffer =
std::slice::from_raw_parts_mut(output_buffer, output_buffer_size as usize);

let plain_text = expand_zlib_chunks(&input_buffer, 0)?;

*result_size = zstd::bulk::compress_to_buffer(&plain_text, output_buffer, 9)? as u64;

Result::<(), PreflateError>::Ok(())
}) {
Ok(x) => {
if let Err(_) = x {
return -1;
}
return 0;
}
Err(_) => {
return -2;
}
}
use preflate_error::ExitCode;
pub use preflate_error::{PreflateError, Result};

use std::io::{Cursor, Write};

pub struct PreflateCompressionContext {
content: Vec<u8>,
result: Option<Vec<u8>>,
result_pos: usize,
compression_stats: CompressionStats,
}

/// C ABI interface for decompressing Zip, exposed from DLL
#[no_mangle]
pub unsafe extern "C" fn WrapperDecompressZip(
input_buffer: *const u8,
input_buffer_size: u64,
output_buffer: *mut u8,
output_buffer_size: u64,
result_size: *mut u64,
) -> i32 {
match catch_unwind(|| {
let input = std::slice::from_raw_parts(input_buffer, input_buffer_size as usize);
let output = std::slice::from_raw_parts_mut(output_buffer, output_buffer_size as usize);

let compressed_data =
zstd::bulk::decompress(input, 1024 * 1024 * 128).map_err(PreflateError::from)?;

let mut source = Cursor::new(&compressed_data);
let mut destination = Cursor::new(output);

recreated_zlib_chunks(&mut source, &mut destination)?;
*result_size = destination.position();

Result::<(), PreflateError>::Ok(())
}) {
Ok(x) => {
if let Err(_) = x {
return -1;
}
return 0;
}
Err(_) => {
return -2;
#[derive(Debug, Copy, Clone, Default)]
pub struct CompressionStats {
compressed_size: u64,
uncompressed_size: u64,
overhead_bytes: u64,
hash_algorithm: HashAlgorithm,
}

impl PreflateCompressionContext {
pub fn new() -> Self {
PreflateCompressionContext {
content: Vec::new(),
compression_stats: CompressionStats::default(),
result: None,
result_pos: 0,
}
}
}

#[test]
fn extern_interface() {
use crate::process::read_file;
let input = read_file("samplezip.zip");
fn process_buffer(
&mut self,
input: &[u8],
input_complete: bool,
writer: &mut impl Write,
max_output_write: usize,
) -> Result<bool> {
self.content.extend_from_slice(input);

if input_complete {
if self.result.is_some() {
if input.len() > 0 {
return Err(PreflateError::new(
ExitCode::InvalidParameter,
"more data provided after input_complete signaled",
));
}
} else {
self.result = Some(compress_zstd(
&self.content,
9,
&mut self.compression_stats,
)?);
}
}

let mut compressed = Vec::new();
if let Some(result) = &mut self.result {
let amount_to_write = std::cmp::min(max_output_write, result.len() - self.result_pos);

compressed.resize(input.len() + 10000, 0);
writer.write(&result[self.result_pos..self.result_pos + amount_to_write])?;
self.result_pos += amount_to_write;
Ok(self.result_pos == result.len())
} else {
Ok(false)
}
}

let mut result_size: u64 = 0;
pub fn stats(&self) -> CompressionStats {
self.compression_stats
}
}

unsafe {
let retval = WrapperCompressZip(
input[..].as_ptr(),
input.len() as u64,
compressed[..].as_mut_ptr(),
compressed.len() as u64,
(&mut result_size) as *mut u64,
);
struct PreflateDecompressionContext {
capacity: usize,
content: Vec<u8>,
result: Option<Vec<u8>>,
result_pos: usize,
}

assert_eq!(retval, 0);
impl PreflateDecompressionContext {
fn new(capacity: usize) -> Self {
PreflateDecompressionContext {
content: Vec::new(),
result: None,
result_pos: 0,
capacity,
}
}

let mut original = Vec::new();
original.resize(input.len() + 10000, 0);
fn process_buffer(
&mut self,
input: &[u8],
input_complete: bool,
writer: &mut impl Write,
max_output_write: usize,
) -> Result<bool> {
self.content.extend_from_slice(input);
if input_complete {
if self.result.is_some() {
if input.len() > 0 {
return Err(PreflateError::new(
ExitCode::InvalidParameter,
"more data provided after input_complete signaled",
));
}
} else {
self.result = Some(decompress_zstd(&self.content, self.capacity)?);
}
}

let mut original_size: u64 = 0;
unsafe {
let retval = WrapperDecompressZip(
compressed[..].as_ptr(),
result_size,
original[..].as_mut_ptr(),
original.len() as u64,
(&mut original_size) as *mut u64,
);
if let Some(result) = &mut self.result {
let amount_to_write = std::cmp::min(max_output_write, result.len() - self.result_pos);

assert_eq!(retval, 0);
writer.write(&result[self.result_pos..self.result_pos + amount_to_write])?;
self.result_pos += amount_to_write;
Ok(self.result_pos == result.len())
} else {
Ok(false)
}
}
assert_eq!(input.len() as u64, original_size);
assert_eq!(input[..], original[..(original_size as usize)]);
}
5 changes: 3 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
path::{Path, PathBuf},
};

use preflate_rs::{compress_zstd, decompress_zstd};
use preflate_rs::{compress_zstd, decompress_zstd, CompressionStats};

fn enumerate_directory_recursively(path: &Path) -> Result<Vec<PathBuf>, std::io::Error> {
let mut results = Vec::new();
Expand Down Expand Up @@ -49,7 +49,8 @@ fn main() {

let zstdlen = zstd::bulk::compress(&file, 9).unwrap();

let preflatecompressed = compress_zstd(&file, 1).unwrap();
let mut stats = CompressionStats::default();
let preflatecompressed = compress_zstd(&file, 1, &mut stats).unwrap();

totalseen += zstdlen.len() as u64;
totalzstd += preflatecompressed.len() as u64;
Expand Down
Loading

0 comments on commit 013011d

Please sign in to comment.