lockfree_cuckoohash/
pointer.rs

1use clippy_utilities::{Cast, OverflowArithmetic};
2use crossbeam_epoch::Guard;
3use std::marker::PhantomData;
4use std::sync::atomic::{AtomicUsize, Ordering};
5
6/// `AtomicPtr` is a pointer which can only be manipulated by
7/// atomic operations.
8#[derive(Debug)]
9pub struct AtomicPtr<T: ?Sized> {
10    /// `data` is the value of the atomic pointer.
11    data: AtomicUsize,
12    /// used for type inference.
13    _marker: PhantomData<*mut T>,
14}
15
16unsafe impl<T: ?Sized + Send + Sync> Send for AtomicPtr<T> {}
17unsafe impl<T: ?Sized + Send + Sync> Sync for AtomicPtr<T> {}
18
19impl<T> AtomicPtr<T> {
20    /// `from_usize` creates an `AtomicPtr` from `usize`.
21    const fn from_usize(data: usize) -> Self {
22        Self {
23            data: AtomicUsize::new(data),
24            _marker: PhantomData,
25        }
26    }
27
28    /// `new` creates the `value` on heap and returns its `AtomicPtr`.
29    #[allow(clippy::as_conversions)]
30    pub fn new(value: T) -> Self {
31        let b = Box::new(value);
32        let raw_ptr = Box::into_raw(b);
33        Self::from_usize(raw_ptr as usize)
34    }
35
36    /// `null` returns a `AtomicPtr` with a null pointer.
37    pub const fn null() -> Self {
38        Self::from_usize(0)
39    }
40
41    /// `load` atomically loads the pointer.
42    pub fn load<'g>(&self, ord: Ordering, _: &'g Guard) -> SharedPtr<'g, T> {
43        SharedPtr::from_usize(self.data.load(ord))
44    }
45
46    /// `compare_and_set` wraps the `compare_exchange` method of `AtomicUsize`.
47    pub fn compare_and_set<'g>(
48        &self,
49        current_ptr: SharedPtr<'_, T>,
50        new_ptr: SharedPtr<'_, T>,
51        ord: Ordering,
52        _: &'g Guard,
53    ) -> Result<SharedPtr<'g, T>, (SharedPtr<'g, T>, SharedPtr<'g, T>)> {
54        let new = new_ptr.as_usize();
55        // TODO: different ordering.
56        self.data
57            .compare_exchange(current_ptr.as_usize(), new, ord, ord)
58            .map(|_| SharedPtr::from_usize(current_ptr.as_usize()))
59            .map_err(|current| (SharedPtr::from_usize(current), SharedPtr::from_usize(new)))
60    }
61}
62
63/// `SharedPtr` is a pointer which can be shared by multi-threads.
64/// `SharedPtr` can only be used with 64bit-wide pointer, and the
65/// pointer address must be 4-byte aligned.
66pub struct SharedPtr<'g, T: 'g> {
67    /// `data` is the value of the pointers.
68    /// It will be spilt into three parts:
69    /// [higher_u8, raw_pointer, lower_u2]
70    ///    8 bits     54 bits     2 bits
71    data: usize,
72    /// used for type inference.
73    _marker: PhantomData<(&'g (), *const T)>,
74}
75
76impl<T> Clone for SharedPtr<'_, T> {
77    fn clone(&self) -> Self {
78        Self {
79            data: self.data,
80            _marker: PhantomData,
81        }
82    }
83}
84
85impl<T> Copy for SharedPtr<'_, T> {}
86
87impl<T> SharedPtr<'_, T> {
88    /// `from_usize` creates a `SharedPtr` from a `usize`.
89    pub const fn from_usize(data: usize) -> Self {
90        SharedPtr {
91            data,
92            _marker: PhantomData,
93        }
94    }
95
96    /// `from_box` creates a `SharedPtr` from a `Box`.
97    pub fn from_box(b: Box<T>) -> Self {
98        Self::from_raw(Box::into_raw(b))
99    }
100
101    /// `from_raw` creates a `SharedPtr` from a raw pointer.
102    #[allow(clippy::as_conversions)]
103    pub fn from_raw(raw: *const T) -> Self {
104        Self::from_usize(raw as usize)
105    }
106
107    /// `null` returns a null `SharedPtr`.
108    pub const fn null() -> Self {
109        Self::from_usize(0)
110    }
111
112    /// `into_box` converts the pointer into a Box<T>.
113    pub unsafe fn into_box(self) -> Box<T> {
114        Box::from_raw(self.as_mut_raw())
115    }
116
117    /// `as_usize` converts the pointer to `usize`.
118    pub const fn as_usize(self) -> usize {
119        self.data
120    }
121
122    /// `decompose_lower_u2` decomposes the pointer into two parts:
123    /// 1. the higher 62 bits
124    /// 2. the lower 2 bits
125    fn decompose_lower_u2(data: usize) -> (usize, u8) {
126        let mask: usize = 3;
127        // The unwrap is safe here, because we have mask the lower 2 bits.
128        (data & !mask, (data & mask).cast::<u8>())
129    }
130
131    /// `decompose_higher_u8` decomposes the pointer into two parts:
132    /// 1. the higher 8 bits
133    /// 2. the lower 56 bits
134    fn decompose_higher_u8(data: usize) -> (u8, usize) {
135        let mask: usize = (1_usize.overflowing_shl(56).0).overflow_sub(1);
136        // The conversion is safe here, because we have shifted 56 bits.
137        (data.overflow_shr(56).cast::<u8>(), data & mask)
138    }
139
140    /// `decompose` decomposes the pointer into three parts:
141    /// 1. the higher 8 bits
142    /// 2. the raw pointer
143    /// 3. the lower 2 bits
144    #[allow(clippy::as_conversions)]
145    pub fn decompose(self) -> (u8, *const T, u8) {
146        let data = self.data;
147        let (higher_u62, lower_u2) = Self::decompose_lower_u2(data);
148        let (higher_u8, raw_ptr) = Self::decompose_higher_u8(higher_u62);
149        (higher_u8, raw_ptr as *const T, lower_u2)
150    }
151
152    /// `as_raw` extracts the raw pointer.
153    pub fn as_raw(self) -> *const T {
154        let (_, raw, _) = self.decompose();
155        raw
156    }
157
158    /// `as_mut_raw` extracts the mutable raw pointer.
159    #[allow(clippy::as_conversions)]
160    pub fn as_mut_raw(self) -> *mut T {
161        let const_raw = self.as_raw();
162        const_raw as *mut T
163    }
164
165    /// `with_lower_u2` resets the lower 2 bits of the pointer.
166    pub fn with_lower_u2(self, lower_u8: u8) -> Self {
167        let mask: usize = 3;
168        // Convert a u8 to usize is always safe.
169        Self::from_usize(self.data & !mask | lower_u8.cast::<usize>())
170    }
171
172    /// `with_higher_u8` resets the higher 8 bits of pointer.
173    pub fn with_higher_u8(self, higher_u8: u8) -> Self {
174        let data = self.data;
175        let mask: usize = (1_usize.overflowing_shl(56).0).overflow_sub(1);
176        // Convert a u8 to usize is always safe.
177        Self::from_usize((data & mask) | ((higher_u8.cast::<usize>()).overflowing_shl(56).0))
178    }
179}