toolbox_rs/unsafe_slice.rs
1use std::cell::UnsafeCell;
2
3/// This struct serves as a wrapper around unsafe accesses to a vector of
4/// elements.
5///
6/// This can be used if (and only if) we know (and the compiler doesn't) that
7/// threads do not access the same index during the concurrent processing.
8///
9/// DO NOT REUSE IN YOUR PROJECTS!
10///
11#[derive(Copy, Clone)]
12pub struct UnsafeSlice<'a, T> {
13 slice: &'a [UnsafeCell<T>],
14}
15unsafe impl<T: Send + Sync> Send for UnsafeSlice<'_, T> {}
16unsafe impl<T: Send + Sync> Sync for UnsafeSlice<'_, T> {}
17
18impl<'a, T> UnsafeSlice<'a, T> {
19 pub fn new(slice: &'a mut [T]) -> Self {
20 let ptr = slice as *mut [T] as *const [UnsafeCell<T>];
21 Self {
22 slice: unsafe { &*ptr },
23 }
24 }
25
26 /// # Safety
27 /// Two threads concurrently writing to the same location will cause UB!!
28 #[allow(clippy::mut_from_ref)]
29 pub unsafe fn get_mut(&self, index: usize) -> &mut T {
30 unsafe { &mut *self.slice[index].get() }
31 }
32
33 /// Returns a shared reference to the element at the given index.
34 ///
35 /// This function is safe to call because it returns an immutable reference,
36 /// which can be shared between multiple threads. However, it's important to note
37 /// that this safety relies on the exclusive access guarantee provided by the
38 /// mutable reference passed to `UnsafeSlice::new()`.
39 ///
40 /// # Arguments
41 ///
42 /// * `index` - Position of the element to access
43 ///
44 /// # Returns
45 ///
46 /// Reference to the element at `index`
47 ///
48 /// # Panics
49 ///
50 /// Panics if `index` is out of bounds
51 ///
52 /// # Examples
53 ///
54 /// ```
55 /// # use toolbox_rs::unsafe_slice::UnsafeSlice;
56 /// let mut data = vec![1, 2, 3];
57 /// let slice = UnsafeSlice::new(&mut data);
58 /// assert_eq!(*slice.get(0), 1);
59 /// ```
60 pub fn get(&self, index: usize) -> &T {
61 unsafe { &mut *self.slice[index].get() }
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::UnsafeSlice;
68
69 #[test]
70 fn instantiate() {
71 let mut data = vec![0, 1, 23, 83, 38, 3, 8947, 2762];
72 let slice = UnsafeSlice::new(&mut data);
73 assert_eq!(*slice.get(0), 0);
74 assert_eq!(*slice.get(1), 1);
75 assert_eq!(*slice.get(2), 23);
76 assert_eq!(*slice.get(3), 83);
77 assert_eq!(*slice.get(4), 38);
78 assert_eq!(*slice.get(5), 3);
79 assert_eq!(*slice.get(6), 8947);
80 assert_eq!(*slice.get(7), 2762);
81 }
82
83 #[test]
84 fn test_get_mut() {
85 let mut data = vec![1, 2, 3, 4];
86 let slice = UnsafeSlice::new(&mut data);
87
88 // SAFETY: We're only accessing each index once
89 // and not sharing the slice between threads
90 unsafe {
91 *slice.get_mut(0) = 10;
92 *slice.get_mut(2) = 30;
93 }
94
95 assert_eq!(*slice.get(0), 10);
96 assert_eq!(*slice.get(1), 2);
97 assert_eq!(*slice.get(2), 30);
98 assert_eq!(*slice.get(3), 4);
99
100 // Verify we can read the modified values
101 assert_eq!(data[0], 10);
102 assert_eq!(data[2], 30);
103 }
104}