extern crate alloc;
use alloc::alloc::{alloc, dealloc, Layout};
use core::ffi::c_void;
use core::ptr;
const HEADER: usize = 16;
unsafe extern "C" fn rust_alloc(
_opaque: *mut c_void,
size: usize,
) -> *mut c_void {
let total = match size.checked_add(HEADER) {
Some(total) => total,
None => return ptr::null_mut(),
};
let layout = match Layout::from_size_align(total, HEADER) {
Ok(layout) => layout,
Err(_) => return ptr::null_mut(),
};
let base = alloc(layout);
if base.is_null() {
return ptr::null_mut();
}
base.cast::<usize>().write(total);
base.add(HEADER).cast()
}
unsafe extern "C" fn rust_free(_opaque: *mut c_void, address: *mut c_void) {
if address.is_null() {
return;
}
let base = address.cast::<u8>().sub(HEADER);
let total = base.cast::<usize>().read();
dealloc(base, Layout::from_size_align_unchecked(total, HEADER));
}
pub(crate) const RUST_GLOBAL_ALLOCATOR: zstd_sys::ZSTD_customMem =
zstd_sys::ZSTD_customMem {
customAlloc: Some(rust_alloc),
customFree: Some(rust_free),
opaque: ptr::null_mut(),
};
pub(crate) unsafe fn create_cdict(
dict_buffer: &[u8],
compression_level: crate::CompressionLevel,
load_method: zstd_sys::ZSTD_dictLoadMethod_e,
) -> *mut zstd_sys::ZSTD_CDict {
let cparams = zstd_sys::ZSTD_getCParams(
compression_level,
crate::CONTENTSIZE_UNKNOWN as core::ffi::c_ulonglong,
dict_buffer.len(),
);
zstd_sys::ZSTD_createCDict_advanced(
crate::ptr_void(dict_buffer),
dict_buffer.len(),
load_method,
zstd_sys::ZSTD_dictContentType_e::ZSTD_dct_auto,
cparams,
RUST_GLOBAL_ALLOCATOR,
)
}
pub(crate) unsafe fn create_ddict(
dict_buffer: &[u8],
load_method: zstd_sys::ZSTD_dictLoadMethod_e,
) -> *mut zstd_sys::ZSTD_DDict {
zstd_sys::ZSTD_createDDict_advanced(
crate::ptr_void(dict_buffer),
dict_buffer.len(),
load_method,
zstd_sys::ZSTD_dictContentType_e::ZSTD_dct_auto,
RUST_GLOBAL_ALLOCATOR,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn oversized_allocations_return_null() {
unsafe {
assert!(rust_alloc(ptr::null_mut(), usize::MAX).is_null());
assert!(rust_alloc(ptr::null_mut(), isize::MAX as usize).is_null());
assert!(rust_alloc(ptr::null_mut(), isize::MAX as usize - HEADER)
.is_null());
}
}
#[test]
fn zero_sized_allocation_is_aligned_and_can_be_freed() {
unsafe {
let address = rust_alloc(ptr::null_mut(), 0);
assert!(!address.is_null());
assert_eq!(address as usize % HEADER, 0);
rust_free(ptr::null_mut(), address);
rust_free(ptr::null_mut(), ptr::null_mut());
}
}
}