1#![allow(dead_code)]
4
5pub type GcId = usize;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum GcState {
14 White, Grey, Black, }
18
19#[derive(Debug)]
21pub struct GcObject {
22 pub id: GcId,
23 pub state: GcState,
24 pub refs: Vec<GcId>,
25}
26
27impl GcObject {
28 fn new(id: GcId) -> Self {
29 Self {
30 id,
31 state: GcState::White,
32 refs: Vec::new(),
33 }
34 }
35}
36
37pub struct GcStub {
39 objects: Vec<GcObject>,
40 roots: Vec<GcId>,
41 collected: usize,
42}
43
44impl GcStub {
45 pub fn new() -> Self {
47 Self {
48 objects: Vec::new(),
49 roots: Vec::new(),
50 collected: 0,
51 }
52 }
53
54 pub fn alloc(&mut self) -> GcId {
56 let id = self.objects.len();
57 self.objects.push(GcObject::new(id));
58 id
59 }
60
61 pub fn add_ref(&mut self, from: GcId, to: GcId) {
63 if from < self.objects.len() {
64 self.objects[from].refs.push(to);
65 }
66 }
67
68 pub fn add_root(&mut self, id: GcId) {
70 self.roots.push(id);
71 }
72
73 pub fn mark(&mut self) {
75 for obj in &mut self.objects {
77 obj.state = GcState::White;
78 }
79 let mut grey: Vec<GcId> = self.roots.clone();
81 for &r in &self.roots {
82 if r < self.objects.len() {
83 self.objects[r].state = GcState::Grey;
84 }
85 }
86 while let Some(id) = grey.pop() {
87 if id >= self.objects.len() {
88 continue;
89 }
90 let children: Vec<GcId> = self.objects[id].refs.clone();
91 for child in children {
92 if child < self.objects.len() && self.objects[child].state == GcState::White {
93 self.objects[child].state = GcState::Grey;
94 grey.push(child);
95 }
96 }
97 self.objects[id].state = GcState::Black;
98 }
99 }
100
101 pub fn sweep(&mut self) -> usize {
103 let before = self.objects.len();
104 self.objects.retain(|o| o.state != GcState::White);
105 let freed = before - self.objects.len();
106 self.collected += freed;
107 freed
108 }
109
110 pub fn collect(&mut self) -> usize {
112 self.mark();
113 self.sweep()
114 }
115
116 pub fn live_count(&self) -> usize {
118 self.objects.len()
119 }
120
121 pub fn total_collected(&self) -> usize {
123 self.collected
124 }
125}
126
127impl Default for GcStub {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133pub fn new_gc_stub() -> GcStub {
135 GcStub::new()
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn test_alloc() {
144 let mut gc = GcStub::new();
145 let id = gc.alloc();
146 assert_eq!(id, 0); }
148
149 #[test]
150 fn test_collect_unreachable() {
151 let mut gc = GcStub::new();
152 gc.alloc(); let freed = gc.collect();
154 assert_eq!(freed, 1); }
156
157 #[test]
158 fn test_root_not_collected() {
159 let mut gc = GcStub::new();
160 let id = gc.alloc();
161 gc.add_root(id);
162 let freed = gc.collect();
163 assert_eq!(freed, 0); }
165
166 #[test]
167 fn test_reachable_via_root() {
168 let mut gc = GcStub::new();
169 let root = gc.alloc();
170 let child = gc.alloc();
171 gc.add_root(root);
172 gc.add_ref(root, child);
173 let freed = gc.collect();
174 assert_eq!(freed, 0); }
176
177 #[test]
178 fn test_unreachable_child() {
179 let mut gc = GcStub::new();
180 let root = gc.alloc();
181 let orphan = gc.alloc();
182 gc.add_root(root);
183 let _ = orphan;
184 let freed = gc.collect();
185 assert_eq!(freed, 1); }
187
188 #[test]
189 fn test_live_count() {
190 let mut gc = GcStub::new();
191 let id = gc.alloc();
192 gc.add_root(id);
193 gc.collect();
194 assert_eq!(gc.live_count(), 1); }
196
197 #[test]
198 fn test_total_collected() {
199 let mut gc = GcStub::new();
200 gc.alloc();
201 gc.alloc();
202 gc.collect();
203 assert_eq!(gc.total_collected(), 2); }
205
206 #[test]
207 fn test_multiple_roots() {
208 let mut gc = GcStub::new();
209 let a = gc.alloc();
210 let b = gc.alloc();
211 gc.add_root(a);
212 gc.add_root(b);
213 let freed = gc.collect();
214 assert_eq!(freed, 0); }
216
217 #[test]
218 fn test_default() {
219 let gc = GcStub::default();
220 assert_eq!(gc.live_count(), 0); }
222
223 #[test]
224 fn test_new_helper() {
225 let gc = new_gc_stub();
226 assert_eq!(gc.live_count(), 0); }
228}