Skip to main content

facet_core/impls/alloc/
btreeset.rs

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