├── COPYING ├── Cargo.toml ├── README.md └── lib.rs /COPYING: -------------------------------------------------------------------------------- 1 | `multilist` is distributed under the same terms as the Rust compiler itself. 2 | 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "multilist" 4 | version = "0.1.0" 5 | authors = ["Patrick Walton"] 6 | 7 | [lib] 8 | 9 | name = "multilist" 10 | path = "lib.rs" 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `multilist` is a Rust data structure that represents an *intrusive set of doubly-linked lists*, indexed by 2 | number. Objects owned by the multilist can belong to any number of the constituent linked lists. Only one 3 | allocation is used per object, regardless of the number of lists it is in. 4 | 5 | When first adding an object (e.g. via `push_back()`), you choose which linked list it is to initially belong 6 | to. You can then find it with an iterator and add it to other lists via `push_back_existing()`. Objects can 7 | be removed from individual lists with `remove_existing()` and removed from the list entirely with 8 | `pop_back()`. You can iterate over linked lists with `iter()`. When the multilist is destroyed, all objects 9 | within it are destroyed as well; in this way, the lists *collectively own* the objects. 10 | 11 | Objects owned by the multilist are normally immutable, but you can use `Cell` or `RefCell` as usual to make their fields mutable. `multilist` is believed to be a memory-safe design, although it is possible to leak with incorrect use of `remove_existing()`. Fixing this would require reference counting the list items. 12 | 13 | Example code is provided inside `lib.rs`. 14 | 15 | `multilist` is distributed under the same terms as the Rust compiler itself. 16 | -------------------------------------------------------------------------------- /lib.rs: -------------------------------------------------------------------------------- 1 | // 2 | // multilist/lib.rs 3 | // 4 | // Copyright (c) 2015 Mozilla Foundation 5 | // 6 | 7 | #![feature(alloc, core, unsafe_destructor)] 8 | 9 | extern crate alloc; 10 | 11 | use alloc::heap; 12 | use std::cell::UnsafeCell; 13 | use std::iter; 14 | use std::mem; 15 | use std::ops::Deref; 16 | use std::ptr; 17 | 18 | /// An intrusive set of doubly-linked lists, indexed by number. Objects owned by the multilist can 19 | /// belong to any number of the constituent linked lists. Only one allocation is used per object, 20 | /// regardless of the number of lists it is in. 21 | /// 22 | /// When first adding an object (e.g. via `push_back()`), you choose which linked list it is to 23 | /// initially belong to. You can then find it with an iterator and add it to other lists via 24 | /// `push_back_existing()`. Objects can be removed from individual lists with `remove_existing()` 25 | /// and removed from the list entirely with `pop_back()`. You can iterate over linked lists with 26 | /// `iter()`. When the multilist is destroyed, all objects within it are destroyed as well; in 27 | /// this way, the lists *collectively own* the objects. 28 | /// 29 | /// Objects owned by the multilist are normally immutable, but you can use `Cell` or `RefCell` as 30 | /// usual to make their fields mutable. `multilist` is believed to be a memory-safe design, 31 | /// although it is possible to leak with incorrect use of `remove_existing()`. Fixing this would 32 | /// require reference counting the list items. 33 | pub struct Multilist { 34 | pointers: UnsafeCell>>, 35 | } 36 | 37 | #[unsafe_destructor] 38 | impl Drop for Multilist { 39 | fn drop(&mut self) { 40 | for i in range(0, self.list_count()) { 41 | while self.pop_back(i).is_some() {} 42 | } 43 | } 44 | } 45 | 46 | impl Multilist { 47 | #[inline] 48 | pub fn new(list_count: usize) -> Multilist { 49 | Multilist { 50 | pointers: UnsafeCell::new(iter::repeat(MultilistListPointers::new()).take(list_count as 51 | usize) 52 | .collect()), 53 | } 54 | } 55 | 56 | #[inline] 57 | pub fn list_count(&self) -> usize { 58 | unsafe { 59 | (*self.pointers.get()).len() 60 | } 61 | } 62 | 63 | #[inline] 64 | pub fn is_empty(&self, list_index: usize) -> bool { 65 | unsafe { 66 | (*self.pointers.get())[list_index].head.is_null() 67 | } 68 | } 69 | 70 | /// Inserts a brand-new element into one of the lists. 71 | #[inline] 72 | pub fn push_back(&self, list_index: usize, value: Value) { 73 | let element = MultilistElement::new(value, self); 74 | self.push_back_existing(list_index, element); 75 | } 76 | 77 | /// Inserts an element that is already in at least one of the lists into another list. 78 | #[inline] 79 | pub fn push_back_existing(&self, list_index: usize, element: MultilistElement) { 80 | unsafe { 81 | assert!(element.associated_multilist() == self as *const _); 82 | let pointers = element.pointers(list_index); 83 | assert!((*pointers).next.is_null()); 84 | debug_assert!((*pointers).prev.is_null()); 85 | let list_pointers = &mut (*self.pointers.get())[list_index]; 86 | if list_pointers.tail.is_null() { 87 | list_pointers.head = element.holder as *mut _; 88 | } else { 89 | (*(*list_pointers.tail).pointers(list_index)).next = element.holder as *mut _; 90 | (*pointers).prev = list_pointers.tail; 91 | } 92 | list_pointers.tail = element.holder as *mut _; 93 | } 94 | } 95 | 96 | /// Removes an element from one of the lists. 97 | /// 98 | /// NB: If the element is no longer a member of any lists, this will leak the element! You 99 | /// should use `pop_back()` to remove the element from the last list it's a member of. 100 | #[inline] 101 | pub fn remove_existing(&self, list_index: usize, element: MultilistElement) { 102 | unsafe { 103 | assert!(element.associated_multilist() == self as *const _); 104 | let pointers = element.pointers(list_index); 105 | let list_pointers = &mut (*self.pointers.get())[list_index]; 106 | if (*pointers).next.is_null() { 107 | // Make sure it's actually in the list! 108 | assert!(list_pointers.tail == element.holder as *mut _); 109 | 110 | list_pointers.tail = ptr::null_mut(); 111 | } else { 112 | (*((*(*pointers).next)).pointers(list_index)).prev = (*pointers).prev; 113 | } 114 | if (*element.pointers(list_index)).prev.is_null() { 115 | list_pointers.head = (*element.pointers(list_index)).next; 116 | } else { 117 | (*((*(*pointers).prev)).pointers(list_index)).next = (*pointers).next; 118 | } 119 | } 120 | } 121 | 122 | /// Removes the last element of the given list from all of the lists it's a member of and 123 | /// returns it. 124 | #[inline] 125 | pub fn pop_back(&mut self, list_index: usize) -> Option { 126 | unsafe { 127 | let tail = (*self.pointers.get())[list_index].tail; 128 | let mut element = if !tail.is_null() { 129 | MultilistElement { 130 | holder: tail, 131 | } 132 | } else { 133 | return None 134 | }; 135 | for i in range(0, self.list_count()) { 136 | if element.is_in_list(i) { 137 | self.remove_existing(i, element) 138 | } 139 | } 140 | let value = ptr::read(&(*element.holder).value); 141 | element.destroy(); 142 | Some(value) 143 | } 144 | } 145 | 146 | /// Iterates over one of the linked lists. 147 | #[inline] 148 | pub fn iter<'a>(&'a self, list_index: usize) -> MultilistIterator<'a,Value> { 149 | unsafe { 150 | MultilistIterator { 151 | element: (*self.pointers.get())[list_index].head, 152 | list_index: list_index, 153 | } 154 | } 155 | } 156 | } 157 | 158 | struct MultilistElementHolder { 159 | value: Value, 160 | associated_multilist: *const Multilist, 161 | pointers: UnsafeCell<[MultilistPointers; 1]>, 162 | } 163 | 164 | impl MultilistElementHolder { 165 | fn size(list_count: usize) -> usize { 166 | debug_assert!(list_count > 0); 167 | mem::size_of::>() + 168 | (mem::min_align_of::>() * (list_count - 1) as usize) 169 | 170 | } 171 | 172 | #[inline] 173 | fn pointers(&self, list_index: usize) -> *mut MultilistPointers { 174 | unsafe { 175 | debug_assert!(list_index < (*self.associated_multilist).list_count()); 176 | (*self.pointers.get()).as_ptr().offset(list_index as isize) as 177 | *mut MultilistPointers 178 | } 179 | } 180 | } 181 | 182 | /// One element in a multilist. 183 | pub struct MultilistElement<'a,Value> { 184 | holder: *const MultilistElementHolder, 185 | } 186 | 187 | impl<'a,Value> Copy for MultilistElement<'a,Value> {} 188 | 189 | impl<'a,Value> Clone for MultilistElement<'a,Value> { 190 | fn clone(&self) -> MultilistElement<'a,Value> { 191 | *self 192 | } 193 | } 194 | 195 | impl<'a,Value> Deref for MultilistElement<'a,Value> { 196 | type Target = Value; 197 | 198 | #[inline] 199 | fn deref<'b>(&'b self) -> &'b Value { 200 | unsafe { 201 | &(*self.holder).value 202 | } 203 | } 204 | } 205 | 206 | impl<'a,Value> MultilistElement<'a,Value> { 207 | #[inline] 208 | fn new(value: Value, associated_multilist: &'a Multilist) 209 | -> MultilistElement<'a,Value> { 210 | unsafe { 211 | let byte_size = 212 | MultilistElementHolder::::size((*associated_multilist.pointers 213 | .get()).len()); 214 | let holder = heap::allocate(byte_size, byte_size) as 215 | *mut MultilistElementHolder; 216 | if holder.is_null() { 217 | alloc::oom() 218 | } 219 | ptr::write(holder, MultilistElementHolder { 220 | value: value, 221 | associated_multilist: associated_multilist, 222 | pointers: UnsafeCell::new([MultilistPointers::new()]), 223 | }); 224 | for i in range(mem::size_of::>(), byte_size) { 225 | ptr::write((*(*holder).pointers.get()).as_mut_ptr().offset(i as isize), 226 | MultilistPointers::new()) 227 | } 228 | MultilistElement { 229 | holder: holder, 230 | } 231 | } 232 | } 233 | 234 | #[inline] 235 | unsafe fn pointers(&self, list_index: usize) -> *mut MultilistPointers { 236 | (*self.holder).pointers(list_index) 237 | } 238 | 239 | #[inline] 240 | fn associated_multilist(&self) -> *const Multilist { 241 | unsafe { 242 | (*self.holder).associated_multilist 243 | } 244 | } 245 | 246 | #[inline] 247 | unsafe fn destroy(&mut self) { 248 | let byte_size = 249 | MultilistElementHolder::::size((*(*self.associated_multilist()).pointers 250 | .get()).len()); 251 | drop(heap::deallocate(self.holder as *mut u8, byte_size, byte_size)) 252 | } 253 | 254 | /// Returns true if this element is a member of the given list. 255 | #[inline] 256 | pub fn is_in_list(&self, list_index: usize) -> bool { 257 | unsafe { 258 | let pointers = self.pointers(list_index); 259 | !(*pointers).next.is_null() || !(*pointers).prev.is_null() 260 | } 261 | } 262 | } 263 | 264 | pub struct MultilistPointers { 265 | next: *mut MultilistElementHolder, 266 | prev: *mut MultilistElementHolder, 267 | } 268 | 269 | impl Copy for MultilistPointers {} 270 | 271 | impl Clone for MultilistPointers { 272 | fn clone(&self) -> MultilistPointers { 273 | *self 274 | } 275 | } 276 | 277 | impl MultilistPointers { 278 | pub fn new() -> MultilistPointers { 279 | MultilistPointers { 280 | next: ptr::null_mut(), 281 | prev: ptr::null_mut(), 282 | } 283 | } 284 | } 285 | 286 | pub struct MultilistListPointers { 287 | head: *mut MultilistElementHolder, 288 | tail: *mut MultilistElementHolder, 289 | } 290 | 291 | impl Copy for MultilistListPointers {} 292 | 293 | impl Clone for MultilistListPointers { 294 | fn clone(&self) -> MultilistListPointers { 295 | *self 296 | } 297 | } 298 | 299 | impl MultilistListPointers { 300 | pub fn new() -> MultilistListPointers { 301 | MultilistListPointers { 302 | head: ptr::null_mut(), 303 | tail: ptr::null_mut(), 304 | } 305 | } 306 | } 307 | 308 | pub struct MultilistIterator<'a,Value> { 309 | element: *mut MultilistElementHolder, 310 | list_index: usize, 311 | } 312 | 313 | impl<'a,Value> Iterator for MultilistIterator<'a,Value> { 314 | type Item = MultilistElement<'a,Value>; 315 | 316 | fn next(&mut self) -> Option> { 317 | let element = self.element; 318 | if element.is_null() { 319 | return None 320 | } 321 | 322 | unsafe { 323 | let next = (*(*element).pointers(self.list_index)).next; 324 | self.element = next; 325 | Some(MultilistElement { 326 | holder: element, 327 | }) 328 | } 329 | } 330 | } 331 | 332 | /// Example code. This is skeleton code that shows how this might be used in an operating system 333 | /// kernel to manage tasks. 334 | #[allow(dead_code)] 335 | fn main() { 336 | #[derive(Debug)] 337 | struct TaskStruct { 338 | pid: i32, 339 | gid: i32, 340 | } 341 | 342 | const TASK_LIST: usize = 0; 343 | const RUN_LIST: usize = 1; 344 | 345 | let mut multilist = Multilist::new(2); 346 | multilist.push_back(TASK_LIST, TaskStruct { 347 | pid: 1, 348 | gid: 2, 349 | }); 350 | multilist.push_back(TASK_LIST, TaskStruct { 351 | pid: 3, 352 | gid: 4, 353 | }); 354 | multilist.push_back(TASK_LIST, TaskStruct { 355 | pid: 5, 356 | gid: 6, 357 | }); 358 | println!("After adding 3 tasks to task list:"); 359 | dump_list(&multilist); 360 | 361 | multilist.push_back_existing(RUN_LIST, multilist.iter(TASK_LIST).skip(2).next().unwrap()); 362 | multilist.push_back_existing(RUN_LIST, multilist.iter(TASK_LIST).skip(0).next().unwrap()); 363 | multilist.push_back_existing(RUN_LIST, multilist.iter(TASK_LIST).skip(1).next().unwrap()); 364 | println!("\nAfter adding 3 tasks to run list in order 2, 0, 1:"); 365 | dump_list(&multilist); 366 | 367 | multilist.remove_existing(TASK_LIST, multilist.iter(TASK_LIST).skip(1).next().unwrap()); 368 | println!("\nAfter removing the second task from the task list:"); 369 | dump_list(&multilist); 370 | 371 | multilist.push_back(TASK_LIST, TaskStruct { 372 | pid: 7, 373 | gid: 8, 374 | }); 375 | println!("\nAfter adding a new task to the task list:"); 376 | dump_list(&multilist); 377 | 378 | multilist.pop_back(RUN_LIST); 379 | println!("\nAfter removing the last task on the run list entirely:"); 380 | dump_list(&multilist); 381 | 382 | return; 383 | 384 | fn dump_list(multilist: &Multilist) { 385 | println!("Tasks in task order:"); 386 | for task in multilist.iter(TASK_LIST) { 387 | println!("{:?}", &*task); 388 | } 389 | println!("Tasks in run order:"); 390 | for task in multilist.iter(RUN_LIST) { 391 | println!("{:?}", &*task); 392 | } 393 | } 394 | } 395 | 396 | --------------------------------------------------------------------------------