1use crate::header::{GcColor, GcHeader};
10use crate::region::Region;
11use std::collections::HashMap;
12
13pub struct ForwardingTable {
15 table: HashMap<usize, *mut u8>,
16}
17
18unsafe impl Send for ForwardingTable {}
20unsafe impl Sync for ForwardingTable {}
21
22impl ForwardingTable {
23 pub fn new() -> Self {
24 Self {
25 table: HashMap::with_capacity(4096),
26 }
27 }
28
29 pub fn insert(&mut self, old: *mut u8, new: *mut u8) {
31 self.table.insert(old as usize, new);
32 }
33
34 pub fn lookup(&self, old: *const u8) -> Option<*mut u8> {
36 self.table.get(&(old as usize)).copied()
37 }
38
39 pub fn len(&self) -> usize {
41 self.table.len()
42 }
43
44 pub fn is_empty(&self) -> bool {
46 self.table.is_empty()
47 }
48
49 pub fn clear(&mut self) {
51 self.table.clear();
52 }
53
54 pub fn iter(&self) -> impl Iterator<Item = (*mut u8, *mut u8)> + '_ {
56 self.table.iter().map(|(&old, &new)| (old as *mut u8, new))
57 }
58}
59
60impl Default for ForwardingTable {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66pub struct Relocator {
70 forwarding_table: ForwardingTable,
72}
73
74impl Relocator {
75 pub fn new() -> Self {
77 Self {
78 forwarding_table: ForwardingTable::new(),
79 }
80 }
81
82 pub fn install_forwarding(&mut self, old_addr: *mut u8, new_addr: *mut u8) {
84 self.forwarding_table.insert(old_addr, new_addr);
85 }
86
87 pub fn resolve(&self, ptr: *mut u8) -> *mut u8 {
92 self.forwarding_table
93 .lookup(ptr as *const u8)
94 .unwrap_or(ptr)
95 }
96
97 pub fn compact_region(&mut self, source: &Region, target: &mut Region) -> usize {
107 let header_size = std::mem::size_of::<GcHeader>();
108 let mut moved = 0;
109
110 source.for_each_object(|header, old_obj_ptr| {
111 if header.color() != GcColor::Black {
113 return;
114 }
115
116 let obj_size = header.size as usize;
117 let layout = std::alloc::Layout::from_size_align(obj_size, 8).unwrap();
118
119 if let Some(new_obj_ptr) = target.try_alloc(layout) {
121 unsafe {
123 std::ptr::copy_nonoverlapping(old_obj_ptr, new_obj_ptr, obj_size);
124 }
125
126 let new_header =
128 unsafe { &mut *((new_obj_ptr as *mut u8).sub(header_size) as *mut GcHeader) };
129 *new_header = *header;
130 new_header.set_color(GcColor::White); let old_header =
134 unsafe { &mut *((old_obj_ptr as *mut u8).sub(header_size) as *mut GcHeader) };
135 old_header.set_forwarded(true);
136
137 self.forwarding_table.insert(old_obj_ptr, new_obj_ptr);
139 moved += 1;
140 }
141 });
142
143 moved
144 }
145
146 pub fn forwarding_table(&self) -> &ForwardingTable {
148 &self.forwarding_table
149 }
150
151 pub fn forwarding_table_mut(&mut self) -> &mut ForwardingTable {
153 &mut self.forwarding_table
154 }
155
156 pub fn reset(&mut self) {
158 self.forwarding_table.clear();
159 }
160}
161
162impl Default for Relocator {
163 fn default() -> Self {
164 Self::new()
165 }
166}
167
168pub fn relocate_region(source: &Region, target: &mut Region) -> ForwardingTable {
176 let mut relocator = Relocator::new();
177 relocator.compact_region(source, target);
178 let mut ft = ForwardingTable::new();
180 std::mem::swap(&mut ft, relocator.forwarding_table_mut());
181 ft
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_forwarding_table_insert_lookup() {
190 let mut ft = ForwardingTable::new();
191 let old = 0x1000 as *mut u8;
192 let new = 0x2000 as *mut u8;
193 ft.insert(old, new);
194
195 assert_eq!(ft.lookup(old), Some(new));
196 assert_eq!(ft.lookup(0x3000 as *const u8), None);
197 assert_eq!(ft.len(), 1);
198 }
199
200 #[test]
201 fn test_forwarding_table_overwrite() {
202 let mut ft = ForwardingTable::new();
203 let old = 0x1000 as *mut u8;
204 ft.insert(old, 0x2000 as *mut u8);
205 ft.insert(old, 0x3000 as *mut u8);
206 assert_eq!(ft.lookup(old), Some(0x3000 as *mut u8));
208 assert_eq!(ft.len(), 1);
209 }
210
211 #[test]
212 fn test_forwarding_table_clear() {
213 let mut ft = ForwardingTable::new();
214 ft.insert(0x1000 as *mut u8, 0x2000 as *mut u8);
215 ft.insert(0x3000 as *mut u8, 0x4000 as *mut u8);
216 assert_eq!(ft.len(), 2);
217
218 ft.clear();
219 assert!(ft.is_empty());
220 assert_eq!(ft.len(), 0);
221 assert_eq!(ft.lookup(0x1000 as *const u8), None);
222 }
223
224 #[test]
225 fn test_forwarding_table_iter() {
226 let mut ft = ForwardingTable::new();
227 ft.insert(0x1000 as *mut u8, 0x2000 as *mut u8);
228 ft.insert(0x3000 as *mut u8, 0x4000 as *mut u8);
229
230 let entries: Vec<_> = ft.iter().collect();
231 assert_eq!(entries.len(), 2);
232 }
233
234 #[test]
237 fn test_relocator_install_and_resolve() {
238 let mut relocator = Relocator::new();
239 let old = 0x1000 as *mut u8;
240 let new = 0x2000 as *mut u8;
241
242 assert_eq!(relocator.resolve(old), old);
244
245 relocator.install_forwarding(old, new);
246
247 assert_eq!(relocator.resolve(old), new);
249
250 let unknown = 0x9000 as *mut u8;
252 assert_eq!(relocator.resolve(unknown), unknown);
253 }
254
255 #[test]
256 fn test_relocator_reset() {
257 let mut relocator = Relocator::new();
258 relocator.install_forwarding(0x1000 as *mut u8, 0x2000 as *mut u8);
259 assert_eq!(relocator.forwarding_table().len(), 1);
260
261 relocator.reset();
262 assert!(relocator.forwarding_table().is_empty());
263 }
264
265 #[test]
266 fn test_relocator_compact_region() {
267 let mut source = Region::new();
269 let layout = std::alloc::Layout::from_size_align(64, 8).unwrap();
270
271 let obj1 = source.try_alloc(layout).unwrap();
273 let obj2 = source.try_alloc(layout).unwrap();
274 let obj3 = source.try_alloc(layout).unwrap();
275
276 unsafe {
278 std::ptr::write_bytes(obj1, 0xAA, 64);
279 std::ptr::write_bytes(obj2, 0xBB, 64);
280 std::ptr::write_bytes(obj3, 0xCC, 64);
281 }
282
283 let header_size = std::mem::size_of::<GcHeader>();
285 let h1 = unsafe { &mut *((obj1 as *mut u8).sub(header_size) as *mut GcHeader) };
286 let h3 = unsafe { &mut *((obj3 as *mut u8).sub(header_size) as *mut GcHeader) };
287 h1.set_color(GcColor::Black);
288 h3.set_color(GcColor::Black);
289 let mut target = Region::new();
293 let mut relocator = Relocator::new();
294 let moved = relocator.compact_region(&source, &mut target);
295
296 assert_eq!(moved, 2);
298 assert_eq!(relocator.forwarding_table().len(), 2);
299
300 let new_obj1 = relocator.resolve(obj1);
302 let new_obj3 = relocator.resolve(obj3);
303 assert_ne!(new_obj1, obj1); assert_ne!(new_obj3, obj3);
305
306 assert_eq!(relocator.resolve(obj2), obj2);
308
309 unsafe {
311 assert_eq!(*new_obj1, 0xAA);
312 assert_eq!(*new_obj3, 0xCC);
313 }
314
315 let new_h1 = unsafe { &*((new_obj1 as *mut u8).sub(header_size) as *const GcHeader) };
317 let new_h3 = unsafe { &*((new_obj3 as *mut u8).sub(header_size) as *const GcHeader) };
318 assert_eq!(new_h1.color(), GcColor::White);
319 assert_eq!(new_h3.color(), GcColor::White);
320
321 assert!(h1.is_forwarded());
323 assert!(h3.is_forwarded());
324 }
325
326 #[test]
327 fn test_relocator_compact_region_empty_source() {
328 let source = Region::new();
329 let mut target = Region::new();
330 let mut relocator = Relocator::new();
331
332 let moved = relocator.compact_region(&source, &mut target);
333 assert_eq!(moved, 0);
334 assert!(relocator.forwarding_table().is_empty());
335 }
336
337 #[test]
338 fn test_relocator_compact_region_all_dead() {
339 let mut source = Region::new();
340 let layout = std::alloc::Layout::from_size_align(32, 8).unwrap();
341
342 let _obj1 = source.try_alloc(layout).unwrap();
344 let _obj2 = source.try_alloc(layout).unwrap();
345
346 let mut target = Region::new();
347 let mut relocator = Relocator::new();
348 let moved = relocator.compact_region(&source, &mut target);
349
350 assert_eq!(moved, 0);
351 assert!(relocator.forwarding_table().is_empty());
352 }
353
354 #[test]
355 fn test_relocate_region_compatibility() {
356 let mut source = Region::new();
358 let layout = std::alloc::Layout::from_size_align(32, 8).unwrap();
359 let obj = source.try_alloc(layout).unwrap();
360
361 let header_size = std::mem::size_of::<GcHeader>();
363 let h = unsafe { &mut *((obj as *mut u8).sub(header_size) as *mut GcHeader) };
364 h.set_color(GcColor::Black);
365
366 let mut target = Region::new();
367 let ft = relocate_region(&source, &mut target);
368 assert_eq!(ft.len(), 1);
369 assert!(ft.lookup(obj).is_some());
370 }
371}