├── .gitignore ├── Cargo.toml ├── README.md ├── Cargo.lock └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gc-mark-sweep-rs" 3 | version = "0.1.0" 4 | authors = ["Nazarí González "] 5 | publish = false 6 | 7 | [dependencies] 8 | array-macro = "1.0.2" 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gc-mark-sweep-rs 2 | ====== 3 | 4 | Port of 'Baby's First Garbage Collector' by Bob Nystrom to rust. 5 | Original: http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/ 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "array-macro" 3 | version = "1.0.2" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "gc-mark-sweep-rs" 8 | version = "0.1.0" 9 | dependencies = [ 10 | "array-macro 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 11 | ] 12 | 13 | [metadata] 14 | "checksum array-macro 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8b1b1a00de235e9f2cc0e650423dc249d875c116a5934188c08fdd0c02d840ef" 15 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate array_macro; 2 | 3 | use std::rc::Rc; 4 | use std::cell::RefCell; 5 | 6 | fn main(){} 7 | 8 | const STACK_MAX:usize = 256; 9 | const INITIAL_GC_THRESHOLD:usize = 10; 10 | 11 | type RefObject = Rc>; 12 | 13 | struct Object { 14 | marked: bool, 15 | value: ObjectType, 16 | 17 | next: Option, 18 | } 19 | 20 | impl Object { 21 | fn new(typ: ObjectType) -> RefObject { 22 | Rc::new(RefCell::new( 23 | Object { 24 | marked: false, 25 | value: typ, 26 | next: None, 27 | } 28 | )) 29 | } 30 | 31 | pub fn mark(&mut self) { 32 | if self.marked { return; } 33 | 34 | self.marked = true; 35 | 36 | match self.value { 37 | ObjectType::Pair {ref head, ref tail} => { 38 | head.borrow_mut().mark(); 39 | tail.borrow_mut().mark(); 40 | }, 41 | _ => {} 42 | } 43 | } 44 | } 45 | 46 | enum ObjectType { 47 | Int(i32), 48 | Pair { 49 | head: RefObject, 50 | tail: RefObject 51 | } 52 | } 53 | 54 | struct VM { 55 | stack: [Option; STACK_MAX], 56 | stack_size: usize, 57 | 58 | first_object: Option, 59 | num_objects: usize, 60 | max_objects: usize, 61 | } 62 | 63 | impl VM { 64 | fn new() -> VM { 65 | VM { 66 | stack: array![None; STACK_MAX], 67 | stack_size: 0, 68 | first_object: None, 69 | num_objects: 0, 70 | max_objects: INITIAL_GC_THRESHOLD, 71 | } 72 | } 73 | 74 | fn new_object(&mut self, typ: ObjectType) -> RefObject { 75 | let obj = Object::new(typ); 76 | obj.borrow_mut().next = self.first_object.clone(); 77 | self.first_object = Some(obj.clone()); 78 | 79 | obj 80 | } 81 | 82 | fn mark_all(&mut self) { 83 | self.stack.iter_mut() 84 | .for_each(|obj| { 85 | if let Some(o) = obj { 86 | o.borrow_mut().mark() 87 | } 88 | }); 89 | } 90 | 91 | fn sweep(&mut self) { 92 | let mut object = self.first_object.clone(); 93 | while object.is_some() { 94 | let obj = object.unwrap(); 95 | let mut obj_mut = obj.borrow_mut(); 96 | if !obj_mut.marked { 97 | object = obj_mut.next.clone(); 98 | self.num_objects -= 1; 99 | } else { 100 | obj_mut.marked = false; 101 | object = obj_mut.next.clone(); 102 | } 103 | } 104 | } 105 | 106 | fn gc(&mut self) { 107 | let num_objects = self.num_objects; 108 | 109 | self.mark_all(); 110 | self.sweep(); 111 | 112 | self.max_objects = num_objects * 2; 113 | } 114 | 115 | fn push(&mut self, val:RefObject) { 116 | assert!(self.stack_size < STACK_MAX, "Stack overflow!"); 117 | if self.num_objects == self.max_objects { self.gc(); } 118 | self.num_objects += 1; 119 | 120 | self.stack[self.stack_size] = Some(val); 121 | self.stack_size += 1; 122 | } 123 | 124 | fn pop(&mut self) -> RefObject { 125 | assert!(self.stack_size > 0, "Stack underflow!"); 126 | self.stack_size -= 1; 127 | let obj = self.stack[self.stack_size].clone().unwrap(); 128 | self.stack[self.stack_size] = None; 129 | 130 | obj 131 | } 132 | 133 | fn push_int(&mut self, val: i32) { 134 | let obj = self.new_object(ObjectType::Int(val)); 135 | self.push(obj); 136 | } 137 | 138 | fn push_pair(&mut self) { 139 | let tail = self.pop(); 140 | let head = self.pop(); 141 | 142 | let obj = self.new_object(ObjectType::Pair { 143 | tail: tail, 144 | head: head 145 | }); 146 | 147 | self.push(obj); 148 | } 149 | } 150 | 151 | #[cfg(test)] 152 | mod tests { 153 | use super::*; 154 | 155 | #[test] 156 | fn stack_size_used() { 157 | let mut vm = VM::new(); 158 | vm.push_int(10); 159 | vm.push_int(20); 160 | vm.push_int(30); 161 | 162 | vm.gc(); 163 | 164 | assert_eq!(vm.stack_size, 3); 165 | } 166 | 167 | #[test] 168 | fn stack_size_equal_in_use() { 169 | let mut vm = VM::new(); 170 | vm.push_int(10); 171 | vm.push_int(20); 172 | vm.push_int(30); 173 | 174 | vm.gc(); 175 | 176 | let length = vm.stack.into_iter() 177 | .filter(|obj| obj.is_some()) 178 | .collect::>() 179 | .len(); 180 | 181 | assert_eq!(vm.stack_size, length); 182 | } 183 | 184 | #[test] 185 | fn stack_collected() { 186 | let mut vm = VM::new(); 187 | vm.push_int(10); 188 | vm.push_int(20); 189 | vm.push_int(30); 190 | 191 | vm.pop(); 192 | vm.pop(); 193 | 194 | vm.gc(); 195 | 196 | assert_eq!(vm.stack_size, 1); 197 | } 198 | 199 | #[test] 200 | fn stack_after_collect_is_equal() { 201 | let mut vm = VM::new(); 202 | vm.push_int(10); 203 | vm.push_int(20); 204 | vm.push_int(30); 205 | 206 | vm.pop(); 207 | vm.pop(); 208 | 209 | vm.gc(); 210 | 211 | let length = vm.stack.into_iter() 212 | .filter(|obj| obj.is_some()) 213 | .collect::>() 214 | .len(); 215 | 216 | assert_eq!(vm.stack_size, length); 217 | } 218 | 219 | #[test] 220 | fn stack_nested() { 221 | let mut vm = VM::new(); 222 | vm.push_int(10); 223 | vm.push_int(20); 224 | vm.push_int(30); 225 | vm.push_int(40); 226 | 227 | vm.push_pair(); 228 | vm.push_pair(); 229 | vm.push_pair(); 230 | 231 | vm.gc(); 232 | 233 | assert_eq!(vm.stack_size, 1); 234 | } 235 | 236 | #[test] 237 | fn stack_nested_count() { 238 | let mut vm = VM::new(); 239 | vm.push_int(10); 240 | vm.push_int(20); 241 | vm.push_int(30); 242 | vm.push_int(40); 243 | 244 | vm.push_pair(); 245 | vm.push_pair(); 246 | vm.push_pair(); 247 | 248 | vm.gc(); 249 | 250 | assert_eq!(vm.num_objects, 7); 251 | } 252 | 253 | #[test] 254 | fn gc_clean() { 255 | let mut vm = VM::new(); 256 | vm.push_int(10); 257 | vm.push_int(20); 258 | vm.push_int(30); 259 | vm.push_int(40); 260 | 261 | vm.push_pair(); 262 | vm.push_pair(); 263 | vm.push_pair(); 264 | 265 | vm.pop(); 266 | 267 | vm.gc(); 268 | 269 | assert_eq!(vm.num_objects == 0 && vm.num_objects == vm.stack_size, true); 270 | } 271 | } --------------------------------------------------------------------------------