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}