foyer_intrusive/
adapter.rs

1//  Copyright 2024 foyer Project Authors
2//
3//  Licensed under the Apache License, Version 2.0 (the "License");
4//  you may not use this file except in compliance with the License.
5//  You may obtain a copy of the License at
6//
7//  http://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,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14
15//  Copyright 2020 Amari Robinson
16//
17//  Licensed under the Apache License, Version 2.0 (the "License");
18//  you may not use this file except in compliance with the License.
19//  You may obtain a copy of the License at
20//
21//  http://www.apache.org/licenses/LICENSE-2.0
22//
23//  Unless required by applicable law or agreed to in writing, software
24//  distributed under the License is distributed on an "AS IS" BASIS,
25//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26//  See the License for the specific language governing permissions and
27//  limitations under the License.
28
29//! Intrusive data structure adapter that locates between pointer and item.
30
31use std::fmt::Debug;
32
33/// Intrusive data structure link.
34pub trait Link: Send + Sync + 'static + Default + Debug {
35    /// Check if the link is linked by the intrusive data structure.
36    fn is_linked(&self) -> bool;
37}
38
39/// Intrusive data structure adapter.
40///
41/// # Safety
42///
43/// Pointer operations MUST be valid.
44///
45/// [`Adapter`] is recommended to be generated by macro `intrusive_adapter!`.
46pub unsafe trait Adapter: Send + Sync + Debug + 'static {
47    /// Item type for the adapter.
48    type Item: ?Sized;
49    /// Link type for the adapter.
50    type Link: Link;
51
52    /// Create a new intrusive data structure link.
53    fn new() -> Self;
54
55    /// # Safety
56    ///
57    /// Pointer operations MUST be valid.
58    unsafe fn link2ptr(&self, link: std::ptr::NonNull<Self::Link>) -> std::ptr::NonNull<Self::Item>;
59
60    /// # Safety
61    ///
62    /// Pointer operations MUST be valid.
63    unsafe fn ptr2link(&self, item: std::ptr::NonNull<Self::Item>) -> std::ptr::NonNull<Self::Link>;
64}
65
66/// Macro to generate an implementation of [`Adapter`] for intrusive container and items.
67///
68/// The basic syntax to create an adapter is:
69///
70/// ```rust,ignore
71/// intrusive_adapter! { Adapter = Pointer: Item { link_field: LinkType } }
72/// ```
73///
74/// # Generics
75///
76/// This macro supports generic arguments:
77///
78/// Note that due to macro parsing limitations, `T: Trait` bounds are not
79/// supported in the generic argument list. You must list any trait bounds in
80/// a separate `where` clause at the end of the macro.
81///
82/// # Examples
83///
84/// ```
85/// use foyer_intrusive::intrusive_adapter;
86/// use foyer_intrusive::adapter::Link;
87///
88/// #[derive(Debug)]
89/// pub struct Item<L>
90/// where
91///     L: Link
92/// {
93///     link: L,
94///     key: u64,
95/// }
96///
97/// intrusive_adapter! { ItemAdapter<L> = Item<L> { link: L } where L: Link }
98/// ```
99#[macro_export]
100macro_rules! intrusive_adapter {
101    (@impl
102        $vis:vis $name:ident ($($args:tt),*) = $item:ty { $field:ident: $link:ty } $($where_:tt)*
103    ) => {
104        $vis struct $name<$($args),*> $($where_)* {
105            _marker: std::marker::PhantomData<($($args),*)>
106        }
107
108        unsafe impl<$($args),*> Send for $name<$($args),*> $($where_)* {}
109        unsafe impl<$($args),*> Sync for $name<$($args),*> $($where_)* {}
110
111        unsafe impl<$($args),*> $crate::adapter::Adapter for $name<$($args),*> $($where_)*{
112            type Item = $item;
113            type Link = $link;
114
115            fn new() -> Self {
116                Self {
117                    _marker: std::marker::PhantomData,
118                }
119            }
120
121            unsafe fn link2ptr(&self, link: std::ptr::NonNull<Self::Link>) -> std::ptr::NonNull<Self::Item> {
122                std::ptr::NonNull::new_unchecked($crate::container_of!(link.as_ptr(), $item, $field))
123            }
124
125            unsafe fn ptr2link(&self, item: std::ptr::NonNull<Self::Item>) -> std::ptr::NonNull<Self::Link> {
126                std::ptr::NonNull::new_unchecked((item.as_ptr() as *mut u8).add(std::mem::offset_of!($item, $field)) as *mut Self::Link)
127            }
128        }
129
130        impl<$($args),*> std::fmt::Debug for $name<$($args),*> $($where_)*{
131            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
132                f.debug_struct(stringify!($name)).finish()
133            }
134        }
135    };
136    (
137        $vis:vis $name:ident = $($rest:tt)*
138    ) => {
139        intrusive_adapter! {@impl
140            $vis $name () = $($rest)*
141        }
142    };
143    (
144        $vis:vis $name:ident<$($args:tt),*> = $($rest:tt)*
145    ) => {
146        intrusive_adapter! {@impl
147            $vis $name ($($args),*) = $($rest)*
148        }
149    };
150}
151
152#[cfg(test)]
153mod tests {
154    use std::ptr::NonNull;
155
156    use itertools::Itertools;
157
158    use super::*;
159    use crate::{dlist::*, intrusive_adapter};
160
161    #[derive(Debug)]
162    struct DlistItem {
163        link: DlistLink,
164        val: u64,
165    }
166
167    impl DlistItem {
168        fn new(val: u64) -> Self {
169            Self {
170                link: DlistLink::default(),
171                val,
172            }
173        }
174    }
175
176    intrusive_adapter! { DlistItemAdapter = DlistItem { link: DlistLink }}
177
178    #[test]
179    fn test_adapter_macro() {
180        let mut l = Dlist::<DlistItemAdapter>::new();
181        l.push_front(unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(DlistItem::new(1)))) });
182        let v = l.iter().map(|item| item.val).collect_vec();
183        assert_eq!(v, vec![1]);
184        let _ = unsafe { Box::from_raw(l.pop_front().unwrap().as_ptr()) };
185    }
186}