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
321/// Reserve capacity for at least `additional` more elements.
322///
323/// # Safety
324/// - `ptr` must point to an initialized `Vec<T>`
325unsafe fn vec_reserve<T: 'static>(ptr: PtrMut, additional: usize) {
326    unsafe {
327        let vec = ptr.as_mut::<Vec<T>>();
328        vec.reserve(additional);
329    }
330}
331
332/// Get the current capacity of the Vec.
333///
334/// # Safety
335/// - `ptr` must point to an initialized `Vec<T>`
336unsafe fn vec_capacity<T: 'static>(ptr: PtrConst) -> usize {
337    unsafe {
338        let vec = ptr.get::<Vec<T>>();
339        vec.capacity()
340    }
341}
342
343unsafe fn vec_iter_init<T: 'static>(ptr: PtrConst) -> PtrMut {
344    unsafe {
345        let vec = ptr.get::<Vec<T>>();
346        let iter: VecIterator<T> = vec.iter();
347        let iter_state = Box::new(iter);
348        PtrMut::new(Box::into_raw(iter_state) as *mut u8)
349    }
350}
351
352unsafe fn vec_iter_next<T: 'static>(iter_ptr: PtrMut) -> Option<PtrConst> {
353    unsafe {
354        let state = iter_ptr.as_mut::<VecIterator<'static, T>>();
355        state.next().map(|value| PtrConst::new(value as *const T))
356    }
357}
358
359unsafe fn vec_iter_next_back<T: 'static>(iter_ptr: PtrMut) -> Option<PtrConst> {
360    unsafe {
361        let state = iter_ptr.as_mut::<VecIterator<'static, T>>();
362        state
363            .next_back()
364            .map(|value| PtrConst::new(value as *const T))
365    }
366}
367
368unsafe fn vec_iter_dealloc<T>(iter_ptr: PtrMut) {
369    unsafe {
370        drop(Box::from_raw(
371            iter_ptr.as_ptr::<VecIterator<'_, T>>() as *mut VecIterator<'_, T>
372        ))
373    }
374}
375
376unsafe impl<'a, T> Facet<'a> for Vec<T>
377where
378    T: Facet<'a> + 'static,
379{
380    const SHAPE: &'static Shape =
381        &const {
382            // Per-T operations that must be monomorphized
383            const fn build_list_type_ops<T: 'static>() -> ListTypeOps {
384                ListTypeOps::builder()
385                    .init_in_place_with_capacity(vec_init_in_place_with_capacity::<T>)
386                    .push(vec_push::<T>)
387                    .set_len(vec_set_len::<T>)
388                    .as_mut_ptr_typed(vec_as_mut_ptr_typed::<T>)
389                    .reserve(vec_reserve::<T>)
390                    .capacity(vec_capacity::<T>)
391                    .iter_vtable(IterVTable {
392                        init_with_value: Some(vec_iter_init::<T>),
393                        next: vec_iter_next::<T>,
394                        next_back: Some(vec_iter_next_back::<T>),
395                        size_hint: None,
396                        dealloc: vec_iter_dealloc::<T>,
397                    })
398                    .build()
399            }
400
401            ShapeBuilder::for_sized::<Self>("Vec")
402            .type_name(vec_type_name)
403            .ty(Type::User(UserType::Opaque))
404            .def(Def::List(ListDef::with_type_ops(
405                // Use the SHARED vtable for all Vec<T>!
406                &VEC_LIST_VTABLE,
407                &const { build_list_type_ops::<T>() },
408                T::SHAPE,
409            )))
410            .type_params(&[TypeParam {
411                name: "T",
412                shape: T::SHAPE,
413            }])
414            .inner(T::SHAPE)
415            // Vec<T> propagates T's variance
416            .variance(Shape::computed_variance)
417            .vtable_indirect(&const {
418                VTableIndirect {
419                    debug: Some(vec_debug_erased),
420                    partial_eq: Some(vec_partial_eq_erased),
421                    partial_cmp: Some(vec_partial_cmp_erased),
422                    cmp: Some(vec_cmp_erased),
423                    display: None,
424                    hash: None,
425                    invariants: None,
426                    parse: None,
427                    parse_bytes: None,
428                    try_from: None,
429                    try_into_inner: None,
430                    try_borrow_inner: None,
431                }
432            })
433            .type_ops_indirect(&const {
434                unsafe fn drop_in_place<T>(ox: OxPtrMut) {
435                    unsafe {
436                        core::ptr::drop_in_place(ox.ptr().as_ptr::<Vec<T>>() as *mut Vec<T>);
437                    }
438                }
439
440                unsafe fn default_in_place<T>(ox: OxPtrMut) {
441                    unsafe { ox.ptr().as_uninit().put(Vec::<T>::new()) };
442                }
443
444                unsafe fn truthy<T>(ptr: PtrConst) -> bool {
445                    !unsafe { ptr.get::<Vec<T>>() }.is_empty()
446                }
447
448                TypeOpsIndirect {
449                    drop_in_place: drop_in_place::<T>,
450                    default_in_place: Some(default_in_place::<T>),
451                    clone_into: None,
452                    is_truthy: Some(truthy::<T>),
453                }
454            })
455            .build()
456        };
457}