• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 15200164445

23 May 2025 01:21AM UTC coverage: 57.288% (+0.1%) from 57.185%
15200164445

Pull #666

github

web-flow
Merge 4232978c5 into 4b41e5c8a
Pull Request #666: Add indirection to vtable fns to fix cyclic types

715 of 1482 new or added lines in 34 files covered. (48.25%)

14 existing lines in 5 files now uncovered.

9747 of 17014 relevant lines covered (57.29%)

132.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.14
/facet-core/src/impls_std/hashset.rs
1
use core::hash::{BuildHasher, Hash};
2
use std::collections::HashSet;
3

4
use crate::ptr::{PtrConst, PtrMut};
5

6
use crate::{
7
    Def, Facet, IterVTable, MarkerTraits, SetDef, SetVTable, Shape, Type, TypeParam, UserType,
8
    VTableView, ValueVTable,
9
};
10

11
type HashSetIterator<'mem, T> = std::collections::hash_set::Iter<'mem, T>;
12

13
unsafe impl<'a, T, S> Facet<'a> for HashSet<T, S>
14
where
15
    T: Facet<'a> + core::cmp::Eq + core::hash::Hash,
16
    S: Facet<'a> + Default + BuildHasher,
17
{
18
    const VTABLE: &'static ValueVTable = &const {
19
        ValueVTable::builder::<Self>()
NEW
20
            .marker_traits(|| {
×
21
                MarkerTraits::SEND
UNCOV
22
                    .union(MarkerTraits::SYNC)
×
UNCOV
23
                    .union(MarkerTraits::EQ)
×
UNCOV
24
                    .union(MarkerTraits::UNPIN)
×
NEW
25
                    .intersection(T::SHAPE.vtable.marker_traits())
×
NEW
26
            })
×
27
            .type_name(|f, opts| {
×
28
                if let Some(opts) = opts.for_children() {
×
29
                    write!(f, "HashSet<")?;
×
30
                    (T::SHAPE.vtable.type_name)(f, opts)?;
×
31
                    write!(f, ">")
×
32
                } else {
33
                    write!(f, "HashSet<⋯>")
×
34
                }
35
            })
×
NEW
36
            .default_in_place(|| Some(|target| unsafe { target.put(Self::default()) }))
×
NEW
37
            .eq(|| Some(|a, b| a == b))
×
NEW
38
            .debug(|| {
×
NEW
39
                if (T::SHAPE.vtable.debug)().is_some() {
×
NEW
40
                    Some(|value, f| {
×
NEW
41
                        let t_debug = <VTableView<T>>::of().debug().unwrap();
×
NEW
42
                        write!(f, "{{")?;
×
NEW
43
                        for (i, item) in value.iter().enumerate() {
×
NEW
44
                            if i > 0 {
×
NEW
45
                                write!(f, ", ")?;
×
NEW
46
                            }
×
NEW
47
                            (t_debug)(item, f)?;
×
48
                        }
NEW
49
                        write!(f, "}}")
×
NEW
50
                    })
×
51
                } else {
NEW
52
                    None
×
53
                }
NEW
54
            })
×
NEW
55
            .clone_into(|| {
×
NEW
56
                if (T::SHAPE.vtable.clone_into)().is_some() {
×
57
                    Some(|src, dst| unsafe {
NEW
58
                        let set = src;
×
NEW
59
                        let mut new_set =
×
NEW
60
                            HashSet::with_capacity_and_hasher(set.len(), S::default());
×
61

NEW
62
                        let t_clone_into = <VTableView<T>>::of().clone_into().unwrap();
×
63

NEW
64
                        for item in set {
×
65
                            use crate::TypedPtrUninit;
66
                            use core::mem::MaybeUninit;
67

NEW
68
                            let mut new_item = MaybeUninit::<T>::uninit();
×
NEW
69
                            let uninit_item = TypedPtrUninit::new(new_item.as_mut_ptr());
×
70

NEW
71
                            (t_clone_into)(item, uninit_item);
×
72

NEW
73
                            new_set.insert(new_item.assume_init());
×
74
                        }
75

NEW
76
                        dst.put(new_set)
×
NEW
77
                    })
×
78
                } else {
NEW
79
                    None
×
80
                }
NEW
81
            })
×
NEW
82
            .hash(|| {
×
NEW
83
                if (T::SHAPE.vtable.hash)().is_some() {
×
84
                    Some(|set, hasher_this, hasher_write_fn| unsafe {
85
                        use crate::HasherProxy;
NEW
86
                        let t_hash = <VTableView<T>>::of().hash().unwrap();
×
NEW
87
                        let mut hasher = HasherProxy::new(hasher_this, hasher_write_fn);
×
NEW
88
                        set.len().hash(&mut hasher);
×
NEW
89
                        for item in set {
×
NEW
90
                            (t_hash)(item, hasher_this, hasher_write_fn);
×
NEW
91
                        }
×
NEW
92
                    })
×
93
                } else {
NEW
94
                    None
×
95
                }
NEW
96
            })
×
97
            .build()
98
    };
