diff --git a/libbz2-rs-sys/src/allocator.rs b/libbz2-rs-sys/src/allocator.rs index 8828896fd..702e1f7af 100644 --- a/libbz2-rs-sys/src/allocator.rs +++ b/libbz2-rs-sys/src/allocator.rs @@ -60,10 +60,13 @@ impl Allocator { /// # Safety /// - /// - `strm.bzalloc` and `strm.opaque` must form a valid allocator, meaning `strm.bzalloc` returns either + /// - `strm.bzalloc` and `strm.opaque` must form a valid allocator, meaning that + /// `strm.bzalloc(strm.opaque, items, size)` returns either /// * a `NULL` pointer - /// * a valid pointer to an allocation of `len * size_of::()` bytes aligned to at least `align_of::()` - /// - `strm.bzfree` frees memory allocated by `strm.bzalloc` + /// * a valid pointer to an allocation of at least `items * size` bytes, aligned to at least + /// `align_of::()`. The byte count (`items * size`) is passed as `items` + /// and the size as `1`, just as libbzip2 does + /// - `strm.bzfree(strm.opaque, ptr)` frees memory that `strm.bzalloc` returned pub(crate) unsafe fn from_bz_stream(strm: &BzStream) -> Option { let bzalloc = strm.bzalloc?; let bzfree = strm.bzfree?; @@ -83,10 +86,13 @@ impl Allocator { /// # Safety /// - /// - `allocate` and `opaque` must form a valid allocator, meaning `allocate` returns either + /// - `allocate` and `opaque` must form a valid allocator, meaning that + /// `allocate(opaque, items, size)` returns either /// * a `NULL` pointer - /// * a valid pointer to an allocation of `len * size_of::()` bytes aligned to at least `align_of::()` - /// - `deallocate` frees memory allocated by `allocate` + /// * a valid pointer to an allocation of at least `items * size` bytes, aligned to at least + /// `align_of::()`. The byte count (`items * size`) is passed as `items` + /// and the size as `1`, just as libbzip2 does + /// - `deallocate(opaque, ptr)` frees memory that `allocate` returned pub(crate) fn custom(allocate: AllocFunc, deallocate: FreeFunc, opaque: *mut c_void) -> Self { Self::Custom { allocate, @@ -176,7 +182,18 @@ impl Allocator { Allocator::Custom { allocate, opaque, .. } => unsafe { - let ptr = (allocate)(*opaque, count as i32, core::mem::size_of::() as i32); + // libbzip2 requests every block as `bzalloc(opaque, n, 1)`, + // and allocators written against it may honour only `n`. Pass + // the byte count as `items` and size as `1`. + // + // Passing `(count, size_of::())` made such an allocator return + // `count` bytes for a `count * size_of::()`-byte + // block. + // + // A request that does not fit the C `int` fails like a `NULL` return. + let len = count.checked_mul(core::mem::size_of::())?; + let len = c_int::try_from(len).ok()?; + let ptr = (allocate)(*opaque, len, 1); let ptr = ptr.cast::(); if ptr.is_null() { diff --git a/libbz2-rs-sys/src/bzlib.rs b/libbz2-rs-sys/src/bzlib.rs index d7d94de65..ca5923e67 100644 --- a/libbz2-rs-sys/src/bzlib.rs +++ b/libbz2-rs-sys/src/bzlib.rs @@ -122,6 +122,11 @@ type FreeFunc = unsafe extern "C" fn(*mut c_void, *mut c_void) -> (); /// } /// ``` /// +/// `bzalloc` is called as `bzalloc(opaque, n, 1)`: `n` is the number of bytes requested and the +/// third argument is always `1`, as in libbzip2. The returned memory must be aligned to at +/// least `align_of::()`, which `malloc` guarantees. `bzfree` receives `opaque` and a pointer +/// that `bzalloc` returned. +/// /// When these fields are `None` (or `NULL` in C), the initialization functions will try to /// put in a default allocator, based on feature flags: ///