use std::{marker::PhantomData, mem};
#[repr(transparent)]
#[derive(Debug)]
pub struct PointerValuePair<T> {
repr: usize,
_phantom: PhantomData<T>,
}
impl<T> Copy for PointerValuePair<T> {}
impl<T> Clone for PointerValuePair<T> {
fn clone(&self) -> Self {
PointerValuePair {
repr: self.repr,
_phantom: PhantomData,
}
}
}
const fn align_bits_mask<T>() -> usize {
mem::align_of::<T>() - 1
}
impl<T> PointerValuePair<T> {
pub fn new(ptr: *const T, value: usize) -> PointerValuePair<T> {
let m = align_bits_mask::<T>();
assert!(
value <= m,
"not enough alignment bits ({}) to store the value ({})",
Self::available_bits(),
value
);
let mut repr = ptr as usize;
repr |= value;
PointerValuePair {
repr,
_phantom: PhantomData,
}
}
pub const fn available_bits() -> u32 {
align_bits_mask::<T>().count_ones()
}
pub const fn max_value() -> usize {
align_bits_mask::<T>()
}
pub const fn ptr(self) -> *const T {
(self.repr & !align_bits_mask::<T>()) as *const T
}
pub const fn value(self) -> usize {
self.repr & align_bits_mask::<T>()
}
}
#[cfg(test)]
mod tests {
use std::mem;
use super::PointerValuePair;
#[test]
fn pointer_sized() {
assert_eq!(mem::size_of::<*const i32>(), mem::size_of::<PointerValuePair<i32>>());
}
#[test]
fn basic_get_set() {
let pointee = 42usize;
let pv = PointerValuePair::new(&pointee, 3);
assert_eq!(pv.ptr(), &pointee as *const _);
let p_val = unsafe { *pv.ptr() };
assert_eq!(p_val, 42usize);
assert_eq!(pv.value(), 3);
}
#[test]
fn custom_alignments() {
#[repr(C, align(8))]
struct Align8(i32);
assert!(PointerValuePair::<Align8>::max_value() >= 0x7);
assert!(PointerValuePair::<Align8>::available_bits() >= 3);
#[repr(C, align(16))]
struct Align16(i32);
assert!(PointerValuePair::<Align16>::max_value() >= 0xF);
assert!(PointerValuePair::<Align16>::available_bits() >= 4);
#[repr(C, align(32))]
struct Align32(i32);
assert!(PointerValuePair::<Align32>::max_value() >= 0x1F);
assert!(PointerValuePair::<Align32>::available_bits() >= 5);
}
}