99

100
    const SHAPE: &'static Shape<'static> = &const {
101
        Shape::builder_for_sized::<Self>()
102
            .type_params(&[
103
                TypeParam {
104
                    name: "T",
105
                    shape: || T::SHAPE,
106
                },
107
                TypeParam {
108
                    name: "S",
109
                    shape: || S::SHAPE,
110
                },
111
            ])
112
            .ty(Type::User(UserType::Opaque))
113
            .def(Def::Set(
114
                SetDef::builder()
115
                    .t(|| T::SHAPE)
116
                    .vtable(
117
                        &const {
118
                            SetVTable::builder()
119
                                .init_in_place_with_capacity(|uninit, capacity| unsafe {
120
                                    uninit
1✔
121
                                        .put(Self::with_capacity_and_hasher(capacity, S::default()))
1✔
122
                                })
1✔
123
                                .insert(|ptr, item| unsafe {
124
                                    let set = ptr.as_mut::<HashSet<T>>();
10✔
125
                                    let item = item.read::<T>();
10✔
126
                                    set.insert(item)
10✔
127
                                })
10✔
128
                                .len(|ptr| unsafe {
129
                                    let set = ptr.get::<HashSet<T>>();
11✔
130
                                    set.len()
11✔
131
                                })
11✔
132
                                .contains(|ptr, item| unsafe {
133
                                    let set = ptr.get::<HashSet<T>>();
×
134
                                    set.contains(item.get())
×
135
                                })
×
136
                                .iter_vtable(
137
                                    IterVTable::builder()
138
                                        .init_with_value(|ptr| unsafe {
139
                                            let set = ptr.get::<HashSet<T>>();
1✔
140
                                            let iter: HashSetIterator<'_, T> = set.iter();
1✔
141
                                            let iter_state = Box::new(iter);
1✔
142
                                            PtrMut::new(Box::into_raw(iter_state) as *mut u8)
1✔
143
                                        })
1✔
144
                                        .next(|iter_ptr| unsafe {
145
                                            let state = iter_ptr.as_mut::<HashSetIterator<'_, T>>();
6✔
146
                                            state.next().map(|value| PtrConst::new(value))
6✔
147
                                        })
6✔
148
                                        .dealloc(|iter_ptr| unsafe {
149
                                            drop(Box::from_raw(
1✔
150
                                                iter_ptr.as_ptr::<HashSetIterator<'_, T>>()
1✔
151
                                                    as *mut HashSetIterator<'_, T>,
1✔
152
                                            ));
153
                                        })
1✔
154
                                        .build(),
155
                                )
156
                                .build()
157
                        },
158
                    )
159
                    .build(),
160
            ))
161
            .build()
162
    };
163
}
164

