use std::ffi::c_void;
use jpegxl_sys::memory_manager::JxlMemoryManager;
pub type AllocFn = unsafe extern "C" fn(opaque: *mut c_void, size: usize) -> *mut c_void;
pub type FreeFn = unsafe extern "C" fn(opaque: *mut c_void, address: *mut c_void);
#[allow(clippy::module_name_repetitions)]
pub trait MemoryManager {
fn alloc(&self) -> AllocFn;
fn free(&self) -> FreeFn;
#[must_use]
fn manager(&self) -> JxlMemoryManager {
JxlMemoryManager {
opaque: self as *const _ as *mut _,
alloc: self.alloc(),
free: self.free(),
}
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::{
ptr::null_mut,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{decoder_builder, encoder_builder, DecodeError, EncodeError};
#[cfg(feature = "threads")]
use crate::ThreadsRunner;
use super::*;
pub struct NoManager {}
impl MemoryManager for NoManager {
fn alloc(&self) -> AllocFn {
unsafe extern "C" fn alloc(_a: *mut c_void, _b: usize) -> *mut c_void {
null_mut()
}
alloc
}
fn free(&self) -> FreeFn {
unsafe extern "C" fn free(_opaque: *mut c_void, _address: *mut c_void) {}
free
}
}
pub struct BumpManager<const N: usize> {
arena: Box<[u8; N]>,
footer: AtomicUsize,
}
impl<const N: usize> Default for BumpManager<N> {
fn default() -> Self {
Self {
arena: Box::new([0_u8; N]),
footer: AtomicUsize::new(0),
}
}
}
impl<const N: usize> MemoryManager for BumpManager<N> {
fn alloc(&self) -> AllocFn {
unsafe extern "C" fn alloc<const N: usize>(
opaque: *mut c_void,
size: usize,
) -> *mut c_void {
let mm = &mut *opaque.cast::<BumpManager<{ N }>>();
let mut footer = mm.footer.load(Ordering::Acquire);
let mut new = footer + size;
loop {
if new > N {
break null_mut();
} else if mm
.footer
.compare_exchange_weak(footer, new, Ordering::AcqRel, Ordering::Relaxed)
.is_err()
{
footer = mm.footer.load(Ordering::Acquire);
new = footer + size;
} else {
let addr = mm.arena.get_unchecked_mut(footer);
break (addr as *mut u8).cast();
}
}
}
alloc::<N>
}
fn free(&self) -> FreeFn {
unsafe extern "C" fn free(_opaque: *mut c_void, _address: *mut c_void) {}
free
}
}
#[test]
fn test_no_manager() {
let mm = NoManager {};
let decoder = decoder_builder().memory_manager(&mm).build();
assert!(matches!(decoder, Err(DecodeError::CannotCreateDecoder)));
let encoder = encoder_builder().memory_manager(&mm).build();
assert!(matches!(encoder, Err(EncodeError::CannotCreateEncoder)));
}
#[test]
#[cfg(feature = "threads")]
fn test_bump_manager() -> Result<(), DecodeError> {
let mm = BumpManager::<{ 1024 * 5 }>::default();
let parallel_runner =
ThreadsRunner::new(Some(&mm), Some(64)).expect("Failed to create ThreadsRunner");
let decoder = decoder_builder()
.parallel_runner(¶llel_runner)
.memory_manager(&mm)
.build()?;
decoder.decode(crate::tests::SAMPLE_JXL)?;
Ok(())
}
}