use crate::*;
use alloc::boxed::Box;
use alloc::vec::Vec;
struct DebugViaShape(&'static Shape, PtrConst);
impl core::fmt::Debug for DebugViaShape {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match unsafe { self.0.call_debug(self.1, f) } {
Some(result) => result,
None => write!(f, "???"),
}
}
}
#[repr(C)]
struct VecLayout {
#[allow(dead_code)]
cap: usize,
ptr: *mut u8,
len: usize,
}
const _: () = {
let v: Vec<u8> = Vec::new();
let fields: [usize; 3] = unsafe { core::mem::transmute(v) };
assert!(
core::mem::size_of::<Vec<u8>>() == core::mem::size_of::<VecLayout>(),
"VecLayout size mismatch"
);
assert!(
core::mem::align_of::<Vec<u8>>() == core::mem::align_of::<VecLayout>(),
"VecLayout align mismatch"
);
assert!(fields[0] == 0, "expected cap=0 at offset 0");
assert!(fields[1] != 0, "expected non-null ptr at offset 1");
assert!(fields[2] == 0, "expected len=0 at offset 2");
};
unsafe extern "C" fn vec_len_erased(ptr: PtrConst) -> usize {
unsafe {
let layout = ptr.as_byte_ptr() as *const VecLayout;
(*layout).len
}
}
unsafe fn vec_get_erased(ptr: PtrConst, index: usize, shape: &'static Shape) -> Option<PtrConst> {
unsafe {
let layout = ptr.as_byte_ptr() as *const VecLayout;
let len = (*layout).len;
if index >= len {
return None;
}
let elem_size = shape
.type_params
.first()?
.shape
.layout
.sized_layout()
.ok()?
.size();
let data_ptr = (*layout).ptr;
Some(PtrConst::new(data_ptr.add(index * elem_size)))
}
}
unsafe fn vec_get_mut_erased(ptr: PtrMut, index: usize, shape: &'static Shape) -> Option<PtrMut> {
unsafe {
let layout = ptr.as_byte_ptr() as *const VecLayout;
let len = (*layout).len;
if index >= len {
return None;
}
let elem_size = shape
.type_params
.first()?
.shape
.layout
.sized_layout()
.ok()?
.size();
let data_ptr = (*layout).ptr;
Some(PtrMut::new(data_ptr.add(index * elem_size)))
}
}
static VEC_LIST_VTABLE: ListVTable = ListVTable {
len: vec_len_erased,
get: vec_get_erased,
get_mut: Some(vec_get_mut_erased),
as_ptr: Some(vec_as_ptr_erased),
as_mut_ptr: Some(vec_as_mut_ptr_erased),
swap: Some(vec_swap_erased),
};
unsafe extern "C" fn vec_as_ptr_erased(ptr: PtrConst) -> PtrConst {
unsafe {
let layout = ptr.as_byte_ptr() as *const VecLayout;
PtrConst::new((*layout).ptr)
}
}
unsafe extern "C" fn vec_as_mut_ptr_erased(ptr: PtrMut) -> PtrMut {
unsafe {
let layout = ptr.as_byte_ptr() as *const VecLayout;
PtrMut::new((*layout).ptr)
}
}
unsafe fn vec_swap_erased(ptr: PtrMut, a: usize, b: usize, shape: &'static Shape) -> bool {
unsafe {
let layout = ptr.as_byte_ptr() as *const VecLayout;
let len = (*layout).len;
if a >= len || b >= len {
return false;
}
if a == b {
return true;
}
let Ok(elem_layout) = shape
.type_params
.first()
.expect("Vec shape must have an element type parameter")
.shape
.layout
.sized_layout()
else {
return false;
};
let elem_size = elem_layout.size();
let data_ptr = (*layout).ptr;
core::ptr::swap_nonoverlapping(
data_ptr.add(a * elem_size),
data_ptr.add(b * elem_size),
elem_size,
);
true
}
}
fn vec_type_name(
shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
write!(f, "Vec")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
if let Some(tp) = shape.type_params.first() {
tp.shape.write_type_name(f, opts)?;
}
write!(f, ">")?;
} else {
write!(f, "<…>")?;
}
Ok(())
}
#[inline]
fn get_list_def(shape: &'static Shape) -> &'static ListDef {
match &shape.def {
Def::List(list_def) => list_def,
_ => panic!("expected List def"),
}
}
unsafe fn vec_debug_erased(
ox: OxPtrConst,
f: &mut core::fmt::Formatter<'_>,
) -> Option<core::fmt::Result> {
let shape = ox.shape();
let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
if !elem_shape.vtable.has_debug() {
return None;
}
let list_def = get_list_def(shape);
let ptr = ox.ptr();
let len = unsafe { (list_def.vtable.len)(ptr) };
let mut list = f.debug_list();
for i in 0..len {
if let Some(item_ptr) = unsafe { (list_def.vtable.get)(ptr, i, shape) } {
list.entry(&DebugViaShape(elem_shape, item_ptr));
}
}
Some(list.finish())
}
unsafe fn vec_partial_eq_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<bool> {
let shape = ox_a.shape();
let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
if !elem_shape.vtable.has_partial_eq() {
return None;
}
let list_def = get_list_def(shape);
let ptr_a = ox_a.ptr();
let ptr_b = ox_b.ptr();
let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
if len_a != len_b {
return Some(false);
}
for i in 0..len_a {
let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
match unsafe { elem_shape.call_partial_eq(item_a, item_b) } {
Some(true) => continue,
Some(false) => return Some(false),
None => return None,
}
}
Some(true)
}
unsafe fn vec_partial_cmp_erased(
ox_a: OxPtrConst,
ox_b: OxPtrConst,
) -> Option<Option<core::cmp::Ordering>> {
let shape = ox_a.shape();
let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
if !elem_shape.vtable.has_partial_ord() {
return None;
}
let list_def = get_list_def(shape);
let ptr_a = ox_a.ptr();
let ptr_b = ox_b.ptr();
let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
let min_len = len_a.min(len_b);
for i in 0..min_len {
let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
match unsafe { elem_shape.call_partial_cmp(item_a, item_b) } {
Some(Some(core::cmp::Ordering::Equal)) => continue,
Some(ord) => return Some(ord),
None => return None,
}
}
Some(Some(len_a.cmp(&len_b)))
}
unsafe fn vec_cmp_erased(ox_a: OxPtrConst, ox_b: OxPtrConst) -> Option<core::cmp::Ordering> {
let shape = ox_a.shape();
let elem_shape = shape.type_params.first().map(|tp| tp.shape)?;
if !elem_shape.vtable.has_ord() {
return None;
}
let list_def = get_list_def(shape);
let ptr_a = ox_a.ptr();
let ptr_b = ox_b.ptr();
let len_a = unsafe { (list_def.vtable.len)(ptr_a) };
let len_b = unsafe { (list_def.vtable.len)(ptr_b) };
let min_len = len_a.min(len_b);
for i in 0..min_len {
let item_a = unsafe { (list_def.vtable.get)(ptr_a, i, shape) }?;
let item_b = unsafe { (list_def.vtable.get)(ptr_b, i, shape) }?;
match unsafe { elem_shape.call_cmp(item_a, item_b) } {
Some(core::cmp::Ordering::Equal) => continue,
Some(ord) => return Some(ord),
None => return None,
}
}
Some(len_a.cmp(&len_b))
}
type VecIterator<'mem, T> = core::slice::Iter<'mem, T>;
unsafe extern "C" fn vec_init_in_place_with_capacity<T>(
uninit: PtrUninit,
capacity: usize,
) -> PtrMut {
unsafe { uninit.put(Vec::<T>::with_capacity(capacity)) }
}
unsafe extern "C" fn vec_push<T>(ptr: PtrMut, item: PtrMut) {
unsafe {
let vec = ptr.as_mut::<Vec<T>>();
let item = item.read::<T>();
vec.push(item);
}
}
unsafe extern "C" fn vec_pop<T>(ptr: PtrMut, out: PtrUninit) -> bool {
unsafe {
let vec = ptr.as_mut::<Vec<T>>();
match vec.pop() {
Some(value) => {
out.put(value);
true
}
None => false,
}
}
}
unsafe extern "C" fn vec_set_len<T>(ptr: PtrMut, len: usize) {
unsafe {
let vec = ptr.as_mut::<Vec<T>>();
vec.set_len(len);
}
}
unsafe extern "C" fn vec_as_mut_ptr_typed<T>(ptr: PtrMut) -> *mut u8 {
unsafe {
let vec = ptr.as_mut::<Vec<T>>();
vec.as_mut_ptr() as *mut u8
}
}
unsafe extern "C" fn vec_reserve<T>(ptr: PtrMut, additional: usize) {
unsafe {
let vec = ptr.as_mut::<Vec<T>>();
vec.reserve(additional);
}
}
unsafe extern "C" fn vec_capacity<T>(ptr: PtrConst) -> usize {
unsafe {
let vec = ptr.get::<Vec<T>>();
vec.capacity()
}
}
unsafe extern "C" fn vec_iter_init<T>(ptr: PtrConst) -> PtrMut {
unsafe {
let vec = ptr.get::<Vec<T>>();
let iter: VecIterator<T> = vec.iter();
let iter_state = Box::new(iter);
PtrMut::new(Box::into_raw(iter_state) as *mut u8)
}
}
unsafe fn vec_iter_next<T>(iter_ptr: PtrMut) -> Option<PtrConst> {
unsafe {
let state: *mut VecIterator<'_, T> = iter_ptr.as_ptr::<()>() as *mut _;
(*state)
.next()
.map(|value| PtrConst::new(value as *const T))
}
}
unsafe fn vec_iter_next_back<T>(iter_ptr: PtrMut) -> Option<PtrConst> {
unsafe {
let state: *mut VecIterator<'_, T> = iter_ptr.as_ptr::<()>() as *mut _;
(*state)
.next_back()
.map(|value| PtrConst::new(value as *const T))
}
}
unsafe extern "C" fn vec_iter_dealloc<T>(iter_ptr: PtrMut) {
unsafe {
drop(Box::from_raw(
iter_ptr.as_ptr::<VecIterator<'_, T>>() as *mut VecIterator<'_, T>
))
}
}
unsafe impl<'a, T: Facet<'a>> Facet<'a> for Vec<T> {
const SHAPE: &'static Shape =
&const {
const fn build_list_type_ops<T>() -> ListTypeOps {
ListTypeOps::builder()
.init_in_place_with_capacity(vec_init_in_place_with_capacity::<T>)
.push(vec_push::<T>)
.pop(vec_pop::<T>)
.set_len(vec_set_len::<T>)
.as_mut_ptr_typed(vec_as_mut_ptr_typed::<T>)
.reserve(vec_reserve::<T>)
.capacity(vec_capacity::<T>)
.iter_vtable(IterVTable {
init_with_value: Some(vec_iter_init::<T>),
next: vec_iter_next::<T>,
next_back: Some(vec_iter_next_back::<T>),
size_hint: None,
dealloc: vec_iter_dealloc::<T>,
})
.build()
}
ShapeBuilder::for_sized::<Self>("Vec") .module_path("alloc::vec")
.type_name(vec_type_name)
.ty(Type::User(UserType::Opaque))
.def(Def::List(ListDef::with_type_ops(
&VEC_LIST_VTABLE,
&const { build_list_type_ops::<T>() },
T::SHAPE,
)))
.type_params(&[TypeParam {
name: "T",
shape: T::SHAPE,
}])
.inner(T::SHAPE)
.variance(VarianceDesc {
base: Variance::Bivariant,
deps: &const { [VarianceDep::covariant(T::SHAPE)] },
})
.vtable_indirect(&const {
VTableIndirect {
debug: Some(vec_debug_erased),
partial_eq: Some(vec_partial_eq_erased),
partial_cmp: Some(vec_partial_cmp_erased),
cmp: Some(vec_cmp_erased),
display: None,
hash: None,
invariants: None,
parse: None,
parse_bytes: None,
try_from: None,
try_into_inner: None,
try_borrow_inner: None,
}
})
.type_ops_indirect(&const {
unsafe fn drop_in_place<T>(ox: OxPtrMut) {
unsafe {
core::ptr::drop_in_place(ox.ptr().as_ptr::<Vec<T>>() as *mut Vec<T>);
}
}
unsafe fn default_in_place<T>(ox: OxPtrUninit) -> bool {
unsafe { ox.put(Vec::<T>::new()) };
true
}
unsafe fn truthy<T>(ptr: PtrConst) -> bool {
!unsafe { ptr.get::<Vec<T>>() }.is_empty()
}
TypeOpsIndirect {
drop_in_place: drop_in_place::<T>,
default_in_place: Some(default_in_place::<T>),
clone_into: None,
is_truthy: Some(truthy::<T>),
}
})
.build()
};
}