Skip to main content

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 extern "C" 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    swap: Some(vec_swap_erased),
121};
122
123/// Type-erased as_ptr implementation - works for any `Vec<T>`
124unsafe extern "C" fn vec_as_ptr_erased(ptr: PtrConst) -> PtrConst {
125    unsafe {
126        let layout = ptr.as_byte_ptr() as *const VecLayout;
127        PtrConst::new((*layout).ptr)
128    }
129}
130
131/// Type-erased as_mut_ptr implementation - works for any `Vec<T>`
132unsafe extern "C" fn vec_as_mut_ptr_erased(ptr: PtrMut) -> PtrMut {
133    unsafe {
134        let layout = ptr.as_byte_ptr() as *const VecLayout;
135        PtrMut::new((*layout).ptr)
136    }
137}
138
139/// Type-erased swap implementation - works for any `Vec<T>` using shape info.
140///
141/// Swaps the bytes of the elements at `a` and `b`. Returns `false` without
142/// modifying the list if either index is out of bounds.
143unsafe fn vec_swap_erased(ptr: PtrMut, a: usize, b: usize, shape: &'static Shape) -> bool {
144    unsafe {
145        let layout = ptr.as_byte_ptr() as *const VecLayout;
146        let len = (*layout).len;
147        if a >= len || b >= len {
148            return false;
149        }
150        if a == b {
151            return true;
152        }
153        let Ok(elem_layout) = shape
154            .type_params
155            .first()
156            .expect("Vec shape must have an element type parameter")
157            .shape
158            .layout
159            .sized_layout()
160        else {
161            return false;
162        };
163        let elem_size = elem_layout.size();
164        let data_ptr = (*layout).ptr;
165        core::ptr::swap_nonoverlapping(
166            data_ptr.add(a * elem_size),
167            data_ptr.add(b * elem_size),
168            elem_size,
169        );
170        true
171    }
172}
173
174/// Type-erased type_name implementation for Vec
175fn vec_type_name(
176    shape: &'static Shape,
177    f: &mut core::fmt::Formatter<'_>,
178    opts: TypeNameOpts,
179) -> core::fmt::Result {
180    write!(f, "Vec")?;
181    if let Some(opts) = opts.for_children() {
182        write!(f, "<")?;
183        if let Some(tp) = shape.type_params.first() {
184            tp.shape.write_type_name(f, opts)?;
185        }
186        write!(f, ">")?;
187    } else {
188        write!(f, "<…>")?;
189    }
190    Ok(())
191}
192
193/// Get the ListDef from a shape, panics if not a list
194#[inline]
195fn get_list_def(shape: &'static Shape) -> &'static ListDef {
196    match &shape.def {
197        Def::List(list_def) => list_def,
198        _ => panic!("expected List def"),
199    }
200}
201
202/// Type-erased debug implementation for Vec
203unsafe fn vec_debug_erased(
204    ox: OxPtrConst,
205    f: &mut core::fmt::Formatter<'_>,
206) -> Option<core::fmt::Result> {
207    let shape = ox.shape();
208    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
209    if !elem_shape.vtable.has_debug() {
210        return None;
211    }
212
213    let list_def = get_list_def(shape);
214    let ptr = ox.ptr();
215    let len = unsafe { (list_def.vtable.len)(ptr) };
216
217    let mut list = f.debug_list();
218    for i in 0..len {
219        if let Some(item_ptr) = unsafe { (list_def.vtable.get)(ptr, i, shape) } {
220            list.entry(&DebugViaShape(elem_shape, item_ptr));
221        }
222    }
223    Some(list.finish())
224}
225
226/// Type-erased partial_eq implementation for Vec
227unsafe fn vec_partial_eq_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<bool> {
228    let shape = ox_a.shape();
229    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
230    if !elem_shape.vtable.has_partial_eq() {
231        return None;
232    }
233
234    let list_def = get_list_def(shape);
235    let ptr_a = ox_a.ptr();
236    let ptr_b = ox_b.ptr();
237    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
238    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
239
240    if len_a != len_b {
241        return Some(false);
242    }
243
244    for i in 0..len_a {
245        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
246        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
247        match unsafe { elem_shape.call_partial_eq(item_a, item_b) } {
248            Some(true) => continue,
249            Some(false) => return Some(false),
250            None => return None,
251        }
252    }
253    Some(true)
254}
255
256/// Type-erased partial_cmp implementation for Vec
257unsafe fn vec_partial_cmp_erased(
258    ox_a: OxPtrConst,
259    ox_b: OxPtrConst,
260) -> Option<Option<core::cmp::Ordering>> {
261    let shape = ox_a.shape();
262    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
263    if !elem_shape.vtable.has_partial_ord() {
264        return None;
265    }
266
267    let list_def = get_list_def(shape);
268    let ptr_a = ox_a.ptr();
269    let ptr_b = ox_b.ptr();
270    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
271    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
272
273    let min_len = len_a.min(len_b);
274
275    for i in 0..min_len {
276        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
277        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
278        match unsafe { elem_shape.call_partial_cmp(item_a, item_b) } {
279            Some(Some(core::cmp::Ordering::Equal)) => continue,
280            Some(ord) => return Some(ord),
281            None => return None,
282        }
283    }
284    Some(Some(len_a.cmp(&len_b)))
285}
286
287/// Type-erased cmp implementation for Vec
288unsafe fn vec_cmp_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<core::cmp::Ordering> {
289    let shape = ox_a.shape();
290    let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
291    if !elem_shape.vtable.has_ord() {
292        return None;
293    }
294
295    let list_def = get_list_def(shape);
296    let ptr_a = ox_a.ptr();
297    let ptr_b = ox_b.ptr();
298    let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
299    let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
300
301    let min_len = len_a.min(len_b);
302
303    for i in 0..min_len {
304        let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
305        let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
306        match unsafe { elem_shape.call_cmp(item_a, item_b) } {
307            Some(core::cmp::Ordering::Equal) => continue,
308            Some(ord) => return Some(ord),
309            None => return None,
310        }
311    }
312    Some(len_a.cmp(&len_b))
313}
314
315// =============================================================================
316// Generic functions that still need T
317// =============================================================================
318
319type VecIterator<'mem, T> = core::slice::Iter<'mem, T>;
320
321unsafe extern "C" fn vec_init_in_place_with_capacity<T>(
322    uninit: PtrUninit,
323    capacity: usize,
324) -> PtrMut {
325    unsafe { uninit.put(Vec::<T>::with_capacity(capacity)) }
326}
327
328unsafe extern "C" fn vec_push<T>(ptr: PtrMut, item: PtrMut) {
329    unsafe {
330        let vec = ptr.as_mut::<Vec<T>>();
331        let item = item.read::<T>();
332        vec.push(item);
333    }
334}
335
336/// Pop the last element from a `Vec<T>`, moving it into `out`.
337///
338/// Returns `true` if an element was popped, `false` if the vec was empty.
339///
340/// # Safety
341/// - `ptr` must point to an initialized `Vec<T>`.
342/// - When returning `true`, `out` must have been uninitialized memory of at
343///   least `size_of::<T>()` bytes, correctly aligned for `T`. The element is
344///   moved into `out` (no destructor is run on the source element).
345unsafe extern "C" fn vec_pop<T>(ptr: PtrMut, out: PtrUninit) -> bool {
346    unsafe {
347        let vec = ptr.as_mut::<Vec<T>>();
348        match vec.pop() {
349            Some(value) => {
350                out.put(value);
351                true
352            }
353            None => false,
354        }
355    }
356}
357
358/// Set the length of a Vec (for direct-fill operations).
359///
360/// # Safety
361/// - `ptr` must point to an initialized `Vec<T>`
362/// - `len` must not exceed the Vec's capacity
363/// - All elements at indices `0..len` must be properly initialized
364unsafe extern "C" fn vec_set_len<T>(ptr: PtrMut, len: usize) {
365    unsafe {
366        let vec = ptr.as_mut::<Vec<T>>();
367        vec.set_len(len);
368    }
369}
370
371/// Get raw mutable pointer to Vec's data buffer as `*mut u8`.
372///
373/// # Safety
374/// - `ptr` must point to an initialized `Vec<T>`
375unsafe extern "C" fn vec_as_mut_ptr_typed<T>(ptr: PtrMut) -> *mut u8 {
376    unsafe {
377        let vec = ptr.as_mut::<Vec<T>>();
378        vec.as_mut_ptr() as *mut u8
379    }
380}
381
382/// Reserve capacity for at least `additional` more elements.
383///
384/// # Safety
385/// - `ptr` must point to an initialized `Vec<T>`
386unsafe extern "C" fn vec_reserve<T>(ptr: PtrMut, additional: usize) {
387    unsafe {
388        let vec = ptr.as_mut::<Vec<T>>();
389        vec.reserve(additional);
390    }
391}
392
393/// Get the current capacity of the Vec.
394///
395/// # Safety
396/// - `ptr` must point to an initialized `Vec<T>`
397unsafe extern "C" fn vec_capacity<T>(ptr: PtrConst) -> usize {
398    unsafe {
399        let vec = ptr.get::<Vec<T>>();
400        vec.capacity()
401    }
402}
403
404unsafe extern "C" fn vec_iter_init<T>(ptr: PtrConst) -> PtrMut {
405    unsafe {
406        let vec = ptr.get::<Vec<T>>();
407        let iter: VecIterator<T> = vec.iter();
408        let iter_state = Box::new(iter);
409        PtrMut::new(Box::into_raw(iter_state) as *mut u8)
410    }
411}
412
413unsafe fn vec_iter_next<T>(iter_ptr: PtrMut) -> Option<PtrConst> {
414    // SAFETY: The iterator was created from a Vec that must outlive this call.
415    // We transmute to erase the lifetime - the actual memory layout of Iter is
416    // the same regardless of lifetime parameter.
417    unsafe {
418        let state: *mut VecIterator<'_, T> = iter_ptr.as_ptr::<()>() as *mut _;
419        (*state)
420            .next()
421            .map(|value| PtrConst::new(value as *const T))
422    }
423}
424
425unsafe fn vec_iter_next_back<T>(iter_ptr: PtrMut) -> Option<PtrConst> {
426    // SAFETY: Same as vec_iter_next
427    unsafe {
428        let state: *mut VecIterator<'_, T> = iter_ptr.as_ptr::<()>() as *mut _;
429        (*state)
430            .next_back()
431            .map(|value| PtrConst::new(value as *const T))
432    }
433}
434
435unsafe extern "C" fn vec_iter_dealloc<T>(iter_ptr: PtrMut) {
436    unsafe {
437        drop(Box::from_raw(
438            iter_ptr.as_ptr::<VecIterator<'_, T>>() as *mut VecIterator<'_, T>
439        ))
440    }
441}
442
443unsafe impl<'a, T: Facet<'a>> Facet<'a> for Vec<T> {
444    const SHAPE: &'static Shape =
445        &const {
446            // Per-T operations that must be monomorphized
447            const fn build_list_type_ops<T>() -> ListTypeOps {
448                ListTypeOps::builder()
449                    .init_in_place_with_capacity(vec_init_in_place_with_capacity::<T>)
450                    .push(vec_push::<T>)
451                    .pop(vec_pop::<T>)
452                    .set_len(vec_set_len::<T>)
453                    .as_mut_ptr_typed(vec_as_mut_ptr_typed::<T>)
454                    .reserve(vec_reserve::<T>)
455                    .capacity(vec_capacity::<T>)
456                    .iter_vtable(IterVTable {
457                        init_with_value: Some(vec_iter_init::<T>),
458                        next: vec_iter_next::<T>,
459                        next_back: Some(vec_iter_next_back::<T>),
460                        size_hint: None,
461                        dealloc: vec_iter_dealloc::<T>,
462                    })
463                    .build()
464            }
465
466            ShapeBuilder::for_sized::<Self>("Vec")            .module_path("alloc::vec")
467            .type_name(vec_type_name)
468            .ty(Type::User(UserType::Opaque))
469            .def(Def::List(ListDef::with_type_ops(
470                // Use the SHARED vtable for all Vec<T>!
471                &VEC_LIST_VTABLE,
472                &const { build_list_type_ops::<T>() },
473                T::SHAPE,
474            )))
475            .type_params(&[TypeParam {
476                name: "T",
477                shape: T::SHAPE,
478            }])
479            .inner(T::SHAPE)
480            // Vec<T> propagates T's variance
481            .variance(VarianceDesc {
482                base: Variance::Bivariant,
483                deps: &const { [VarianceDep::covariant(T::SHAPE)] },
484            })
485            .vtable_indirect(&const {
486                VTableIndirect {
487                    debug: Some(vec_debug_erased),
488                    partial_eq: Some(vec_partial_eq_erased),
489                    partial_cmp: Some(vec_partial_cmp_erased),
490                    cmp: Some(vec_cmp_erased),
491                    display: None,
492                    hash: None,
493                    invariants: None,
494                    parse: None,
495                    parse_bytes: None,
496                    try_from: None,
497                    try_into_inner: None,
498                    try_borrow_inner: None,
499                }
500            })
501            .type_ops_indirect(&const {
502                unsafe fn drop_in_place<T>(ox: OxPtrMut) {
503                    unsafe {
504                        core::ptr::drop_in_place(ox.ptr().as_ptr::<Vec<T>>() as *mut Vec<T>);
505                    }
506                }
507
508                unsafe fn default_in_place<T>(ox: OxPtrUninit) -> bool {
509                    unsafe { ox.put(Vec::<T>::new()) };
510                    true
511                }
512
513                unsafe fn truthy<T>(ptr: PtrConst) -> bool {
514                    !unsafe { ptr.get::<Vec<T>>() }.is_empty()
515                }
516
517                TypeOpsIndirect {
518                    drop_in_place: drop_in_place::<T>,
519                    default_in_place: Some(default_in_place::<T>),
520                    clone_into: None,
521                    is_truthy: Some(truthy::<T>),
522                }
523            })
524            .build()
525        };
526}