• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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