facet_core/impls/alloc/
vec.rs

1use crate::*;
2
3use alloc::boxed::Box;
4use alloc::vec::Vec;
5
6/// Helper for Debug formatting via Shape vtable
7struct DebugViaShape(&'static Shape, PtrConst);
8
9impl core::fmt::Debug for DebugViaShape {
10    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
11        match unsafe { self.0.call_debug(self.1, f) } {
12            Some(result) => result,
13            None => write!(f, "???"),
14        }
15    }
16}
17
18// =============================================================================
19// Type-erased vtable functions for Vec<T> - shared across all instantiations
20// =============================================================================
21
22/// Vec memory layout (stable across all T)
23/// Layout is determined at const time by probing a Vec.
24#[repr(C)]
25struct VecLayout {
26    #[allow(dead_code)]
27    cap: usize,
28    ptr: *mut u8,
29    len: usize,
30}
31
32// Compile-time assertion that our VecLayout matches the actual Vec layout
33const _: () = {
34    // Create a Vec and transmute to probe the layout
35    let v: Vec<u8> = Vec::new();
36    let fields: [usize; 3] = unsafe { core::mem::transmute(v) };
37
38    // Vec::new() has cap=0, len=0, ptr=non-null (dangling)
39    // We can't easily distinguish cap from len when both are 0,
40    // but we can at least verify the size matches
41    assert!(
42        core::mem::size_of::<Vec<u8>>() == core::mem::size_of::<VecLayout>(),
43        "VecLayout size mismatch"
44    );
45    assert!(
46        core::mem::align_of::<Vec<u8>>() == core::mem::align_of::<VecLayout>(),
47        "VecLayout align mismatch"
48    );
49
50    // The pointer field should be non-null even for empty vec (dangling pointer)
51    // Fields 0 and 2 should be 0 (cap and len)
52    // Field 1 should be non-zero (ptr)
53    // This validates our layout: [cap, ptr, len]
54    assert!(fields[0] == 0, "expected cap=0 at offset 0");
55    assert!(fields[1] != 0, "expected non-null ptr at offset 1");
56    assert!(fields[2] == 0, "expected len=0 at offset 2");
57};
58
59/// Type-erased len implementation - works for any `Vec<T>`
60unsafe fn vec_len_erased(ptr: PtrConst) -> usize {
61    unsafe {
62        let layout = ptr.as_byte_ptr() as *const VecLayout;
63        (*layout).len
64    }
65}
66
67/// Type-erased get implementation - works for any `Vec<T>` using shape info
68unsafe fn vec_get_erased(ptr: PtrConst, index: usize, shape: &'static Shape) -> Option<PtrConst> {
69    unsafe {
70        let layout = ptr.as_byte_ptr() as *const VecLayout;
71        let len = (*layout).len;
72        if index >= len {
73            return None;
74        }
75        let elem_size = shape
76            .type_params
77            .first()?
78            .shape
79            .layout
80            .sized_layout()
81            .ok()?
82            .size();
83        let data_ptr = (*layout).ptr;
84        Some(PtrConst::new(data_ptr.add(index * elem_size)))
85    }
86}
87
88/// Type-erased get_mut implementation - works for any `Vec<T>` using shape info
89unsafe fn vec_get_mut_erased(ptr: PtrMut, index: usize, shape: &'static Shape) -> Option<PtrMut> {
90    unsafe {
91        let layout = ptr.as_byte_ptr() as *const VecLayout;
92        let len = (*layout).len;
93        if index >= len {
94            return None;
95        }
96        let elem_size = shape
97            .type_params
98            .first()?
99            .shape
100            .layout
101            .sized_layout()
102            .ok()?
103            .size();
104        let data_ptr = (*layout).ptr;
105        Some(PtrMut::new(data_ptr.add(index * elem_size)))
106    }
107}
108
109/// Shared ListVTable for ALL `Vec<T>` instantiations
110///
111/// This single vtable is used by every `Vec<T>` regardless of T, eliminating
112/// the need to generate separate `vec_len`, `vec_get`, etc. functions for
113/// each element type.
114static VEC_LIST_VTABLE: ListVTable = ListVTable {
115    len: vec_len_erased,
116    get: vec_get_erased,
117    get_mut: Some(vec_get_mut_erased),
118    as_ptr: Some(vec_as_ptr_erased),
119    as_mut_ptr: Some(vec_as_mut_ptr_erased),
120};
121
122/// Type-erased as_ptr implementation - works for any `Vec<T>`
123unsafe fn vec_as_ptr_erased(ptr: PtrConst) -> PtrConst {
124    unsafe {
125        let layout = ptr.as_byte_ptr() as *const VecLayout;
126        PtrConst::new((*layout).ptr)
127    }
128}
129
130/// Type-erased as_mut_ptr implementation - works for any `Vec<T>`
131unsafe fn vec_as_mut_ptr_erased(ptr: PtrMut) -> PtrMut {
132    unsafe {
133        let layout = ptr.as_byte_ptr() as *const VecLayout;
134        PtrMut::new((*layout).ptr)
135    }
136}
137
138/// Type-erased type_name implementation for Vec
139fn vec_type_name(
140    shape: &'static Shape,
141    f: &mut core::fmt::Formatter<'_>,
142    opts: TypeNameOpts,
143) -> core::fmt::Result {
144    write!(f, "Vec")?;
145    if let Some(opts) = opts.for_children() {
146        write!(f, "<")?;
147        if let Some(tp) = shape.type_params.first() {
148            tp.shape.write_type_name(f, opts)?;
149        }
150        write!(f, ">")?;
151    } else {
152        write!(f, "<…>")?;
153    }
154    Ok(())
155}
156
157/// Get the ListDef from a shape, panics if not a list
158#[inline]
159fn get_list_def(shape: &'static Shape) -> &'static ListDef {
160    match &shape.def {
161        Def::List(list_def) => list_def,
162        _ => panic!("expected List def"),
163    }
164}
165
166/// Type-erased debug implementation for Vec
167unsafe fn vec_debug_erased(
168    ox: OxPtrConst,
169    f: &mut core::fmt::Formatter<'_>,
170) -> Option<core::fmt::Result> {
171    let shape = ox.shape();
172    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
173    if !elem_shape.vtable.has_debug() {
174        return None;
175    }
176
177    let list_def = get_list_def(shape);
178    let ptr = ox.ptr();
179    let len = unsafe { (list_def.vtable.len)(ptr) };
180
181    let mut list = f.debug_list();
182    for i in 0..len {
183        if let Some(item_ptr) = unsafe { (list_def.vtable.get)(ptr, i, shape) } {
184            list.entry(&DebugViaShape(elem_shape, item_ptr));
185        }
186    }
187    Some(list.finish())
188}
189
190/// Type-erased partial_eq implementation for Vec
191unsafe fn vec_partial_eq_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<bool> {
192    let shape = ox_a.shape();
193    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
194    if !elem_shape.vtable.has_partial_eq() {
195        return None;
196    }
197
198    let list_def = get_list_def(shape);
199    let ptr_a = ox_a.ptr();
200    let ptr_b = ox_b.ptr();
201    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
202    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
203
204    if len_a != len_b {
205        return Some(false);
206    }
207
208    for i in 0..len_a {
209        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
210        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
211        match unsafe { elem_shape.call_partial_eq(item_a, item_b) } {
212            Some(true) => continue,
213            Some(false) => return Some(false),
214            None => return None,
215        }
216    }
217    Some(true)
218}
219
220/// Type-erased partial_cmp implementation for Vec
221unsafe fn vec_partial_cmp_erased(
222    ox_a: OxPtrConst,
223    ox_b: OxPtrConst,
224) -> Option<Option<core::cmp::Ordering>> {
225    let shape = ox_a.shape();
226    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
227    if !elem_shape.vtable.has_partial_ord() {
228        return None;
229    }
230
231    let list_def = get_list_def(shape);
232    let ptr_a = ox_a.ptr();
233    let ptr_b = ox_b.ptr();
234    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
235    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
236
237    let min_len = len_a.min(len_b);
238
239    for i in 0..min_len {
240        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
241        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
242        match unsafe { elem_shape.call_partial_cmp(item_a, item_b) } {
243            Some(Some(core::cmp::Ordering::Equal)) => continue,
244            Some(ord) => return Some(ord),
245            None => return None,
246        }
247    }
248    Some(Some(len_a.cmp(&len_b)))
249}
250
251/// Type-erased cmp implementation for Vec
252unsafe fn vec_cmp_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<core::cmp::Ordering> {
253    let shape = ox_a.shape();
254    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
255    if !elem_shape.vtable.has_ord() {
256        return None;
257    }
258
259    let list_def = get_list_def(shape);
260    let ptr_a = ox_a.ptr();
261    let ptr_b = ox_b.ptr();
262    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
263    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
264
265    let min_len = len_a.min(len_b);
266
267    for i in 0..min_len {
268        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
269        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
270        match unsafe { elem_shape.call_cmp(item_a, item_b) } {
271            Some(core::cmp::Ordering::Equal) => continue,
272            Some(ord) => return Some(ord),
273            None => return None,
274        }
275    }
276    Some(len_a.cmp(&len_b))
277}
278
279// =============================================================================
280// Generic functions that still need T
281// =============================================================================
282
283type VecIterator<'mem, T> = core::slice::Iter<'mem, T>;
284
285unsafe fn vec_init_in_place_with_capacity<T>(uninit: PtrUninit, capacity: usize) -> PtrMut {
286    unsafe { uninit.put(Vec::<T>::with_capacity(capacity)) }
287}
288
289unsafe fn vec_push<T: 'static>(ptr: PtrMut, item: PtrMut) {
290    unsafe {
291        let vec = ptr.as_mut::<Vec<T>>();
292        let item = item.read::<T>();
293        vec.push(item);
294    }
295}
296
297/// Set the length of a Vec (for direct-fill operations).
298///
299/// # Safety
300/// - `ptr` must point to an initialized `Vec<T>`
301/// - `len` must not exceed the Vec's capacity
302/// - All elements at indices `0..len` must be properly initialized
303unsafe fn vec_set_len<T: 'static>(ptr: PtrMut, len: usize) {
304    unsafe {
305        let vec = ptr.as_mut::<Vec<T>>();
306        vec.set_len(len);
307    }
308}
309
310/// Get raw mutable pointer to Vec's data buffer as `*mut u8`.
311///
312/// # Safety
313/// - `ptr` must point to an initialized `Vec<T>`
314unsafe fn vec_as_mut_ptr_typed<T: 'static>(ptr: PtrMut) -> *mut u8 {
315    unsafe {
316        let vec = ptr.as_mut::<Vec<T>>();
317        vec.as_mut_ptr() as *mut u8
318    }
319}
320
321unsafe fn vec_iter_init<T: 'static>(ptr: PtrConst) -> PtrMut {
322    unsafe {
323        let vec = ptr.get::<Vec<T>>();
324        let iter: VecIterator<T> = vec.iter();
325        let iter_state = Box::new(iter);
326        PtrMut::new(Box::into_raw(iter_state) as *mut u8)
327    }
328}
329
330unsafe fn vec_iter_next<T: 'static>(iter_ptr: PtrMut) -> Option<PtrConst> {
331    unsafe {
332        let state = iter_ptr.as_mut::<VecIterator<'static, T>>();
333        state.next().map(|value| PtrConst::new(value as *const T))
334    }
335}
336
337unsafe fn vec_iter_next_back<T: 'static>(iter_ptr: PtrMut) -> Option<PtrConst> {
338    unsafe {
339        let state = iter_ptr.as_mut::<VecIterator<'static, T>>();
340        state
341            .next_back()
342            .map(|value| PtrConst::new(value as *const T))
343    }
344}
345
346unsafe fn vec_iter_dealloc<T>(iter_ptr: PtrMut) {
347    unsafe {
348        drop(Box::from_raw(
349            iter_ptr.as_ptr::<VecIterator<'_, T>>() as *mut VecIterator<'_, T>
350        ))
351    }
352}
353
354unsafe impl<'a, T> Facet<'a> for Vec<T>
355where
356    T: Facet<'a> + 'static,
357{
358    const SHAPE: &'static Shape =
359        &const {
360            // Per-T operations that must be monomorphized
361            const fn build_list_type_ops<T: 'static>() -> ListTypeOps {
362                ListTypeOps::builder()
363                    .init_in_place_with_capacity(vec_init_in_place_with_capacity::<T>)
364                    .push(vec_push::<T>)
365                    .set_len(vec_set_len::<T>)
366                    .as_mut_ptr_typed(vec_as_mut_ptr_typed::<T>)
367                    .iter_vtable(IterVTable {
368                        init_with_value: Some(vec_iter_init::<T>),
369                        next: vec_iter_next::<T>,
370                        next_back: Some(vec_iter_next_back::<T>),
371                        size_hint: None,
372                        dealloc: vec_iter_dealloc::<T>,
373                    })
374                    .build()
375            }
376
377            ShapeBuilder::for_sized::<Self>("Vec")
378            .type_name(vec_type_name)
379            .ty(Type::User(UserType::Opaque))
380            .def(Def::List(ListDef::with_type_ops(
381                // Use the SHARED vtable for all Vec<T>!
382                &VEC_LIST_VTABLE,
383                &const { build_list_type_ops::<T>() },
384                T::SHAPE,
385            )))
386            .type_params(&[TypeParam {
387                name: "T",
388                shape: T::SHAPE,
389            }])
390            .inner(T::SHAPE)
391            // Vec<T> propagates T's variance
392            .variance(Shape::computed_variance)
393            .vtable_indirect(&const {
394                VTableIndirect {
395                    debug: Some(vec_debug_erased),
396                    partial_eq: Some(vec_partial_eq_erased),
397                    partial_cmp: Some(vec_partial_cmp_erased),
398                    cmp: Some(vec_cmp_erased),
399                    display: None,
400                    hash: None,
401                    invariants: None,
402                    parse: None,
403                    try_from: None,
404                    try_into_inner: None,
405                    try_borrow_inner: None,
406                }
407            })
408            .type_ops_indirect(&const {
409                unsafe fn drop_in_place<T>(ox: OxPtrMut) {
410                    unsafe {
411                        core::ptr::drop_in_place(ox.ptr().as_ptr::<Vec<T>>() as *mut Vec<T>);
412                    }
413                }
414
415                unsafe fn default_in_place<T>(ox: OxPtrMut) {
416                    unsafe { ox.ptr().as_uninit().put(Vec::<T>::new()) };
417                }
418
419                unsafe fn truthy<T>(ptr: PtrConst) -> bool {
420                    !unsafe { ptr.get::<Vec<T>>() }.is_empty()
421                }
422
423                TypeOpsIndirect {
424                    drop_in_place: drop_in_place::<T>,
425                    default_in_place: Some(default_in_place::<T>),
426                    clone_into: None,
427                    is_truthy: Some(truthy::<T>),
428                }
429            })
430            .build()
431        };
432}