165
#[cfg(test)]
166
mod tests {
167
    use alloc::string::String;
168
    use std::collections::HashSet;
169
    use std::hash::RandomState;
170

171
    use super::*;
172

173
    #[test]
174
    fn test_hashset_type_params() {
1✔
175
        // HashSet should have a type param for both its value type
176
        // and its hasher state
177
        let [type_param_1, type_param_2] = <HashSet<i32>>::SHAPE.type_params else {
1✔
178
            panic!("HashSet<T> should have 2 type params")
×
179
        };
180
        assert_eq!(type_param_1.shape(), i32::SHAPE);
1✔
181
        assert_eq!(type_param_2.shape(), RandomState::SHAPE);
1✔
182
    }
1✔
183

184
    #[test]
185
    fn test_hashset_vtable_1_new_insert_iter_drop() -> eyre::Result<()> {
1✔
186
        facet_testhelpers::setup();
1✔
187

188
        let hashset_shape = <HashSet<String>>::SHAPE;
1✔
189
        let hashset_def = hashset_shape
1✔
190
            .def
1✔
191
            .into_set()
1✔
192
            .expect("HashSet<T> should have a set definition");
1✔
193

194
        // Allocate memory for the HashSet
195
        let hashset_uninit_ptr = hashset_shape.allocate()?;
1✔
196

197
        // Create the HashSet with a capacity of 3
198
        let hashset_ptr =
1✔
199
            unsafe { (hashset_def.vtable.init_in_place_with_capacity_fn)(hashset_uninit_ptr, 3) };
1✔
200

201
        // The HashSet is empty, so ensure its length is 0
202
        let hashset_actual_length = unsafe { (hashset_def.vtable.len_fn)(hashset_ptr.as_const()) };
1✔
203
        assert_eq!(hashset_actual_length, 0);
1✔
204

205
        // 5 sample values to insert
206
        let strings = ["foo", "bar", "bazz", "fizzbuzz", "fifth thing"];
1✔
207

208
        // Insert the 5 values into the HashSet
209
        let mut hashset_length = 0;
1✔
210
        for string in strings {
6✔
211
            // Create the value
212
            let mut new_value = string.to_string();
5✔
213

214
            // Insert the value
215
            let did_insert = unsafe {
5✔
216
                (hashset_def.vtable.insert_fn)(hashset_ptr, PtrMut::new(&raw mut new_value))
5✔
217
            };
218

219
            // The value now belongs to the HashSet, so forget it
220
            core::mem::forget(new_value);
5✔
221

222
            assert!(did_insert, "expected value to be inserted in the HashSet");
5✔
223

224
            // Ensure the HashSet's length increased by 1
225
            hashset_length += 1;
5✔
226
            let hashset_actual_length =
5✔
227
                unsafe { (hashset_def.vtable.len_fn)(hashset_ptr.as_const()) };
5✔
228
            assert_eq!(hashset_actual_length, hashset_length);
5✔
229
        }
230

231
        // Insert the same 5 values again, ensuring they are deduplicated
232
        for string in strings {
6✔
233
            // Create the value
234
            let mut new_value = string.to_string();
5✔
235

236
            // Try to insert the value
237
            let did_insert = unsafe {
5✔
238
                (hashset_def.vtable.insert_fn)(hashset_ptr, PtrMut::new(&raw mut new_value))
5✔
239
            };
240

241
            // The value now belongs to the HashSet, so forget it
242
            core::mem::forget(new_value);
5✔
243

244
            assert!(
5✔
245
                !did_insert,
5✔
246
                "expected value to not be inserted in the HashSet"
×
247
            );
248

249
            // Ensure the HashSet's length did not increase
250
            let hashset_actual_length =
5✔
251
                unsafe { (hashset_def.vtable.len_fn)(hashset_ptr.as_const()) };
5✔
252
            assert_eq!(hashset_actual_length, hashset_length);
5✔
253
        }
254

255
        // Create a new iterator over the HashSet
256
        let iter_init_with_value_fn = hashset_def.vtable.iter_vtable.init_with_value.unwrap();
1✔
257
        let hashset_iter_ptr = unsafe { iter_init_with_value_fn(hashset_ptr.as_const()) };
1✔
258

259
        // Collect all the items from the HashSet's iterator
260
        let mut iter_items = HashSet::<&str>::new();
1✔
261
        loop {
262
            // Get the next item from the iterator
263
            let item_ptr = unsafe { (hashset_def.vtable.iter_vtable.next)(hashset_iter_ptr) };
6✔
264
            let Some(item_ptr) = item_ptr else {
6✔
265
                break;
1✔
266
            };
267

268
            let item = unsafe { item_ptr.get::<String>() };
5✔
269

270
            // Insert the item into the set of items returned from the iterator
271
            let did_insert = iter_items.insert(&**item);
5✔
272

273
            assert!(did_insert, "HashSet iterator returned duplicate item");
5✔
274
        }
275

276
        // Deallocate the iterator
277
        unsafe {
1✔
278
            (hashset_def.vtable.iter_vtable.dealloc)(hashset_iter_ptr);
1✔
279
        }
1✔
280

281
        // Ensure the iterator returned all of the strings
282
        assert_eq!(iter_items, strings.iter().copied().collect::<HashSet<_>>());
1✔
283

284
        // Get the function pointer for dropping the HashSet
285
        let drop_fn =
1✔
286
            (hashset_shape.vtable.drop_in_place)().expect("HashSet<T> should have drop_in_place");
1✔
287

288
        // Drop the HashSet in place
289
        unsafe { drop_fn(hashset_ptr) };
1✔
290

291
        // Deallocate the memory
292
        unsafe { hashset_shape.deallocate_mut(hashset_ptr)? };
1✔
293

294
        Ok(())
1✔
295
    }
1✔
296
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc