Skip to main content

compu/
mem.rs

1//!Custom malloc implementation which uses Rust's allocator and provides common interface required by compression libraries
2use core::ffi::{c_uint, c_void};
3
4extern crate alloc;
5
6use alloc::alloc::Layout;
7pub use alloc::boxed::Box;
8use core::{mem, ptr};
9
10//Linux & win 32 bit are 8
11#[cfg(not(any(target_os = "macos", all(windows, target_pointer_width = "64"))))]
12const MIN_ALIGN: usize = 8;
13//Mac and  win 64 bit are 16
14#[cfg(any(target_os = "macos", all(windows, target_pointer_width = "64")))]
15const MIN_ALIGN: usize = 16;
16
17const LAYOUT_OFFSET: usize = mem::size_of::<usize>();
18
19#[cold]
20#[inline(never)]
21fn unlikely_null() -> *mut c_void {
22    ptr::null_mut()
23}
24
25#[inline]
26///`malloc` impl with Rust allocator
27pub unsafe extern "C" fn compu_malloc(size: usize) -> *mut c_void {
28    if let Ok(layout) = Layout::from_size_align(size + LAYOUT_OFFSET, MIN_ALIGN) {
29        let mem = alloc::alloc::alloc(layout);
30        if !mem.is_null() {
31            ptr::write(mem as *mut usize, size);
32            return mem.add(LAYOUT_OFFSET) as _;
33        }
34    }
35
36    unlikely_null()
37}
38
39#[inline]
40///`free` impl with Rust allocator
41pub unsafe extern "C" fn compu_free(mem: *mut c_void) {
42    if !mem.is_null() {
43        let mem = (mem as *mut u8).offset(-(LAYOUT_OFFSET as isize));
44        let size = ptr::read(mem as *const usize);
45        let layout = Layout::from_size_align_unchecked(size + LAYOUT_OFFSET, MIN_ALIGN);
46        alloc::alloc::dealloc(mem, layout);
47    }
48}
49
50#[allow(unused)]
51///`malloc` alternative with Rust allocator
52pub(crate) unsafe extern "C" fn compu_malloc_with_state(
53    _: *mut c_void,
54    size: usize,
55) -> *mut c_void {
56    compu_malloc(size)
57}
58
59#[allow(unused)]
60///`alloc` alternative with Rust allocator
61pub(crate) unsafe extern "C" fn compu_alloc(
62    _: *mut c_void,
63    items: c_uint,
64    size: c_uint,
65) -> *mut c_void {
66    let size = match (items as usize).checked_mul(size as usize) {
67        Some(0) | None => return unlikely_null(),
68        Some(size) => size,
69    };
70    compu_malloc(size)
71}
72
73#[allow(unused)]
74pub(crate) unsafe extern "C" fn compu_free_with_state(_: *mut c_void, mem: *mut c_void) {
75    compu_free(mem)
76}
77
78#[cfg(feature = "brotli-rust")]
79///Allocator implementation using Rust's global allocator
80pub mod brotli_rust {
81    extern crate alloc;
82
83    use super::Box;
84    use alloc::vec::Vec;
85
86    ///Boxed slice wrapper
87    pub struct BoxedSlice<T>(Box<[T]>);
88
89    impl<T> Default for BoxedSlice<T> {
90        #[inline(always)]
91        fn default() -> Self {
92            Self(Vec::new().into_boxed_slice())
93        }
94    }
95    impl<T> brotli::SliceWrapper<T> for BoxedSlice<T> {
96        #[inline(always)]
97        fn slice(&self) -> &[T] {
98            &self.0
99        }
100    }
101
102    impl<T> brotli::SliceWrapperMut<T> for BoxedSlice<T> {
103        #[inline(always)]
104        fn slice_mut(&mut self) -> &mut [T] {
105            &mut self.0
106        }
107    }
108
109    #[derive(Copy, Clone, Default)]
110    ///Default allocator
111    pub struct BrotliAllocator;
112
113    impl<T: Default> brotli::Allocator<T> for BrotliAllocator {
114        type AllocatedMemory = BoxedSlice<T>;
115        fn alloc_cell(&mut self, len: usize) -> Self::AllocatedMemory {
116            let mut vec = Vec::with_capacity(len);
117            for _ in 0..len {
118                vec.push(Default::default());
119            }
120            BoxedSlice(vec.into_boxed_slice())
121        }
122
123        fn free_cell(&mut self, _data: Self::AllocatedMemory) {}
124    }
125
126    impl brotli::enc::BrotliAlloc for BrotliAllocator {}
127}