1 use std::{
2 hash::{Hash, Hasher},
3 mem,
4 ops::Deref,
5 ptr::{self, NonNull},
6 };
7
8 /// A wrapper around pointers to C data type which are considered thread safe.
9 #[derive(Eq)]
10 pub struct ThreadSafeCPtr<T>(NonNull<T>);
11
12 impl<T> ThreadSafeCPtr<T> {
13 /// # Safety
14 ///
15 /// Only safe on `T` which are thread-safe C data types. That usually means the following:
16 /// * Fields are accessed in a thread-safe manner, either through atomic operations or
17 /// functions
18 /// * Bugs and Data races caused by accessing the type in multiple threads is considered a bug.
19 ///
20 /// As nothing of this can actually be verified this solely relies on contracts made on those
21 /// types, either by a specification or by convention. In practical terms this means that a
22 /// pointer to `T` meets all requirements expected by [Send] and [Sync]
new(ptr: *mut T) -> Option<Self>23 pub unsafe fn new(ptr: *mut T) -> Option<Self> {
24 Some(Self(NonNull::new(ptr)?))
25 }
26 }
27
28 impl<T> Deref for ThreadSafeCPtr<T> {
29 type Target = NonNull<T>;
30
deref(&self) -> &Self::Target31 fn deref(&self) -> &Self::Target {
32 &self.0
33 }
34 }
35
36 impl<T> Hash for ThreadSafeCPtr<T> {
hash<H: Hasher>(&self, state: &mut H)37 fn hash<H: Hasher>(&self, state: &mut H) {
38 self.0.as_ptr().hash(state)
39 }
40 }
41
42 impl<T> PartialEq for ThreadSafeCPtr<T> {
eq(&self, other: &Self) -> bool43 fn eq(&self, other: &Self) -> bool {
44 self.0.as_ptr() == other.0.as_ptr()
45 }
46 }
47
48 // SAFETY: safety requierements of Send fullfilled at [ThreadSafeCPtr::new] time
49 unsafe impl<T> Send for ThreadSafeCPtr<T> {}
50
51 // SAFETY: safety requierements of Sync fullfilled at [ThreadSafeCPtr::new] time
52 unsafe impl<T> Sync for ThreadSafeCPtr<T> {}
53
54 pub trait CheckedPtr<T> {
55 /// # Safety
56 ///
57 /// besides a null check the function can't make sure the pointer is valid
58 /// for the entire size
copy_checked(self, val: *const T, size: usize)59 unsafe fn copy_checked(self, val: *const T, size: usize);
write_checked(self, val: T)60 fn write_checked(self, val: T);
61 }
62
63 impl<T> CheckedPtr<T> for *mut T {
64 /// # Safety
65 ///
66 /// This function follows the same safety rules as `std::ptr::copy` except that it already
67 /// checks for a NULL pointer.
copy_checked(self, val: *const T, size: usize)68 unsafe fn copy_checked(self, val: *const T, size: usize) {
69 if !self.is_null() {
70 // SAFETY: we move the responsibilities up to the caller
71 unsafe {
72 ptr::copy(val, self, size);
73 }
74 }
75 }
76
write_checked(self, val: T)77 fn write_checked(self, val: T) {
78 if !self.is_null() {
79 unsafe {
80 *self = val;
81 }
82 }
83 }
84 }
85
86 // from https://internals.rust-lang.org/t/discussion-on-offset-of/7440/2
87 #[macro_export]
88 macro_rules! offset_of {
89 ($Struct:path, $($field:ident).+ $(,)?) => {{
90 // Using a separate function to minimize unhygienic hazards
91 // (e.g. unsafety of #[repr(packed)] field borrows).
92 // Uncomment `const` when `const fn`s can juggle pointers.
93 /*const*/
94 fn offset() -> usize {
95 let u = std::mem::MaybeUninit::<$Struct>::uninit();
96 let f = unsafe { &(*u.as_ptr()).$($field).+ };
97 let o = (f as *const _ as usize).wrapping_sub(&u as *const _ as usize);
98 // Triple check that we are within `u` still.
99 assert!((0..=std::mem::size_of_val(&u)).contains(&o));
100 o
101 }
102 offset()
103 }};
104 }
105
106 // Adapted from libstd since std::ptr::is_aligned is still unstable
107 // See https://github.com/rust-lang/rust/issues/96284
108 #[must_use]
109 #[inline]
is_aligned<T>(ptr: *const T) -> bool where T: Sized,110 pub const fn is_aligned<T>(ptr: *const T) -> bool
111 where
112 T: Sized,
113 {
114 let align = mem::align_of::<T>();
115 addr(ptr) & (align - 1) == 0
116 }
117
118 // Adapted from libstd since std::ptr::addr is still unstable
119 // See https://github.com/rust-lang/rust/issues/95228
120 #[must_use]
121 #[inline(always)]
addr<T>(ptr: *const T) -> usize122 pub const fn addr<T>(ptr: *const T) -> usize {
123 // The libcore implementations of `addr` and `expose_addr` suggest that, while both transmuting
124 // and casting to usize will give you the address of a ptr in the end, they are not identical
125 // in their side-effects.
126 // A cast "exposes" a ptr, which can potentially cause the compiler to optimize less
127 // aggressively around it.
128 // Let's trust the libcore devs over clippy on whether a transmute also exposes a ptr.
129 #[allow(clippy::transmutes_expressible_as_ptr_casts)]
130 // SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
131 // provenance).
132 unsafe {
133 mem::transmute(ptr.cast::<()>())
134 }
135 }
136