1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
//! Bit packing and pointer alignment utilities that make it easier to fit
//! additional state into an `Atom<T>`
use std::marker::PhantomData;
/// A packed pointer type that steals some bits to
/// make room for a 3-bit flag
///
/// T must be aligned to 8 bytes:
/// ```
/// # #[repr(align(8))]
/// struct MyStruct { }
/// ```
///
/// If you don't control the alignment requirements of `T`, consider using the
/// wrapper `Align8<T>` in this module instead:
/// ```
/// # use atomic_try_update::bits::{Align8, FlagPtr};
/// struct MaybeUnaligned { b: bool }
/// let ptr : FlagPtr<Align8<MaybeUnaligned>> = Default::default();
/// ```
pub struct FlagPtr<T> {
val: usize,
_phantom: PhantomData<T>,
}
impl<T> Default for FlagPtr<T> {
fn default() -> Self {
Self {
val: 0,
_phantom: Default::default(),
}
}
}
impl<T> FlagPtr<T> {
// Assuming 8 byte alignment.
const MASK: usize = 0b111;
pub fn get_ptr(&self) -> *mut T {
(self.val & !Self::MASK) as *mut T
}
/// This function panics if ptr is not 8 byte aligned.
pub fn set_ptr(&mut self, ptr: *mut T) {
let ptr = ptr as usize;
assert_eq!(ptr & Self::MASK, 0);
self.val = (ptr & !Self::MASK) | (self.val & Self::MASK);
}
pub fn get_flag(&self) -> usize {
self.val & Self::MASK
}
/// This function panics if flag is greater than seven (0b111).
pub fn set_flag(&mut self, flag: usize) {
assert_eq!(flag & !Self::MASK, 0);
self.val = (self.val & !Self::MASK) | (flag & Self::MASK);
}
}
/// Bottom bit is the flag; you get 63 bits for val.
#[derive(Default)]
pub struct FlagU64 {
val: u64,
}
impl FlagU64 {
pub fn get_val(&self) -> u64 {
self.val >> 1
}
pub fn get_flag(&self) -> bool {
(self.val & 0x1) == 1
}
pub fn set_val(&mut self, val: u64) {
self.val = (self.val & 0x1) | (val << 1); // TODO: Check for overflow!
}
pub fn set_flag(&mut self, flag: bool) {
self.val = (self.val & !0x1) | u64::from(flag)
}
}
/// Bottom bit is the flag; you get 31 bits for val.
pub struct FlagU32 {
val: u32,
}
impl FlagU32 {
pub fn get_val(&self) -> u32 {
self.val >> 1
}
pub fn get_flag(&self) -> bool {
(self.val & 0x1) == 1
}
pub fn set_val(&mut self, val: u32) {
self.val = (self.val & 0x1) | (val << 1); // TODO: Check for overflow!
}
pub fn set_flag(&mut self, flag: bool) {
self.val = (self.val & !0x1) | u32::from(flag)
}
}
/// A wrapper around an instance of T that is aligned on an eight
/// byte boundary. This allows FlagPtr to steal the bottom three
/// bits of pointers to instances of T without worrying about T's
/// alignment requirements.
///
/// TODO: Use Deref or something to make this transparently act
/// like a T?
#[repr(align(8))]
pub struct Align8<T> {
pub inner: T,
}
impl<T> From<T> for Align8<T> {
fn from(inner: T) -> Self {
Align8 { inner }
}
}