fix: correct libaec FFI to use aec_stream struct (fixes SIGSEGV)

The previous aec_buffer_decode declaration used flat parameters which
don't match the actual libaec C API; this caused a SIGSEGV at runtime.
Replace with the correct aec_stream struct (mirroring <libaec.h>) and
update filters_szip.rs to populate and pass &mut AecStream.
Also add empty-input guard in szip_decode_impl and fallback library
path search in build.rs for distros that omit the .pc file.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-30 11:41:41 +00:00
co-authored by Claude Sonnet 4.6
parent d6c4d4f111
commit e91f7fc539
3 changed files with 91 additions and 29 deletions
+54 -12
View File
@@ -1,6 +1,9 @@
//! Raw FFI bindings to libaec (Adaptive Entropy Coding library).
//!
//! Provides the `aec_buffer_decode` convenience function for one-shot decompression.
//! Exposes the `aec_buffer_decode` one-shot convenience function via
//! the `AecStream` control structure, matching the libaec C API.
use std::os::raw::c_void;
// AEC flag constants matching aec.h
pub const AEC_DATA_PREPROCESS: u32 = 1; // NN preprocessing
@@ -8,20 +11,51 @@ pub const AEC_DATA_MSB: u32 = 2; // big-endian sample order
pub const AEC_RESTRICTED: u32 = 4; // restricted coding set
pub const AEC_ALLOW_K13: u32 = 8; // allow k=13 option
/// Mirror of `struct aec_stream` from `<libaec.h>`.
///
/// Must match the C layout exactly — all fields are C ABI integers/pointers.
#[repr(C)]
pub struct AecStream {
pub next_in: *const u8,
pub avail_in: usize,
pub total_in: usize,
pub next_out: *mut u8,
pub avail_out: usize,
pub total_out: usize,
pub bits_per_sample: u32,
pub block_size: u32,
pub rsi: u32,
pub flags: u32,
/// Opaque internal state; initialised to null, set by libaec on first call.
pub state: *mut c_void,
}
impl AecStream {
/// Return a zero-initialised stream safe to pass to libaec.
pub fn zeroed() -> Self {
Self {
next_in: std::ptr::null(),
avail_in: 0,
total_in: 0,
next_out: std::ptr::null_mut(),
avail_out: 0,
total_out: 0,
bits_per_sample: 0,
block_size: 0,
rsi: 0,
flags: 0,
state: std::ptr::null_mut(),
}
}
}
unsafe extern "C" {
/// One-shot decompression. Returns 0 on success.
/// One-shot decompression. Returns `AEC_OK` (0) on success.
///
/// # Safety
/// `src` must be valid for `src_len` bytes; `dst` must be valid for `*dst_len` bytes.
pub fn aec_buffer_decode(
src: *const u8,
src_len: usize,
dst: *mut u8,
dst_len: *mut usize,
bits_per_sample: u32,
block_size: u32,
flags: u32,
) -> i32;
/// `strm.next_in` must be valid for `strm.avail_in` bytes;
/// `strm.next_out` must be valid for `strm.avail_out` bytes.
pub fn aec_buffer_decode(strm: *mut AecStream) -> i32;
}
#[cfg(test)]
@@ -33,4 +67,12 @@ mod tests {
assert_eq!(AEC_DATA_PREPROCESS, 1);
assert_eq!(AEC_DATA_MSB, 2);
}
#[test]
fn aec_stream_zeroed_has_null_ptrs() {
let s = AecStream::zeroed();
assert!(s.next_in.is_null());
assert!(s.next_out.is_null());
assert!(s.state.is_null());
}
}