use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
#[derive(Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[repr(transparent)]
pub struct PackedPtr<const CAPACITY: u32, T> {
pointer: NonNull<T>,
}
impl<const CAPACITY: u32, T> AsRef<T> for PackedPtr<CAPACITY, T> {
fn as_ref(&self) -> &T {
unsafe {
&*self.masked_pointer()
}
}
}
impl<const CAPACITY: u32, T> Deref for PackedPtr<CAPACITY, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<const CAPACITY: u32, T> AsMut<T> for PackedPtr<CAPACITY, T> {
fn as_mut(&mut self) -> &mut T {
unsafe {
&mut *self.masked_pointer()
}
}
}
impl<const CAPACITY: u32, T> DerefMut for PackedPtr<CAPACITY, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
impl<const CAPACITY: u32, T> PackedPtr<CAPACITY, T> {
#[inline(always)]
fn check_bitfield_bounds(index: u32) {
if index >= CAPACITY {
panic!("Cannot index bits higher than {}", CAPACITY);
}
}
#[inline(always)]
pub(crate) const fn mask() -> usize {
(1 << CAPACITY) - 1
}
#[inline]
pub(crate) fn masked_pointer(&self) -> *mut T {
(self.pointer.as_ptr() as usize & !Self::mask()) as *mut _
}
#[inline]
pub unsafe fn new_unchecked(pointer: NonNull<T>) -> Self {
Self { pointer }
}
#[inline]
pub unsafe fn new_unchecked_raw(pointer: *mut T) -> Self {
Self::new_unchecked(NonNull::new_unchecked(pointer))
}
#[inline]
pub fn get_bit(&self, index: u32) -> bool {
Self::check_bitfield_bounds(index);
self.pointer.as_ptr() as usize & (1 << index) != 0
}
pub fn set_bit(&mut self, index: u32, value: bool) {
Self::check_bitfield_bounds(index);
let old_off = self.pointer.as_ptr() as usize;
let new_off = if value {
old_off | (1usize << index)
} else {
old_off & !(1usize << index)
};
self.pointer = unsafe {
NonNull::new_unchecked(new_off as *mut _)
};
}
#[inline]
pub fn set_bit_high(&mut self, index: u32) {
self.set_bit(index, true);
}
#[inline]
pub fn set_bit_low(&mut self, index: u32) {
self.set_bit(index, false);
}
#[inline]
pub fn bitfield(&self) -> usize {
self.pointer.as_ptr() as usize & Self::mask()
}
#[inline]
pub fn set_bitfield(&mut self, bitfield: usize) {
let mask = Self::mask();
let old_off = self.pointer.as_ptr() as usize & !mask;
let new_off = old_off | (bitfield & mask);
self.pointer = unsafe {
NonNull::new_unchecked(new_off as *mut _)
};
}
}
#[cfg(test)]
mod tests {
use super::*;
#[repr(align(16))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
struct IntCap4(i32);
#[test]
fn pointer_deref() {
let mut value = IntCap4(1234);
let mut ptr: PackedPtr<1, IntCap4> =
unsafe { PackedPtr::new_unchecked_raw(&mut value as *mut _) };
assert_eq!(*ptr.as_ref(), IntCap4(1234));
assert_eq!(ptr.get_bit(0), false);
ptr.set_bit_high(0);
assert_eq!(*ptr.as_ref(), IntCap4(1234));
assert_eq!(ptr.get_bit(0), true);
*ptr.as_mut() = IntCap4(5678);
assert_eq!(*ptr.as_ref(), IntCap4(5678));
assert_eq!(ptr.get_bit(0), true);
ptr.set_bit_low(0);
assert_eq!(*ptr.as_ref(), IntCap4(5678));
assert_eq!(ptr.get_bit(0), false);
}
#[test]
fn manipulate_entire_bitfield() {
let mut value = IntCap4(1234);
let mut ptr: PackedPtr<4, IntCap4> =
unsafe { PackedPtr::new_unchecked_raw(&mut value as *mut _) };
assert_eq!(ptr.bitfield(), 0b0000);
assert_eq!(*ptr.as_ref(), IntCap4(1234));
ptr.set_bitfield(0b1011);
assert_eq!(ptr.bitfield(), 0b1011);
assert_eq!(*ptr.as_ref(), IntCap4(1234));
ptr.set_bit_low(3);
assert_eq!(ptr.bitfield(), 0b0011);
assert_eq!(*ptr.as_ref(), IntCap4(1234));
ptr.set_bitfield(0b1001);
assert_eq!(ptr.bitfield(), 0b1001);
assert_eq!(*ptr.as_ref(), IntCap4(1234));
}
}