atomic_try_update/
bits.rs

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