use core::alloc::Layout;
use generativity::Id;
use i_slint_core::rtti::FieldOffset;
use std::rc::Rc;
unsafe fn construct_fn<T: Default>(ptr: *mut u8) {
core::ptr::write(ptr as *mut T, T::default());
}
unsafe fn drop_fn<T>(ptr: *mut u8) {
core::ptr::drop_in_place(ptr as *mut T);
}
#[derive(Copy, Clone)]
pub struct StaticTypeInfo {
construct: Option<unsafe fn(*mut u8)>,
drop: Option<unsafe fn(*mut u8)>,
mem_layout: Layout,
}
impl StaticTypeInfo {
pub fn new<T: Default>() -> StaticTypeInfo {
let drop = if core::mem::needs_drop::<T>() { Some(drop_fn::<T> as _) } else { None };
StaticTypeInfo { construct: Some(construct_fn::<T>), drop, mem_layout: Layout::new::<T>() }
}
}
struct FieldInfo {
construct: Option<unsafe fn(*mut u8)>,
drop: Option<unsafe fn(*mut u8)>,
offset: usize,
}
pub struct TypeInfo<'id> {
mem_layout: core::alloc::Layout,
fields: Vec<FieldInfo>,
#[allow(unused)]
id: Id<'id>,
}
pub struct TypeBuilder<'id> {
align: usize,
size: usize,
fields: Vec<FieldInfo>,
id: Id<'id>,
}
impl<'id> TypeBuilder<'id> {
pub fn new(id: generativity::Guard<'id>) -> Self {
let mut s = Self { align: 1, size: 0, fields: vec![], id: id.into() };
type T<'id> = Rc<TypeInfo<'id>>;
s.add_field(StaticTypeInfo {
construct: None,
drop: Some(drop_fn::<T<'id>>),
mem_layout: Layout::new::<T<'id>>(),
});
s
}
pub fn add_field_type<T: Default>(&mut self) -> FieldOffset<Instance<'id>, T> {
unsafe { FieldOffset::new_from_offset_pinned(self.add_field(StaticTypeInfo::new::<T>())) }
}
pub fn add_field(&mut self, ty: StaticTypeInfo) -> usize {
let align = ty.mem_layout.align();
let len_rounded_up = self.size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
self.fields.push(FieldInfo {
construct: ty.construct,
drop: ty.drop,
offset: len_rounded_up,
});
self.size = len_rounded_up + ty.mem_layout.size();
self.align = self.align.max(align);
len_rounded_up
}
pub fn build(self) -> Rc<TypeInfo<'id>> {
let size = self.size.wrapping_add(self.align).wrapping_sub(1) & !self.align.wrapping_sub(1);
Rc::new(TypeInfo {
mem_layout: core::alloc::Layout::from_size_align(size, self.align).unwrap(),
fields: self.fields,
id: self.id,
})
}
}
impl<'id> TypeInfo<'id> {
pub fn create_instance(self: Rc<Self>) -> InstanceBox<'id> {
unsafe {
let mem = std::alloc::alloc(self.mem_layout) as *mut Instance;
self.create_instance_in_place(mem);
InstanceBox(core::ptr::NonNull::new_unchecked(mem))
}
}
pub unsafe fn create_instance_in_place(self: Rc<Self>, mem: *mut Instance<'id>) {
let mem = mem as *mut u8;
std::ptr::write(mem as *mut Rc<_>, self.clone());
for f in &self.fields {
if let Some(ctor) = f.construct {
ctor(mem.add(f.offset));
}
}
}
pub unsafe fn drop_in_place(instance: *mut Instance) {
let type_info = (*instance).type_info.clone();
let mem = instance as *mut u8;
for f in &type_info.fields {
if let Some(dtor) = f.drop {
dtor(mem.add(f.offset));
}
}
}
unsafe fn delete_instance(instance: *mut Instance) {
let mem_layout = (*instance).type_info.mem_layout;
Self::drop_in_place(instance);
let mem = instance as *mut u8;
std::alloc::dealloc(mem, mem_layout);
}
pub fn layout(&self) -> core::alloc::Layout {
self.mem_layout
}
}
#[repr(C)]
pub struct Instance<'id> {
type_info: Rc<TypeInfo<'id>>,
_opaque: [u8; 0],
}
impl<'id> Instance<'id> {
pub fn type_info(&self) -> Rc<TypeInfo<'id>> {
self.type_info.clone()
}
}
impl<'id> core::fmt::Debug for Instance<'id> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Instance({:p})", self)
}
}
pub struct InstanceBox<'id>(core::ptr::NonNull<Instance<'id>>);
impl<'id> InstanceBox<'id> {
pub fn as_ptr(&self) -> core::ptr::NonNull<Instance<'id>> {
self.0
}
pub fn as_pin_ref(&self) -> core::pin::Pin<&Instance<'id>> {
unsafe { core::pin::Pin::new_unchecked(self.0.as_ref()) }
}
pub fn as_mut(&mut self) -> &mut Instance<'id> {
unsafe { self.0.as_mut() }
}
}
impl<'id> Drop for InstanceBox<'id> {
fn drop(&mut self) {
unsafe { TypeInfo::delete_instance(self.0.as_mut()) }
}
}