Skip to main content

trame_runtime/live/
mod.rs

1//! Live implementations of all of trame's runtime traits: no verification involved, real memory
2//! allocations, etc.
3
4use crate::{IArena, IField, IHeap, IRuntime, IShape, IShapeStore, IStructType, Idx};
5use facet_core::{Field, Shape, StructType, Type, UserType};
6
7/// A "live" runtime that just peforms raw unsafe Rust operations
8pub struct LRuntime;
9
10impl IRuntime for LRuntime {
11    type Shape = &'static Shape;
12    type Heap = LHeap;
13    type Arena<T> = LArena<T>;
14
15    fn heap() -> Self::Heap {
16        LHeap::new()
17    }
18
19    fn arena<T>() -> Self::Arena<T> {
20        LArena::new()
21    }
22}
23
24// ==================================================================
25// Shape
26// ==================================================================
27
28/// Live shape store: handle and view are the static shape itself.
29#[derive(Clone, Copy, Default)]
30pub struct LShapeStore;
31
32impl IShapeStore for LShapeStore {
33    type Handle = &'static Shape;
34    type View<'a>
35        = &'static Shape
36    where
37        Self: 'a;
38
39    fn get<'a>(&'a self, handle: Self::Handle) -> Self::View<'a> {
40        handle
41    }
42}
43
44impl IShape for &'static Shape {
45    type StructType = &'static StructType;
46    type Field = &'static Field;
47
48    #[inline]
49    fn layout(&self) -> Option<std::alloc::Layout> {
50        self.layout.sized_layout().ok()
51    }
52
53    #[inline]
54    fn is_struct(&self) -> bool {
55        matches!(self.ty, Type::User(UserType::Struct(_)))
56    }
57
58    #[inline]
59    fn as_struct(&self) -> Option<Self::StructType> {
60        match &self.ty {
61            Type::User(UserType::Struct(st)) => Some(st),
62            _ => None,
63        }
64    }
65}
66
67impl IStructType for &'static StructType {
68    type Field = &'static Field;
69
70    #[inline]
71    fn field_count(&self) -> usize {
72        self.fields.len()
73    }
74
75    #[inline]
76    fn field(&self, idx: usize) -> Option<Self::Field> {
77        self.fields.get(idx)
78    }
79}
80
81impl IField for &'static Field {
82    type Shape = &'static Shape;
83
84    #[inline]
85    fn offset(&self) -> usize {
86        self.offset
87    }
88
89    #[inline]
90    fn shape(&self) -> Self::Shape {
91        self.shape.get()
92    }
93}
94
95// ==================================================================
96// Heap
97// ==================================================================
98
99/// Live heap that performs actual memory operations.
100#[derive(Debug)]
101pub struct LHeap;
102
103impl LHeap {
104    /// Create a new live heap.
105    pub const fn new() -> Self {
106        Self
107    }
108}
109
110impl Default for LHeap {
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116impl IHeap<&'static Shape> for LHeap {
117    type Ptr = *mut u8;
118
119    unsafe fn alloc(&mut self, shape: &'static Shape) -> *mut u8 {
120        let layout = shape.layout();
121        if let Some(layout) = layout {
122            if layout.size() == 0 {
123                // "Note that layouts are not required to have non-zero size, even though
124                // GlobalAlloc requires that all memory requests be non-zero in size. A caller
125                // must either ensure that conditions like this are met, use specific allocators
126                // with looser requirements, or use the more lenient Allocator interface"
127                // (std::alloc::Layout)
128                // https://doc.rust-lang.org/stable/std/alloc/struct.Layout.html
129                // ZST - no allocation required
130                core::ptr::NonNull::dangling().as_ptr()
131            } else {
132                // SAFETY: layout.size() > 0
133                let ptr = unsafe { std::alloc::alloc(layout) };
134                if ptr.is_null() {
135                    std::alloc::handle_alloc_error(layout);
136                }
137                ptr
138            }
139        } else {
140            // No layout available (e.g., ZST/unsized) - return dangling.
141            core::ptr::NonNull::dangling().as_ptr()
142        }
143    }
144
145    unsafe fn dealloc(&mut self, ptr: *mut u8, shape: &'static Shape) {
146        if let Some(layout) = shape.layout() {
147            if layout.size() > 0 {
148                // SAFETY: caller guarantees this is a live allocation
149                unsafe { std::alloc::dealloc(ptr, layout) };
150            }
151        }
152    }
153
154    unsafe fn memcpy(&mut self, dst: *mut u8, src: *mut u8, len: usize) {
155        if len > 0 {
156            // SAFETY: caller guarantees non-overlapping, valid pointers
157            unsafe {
158                core::ptr::copy_nonoverlapping(src, dst, len);
159            }
160        }
161    }
162
163    unsafe fn drop_in_place(&mut self, ptr: *mut u8, shape: &'static Shape) {
164        unsafe { shape.call_drop_in_place(facet_core::PtrMut::new(ptr)) };
165    }
166
167    unsafe fn default_in_place(&mut self, ptr: *mut u8, shape: &'static Shape) -> bool {
168        unsafe {
169            shape
170                .call_default_in_place(facet_core::PtrMut::new(ptr).into())
171                .is_some()
172        }
173    }
174}
175
176// ==================================================================
177// Arena
178// ==================================================================
179
180/// Vec-based arena with free list for production use.
181pub struct LArena<T> {
182    slots: Vec<Option<T>>,
183    free_list: Vec<u32>,
184}
185
186impl<T> LArena<T> {
187    /// Create a new live arena.
188    pub fn new() -> Self {
189        Self {
190            slots: vec![None], // Slot 0 reserved for NOT_STARTED
191            free_list: Vec::new(),
192        }
193    }
194}
195
196impl<T> Default for LArena<T> {
197    fn default() -> Self {
198        Self::new()
199    }
200}
201
202impl<T> IArena<T> for LArena<T> {
203    fn alloc(&mut self, value: T) -> Idx<T> {
204        let raw = if let Some(idx) = self.free_list.pop() {
205            debug_assert!(self.slots[idx as usize].is_none());
206            self.slots[idx as usize] = Some(value);
207            idx
208        } else {
209            let idx = self.slots.len();
210            assert!(idx < u32::MAX as usize, "arena full");
211            self.slots.push(Some(value));
212            idx as u32
213        };
214        Idx::from_raw(raw)
215    }
216
217    fn free(&mut self, id: Idx<T>) -> T {
218        debug_assert!(id.is_valid());
219        let value = self.slots[id.index()].take().expect("double-free");
220        self.free_list.push(id.raw);
221        value
222    }
223
224    fn get(&self, id: Idx<T>) -> &T {
225        debug_assert!(id.is_valid());
226        self.slots[id.index()].as_ref().expect("slot empty")
227    }
228
229    fn get_mut(&mut self, id: Idx<T>) -> &mut T {
230        debug_assert!(id.is_valid());
231        self.slots[id.index()].as_mut().expect("slot empty")
232    }
233}
234
235#[cfg(test)]
236mod tests;