intrusive_collections/
adapter.rs

1// Copyright 2016 Amanieu d'Antras
2// Copyright 2020 Amari Robinson
3//
4// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
5// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6// http://opensource.org/licenses/MIT>, at your option. This file may not be
7// copied, modified, or distributed except according to those terms.
8
9use crate::link_ops::LinkOps;
10use crate::pointer_ops::PointerOps;
11
12/// Trait for a adapter which allows a type to be inserted into an intrusive
13/// collection.
14///
15/// `LinkOps` implements the collection-specific operations which
16/// allows an object to be inserted into an intrusive collection. This type
17/// needs to implement the appropriate trait for the collection type
18/// (eg. `LinkedListOps` for inserting into a `LinkedList`).
19/// `LinkOps` type may be stateful, allowing custom link types.
20///
21/// `PointerOps` implements the collection-specific pointer conversions which
22/// allow an object to be inserted into an intrusive collection.
23/// `PointerOps` type may be stateful, allowing custom pointer types.
24///
25/// A single object type may have multiple adapters, which allows it to be part
26/// of multiple intrusive collections simultaneously.
27///
28/// In most cases you do not need to implement this trait manually: the
29/// `intrusive_adapter!` macro will generate the necessary implementation for a
30/// given type and its link field. However it is possible to implement it
31/// manually if the intrusive link is not a direct field of the object type.
32///
33/// It is also possible to create stateful adapters.
34/// This allows links and containers to be separated and avoids the need for objects to be modified to
35/// contain a link.
36///
37/// # Safety
38///
39/// It must be possible to get back a reference to the container by passing a
40/// pointer returned by `get_link` to `get_container`.
41pub unsafe trait Adapter {
42    /// Collection-specific link operations which allow an object to be inserted in
43    /// an intrusive collection.
44    type LinkOps: LinkOps;
45
46    /// Collection-specific pointer conversions which allow an object to
47    /// be inserted in an intrusive collection.
48    type PointerOps: PointerOps;
49
50    /// Gets a reference to an object from a reference to a link in that object.
51    ///
52    /// # Safety
53    ///
54    /// `link` must be a valid pointer previously returned by `get_link`.
55    unsafe fn get_value(
56        &self,
57        link: <Self::LinkOps as LinkOps>::LinkPtr,
58    ) -> *const <Self::PointerOps as PointerOps>::Value;
59
60    /// Gets a reference to the link for the given object.
61    ///
62    /// # Safety
63    ///
64    /// `value` must be a valid pointer.
65    unsafe fn get_link(
66        &self,
67        value: *const <Self::PointerOps as PointerOps>::Value,
68    ) -> <Self::LinkOps as LinkOps>::LinkPtr;
69
70    /// Returns a reference to the link operations.
71    fn link_ops(&self) -> &Self::LinkOps;
72
73    /// Returns a reference to the mutable link operations.
74    fn link_ops_mut(&mut self) -> &mut Self::LinkOps;
75
76    /// Returns a reference to the pointer converter.
77    fn pointer_ops(&self) -> &Self::PointerOps;
78}
79
80/// Unsafe macro to get a raw pointer to an outer object from a pointer to one
81/// of its fields.
82///
83/// # Examples
84///
85/// ```
86/// use intrusive_collections::container_of;
87///
88/// struct S { x: u32, y: u32 };
89/// let container = S { x: 1, y: 2 };
90/// let field = &container.x;
91/// let container2: *const S = unsafe { container_of!(field, S, x) };
92/// assert_eq!(&container as *const S, container2);
93/// ```
94///
95/// # Safety
96///
97/// This is unsafe because it assumes that the given expression is a valid
98/// pointer to the specified field of some container type.
99#[macro_export]
100macro_rules! container_of {
101    ($ptr:expr, $container:path, $field:ident) => {
102        #[allow(clippy::cast_ptr_alignment)]
103        {
104            ($ptr as *const _ as *const u8).sub($crate::offset_of!($container, $field))
105                as *const $container
106        }
107    };
108}
109
110/// Macro to generate an implementation of `Adapter` for a given set of types.
111/// In particular this will automatically generate implementations of the
112/// `get_value` and `get_link` methods for a given named field in a struct.
113///
114/// The basic syntax to create an adapter is:
115///
116/// ```rust,ignore
117/// intrusive_adapter!(Adapter = Pointer: Value { link_field: LinkType });
118/// ```
119///
120/// You can create a new instance of an adapter using the `new` method or the
121/// `NEW` associated constant. The adapter also implements the `Default` trait.
122///
123/// # Generics
124///
125/// This macro supports generic arguments:
126///
127/// ```rust,ignore
128/// intrusive_adapter!(
129///     Adapter<'lifetime, Type, Type2> =
130///         Pointer: Value {
131///             link_field: LinkType
132///         }
133///         where
134///             Type: Copy,
135///             Type2: ?Sized + 'lifetime
136///     );
137/// ```
138///
139/// Note that due to macro parsing limitations, `T: Trait` bounds are not
140/// supported in the generic argument list. You must list any trait bounds in
141/// a separate `where` clause at the end of the macro.
142///
143/// # Examples
144///
145/// ```
146/// use intrusive_collections::{LinkedListLink, RBTreeLink};
147/// use intrusive_collections::intrusive_adapter;
148///
149/// pub struct Test {
150///     link: LinkedListLink,
151///     link2: RBTreeLink,
152/// }
153/// intrusive_adapter!(MyAdapter = Box<Test>: Test { link: LinkedListLink });
154/// intrusive_adapter!(pub MyAdapter2 = Box<Test>: Test { link2: RBTreeLink });
155/// intrusive_adapter!(pub(crate) MyAdapter3 = Box<Test>: Test { link2: RBTreeLink });
156///
157/// pub struct Test2<T>
158///     where T: Clone + ?Sized
159/// {
160///     link: LinkedListLink,
161///     val: T,
162/// }
163/// intrusive_adapter!(MyAdapter4<'a, T> = &'a Test2<T>: Test2<T> { link: LinkedListLink } where T: ?Sized + Clone + 'a);
164/// ```
165#[macro_export]
166macro_rules! intrusive_adapter {
167    (@impl
168        $(#[$attr:meta])* $vis:vis $name:ident ($($args:tt),*)
169        = $pointer:ty: $value:path { $field:ident: $link:ty } $($where_:tt)*
170    ) => {
171        #[allow(explicit_outlives_requirements)]
172        $(#[$attr])*
173        $vis struct $name<$($args),*> $($where_)* {
174            link_ops: <$link as $crate::DefaultLinkOps>::Ops,
175            pointer_ops: $crate::DefaultPointerOps<$pointer>,
176        }
177        unsafe impl<$($args),*> Send for $name<$($args),*> $($where_)* {}
178        unsafe impl<$($args),*> Sync for $name<$($args),*> $($where_)* {}
179        impl<$($args),*> Copy for $name<$($args),*> $($where_)* {}
180        impl<$($args),*> Clone for $name<$($args),*> $($where_)* {
181            #[inline]
182            fn clone(&self) -> Self {
183                *self
184            }
185        }
186        impl<$($args),*> Default for $name<$($args),*> $($where_)* {
187            #[inline]
188            fn default() -> Self {
189                Self::NEW
190            }
191        }
192        #[allow(dead_code)]
193        impl<$($args),*> $name<$($args),*> $($where_)* {
194            pub const NEW: Self = $name {
195                link_ops: <$link as $crate::DefaultLinkOps>::NEW,
196                pointer_ops: $crate::DefaultPointerOps::<$pointer>::new(),
197            };
198            #[inline]
199            pub fn new() -> Self {
200                Self::NEW
201            }
202        }
203        #[allow(dead_code, unsafe_code)]
204        unsafe impl<$($args),*> $crate::Adapter for $name<$($args),*> $($where_)* {
205            type LinkOps = <$link as $crate::DefaultLinkOps>::Ops;
206            type PointerOps = $crate::DefaultPointerOps<$pointer>;
207
208            #[inline]
209            unsafe fn get_value(&self, link: <Self::LinkOps as $crate::LinkOps>::LinkPtr) -> *const <Self::PointerOps as $crate::PointerOps>::Value {
210                $crate::container_of!(link.as_ptr(), $value, $field)
211            }
212            #[inline]
213            unsafe fn get_link(&self, value: *const <Self::PointerOps as $crate::PointerOps>::Value) -> <Self::LinkOps as $crate::LinkOps>::LinkPtr {
214                // We need to do this instead of just accessing the field directly
215                // to strictly follow the stack borrow rules.
216                let ptr = (value as *const u8).add($crate::offset_of!($value, $field));
217                core::ptr::NonNull::new_unchecked(ptr as *mut _)
218            }
219            #[inline]
220            fn link_ops(&self) -> &Self::LinkOps {
221                &self.link_ops
222            }
223            #[inline]
224            fn link_ops_mut(&mut self) -> &mut Self::LinkOps {
225                &mut self.link_ops
226            }
227            #[inline]
228            fn pointer_ops(&self) -> &Self::PointerOps {
229                &self.pointer_ops
230            }
231        }
232    };
233    (@find_generic
234        $(#[$attr:meta])* $vis:vis $name:ident ($($prev:tt)*) > $($rest:tt)*
235    ) => {
236        intrusive_adapter!(@impl
237            $(#[$attr])* $vis $name ($($prev)*) $($rest)*
238        );
239    };
240    (@find_generic
241        $(#[$attr:meta])* $vis:vis $name:ident ($($prev:tt)*) $cur:tt $($rest:tt)*
242    ) => {
243        intrusive_adapter!(@find_generic
244            $(#[$attr])* $vis $name ($($prev)* $cur) $($rest)*
245        );
246    };
247    (@find_if_generic
248        $(#[$attr:meta])* $vis:vis $name:ident < $($rest:tt)*
249    ) => {
250        intrusive_adapter!(@find_generic
251            $(#[$attr])* $vis $name () $($rest)*
252        );
253    };
254    (@find_if_generic
255        $(#[$attr:meta])* $vis:vis $name:ident $($rest:tt)*
256    ) => {
257        intrusive_adapter!(@impl
258            $(#[$attr])* $vis $name () $($rest)*
259        );
260    };
261    ($(#[$attr:meta])* $vis:vis $name:ident $($rest:tt)*) => {
262        intrusive_adapter!(@find_if_generic
263            $(#[$attr])* $vis $name $($rest)*
264        );
265    };
266}
267
268#[cfg(test)]
269mod tests {
270    use crate::LinkedListLink;
271    use std::rc::Rc;
272
273    struct Obj {
274        link: LinkedListLink,
275    }
276
277    intrusive_adapter! {
278        /// Test doc comment
279        ObjAdapter1 = Rc<Obj>: Obj { link: LinkedListLink }
280    }
281}