use core::ops::Rem;
use core::{
ops::{Add, Not},
ops::{BitAnd, Sub},
ptr::NonNull,
slice,
};
#[macro_export]
macro_rules! get_field_ptr {
($non_null_ptr:expr, $field:ident) => {{
let ptr = $non_null_ptr;
unsafe {
core::ptr::NonNull::new_unchecked(core::ptr::addr_of_mut!((*ptr.as_ptr()).$field))
}
}};
}
pub trait NonNullEx<T> {
type Type;
fn is_aligned_on_pow2(&self, align: usize) -> bool;
fn as_address(&self) -> usize;
fn from_address(address: usize) -> Option<NonNull<Self::Type>>;
#[allow(unused)]
fn as_slice_mut<'a>(&mut self, size: usize) -> &'a mut [Self::Type];
fn align_up_buffer(&self, align: usize, buffer_size: usize) -> Option<(NonNull<T>, usize)>;
fn unsafe_unsafe_add(&self, offset: usize) -> NonNull<T>;
fn unsafe_ref<'a>(&self) -> &'a Self::Type;
fn unsafe_mut_ref<'a>(&mut self) -> &'a mut Self::Type;
}
pub trait NumEx {
type Type;
#[allow(unused)]
fn is_aligned_to_pow2(&self, align: Self::Type) -> bool;
fn is_aligned_to(&self, align: Self::Type) -> bool;
#[allow(unused)]
fn align_up(self, align: Self::Type) -> Self::Type;
fn align_down(self, align: Self::Type) -> Self::Type;
#[allow(unused)]
fn zero_err<E>(self, err: E) -> Result<(), E>;
}
impl<T> NonNullEx<T> for NonNull<T> {
fn is_aligned_on_pow2(&self, align: usize) -> bool {
let address = self.as_ptr() as usize;
address & (align - 1) == 0
}
type Type = T;
fn as_address(&self) -> usize {
self.as_ptr() as usize
}
fn from_address(address: usize) -> Option<NonNull<Self::Type>> {
let ptr = address as *mut Self::Type;
let n_n = NonNull::new(ptr)?;
Some(n_n)
}
fn as_slice_mut<'a>(&mut self, size: usize) -> &'a mut [Self::Type] {
unsafe {
let slice: &'a mut [Self::Type] = slice::from_raw_parts_mut(self.as_ptr().cast(), size);
slice
}
}
fn align_up_buffer(&self, align: usize, buffer_size: usize) -> Option<(NonNull<T>, usize)> {
let offset_elements = self.align_offset(align);
if offset_elements == usize::MAX {
return None;
}
let offset_bytes = offset_elements * size_of::<T>();
let new_bfs = buffer_size.checked_sub(offset_bytes)?;
if new_bfs == 0 {
None
} else {
let aligned_ptr = unsafe { self.add(offset_elements) };
Some((aligned_ptr, new_bfs))
}
}
fn unsafe_unsafe_add(&self, offset: usize) -> NonNull<T> {
unsafe { self.add(offset) }
}
fn unsafe_ref<'a>(&self) -> &'a Self::Type {
unsafe { self.as_ref() }
}
fn unsafe_mut_ref<'a>(&mut self) -> &'a mut Self::Type {
unsafe { self.as_mut() }
}
}
impl<T> NumEx for T
where
T: Copy
+ BitAnd<Output = T>
+ Sub<Output = T>
+ Add<Output = T>
+ From<u8>
+ PartialEq
+ Default
+ Not<Output = T>
+ Rem<Output = T>,
{
type Type = T;
fn is_aligned_to_pow2(&self, align: Self::Type) -> bool {
let one: T = T::from(1u8);
let zero: T = T::default();
(*self & (align - one)) == zero
}
fn zero_err<E>(self, err: E) -> Result<(), E> {
let zero = T::default();
if self == zero { Err(err) } else { Ok(()) }
}
fn align_up(self, align: Self::Type) -> Self::Type {
let one: T = T::from(1u8);
let mask = align - one;
(self + mask) & !mask
}
fn align_down(self, align: Self::Type) -> Self::Type {
let one: T = T::from(1u8);
let mask = align - one;
self & !mask
}
fn is_aligned_to(&self, align: Self::Type) -> bool {
let zero: T = T::default();
(*self % align) == zero
}
}
pub trait BoolEx {
fn as_result<O, E>(&self, ok: O, err: E) -> Result<O, E>;
fn on_err<E>(&self, err: E) -> Result<(), E>;
}
impl BoolEx for bool {
fn as_result<O, E>(&self, ok: O, err: E) -> Result<O, E> {
match self {
true => Ok(ok),
false => Err(err),
}
}
fn on_err<E>(&self, err: E) -> Result<(), E> {
match self {
true => Ok(()),
false => Err(err),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::defs::SlabError;
#[test]
fn test_num_ex() {
assert!(16usize.is_aligned_to_pow2(8));
assert!(!15usize.is_aligned_to_pow2(8));
assert!(0usize.is_aligned_to_pow2(8));
assert_eq!(13u32.align_up(4), 16);
assert_eq!(16u32.align_up(4), 16);
assert_eq!(0u32.align_up(4), 0);
assert_eq!(13u32.align_down(4), 12);
assert_eq!(16u32.align_down(4), 16);
assert_eq!(0u32.align_down(4), 0);
assert!(10u32.zero_err(SlabError::FatalError).is_ok());
assert!(0u32.zero_err(SlabError::FatalError).is_err());
}
#[test]
fn test_bool_ex() {
assert_eq!(true.as_result(10, 20), Ok(10));
assert_eq!(false.as_result(10, 20), Err(20));
assert!(true.on_err(SlabError::FatalError).is_ok());
assert!(false.on_err(SlabError::FatalError).is_err());
}
#[test]
fn test_non_null_ex() {
let mut val = 123u64;
let mut ptr = NonNull::new(&mut val as *mut u64).unwrap();
assert_eq!(ptr.as_address(), &val as *const u64 as usize);
assert_eq!(NonNull::from_address(ptr.as_address()), Some(ptr));
assert!(ptr.is_aligned_on_pow2(1));
let addr = ptr.as_address();
assert_eq!(ptr.is_aligned_on_pow2(8), addr % 8 == 0);
let slice = ptr.as_slice_mut(1);
assert_eq!(slice.len(), 1);
assert_eq!(slice[0], 123);
}
#[test]
fn test_align_up_buffer() {
let mut data = [0u8; 64];
let ptr = NonNull::new(data.as_mut_ptr()).unwrap();
if let Some((aligned_ptr, remaining)) = ptr.align_up_buffer(32, 64) {
assert!(aligned_ptr.as_address() % 32 == 0);
let offset = aligned_ptr.as_address() - ptr.as_address();
assert_eq!(remaining, 64 - offset);
}
let mem = NonNull::new(data.as_mut_ptr()).unwrap();
let unaligned_off = mem.align_offset(64) + 1;
let unaligned_ptr = unsafe { mem.add(unaligned_off) };
assert!(unaligned_ptr.align_up_buffer(64, 10).is_none());
}
}