use core::ffi::c_int; use core::{
alloc::Layout,
ffi::{c_uint, c_void},
marker::PhantomData,
mem::MaybeUninit,
};
#[cfg(feature = "rust-allocator")] use alloc::alloc::GlobalAlloc;
#[allow(non_camel_case_types)] type size_t = usize;
/// # Safety /// /// This function is safe, but must have this type signature to be used elsewhere in the library #[cfg(unix)] unsafeextern"C"fn zalloc_c(opaque: *mut c_void, items: c_uint, size: c_uint) -> *mut c_void { let _ = opaque;
letmut ptr = core::ptr::null_mut(); match posix_memalign(&mut ptr, 64, items as size_t * size as size_t) { 0 => ptr,
_ => core::ptr::null_mut(),
}
}
/// # Safety /// /// This function is safe, but must have this type signature to be used elsewhere in the library #[cfg(not(unix))] unsafeextern"C"fn zalloc_c(opaque: *mut c_void, items: c_uint, size: c_uint) -> *mut c_void { let _ = opaque;
/// # Safety /// /// The `ptr` must be allocated with the allocator that is used internally by `zcfree` unsafeextern"C"fn zfree_c(opaque: *mut c_void, ptr: *mut c_void) { let _ = opaque;
extern"C" { fn free(p: *mut c_void);
}
unsafe { free(ptr) }
}
/// # Safety /// /// This function is safe to call. #[cfg(feature = "rust-allocator")] unsafeextern"C"fn zalloc_rust(_opaque: *mut c_void, count: c_uint, size: c_uint) -> *mut c_void { let align = 64; let size = count as usize * size as usize;
// internally, we want to align allocations to 64 bytes (in part for SIMD reasons) let layout = Layout::from_size_align(size, align).unwrap();
let ptr = std::alloc::System.alloc(layout);
ptr as *mut c_void
}
/// # Safety /// /// - `ptr` must be allocated with the rust `alloc::System` allocator /// - `opaque` is a `&usize` that represents the size of the allocation #[cfg(feature = "rust-allocator")] unsafeextern"C"fn zfree_rust(opaque: *mut c_void, ptr: *mut c_void) { if ptr.is_null() { return;
}
// we can't really do much else. Deallocating with an invalid layout is UB.
debug_assert!(!opaque.is_null()); if opaque.is_null() { return;
}
let size = *(opaque as *mut usize); let align = 64;
let layout = Layout::from_size_align(size, align); let layout = layout.unwrap();
impl<'a> Allocator<'a> { pubfn allocate_layout(&self, layout: Layout) -> *mut c_void { // Special case for the Rust `alloc` backed allocator #[cfg(feature = "rust-allocator")] ifself.zalloc == Allocator::RUST.zalloc { let ptr = unsafe { (Allocator::RUST.zalloc)(self.opaque, layout.size() as _, 1) };
debug_assert_eq!(ptr as usize % layout.align(), 0);
return ptr;
}
// General case for c-style allocation
// We cannot rely on the allocator giving properly aligned allocations and have to fix that ourselves. // // The general approach is to allocate a bit more than the layout needs, so that we can // give the application a properly aligned address and also store the real allocation // pointer in the allocation so that `free` can free the real allocation pointer. // // // Example: The layout represents `(u32, u32)`, with an alignment of 4 bytes and a // total size of 8 bytes. // // Assume that the allocator will give us address `0x07`. We need that to be a multiple // of the alignment, so that shifts the starting position to `0x08`. Then we also need // to store the pointer to the start of the allocation so that `free` can free that // pointer, bumping to `0x10`. The `0x10` pointer is then the pointer that the application // deals with. When free'ing, the original allocation pointer can be read from `0x10 - size_of::<*const c_void>()`. // // Of course there does need to be enough space in the allocation such that when we // shift the start forwards, the end is still within the allocation. Hence we allocate // `extra_space` bytes: enough for a full alignment plus a pointer.
// we need at least // // - `align` extra space so that no matter what pointer we get from zalloc, we can shift the start of the // allocation by at most `align - 1` so that `ptr as usize % align == 0 // - `size_of::<*mut _>` extra space so that after aligning to `align`, // there is `size_of::<*mut _>` space to store the pointer to the allocation. // This pointer is then retrieved in `free` let extra_space = core::mem::size_of::<*mut c_void>() + layout.align();
// Safety: we assume allocating works correctly in the safety assumptions on // `DeflateStream` and `InflateStream`. let ptr = unsafe { (self.zalloc)(self.opaque, (layout.size() + extra_space) as _, 1) };
if ptr.is_null() { return ptr;
}
// Calculate return pointer address with space enough to store original pointer let align_diff = (ptr as usize).next_multiple_of(layout.align()) - (ptr as usize);
// Safety: offset is smaller than 64, and we allocated 64 extra bytes in the allocation letmut return_ptr = unsafe { ptr.cast::<u8>().add(align_diff) };
// if there is not enough space to store a pointer we need to make more if align_diff < core::mem::size_of::<*mut c_void>() { // # Safety // // - `return_ptr` is well-aligned, therefore `return_ptr + align` is also well-aligned // - we reserve `size_of::<*mut _> + align` extra space in the allocation, so // `ptr + align_diff + align` is still valid for (at least) `layout.size` bytes let offset = Ord::max(core::mem::size_of::<*mut c_void>(), layout.align());
return_ptr = unsafe { return_ptr.add(offset) };
}
// Store the original pointer for free() // // Safety: `align >= size_of::<*mut _>`, so there is now space for a pointer before `return_ptr` // in the allocation unsafe { let original_ptr = return_ptr.sub(core::mem::size_of::<*mut c_void>());
core::ptr::write_unaligned(original_ptr.cast::<*mut c_void>(), ptr);
};
// Return properly aligned pointer in allocation let ptr = return_ptr.cast::<c_void>();
debug_assert_eq!(ptr as usize % layout.align(), 0);
/// # Panics /// /// - when `len` is 0 /// /// # Safety /// /// - `ptr` must be allocated with this allocator /// - `len` must be the number of `T`s that are in this allocation #[allow(unused)] // Rust needs `len` for deallocation pubunsafefn deallocate<T>(&self, ptr: *mut T, len: usize) { if !ptr.is_null() { // Special case for the Rust `alloc` backed allocator #[cfg(feature = "rust-allocator")] ifself.zfree == Allocator::RUST.zfree {
assert_ne!(len, 0, "invalid size for {:?}", ptr); letmut size = core::mem::size_of::<T>() * len; return (Allocator::RUST.zfree)(&mut size as *mut usize as *mut c_void, ptr.cast());
}
// General case for c-style allocation let original_ptr = (ptr as *mut u8).sub(core::mem::size_of::<*const c_void>()); let free_ptr = core::ptr::read_unaligned(original_ptr as *mut *mut c_void);
(self.zfree)(self.opaque, free_ptr)
}
}
}
#[cfg(test)] mod tests { use core::sync::atomic::{AtomicPtr, Ordering}; use std::sync::Mutex;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.