Packages
Fast deflate/zlib/gzip compression and decompression using libdeflate via Rust NIF.
Current section
Files
Jump to
Current section
Files
native/libdeflate_ex_nif/src/lib.rs
use libdeflater::{CompressionLvl, Compressor, Decompressor};
use rustler::{Binary, Env, NewBinary};
// ── Compression ──────────────────────────────────────────────────────────────
/// Compress data using raw deflate (no header).
/// `level` must be 1..=12 (libdeflate supports levels 1-12).
#[rustler::nif]
fn deflate_compress<'a>(env: Env<'a>, data: Binary, level: i32) -> Result<Binary<'a>, String> {
let lvl = CompressionLvl::new(level).map_err(|_| format!("invalid compression level: {level}"))?;
let mut compressor = Compressor::new(lvl);
let max_sz = compressor.deflate_compress_bound(data.len());
let mut buf = vec![0u8; max_sz];
let actual = compressor
.deflate_compress(data.as_slice(), &mut buf)
.map_err(|e| format!("deflate compress failed: {e:?}"))?;
let mut out = NewBinary::new(env, actual);
out.as_mut_slice().copy_from_slice(&buf[..actual]);
Ok(out.into())
}
/// Compress data using zlib format (deflate with zlib header).
/// `level` must be 1..=12.
#[rustler::nif]
fn zlib_compress<'a>(env: Env<'a>, data: Binary, level: i32) -> Result<Binary<'a>, String> {
let lvl = CompressionLvl::new(level).map_err(|_| format!("invalid compression level: {level}"))?;
let mut compressor = Compressor::new(lvl);
let max_sz = compressor.zlib_compress_bound(data.len());
let mut buf = vec![0u8; max_sz];
let actual = compressor
.zlib_compress(data.as_slice(), &mut buf)
.map_err(|e| format!("zlib compress failed: {e:?}"))?;
let mut out = NewBinary::new(env, actual);
out.as_mut_slice().copy_from_slice(&buf[..actual]);
Ok(out.into())
}
/// Compress data using gzip format.
/// `level` must be 1..=12.
#[rustler::nif]
fn gzip_compress<'a>(env: Env<'a>, data: Binary, level: i32) -> Result<Binary<'a>, String> {
let lvl = CompressionLvl::new(level).map_err(|_| format!("invalid compression level: {level}"))?;
let mut compressor = Compressor::new(lvl);
let max_sz = compressor.gzip_compress_bound(data.len());
let mut buf = vec![0u8; max_sz];
let actual = compressor
.gzip_compress(data.as_slice(), &mut buf)
.map_err(|e| format!("gzip compress failed: {e:?}"))?;
let mut out = NewBinary::new(env, actual);
out.as_mut_slice().copy_from_slice(&buf[..actual]);
Ok(out.into())
}
// ── Decompression ────────────────────────────────────────────────────────────
/// Decompress raw deflate data (no zlib/gzip header).
/// Requires the exact uncompressed size to be known.
#[rustler::nif]
fn deflate_decompress<'a>(env: Env<'a>, data: Binary, uncompressed_size: usize) -> Result<Binary<'a>, String> {
let mut decompressor = Decompressor::new();
let mut out = NewBinary::new(env, uncompressed_size);
decompressor
.deflate_decompress(data.as_slice(), out.as_mut_slice())
.map_err(|e| format!("deflate decompress failed: {e:?}"))?;
Ok(out.into())
}
/// Decompress zlib-wrapped data (deflate with zlib header).
/// Requires the exact uncompressed size to be known.
#[rustler::nif]
fn zlib_decompress<'a>(env: Env<'a>, data: Binary, uncompressed_size: usize) -> Result<Binary<'a>, String> {
let mut decompressor = Decompressor::new();
let mut out = NewBinary::new(env, uncompressed_size);
decompressor
.zlib_decompress(data.as_slice(), out.as_mut_slice())
.map_err(|e| format!("zlib decompress failed: {e:?}"))?;
Ok(out.into())
}
/// Decompress gzip data.
/// Requires the exact uncompressed size to be known.
#[rustler::nif]
fn gzip_decompress<'a>(env: Env<'a>, data: Binary, uncompressed_size: usize) -> Result<Binary<'a>, String> {
let mut decompressor = Decompressor::new();
let mut out = NewBinary::new(env, uncompressed_size);
decompressor
.gzip_decompress(data.as_slice(), out.as_mut_slice())
.map_err(|e| format!("gzip decompress failed: {e:?}"))?;
Ok(out.into())
}
rustler::init!("Elixir.LibdeflateEx.Native");