facet_core/impls_alloc/
btreeset.rs

1use core::hash::Hash;
2
3use alloc::boxed::Box;
4use alloc::collections::BTreeSet;
5
6use crate::ptr::{PtrConst, PtrMut};
7
8use crate::{
9    Def, Facet, IterVTable, MarkerTraits, SetDef, SetVTable, Shape, Type, TypeParam, UserType,
10    VTableView, ValueVTable,
11};
12
13type BTreeSetIterator<'mem, T> = alloc::collections::btree_set::Iter<'mem, T>;
14
15unsafe impl<'a, T> Facet<'a> for BTreeSet<T>
16where
17    T: Facet<'a> + core::cmp::Eq + core::cmp::Ord,
18{
19    const VTABLE: &'static ValueVTable = &const {
20        let mut builder = ValueVTable::builder::<Self>()
21            .marker_traits(
22                MarkerTraits::SEND
23                    .union(MarkerTraits::SYNC)
24                    .union(MarkerTraits::EQ)
25                    .union(MarkerTraits::UNPIN)
26                    .intersection(T::SHAPE.vtable.marker_traits),
27            )
28            .type_name(|f, opts| {
29                if let Some(opts) = opts.for_children() {
30                    write!(f, "BTreeSet<")?;
31                    (T::SHAPE.vtable.type_name)(f, opts)?;
32                    write!(f, ">")
33                } else {
34                    write!(f, "BTreeSet<⋯>")
35                }
36            })
37            .default_in_place(|target| unsafe { target.put(Self::default()) })
38            .eq(|a, b| a == b);
39
40        if T::SHAPE.vtable.debug.is_some() {
41            builder = builder.debug(|value, f| {
42                let t_debug = <VTableView<T>>::of().debug().unwrap();
43                write!(f, "{{")?;
44                for (i, item) in value.iter().enumerate() {
45                    if i > 0 {
46                        write!(f, ", ")?;
47                    }
48                    (t_debug)(item, f)?;
49                }
50                write!(f, "}}")
51            });
52        }
53
54        if T::SHAPE.vtable.clone_into.is_some() {
55            builder = builder.clone_into(|src, dst| unsafe {
56                let set = src;
57                let mut new_set = BTreeSet::new();
58
59                let t_clone_into = <VTableView<T>>::of().clone_into().unwrap();
60
61                for item in set {
62                    use crate::TypedPtrUninit;
63                    use core::mem::MaybeUninit;
64
65                    let mut new_item = MaybeUninit::<T>::uninit();
66                    let uninit_item = TypedPtrUninit::new(new_item.as_mut_ptr());
67
68                    (t_clone_into)(item, uninit_item);
69
70                    new_set.insert(new_item.assume_init());
71                }
72
73                dst.put(new_set)
74            });
75        }
76
77        if T::SHAPE.vtable.hash.is_some() {
78            builder = builder.hash(|set, hasher_this, hasher_write_fn| unsafe {
79                use crate::HasherProxy;
80                let t_hash = <VTableView<T>>::of().hash().unwrap();
81                let mut hasher = HasherProxy::new(hasher_this, hasher_write_fn);
82                set.len().hash(&mut hasher);
83                for item in set {
84                    (t_hash)(item, hasher_this, hasher_write_fn);
85                }
86            });
87        }
88
89        builder.build()
90    };
91
92    const SHAPE: &'static Shape<'static> = &const {
93        Shape::builder_for_sized::<Self>()
94            .type_params(&[TypeParam {
95                name: "T",
96                shape: || T::SHAPE,
97            }])
98            .ty(Type::User(UserType::Opaque))
99            .def(Def::Set(
100                SetDef::builder()
101                    .t(|| T::SHAPE)
102                    .vtable(
103                        &const {
104                            SetVTable::builder()
105                                .init_in_place_with_capacity(|uninit, _capacity| unsafe {
106                                    uninit.put(Self::new())
107                                })
108                                .insert(|ptr, item| unsafe {
109                                    let set = ptr.as_mut::<BTreeSet<T>>();
110                                    let item = item.read::<T>();
111                                    set.insert(item)
112                                })
113                                .len(|ptr| unsafe {
114                                    let set = ptr.get::<BTreeSet<T>>();
115                                    set.len()
116                                })
117                                .contains(|ptr, item| unsafe {
118                                    let set = ptr.get::<BTreeSet<T>>();
119                                    set.contains(item.get())
120                                })
121                                .iter_vtable(
122                                    IterVTable::builder()
123                                        .init_with_value(|ptr| {
124                                            let set = unsafe { ptr.get::<BTreeSet<T>>() };
125                                            let iter: BTreeSetIterator<'_, T> = set.iter();
126                                            let iter_state = Box::new(iter);
127                                            PtrMut::new(Box::into_raw(iter_state) as *mut u8)
128                                        })
129                                        .next(|iter_ptr| {
130                                            let state = unsafe {
131                                                iter_ptr.as_mut::<BTreeSetIterator<'_, T>>()
132                                            };
133                                            state
134                                                .next()
135                                                .map(|value| PtrConst::new(value as *const T))
136                                        })
137                                        .next_back(|iter_ptr| {
138                                            let state = unsafe {
139                                                iter_ptr.as_mut::<BTreeSetIterator<'_, T>>()
140                                            };
141                                            state
142                                                .next_back()
143                                                .map(|value| PtrConst::new(value as *const T))
144                                        })
145                                        .dealloc(|iter_ptr| unsafe {
146                                            drop(Box::from_raw(
147                                                iter_ptr.as_ptr::<BTreeSetIterator<'_, T>>()
148                                                    as *mut BTreeSetIterator<'_, T>,
149                                            ));
150                                        })
151                                        .build(),
152                                )
153                                .build()
154                        },
155                    )
156                    .build(),
157            ))
158            .build()
159    };
160}
161
162#[cfg(test)]
163mod tests {
164    use alloc::collections::BTreeSet;
165    use alloc::string::String;
166    use alloc::vec::Vec;
167
168    use super::*;
169
170    #[test]
171    fn test_btreesetset_type_params() {
172        let [type_param_1] = <BTreeSet<i32>>::SHAPE.type_params else {
173            panic!("BTreeSet<T> should have 1 type param")
174        };
175        assert_eq!(type_param_1.shape(), i32::SHAPE);
176    }
177
178    #[test]
179    fn test_btreeset_vtable_1_new_insert_iter_drop() -> eyre::Result<()> {
180        facet_testhelpers::setup();
181
182        let btreeset_shape = <BTreeSet<String>>::SHAPE;
183        let btreeset_def = btreeset_shape
184            .def
185            .into_set()
186            .expect("BTreeSet<T> should have a set definition");
187
188        // Allocate memory for the BTreeSet
189        let btreeset_uninit_ptr = btreeset_shape.allocate()?;
190
191        // Create the BTreeSet
192        let btreeset_ptr =
193            unsafe { (btreeset_def.vtable.init_in_place_with_capacity_fn)(btreeset_uninit_ptr, 0) };
194
195        // The BTreeSet is empty, so ensure its length is 0
196        let btreeset_actual_length =
197            unsafe { (btreeset_def.vtable.len_fn)(btreeset_ptr.as_const()) };
198        assert_eq!(btreeset_actual_length, 0);
199
200        // 5 sample values to insert
201        let strings = ["foo", "bar", "bazz", "fizzbuzz", "fifth thing"];
202
203        // Insert the 5 values into the BTreeSet
204        let mut btreeset_length = 0;
205        for string in strings {
206            // Create the value
207            let mut new_value = string.to_string();
208
209            // Insert the value
210            let did_insert = unsafe {
211                (btreeset_def.vtable.insert_fn)(btreeset_ptr, PtrMut::new(&raw mut new_value))
212            };
213
214            // The value now belongs to the BTreeSet, so forget it
215            core::mem::forget(new_value);
216
217            assert!(did_insert, "expected value to be inserted in the BTreeSet");
218
219            // Ensure the BTreeSet's length increased by 1
220            btreeset_length += 1;
221            let btreeset_actual_length =
222                unsafe { (btreeset_def.vtable.len_fn)(btreeset_ptr.as_const()) };
223            assert_eq!(btreeset_actual_length, btreeset_length);
224        }
225
226        // Insert the same 5 values again, ensuring they are deduplicated
227        for string in strings {
228            // Create the value
229            let mut new_value = string.to_string();
230
231            // Try to insert the value
232            let did_insert = unsafe {
233                (btreeset_def.vtable.insert_fn)(btreeset_ptr, PtrMut::new(&raw mut new_value))
234            };
235
236            // The value now belongs to the BTreeSet, so forget it
237            core::mem::forget(new_value);
238
239            assert!(
240                !did_insert,
241                "expected value to not be inserted in the BTreeSet"
242            );
243
244            // Ensure the BTreeSet's length did not increase
245            let btreeset_actual_length =
246                unsafe { (btreeset_def.vtable.len_fn)(btreeset_ptr.as_const()) };
247            assert_eq!(btreeset_actual_length, btreeset_length);
248        }
249
250        // Create a new iterator over the BTreeSet
251        let iter_init_with_value_fn = btreeset_def.vtable.iter_vtable.init_with_value.unwrap();
252        let btreeset_iter_ptr = unsafe { iter_init_with_value_fn(btreeset_ptr.as_const()) };
253
254        // Collect all the items from the BTreeSet's iterator
255        let mut iter_items = Vec::<&str>::new();
256        loop {
257            // Get the next item from the iterator
258            let item_ptr = unsafe { (btreeset_def.vtable.iter_vtable.next)(btreeset_iter_ptr) };
259            let Some(item_ptr) = item_ptr else {
260                break;
261            };
262
263            let item = unsafe { item_ptr.get::<String>() };
264
265            // Add the item into the list of items returned from the iterator
266            iter_items.push(&**item);
267        }
268
269        // Deallocate the iterator
270        unsafe {
271            (btreeset_def.vtable.iter_vtable.dealloc)(btreeset_iter_ptr);
272        }
273
274        // BTrees iterate in sorted order, so ensure the iterator returned
275        // each item in order
276        let mut strings_sorted = strings.to_vec();
277        strings_sorted.sort();
278        assert_eq!(iter_items, strings_sorted);
279
280        // Get the function pointer for dropping the BTreeSet
281        let drop_fn = btreeset_shape
282            .vtable
283            .drop_in_place
284            .expect("BTreeSet<T> should have drop_in_place");
285
286        // Drop the BTreeSet in place
287        unsafe { drop_fn(btreeset_ptr) };
288
289        // Deallocate the memory
290        unsafe { btreeset_shape.deallocate_mut(btreeset_ptr)? };
291
292        Ok(())
293    }
294}