use std::alloc::Layout;
use std::mem::align_of;
use std::ptr::NonNull;
#[cfg(target_arch = "x86")]
pub const ALIGNMENT: usize = 1 << 6;
#[cfg(target_arch = "x86_64")]
pub const ALIGNMENT: usize = 1 << 7;
#[cfg(target_arch = "mips")]
pub const ALIGNMENT: usize = 1 << 5;
#[cfg(target_arch = "mips64")]
pub const ALIGNMENT: usize = 1 << 5;
#[cfg(target_arch = "powerpc")]
pub const ALIGNMENT: usize = 1 << 5;
#[cfg(target_arch = "powerpc64")]
pub const ALIGNMENT: usize = 1 << 6;
#[cfg(target_arch = "riscv")]
pub const ALIGNMENT: usize = 1 << 6;
#[cfg(target_arch = "s390x")]
pub const ALIGNMENT: usize = 1 << 8;
#[cfg(target_arch = "sparc")]
pub const ALIGNMENT: usize = 1 << 5;
#[cfg(target_arch = "sparc64")]
pub const ALIGNMENT: usize = 1 << 6;
#[cfg(target_arch = "thumbv6")]
pub const ALIGNMENT: usize = 1 << 5;
#[cfg(target_arch = "thumbv7")]
pub const ALIGNMENT: usize = 1 << 5;
#[cfg(target_arch = "wasm32")]
pub const ALIGNMENT: usize = FALLBACK_ALIGNMENT;
#[cfg(target_arch = "arm")]
pub const ALIGNMENT: usize = 1 << 5;
#[cfg(target_arch = "nvptx")]
pub const ALIGNMENT: usize = 1 << 7;
#[cfg(target_arch = "nvptx64")]
pub const ALIGNMENT: usize = 1 << 7;
#[cfg(target_arch = "aarch64")]
pub const ALIGNMENT: usize = 1 << 6;
#[doc(hidden)]
const FALLBACK_ALIGNMENT: usize = 1 << 6;
const BYPASS_PTR: NonNull<u8> = unsafe { NonNull::new_unchecked(ALIGNMENT as *mut u8) };
pub fn allocate_aligned(size: usize) -> *mut u8 {
unsafe {
if size == 0 {
BYPASS_PTR.as_ptr()
} else {
let layout = Layout::from_size_align_unchecked(size, ALIGNMENT);
std::alloc::alloc_zeroed(layout)
}
}
}
pub unsafe fn free_aligned(ptr: *mut u8, size: usize) {
if ptr != BYPASS_PTR.as_ptr() {
std::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, ALIGNMENT));
}
}
pub unsafe fn reallocate(ptr: *mut u8, old_size: usize, new_size: usize) -> *mut u8 {
if ptr == BYPASS_PTR.as_ptr() {
return allocate_aligned(new_size);
}
if new_size == 0 {
free_aligned(ptr, old_size);
return BYPASS_PTR.as_ptr();
}
let new_ptr = std::alloc::realloc(
ptr,
Layout::from_size_align_unchecked(old_size, ALIGNMENT),
new_size,
);
if !new_ptr.is_null() && new_size > old_size {
new_ptr.add(old_size).write_bytes(0, new_size - old_size);
}
new_ptr
}
pub unsafe fn memcpy(dst: *mut u8, src: *const u8, len: usize) {
if len != 0x00 && src != BYPASS_PTR.as_ptr() {
std::ptr::copy_nonoverlapping(src, dst, len)
}
}
extern "C" {
pub fn memcmp(p1: *const u8, p2: *const u8, len: usize) -> i32;
}
pub fn is_aligned<T>(p: *const T, a: usize) -> bool {
let a_minus_one = a.wrapping_sub(1);
let pmoda = p as usize & a_minus_one;
pmoda == 0
}
pub fn is_ptr_aligned<T>(p: *const T) -> bool {
p.align_offset(align_of::<T>()) == 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_allocate() {
for _ in 0..10 {
let p = allocate_aligned(1024);
assert_eq!(0, (p as usize) % 64);
}
}
#[test]
fn test_is_aligned() {
let mut ptr = allocate_aligned(10);
assert_eq!(true, is_aligned::<u8>(ptr, 1));
assert_eq!(true, is_aligned::<u8>(ptr, 2));
assert_eq!(true, is_aligned::<u8>(ptr, 4));
ptr = unsafe { ptr.offset(1) };
assert_eq!(true, is_aligned::<u8>(ptr, 1));
assert_eq!(false, is_aligned::<u8>(ptr, 2));
assert_eq!(false, is_aligned::<u8>(ptr, 4));
}
}