use crate::{
bits::{
BitIdx,
Bits,
},
cursor::Cursor,
domain::*,
slice::BitSlice,
};
use core::{
convert::{
AsMut,
AsRef,
From,
},
default::Default,
fmt::{
self,
Debug,
Formatter,
},
marker::PhantomData,
mem::size_of,
ptr::NonNull,
slice,
};
const PTR_BITS: usize = size_of::<*const u8>() * 8;
const USZ_BITS: usize = size_of::<usize>() * 8;
#[repr(C)]
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct BitPtr<T = u8>
where T: Bits {
_ty: PhantomData<T>,
ptr: NonNull<u8>,
len: usize,
}
impl<T> BitPtr<T>
where T: Bits {
pub const PTR_DATA_BITS: usize = PTR_BITS - Self::PTR_HEAD_BITS;
pub const PTR_DATA_MASK: usize = !Self::PTR_HEAD_MASK;
pub const PTR_HEAD_BITS: usize = T::INDX as usize - Self::LEN_HEAD_BITS;
pub const PTR_HEAD_MASK: usize = T::MASK as usize >> Self::LEN_HEAD_BITS;
pub const LEN_HEAD_BITS: usize = 3;
pub const LEN_HEAD_MASK: usize = 0b0111;
pub const LEN_TAIL_BITS: usize = T::INDX as usize;
pub const LEN_TAIL_MASK: usize = (T::MASK as usize) << Self::LEN_HEAD_BITS;
pub const LEN_ELTS_BITS: usize = USZ_BITS - Self::LEN_INDX_BITS;
pub const LEN_ELTS_MASK: usize = !Self::LEN_INDX_MASK;
pub const LEN_INDX_BITS: usize = Self::LEN_TAIL_BITS + Self::LEN_HEAD_BITS;
pub const LEN_INDX_MASK: usize = Self::LEN_TAIL_MASK | Self::LEN_HEAD_MASK;
pub const MAX_ELTS: usize = 1 << Self::LEN_ELTS_BITS;
pub const MAX_BITS: usize = !0 >> Self::LEN_HEAD_BITS;
pub fn empty() -> Self {
Self {
_ty: PhantomData,
ptr: NonNull::dangling(),
len: 0,
}
}
pub fn uninhabited(ptr: *const T) -> Self {
assert!(
(ptr as usize).trailing_zeros() as usize >= Self::PTR_HEAD_BITS,
"Pointer {:p} does not satisfy minimum alignment requirements {}",
ptr,
Self::PTR_HEAD_BITS,
);
Self {
_ty: PhantomData,
ptr: NonNull::new(ptr as *mut u8).unwrap_or_else(NonNull::dangling),
len: 0,
}
}
pub fn new<Head, Tail>(
data: *const T,
elts: usize,
head: Head,
tail: Tail,
) -> Self
where Head: Into<BitIdx>, Tail: Into<BitIdx> {
let (head, tail) = (head.into(), tail.into());
if data.is_null() || elts == 0 || (elts == 1 && head == tail) {
return Self::uninhabited(data);
}
assert!(
(data as usize).trailing_zeros() as usize >= Self::PTR_HEAD_BITS,
"BitPtr domain pointers must be well aligned",
);
assert!(
elts <= Self::MAX_ELTS,
"{} exceeds the BitPtr domain maximum, {}",
elts,
Self::MAX_ELTS,
);
assert!(
head.is_valid::<T>(),
"{} is outside the head domain 0 .. {}",
*head,
T::BITS,
);
assert!(
tail.is_valid_tail::<T>(),
"{} is outside the tail domain 1 ..= {}",
*tail,
T::BITS,
);
if elts == 1 {
assert!(
tail > head,
"BitPtr domains with one element must have the tail cursor \
beyond the head cursor",
);
}
else if elts == Self::MAX_ELTS {
assert!(
tail.is_valid::<T>(),
"BitPtr domains with maximum elements must have the tail ({}) \
cursor in 1 .. {}",
*tail,
T::BITS,
);
}
unsafe { Self::new_unchecked(data, elts, head, tail) }
}
pub(crate) unsafe fn new_unchecked(
data: *const T,
elts: usize,
head: BitIdx,
tail: BitIdx,
) -> Self {
assert!(
data.wrapping_add(elts) >= data,
"The region overflows the address space: {:p} + {:02X} is {:p}",
data,
elts,
data.wrapping_add(elts),
);
let ptr_data = data as usize & Self::PTR_DATA_MASK;
let ptr_head = *head as usize >> Self::LEN_HEAD_BITS;
let len_elts = elts.saturating_sub((*tail < T::BITS) as usize)
<< Self::LEN_INDX_BITS;
let len_tail
= ((*tail as usize) << Self::LEN_HEAD_BITS)
& Self::LEN_TAIL_MASK;
let len_head = *head as usize & Self::LEN_HEAD_MASK;
Self {
_ty: PhantomData,
ptr: NonNull::new_unchecked((ptr_data | ptr_head) as *mut u8),
len: len_elts | len_tail | len_head,
}
}
pub fn pointer(&self) -> *const T {
(self.ptr.as_ptr() as usize & Self::PTR_DATA_MASK) as *const T
}
pub fn elements(&self) -> usize {
let tail_bits = self.len & Self::LEN_TAIL_MASK;
let incr = tail_bits != 0;
(self.len >> Self::LEN_INDX_BITS) + incr as usize
}
pub fn head(&self) -> BitIdx {
let ptr = self.ptr.as_ptr() as usize & Self::PTR_HEAD_MASK;
let ptr = ptr << Self::LEN_HEAD_BITS;
let len = self.len & Self::LEN_HEAD_MASK;
((ptr | len) as u8).into()
}
pub fn tail(&self) -> BitIdx {
let bits = (self.len & Self::LEN_TAIL_MASK) >> Self::LEN_HEAD_BITS;
(
(self.is_empty() as u8).wrapping_sub(1) &
((((bits == 0) as u8) << T::INDX) | bits as u8)
).into()
}
pub fn raw_parts(&self) -> (*const T, usize, BitIdx, BitIdx) {
(self.pointer(), self.elements(), self.head(), self.tail())
}
pub fn region_data(&self) -> (usize, BitIdx, BitIdx) {
(self.elements(), self.head(), self.tail())
}
pub fn cursors(&self) -> (BitIdx, BitIdx) {
(self.head(), self.tail())
}
pub fn is_empty(&self) -> bool {
self.len == 0 && self.ptr.as_ptr() as usize & Self::PTR_HEAD_MASK == 0
}
pub fn len(&self) -> usize {
let (elts, head, tail) = self.region_data();
match elts {
0 => 0,
1 => (*tail - *head) as usize,
e => ((e - 1) << T::INDX) + (*tail as usize) - (*head as usize),
}
}
pub fn as_slice<'a>(&self) -> &'a [T] {
unsafe { slice::from_raw_parts(self.pointer(), self.elements()) }
}
pub fn as_mut_slice<'a>(&self) -> &'a mut [T] {
unsafe {
slice::from_raw_parts_mut(self.pointer() as *mut T, self.elements())
}
}
pub fn domain_kind(&self) -> BitDomainKind {
self.into()
}
pub fn domain(&self) -> BitDomain<T> {
(*self).into()
}
pub fn domain_mut(&self) -> BitDomainMut<T> {
(*self).into()
}
pub unsafe fn incr_head(&mut self) {
match self.len() {
0 => {},
1 => *self = Self::uninhabited(self.pointer()),
_ => {
let (data, elts, head, tail) = self.raw_parts();
let (h, wrap) = head.incr::<T>();
if wrap {
*self = Self::new_unchecked(
data.offset(1),
elts.saturating_sub(1),
h,
tail,
);
}
else {
*self = Self::new_unchecked(data, elts, h, tail);
}
},
}
}
pub unsafe fn decr_head(&mut self) {
if self.is_empty() {
return;
}
let (data, elts, head, tail) = self.raw_parts();
let (h, wrap) = head.decr::<T>();
if wrap {
*self = Self::new_unchecked(
data.offset(-1),
elts.saturating_add(1),
h,
tail,
);
}
else {
*self = Self::new_unchecked(data, elts, h, tail);
}
}
pub unsafe fn incr_tail(&mut self) {
if self.len | Self::LEN_HEAD_MASK == !0 {
return;
}
let (data, elts, head, tail) = self.raw_parts();
let (t, wrap) = tail.incr_tail::<T>();
*self = Self::new_unchecked(
data,
elts.saturating_add(wrap as usize),
head,
t,
);
}
pub unsafe fn decr_tail(&mut self) {
match self.len() {
0 => return,
1 => *self = Self::uninhabited(self.pointer()),
_ => {
let (data, elts, head, tail) = self.raw_parts();
let (t, wrap) = tail.decr_tail::<T>();
*self = Self::new_unchecked(
data,
elts.saturating_sub(wrap as usize),
head,
t,
);
},
}
}
pub(crate) fn from_bitslice<C>(bs: &BitSlice<C, T>) -> Self
where C: Cursor {
let src = unsafe { &*(bs as *const BitSlice<C, T> as *const [()]) };
let (ptr, len) = match (src.as_ptr(), src.len()) {
(_, 0) => (NonNull::dangling(), 0),
(p, _) if p.is_null() => unreachable!("Rust forbids null refs"),
(p, l) => (unsafe { NonNull::new_unchecked(p as *mut u8) }, l),
};
Self { ptr, len, _ty: PhantomData }
}
pub(crate) fn into_bitslice<'a, C>(self) -> &'a BitSlice<C, T>
where C: Cursor {
unsafe {
&*(slice::from_raw_parts(
self.ptr.as_ptr() as *const (),
self.len,
) as *const [()] as *const BitSlice<C, T>)
}
}
pub(crate) fn into_bitslice_mut<'a, C>(self) -> &'a mut BitSlice<C, T>
where C: Cursor {
unsafe {
&mut *(slice::from_raw_parts_mut(
self.ptr.as_ptr() as *const () as *mut (),
self.len,
) as *mut [()] as *mut BitSlice<C, T>)
}
}
}
impl<T> AsMut<[T]> for BitPtr<T>
where T: Bits {
fn as_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T> AsRef<[T]> for BitPtr<T>
where T: Bits {
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}
impl<'a, C, T> From<&'a BitSlice<C, T>> for BitPtr<T>
where C: Cursor, T: 'a + Bits {
fn from(src: &'a BitSlice<C, T>) -> Self {
Self::from_bitslice(src)
}
}
impl<'a, C, T> From<&'a mut BitSlice<C, T>> for BitPtr<T>
where C: Cursor, T: 'a + Bits {
fn from(src: &'a mut BitSlice<C, T>) -> Self {
Self::from_bitslice(src)
}
}
impl<T> Default for BitPtr<T>
where T: Bits {
fn default() -> Self {
Self::empty()
}
}
impl<T> Debug for BitPtr<T>
where T: Bits {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
struct HexPtr<T: Bits>(*const T);
impl<T: Bits> Debug for HexPtr<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_fmt(format_args!("0x{:0>1$X}", self.0 as usize, PTR_BITS >> 2))
}
}
struct HexAddr(usize);
impl Debug for HexAddr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_fmt(format_args!("{:#X}", self.0))
}
}
struct BinAddr<T: Bits>(BitIdx, PhantomData<T>);
impl<T: Bits> Debug for BinAddr<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_fmt(format_args!("0b{:0>1$b}", *self.0, T::INDX as usize))
}
}
write!(f, "BitPtr<{}>", T::TYPENAME)?;
f.debug_struct("")
.field("data", &HexPtr::<T>(self.pointer()))
.field("elts", &HexAddr(self.elements()))
.field("head", &BinAddr::<T>(self.head(), PhantomData))
.field("tail", &BinAddr::<T>(self.tail(), PhantomData))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn associated_consts_u8() {
assert_eq!(BitPtr::<u8>::PTR_DATA_BITS, PTR_BITS);
assert_eq!(BitPtr::<u8>::PTR_HEAD_BITS, 0);
assert_eq!(BitPtr::<u8>::LEN_ELTS_BITS, USZ_BITS - 6);
assert_eq!(BitPtr::<u8>::LEN_TAIL_BITS, 3);
assert_eq!(BitPtr::<u8>::PTR_DATA_MASK, !0);
assert_eq!(BitPtr::<u8>::PTR_HEAD_MASK, 0);
assert_eq!(BitPtr::<u8>::LEN_ELTS_MASK, !0 << 6);
assert_eq!(BitPtr::<u8>::LEN_TAIL_MASK, 7 << 3);
assert_eq!(BitPtr::<u8>::LEN_INDX_MASK, 63);
}
#[test]
fn associated_consts_u16() {
assert_eq!(BitPtr::<u16>::PTR_DATA_BITS, PTR_BITS - 1);
assert_eq!(BitPtr::<u16>::PTR_HEAD_BITS, 1);
assert_eq!(BitPtr::<u16>::LEN_ELTS_BITS, USZ_BITS - 7);
assert_eq!(BitPtr::<u16>::LEN_TAIL_BITS, 4);
assert_eq!(BitPtr::<u16>::PTR_DATA_MASK, !0 << 1);
assert_eq!(BitPtr::<u16>::PTR_HEAD_MASK, 1);
assert_eq!(BitPtr::<u16>::LEN_ELTS_MASK, !0 << 7);
assert_eq!(BitPtr::<u16>::LEN_TAIL_MASK, 15 << 3);
assert_eq!(BitPtr::<u16>::LEN_INDX_MASK, 127);
}
#[test]
fn associated_consts_u32() {
assert_eq!(BitPtr::<u32>::PTR_DATA_BITS, PTR_BITS - 2);
assert_eq!(BitPtr::<u32>::PTR_HEAD_BITS, 2);
assert_eq!(BitPtr::<u32>::LEN_ELTS_BITS, USZ_BITS - 8);
assert_eq!(BitPtr::<u32>::LEN_TAIL_BITS, 5);
assert_eq!(BitPtr::<u32>::PTR_DATA_MASK, !0 << 2);
assert_eq!(BitPtr::<u32>::PTR_HEAD_MASK, 3);
assert_eq!(BitPtr::<u32>::LEN_ELTS_MASK, !0 << 8);
assert_eq!(BitPtr::<u32>::LEN_TAIL_MASK, 31 << 3);
assert_eq!(BitPtr::<u32>::LEN_INDX_MASK, 255);
}
#[test]
fn associated_consts_u64() {
assert_eq!(BitPtr::<u64>::PTR_DATA_BITS, PTR_BITS - 3);
assert_eq!(BitPtr::<u64>::PTR_HEAD_BITS, 3);
assert_eq!(BitPtr::<u64>::LEN_ELTS_BITS, USZ_BITS - 9);
assert_eq!(BitPtr::<u64>::LEN_TAIL_BITS, 6);
assert_eq!(BitPtr::<u64>::PTR_DATA_MASK, !0 << 3);
assert_eq!(BitPtr::<u64>::PTR_HEAD_MASK, 7);
assert_eq!(BitPtr::<u64>::LEN_ELTS_MASK, !0 << 9);
assert_eq!(BitPtr::<u64>::LEN_TAIL_MASK, 63 << 3);
assert_eq!(BitPtr::<u64>::LEN_INDX_MASK, 511);
}
#[test]
fn ctors() {
let data: [u32; 4] = [0x756c6153, 0x2c6e6f74, 0x6e6f6d20, 0x00216f64];
let bp = BitPtr::<u32>::new(&data as *const u32, 4, 0, 32);
assert_eq!(bp.pointer(), &data as *const u32);
assert_eq!(bp.elements(), 4);
assert_eq!(*bp.head(), 0);
assert_eq!(*bp.tail(), 32);
}
#[test]
fn empty() {
let data = [0u8; 4];
assert!(BitPtr::<u8>::new(&data as *const u8, 0, 2, 4).is_empty());
}
#[test]
#[should_panic]
fn overfull() {
BitPtr::<u32>::new(8 as *const u32, BitPtr::<u32>::MAX_ELTS, 0, 32);
}
#[test]
fn patterns() {
fn bp(ptr: *const u32, len: usize) -> BitPtr<u32> {
unsafe { core::mem::transmute((ptr, len)) }
}
let ptr = NonNull::<u32>::dangling().as_ptr();
let h0t0e0 = bp(ptr, 0);
assert!(h0t0e0.is_empty());
let h0t1 = bp(ptr, 8);
assert_eq!(*h0t1.head(), 0);
assert_eq!(*h0t1.tail(), 1);
assert_eq!(h0t1.elements(), 1);
assert_eq!(h0t1.len(), 1);
let h16t17 = bp((ptr as usize | 2) as *const u32, 17 << 3);
assert_eq!(*h16t17.head(), 16);
assert_eq!(*h16t17.tail(), 17);
assert_eq!(h16t17.elements(), 1);
assert_eq!(h16t17.len(), 1);
let h0t0e1 = bp(ptr, 1 << 8);
assert_eq!(h0t0e1.elements(), 1);
assert_eq!(h0t0e1.len(), 32);
}
}