• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #![allow(dead_code)]
15 use core::cell::UnsafeCell;
16 use core::marker::PhantomData;
17 use core::ptr::NonNull;
18 
19 // Intrusive link structures are particularly tricky in Rust because mutable
20 // references are expected to be globally unique.  Accessing the data through
21 // other methods is UB.  There is a good writeup of Tokio's soundness challenges
22 // at: https://gist.github.com/Darksonn/1567538f56af1a8038ecc3c664a42462.
23 //
24 // Here we adapt the strategy that Tokio uses:
25 // * The link pointer structure is marked with `PhantomPinned`.  This does two
26 //   things.  First it "poisons" the containing structure so that it's
27 //   unpinnable (immovable).  Second it triggers the heuristic from
28 //   https://github.com/rust-lang/rust/pull/82834 which will cause the compiler
29 //   to omit emitting a `noalias` annotation on mutable references to disable
30 //   compiler optimization that assume the mutable reference is unique.
31 // * `next` and `prev` themselves are accessed by `Link` through direct pointer
32 //   math on the `LinkInner` struct.  This avoids creating mutable references to
33 //   those fields themselves which can not be annotated with `PhantomPinned`.
34 //   `LinkInner` is `#[repr(C)]` to make the pointer math deterministic.
35 //   `LinkInner` is declared in an `inner` module so that `next` and `prev` can
36 //   not be accessed directly be the rest of the code.
37 //
38 // TODO: konkers - Understand if we need to annotate the alignment of LinkInner.
39 mod inner {
40     use core::{marker::PhantomPinned, mem::offset_of, ptr::NonNull};
41 
42     use super::Link;
43 
44     #[repr(C)]
45     pub struct LinkInner {
46         #[allow(dead_code)]
47         next: Option<NonNull<Link>>,
48         #[allow(dead_code)]
49         prev: Option<NonNull<Link>>,
50         _pin: PhantomPinned,
51     }
52 
53     impl LinkInner {
54         pub const NEXT_OFFSET: usize = offset_of!(LinkInner, next);
55         pub const PREV_OFFSET: usize = offset_of!(LinkInner, prev);
56 
new() -> Self57         pub const fn new() -> Self {
58             Self {
59                 next: None,
60                 prev: None,
61                 _pin: PhantomPinned,
62             }
63         }
64     }
65 }
66 use inner::LinkInner;
67 
68 pub struct Link {
69     // UnsafeCell here is used to allow the code to access the data mutably.
70     // Bare mutable pointer access is unsound without this.
71     inner: UnsafeCell<LinkInner>,
72 }
73 
74 #[inline]
get_element(inner: &UnsafeCell<LinkInner>, offset: usize) -> Option<NonNull<Link>>75 unsafe fn get_element(inner: &UnsafeCell<LinkInner>, offset: usize) -> Option<NonNull<Link>> {
76     let inner_ptr = inner.get() as *const Option<NonNull<Link>>;
77     let element_ptr = inner_ptr.byte_add(offset);
78     core::ptr::read(element_ptr)
79 }
80 
81 #[inline]
set_element(inner: &UnsafeCell<LinkInner>, offset: usize, value: Option<NonNull<Link>>)82 unsafe fn set_element(inner: &UnsafeCell<LinkInner>, offset: usize, value: Option<NonNull<Link>>) {
83     let inner_ptr = inner.get() as *mut Option<NonNull<Link>>;
84     let element_ptr = inner_ptr.byte_add(offset);
85     core::ptr::write(element_ptr, value);
86 }
87 
88 impl Link {
new() -> Self89     pub const fn new() -> Self {
90         Self {
91             inner: UnsafeCell::new(LinkInner::new()),
92         }
93     }
94 
is_unlinked(&self) -> bool95     pub fn is_unlinked(&self) -> bool {
96         self.get_next().is_none() && self.get_prev().is_none()
97     }
98 
is_linked(&self) -> bool99     pub fn is_linked(&self) -> bool {
100         !self.is_unlinked()
101     }
102 
103     #[inline]
get_next(&self) -> Option<NonNull<Link>>104     fn get_next(&self) -> Option<NonNull<Link>> {
105         unsafe { get_element(&self.inner, LinkInner::NEXT_OFFSET) }
106     }
107 
108     #[inline]
set_next(&mut self, value: Option<NonNull<Link>>)109     fn set_next(&mut self, value: Option<NonNull<Link>>) {
110         unsafe { set_element(&self.inner, LinkInner::NEXT_OFFSET, value) }
111     }
112 
113     #[inline]
get_prev(&self) -> Option<NonNull<Link>>114     fn get_prev(&self) -> Option<NonNull<Link>> {
115         unsafe { get_element(&self.inner, LinkInner::PREV_OFFSET) }
116     }
117 
118     #[inline]
set_prev(&mut self, value: Option<NonNull<Link>>)119     fn set_prev(&mut self, value: Option<NonNull<Link>>) {
120         unsafe { set_element(&self.inner, LinkInner::PREV_OFFSET, value) }
121     }
122 }
123 
124 impl Default for Link {
default() -> Self125     fn default() -> Self {
126         Self::new()
127     }
128 }
129 
130 // `UnsafeList` uses None to mark the beginning and end of lists as opposed to
131 // pointers to the base list node.  This means that there are never pointers to
132 // `UnsafeList` and the same care is not needed to avoid mutable references as
133 // is taken with the `Link` structure.
134 pub struct UnsafeList<T, A: Adapter> {
135     head: Option<NonNull<Link>>,
136     tail: Option<NonNull<Link>>,
137     _phantom_type: PhantomData<T>,
138     _phantom_adapter: PhantomData<A>,
139 }
140 
141 pub trait Adapter {
142     const LINK_OFFSET: usize;
143 }
144 
145 impl<T, A: Adapter> UnsafeList<T, A> {
new() -> Self146     pub const fn new() -> Self {
147         Self {
148             head: None,
149             tail: None,
150             _phantom_type: PhantomData,
151             _phantom_adapter: PhantomData,
152         }
153     }
154 
155     /// # Safety
156     /// It is up to the caller to ensure exclusive access to the list and its
157     /// members.
is_empty(&self) -> bool158     pub unsafe fn is_empty(&self) -> bool {
159         self.head.is_none()
160     }
161 
get_link_ptr(element: NonNull<T>) -> NonNull<Link>162     unsafe fn get_link_ptr(element: NonNull<T>) -> NonNull<Link> {
163         let element_ptr: NonNull<Link> = core::mem::transmute::<NonNull<T>, NonNull<Link>>(element);
164         element_ptr.byte_add(A::LINK_OFFSET)
165     }
166 
get_element_ptr(link: NonNull<Link>) -> *const T167     unsafe fn get_element_ptr(link: NonNull<Link>) -> *const T {
168         link.byte_sub(A::LINK_OFFSET).as_ptr() as *const T
169     }
170 
get_element_mut(link: NonNull<Link>) -> *mut T171     unsafe fn get_element_mut(link: NonNull<Link>) -> *mut T {
172         link.byte_sub(A::LINK_OFFSET).as_ptr() as *mut T
173     }
174 
175     /// unchecked means:
176     /// * we don't `assert!((*element_ptr.as_ptr()).is_unlinked());`
177     /// * we don't check that `element` is non-null.
178     ///
179     /// # Safety
180     /// It is up to the caller to ensure exclusive access to the list and its
181     /// members.
182     /// It is up to the caller to ensure the element is not in a list.
183     /// It is up to the caller to ensure the element is non-null.
push_front_unchecked(&mut self, element: *mut T)184     pub unsafe fn push_front_unchecked(&mut self, element: *mut T) {
185         let element = NonNull::new_unchecked(element);
186         let link_ptr = Self::get_link_ptr(element);
187 
188         // Link up the added element.
189         (*link_ptr.as_ptr()).set_next(self.head);
190         (*link_ptr.as_ptr()).set_prev(None);
191 
192         match self.head {
193             // If `head` was `None`, the list is empty and `tail` should point
194             // to the added element.
195             None => self.tail = Some(link_ptr),
196 
197             // If `head` is not `None`, point the previous `head` to the added
198             // element.
199             Some(head) => (*head.as_ptr()).set_prev(Some(link_ptr)),
200         }
201 
202         // Finally point `head` to the added element.
203         self.head = Some(link_ptr);
204     }
205 
206     /// unchecked means:
207     /// * we don't `assert!((*element_ptr.as_ptr()).is_unlinked());`
208     /// * we don't check that `element` is non-null.
209     ///
210     /// # Safety
211     /// It is up to the caller to ensure exclusive access to the list and its
212     /// members.
213     /// It is up to the caller to ensure the element is not in a list.
214     /// It is up to the caller to ensure the element is non-null.
push_back_unchecked(&mut self, element: *mut T)215     pub unsafe fn push_back_unchecked(&mut self, element: *mut T) {
216         let element = NonNull::new_unchecked(element);
217         let link_ptr = Self::get_link_ptr(element);
218 
219         // Link up the added element.
220         (*link_ptr.as_ptr()).set_next(None);
221         (*link_ptr.as_ptr()).set_prev(self.tail);
222 
223         match self.tail {
224             // If `tail` was `None`, the list is empty and `head` should point
225             // to the added element.
226             None => self.head = Some(link_ptr),
227 
228             // If `tail` is not `None`, point the previous `tail` to the added
229             // element.
230             Some(tail) => (*tail.as_ptr()).set_next(Some(link_ptr)),
231         }
232 
233         // Finally point `tail` to the added element.
234         self.tail = Some(link_ptr);
235     }
236 
237     /// unlinks element from the linked list.
238     ///
239     /// # Safety
240     /// It is up to the caller to ensure exclusive access to the list and its
241     /// members.
242     /// It is up to the caller to ensure the element is in the list
243     /// It is up to the caller to ensure the element is non-null.
unlink_element(&mut self, element: *mut T)244     pub unsafe fn unlink_element(&mut self, element: *mut T) {
245         let element = NonNull::new_unchecked(element);
246         let link_ptr = Self::get_link_ptr(element);
247 
248         let prev = (*link_ptr.as_ptr()).get_prev();
249         let next = (*link_ptr.as_ptr()).get_next();
250 
251         match prev {
252             // Element is at the head of the list
253             None => self.head = next,
254 
255             // Element has elements before it in the list.
256             Some(prev_ptr) => (*prev_ptr.as_ptr()).set_next(next),
257         }
258 
259         match next {
260             // Element is at the tail of the list
261             None => self.tail = prev,
262 
263             // Element has elements after it in the list.
264             Some(next_ptr) => (*next_ptr.as_ptr()).set_prev(prev),
265         }
266 
267         (*link_ptr.as_ptr()).set_next(None);
268         (*link_ptr.as_ptr()).set_prev(None);
269     }
270 
271     /// # Safety
272     /// It is up to the caller to ensure exclusive access to the list and its
273     /// members.
for_each<E, F: FnMut(&T) -> Result<(), E>>( &self, mut callback: F, ) -> Result<(), E>274     pub unsafe fn for_each<E, F: FnMut(&T) -> Result<(), E>>(
275         &self,
276         mut callback: F,
277     ) -> Result<(), E> {
278         let mut cur = self.head;
279 
280         loop {
281             let Some(cur_ptr) = cur else {
282                 break;
283             };
284 
285             let element = Self::get_element_ptr(cur_ptr);
286             callback(&*element)?;
287 
288             cur = (*cur_ptr.as_ptr()).get_next();
289         }
290 
291         Ok(())
292     }
293 
294     /// Filter iterates over every element in the list calling `callback` on
295     /// each one.  If `callback` returns false, the element will be removed
296     /// from the list without modifying the element itself.  It is safe to
297     /// add the element to the another linked list within `callback` if it
298     /// returns false.
299     ///
300     /// # Safety
301     /// It is up to the caller to ensure exclusive access to the list and its
302     /// members.
filter<F: FnMut(&mut T) -> bool>(&mut self, mut callback: F)303     pub unsafe fn filter<F: FnMut(&mut T) -> bool>(&mut self, mut callback: F) {
304         let mut cur = self.head;
305 
306         loop {
307             let Some(cur_ptr) = cur else {
308                 break;
309             };
310 
311             let element = Self::get_element_mut(cur_ptr);
312 
313             // Cache the next element so that we don't rely on `element` staying
314             // coherent across calls to `callback`.
315             let next = (*cur_ptr.as_ptr()).get_next();
316 
317             if !callback(&mut *element) {
318                 self.unlink_element(element);
319             }
320 
321             cur = next;
322         }
323     }
324 
325     /// Return a reference to the first element in the list, clearing the
326     /// prev/next fields in the element.
327     ///
328     /// TODO: reevalulate the lifetime marker here, since it is a lie.
329     ///
330     /// # Safety
331     /// It is up to the caller to ensure exclusive access to the list and its
332     /// members.
pop_head(&mut self) -> Option<*mut T>333     pub unsafe fn pop_head(&mut self) -> Option<*mut T> {
334         let cur = self.head?;
335 
336         let element = Self::get_element_mut(cur);
337 
338         self.unlink_element(element);
339         Some(element)
340     }
341 }
342 
343 impl<T, A: Adapter> Default for UnsafeList<T, A> {
default() -> Self344     fn default() -> Self {
345         Self::new()
346     }
347 }
348