1use std::{alloc::Layout, marker::PhantomData, ptr::NonNull};
5
6use crate::{GcError, GcHead, GcHeap, GcNode, GcPartitionId, GcRef, unlikely, weak::GcWeakRawId};
7
8impl GcHeap {
9 fn mem_alloc(&mut self, size: usize) -> Option<NonNull<u8>> {
10 debug_assert_ne!(size, 0);
11
12 let layout = Layout::from_size_align(size, std::mem::align_of::<usize>()).ok()?;
13 debug_assert_eq!(layout.size(), size);
14
15 unsafe {
16 let ptr = std::alloc::alloc(layout);
17
18 if !ptr.is_null() {
19 #[cfg(debug_assertions)]
20 {
21 let n = NonNull::new_unchecked(ptr).cast::<GcHead>();
22 debug_assert!(
23 !self.dbg_living_nodes.contains(&n),
24 "node {ptr:?} already exists"
25 );
26 self.dbg_living_nodes.insert(n);
27 }
28
29 Some(NonNull::new_unchecked(ptr))
30 } else {
31 None
32 }
33 }
34 }
35
36 fn mem_dealloc(&mut self, ptr: NonNull<u8>, gross_size: usize) {
37 debug_assert_ne!(gross_size, 0);
38
39 #[cfg(debug_assertions)]
40 debug_assert!(
41 self.dbg_living_nodes.contains(&ptr.cast()),
42 "[O.o][dealloc] bad pointer {ptr:?}"
43 );
44
45 let ly = Layout::from_size_align(gross_size, std::mem::align_of::<usize>());
46
47 #[cfg(debug_assertions)]
48 let layout = ly.unwrap();
49 #[cfg(not(debug_assertions))]
50 let layout = unsafe { ly.unwrap_unchecked() };
51
52 debug_assert_eq!(layout.size(), gross_size);
53
54 unsafe {
55 #[cfg(debug_assertions)]
56 self.dbg_living_nodes.remove(&ptr.cast());
57
58 std::alloc::dealloc(ptr.as_ptr(), layout);
59 }
60 }
61
62 unsafe fn alloc_node_mem<T: GcNode>(
64 &mut self,
65 partition_id: GcPartitionId,
66 payload: T,
67 ) -> Result<(NonNull<GcHead>, usize), (GcError, T)> {
68 match self.partition_mut(partition_id) {
69 Some(_) => {
70 let size = std::mem::size_of::<T>();
71 let gross_size = std::mem::size_of::<GcHead>() + size;
72
73 if unlikely(
74 self.memory_limit > 0
75 && self.total_memory_used + gross_size > self.memory_limit,
76 ) {
77 Err((GcError::PartitionFull, payload))
78 } else {
79 let gc_type = T::GC_TYPE_ID;
80 let ptr = match self.mem_alloc(gross_size) {
81 Some(p) => p,
82 None => {
83 return Err((GcError::AllocationFailed, payload));
84 }
85 };
86
87 let head = ptr.cast::<GcHead>();
88
89 unsafe {
91 std::ptr::write(head.add(1).cast::<T>().as_ptr(), payload);
92 }
93
94 let node_info = GcHead {
95 attrs: { 0xFF00_0000 | ((gc_type as u32) << 8) },
96 partition: 0,
97 weak_id: GcWeakRawId::NULL,
98 next: None,
99
100 #[cfg(debug_assertions)]
101 dbg_scope_depth: self.scope_max_depth(),
102 #[cfg(debug_assertions)]
103 dbg_string: std::any::type_name::<T>().into(),
104 };
105
106 unsafe {
107 std::ptr::write(head.as_ptr(), node_info);
108 }
109
110 self.update_mem_use(partition_id, gross_size as i32);
111
112 Ok((head, gross_size))
113 }
114 }
115 None => Err((GcError::PartitionNotFound, payload)),
116 }
117 }
118
119 pub unsafe fn alloc_raw<T: GcNode>(
127 &mut self,
128 partition_id: GcPartitionId,
129 payload: T,
130 ) -> Result<GcRef<T>, (GcError, T)> {
131 match unsafe { self.alloc_node_mem(partition_id, payload) } {
132 Ok((head, _)) => {
133 self.attach_node(partition_id, head);
134
135 log::trace!("[alloc] {:?}", unsafe { head.as_ref() });
136
137 Ok(GcRef {
138 head_ptr: head,
139 _marker: PhantomData,
140 })
141 }
142 Err(e) => Err(e),
143 }
144 }
145
146 pub unsafe fn alloc_root_raw<T: GcNode>(
152 &mut self,
153 partition_id: GcPartitionId,
154 payload: T,
155 ) -> Result<GcRef<T>, (GcError, T)> {
156 let (mut node, _) = unsafe { self.alloc_node_mem(partition_id, payload)? };
157
158 unsafe { node.as_mut() }.insert_flag(crate::node::GcNodeFlag::ROOT);
159
160 self.attach_node(partition_id, node);
161
162 let par = self.partition_mut(partition_id).unwrap();
163 if par.is_marking() {
164 par.add_gray_node(node);
165 }
166
167 log::trace!("[alloc_root] {:?}", unsafe { node.as_ref() });
168
169 Ok(GcRef {
170 head_ptr: node,
171 _marker: PhantomData,
172 })
173 }
174
175 pub(crate) fn dispose(&mut self, node: NonNull<GcHead>) -> usize {
177 let hd = unsafe { node.as_ref() };
178 log::trace!("[dispose] {hd:?}");
179
180 #[cfg(debug_assertions)]
181 hd.debug_assert_node_valid(self);
182
183 if !hd.weak_id.is_null() {
184 let widx = hd.weak_id.index();
186 debug_assert!((widx as usize) < self.weak_slots.len());
187 unsafe {
188 self.weak_slots.get_unchecked_mut(widx as usize).1.take();
189 }
190 }
191
192 let dtype = hd.dtype() as usize;
193 let info = &self.node_dtypes.type_info_list[dtype];
194 let gross_size = std::mem::size_of::<GcHead>() + info.size as usize;
195
196 #[cfg(debug_assertions)]
197 unsafe {
198 std::ptr::drop_in_place(node.cast::<GcHead>().as_ptr());
199 }
200
201 if let Some(f) = info.drop_fn {
202 unsafe {
203 f(node.as_ref().payload().as_ptr());
204 }
205 }
206
207 self.mem_dealloc(node.cast::<u8>(), gross_size);
208
209 gross_size
210 }
211}