1 //! A slotmap, a vector-like container with unique keys instead of indices. 2 //! 3 //! See the documentation of [`SlotMap`] for details. 4 //! 5 //! [`SlotMap`]: struct.SlotMap.html 6 use super::{ManagedSlice as Slice}; 7 8 /// Provides links between slots and elements. 9 /// 10 /// The benefit of separating this struct from the elements is that it is unconditionally `Copy` 11 /// and `Default`. It also provides better locality for both the indices and the elements which 12 /// could help with iteration or very large structs. 13 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] 14 pub struct Slot { 15 /// The id of this slot. 16 /// 17 /// If the given out index mismatches the `generation_id` then the element was removed already 18 /// and we can return `None` on lookup. 19 /// 20 /// If the slot is currently unused we will instead provide the index to the previous slot in 21 /// the slot-free-list. 22 generation_id: GenerationOrFreelink, 23 } 24 25 /// Provides a slotmap based on external memory. 26 /// 27 /// A slotmap provides a `Vec`-like interface where each entry is associated with a stable 28 /// index-like key. Lookup with the key will detect if an entry has been removed but does not 29 /// require a lifetime relation. Compared to other slotmap implementations this does not internally 30 /// allocate any memory on its own but only relies on the [`Slice`] arguments in the constructor. 31 /// 32 /// [`Slice`]: ../enum.Slice.html 33 /// 34 /// ## Usage 35 /// 36 /// The important aspect is that the slotmap does not create the storage of its own elements, it 37 /// merely manages one given to it at construction time. 38 /// 39 /// ``` 40 /// # use managed::{ManagedSlice, SlotMap, SlotIndex}; 41 /// 42 /// let mut elements = [0usize; 1024]; 43 /// let mut slots = [SlotIndex::default(); 1024]; 44 /// 45 /// let mut map = SlotMap::new( 46 /// ManagedSlice::Borrowed(&mut elements[..]), 47 /// ManagedSlice::Borrowed(&mut slots[..])); 48 /// let index = map.insert(42).unwrap(); 49 /// assert_eq!(map.get(index).cloned(), Some(42)); 50 /// ``` 51 pub struct SlotMap<'a, T> { 52 /// The slice where elements are placed. 53 /// All of them are initialized at all times but not all are logically part of the map. 54 elements: Slice<'a, T>, 55 /// The logical list of used slots. 56 /// Note that a slot is never remove from this list but instead used to track the generation_id 57 /// and the link in the free list. 58 slots: Partial<'a, Slot>, 59 /// The source of generation ids. 60 /// Generation ids are a positive, non-zero value. 61 generation: Generation, 62 /// An index to the top element of the free list. 63 /// Refers to the one-past-the-end index of `slots` if there are no elements. 64 free_top: usize, 65 /// An abstraction around computing wrapped indices in the free list. 66 indices: IndexComputer, 67 } 68 69 /// A backing slice tracking an index how far it is logically initialized. 70 struct Partial<'a, T> { 71 slice: Slice<'a, T>, 72 next_idx: usize, 73 } 74 75 /// An index into a slotmap. 76 /// 77 /// The index remains valid until the entry is removed. If accessing the slotmap with the index 78 /// again after the entry was removed will fail, even if the index where the element was previously 79 /// stored has been reused for another element. 80 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] 81 pub struct Key { 82 idx: usize, 83 generation: Generation, 84 } 85 86 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] 87 struct GenerationOrFreelink(isize); 88 89 /// Newtype wrapper around the index of a free slot. 90 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] 91 struct FreeIndex(usize); 92 93 /// The generation counter. 94 /// 95 /// Has a strictly positive value. 96 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 97 struct Generation(isize); 98 99 /// Offset of a freelist entry to the next entry. 100 /// 101 /// Has a negative or zero value. Represents the negative of the offset to the next element in the 102 /// free list, wrapping around at the capacity. 103 /// The base for the offset is the *next* element for two reasons: 104 /// * Offset of `0` points to the natural successor. 105 /// * Offset of `len` would point to the element itself and should not occur. 106 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 107 struct Offset(isize); 108 109 /// Links FreeIndex and Offset. 110 struct IndexComputer(usize); 111 112 impl<T> SlotMap<'_, T> { 113 /// Retrieve a value by index. get(&self, index: Key) -> Option<&T>114 pub fn get(&self, index: Key) -> Option<&T> { 115 let slot_generation = self.slots 116 .get(index.idx)? 117 .generation_id 118 .generation().ok()?; 119 120 if slot_generation != index.generation { 121 return None; 122 } 123 124 self.elements.get(index.idx) 125 } 126 127 /// Retrieve a mutable value by index. get_mut(&mut self, index: Key) -> Option<&mut T>128 pub fn get_mut(&mut self, index: Key) -> Option<&mut T> { 129 let slot_generation = self.slots 130 .get(index.idx)? 131 .generation_id 132 .generation().ok()?; 133 134 if slot_generation != index.generation { 135 return None; 136 } 137 138 self.elements.get_mut(index.idx) 139 } 140 141 /// Reserve a new entry. 142 /// 143 /// In case of success, the returned key refers to the entry until it is removed. The entry 144 /// itself is not initialized with any particular value but instead retains the value it had in 145 /// the backing slice. It is only logically placed into the slot map. reserve(&mut self) -> Option<(Key, &mut T)>146 pub fn reserve(&mut self) -> Option<(Key, &mut T)> { 147 let index = self.next_free_slot()?; 148 let slot = self.slots.get_mut(index.0).unwrap(); 149 let element = &mut self.elements[index.0]; 150 151 let offset = slot.generation_id 152 .free_link() 153 .expect("Free link should be free"); 154 slot.generation_id = self.generation.into(); 155 let key = Key { 156 idx: index.0, 157 generation: self.generation, 158 }; 159 160 self.free_top = self.indices.free_list_next(index, offset); 161 self.generation.advance(); 162 Some((key, element)) 163 } 164 165 /// Try to insert a value into the map. 166 /// 167 /// This will fail if there is not enough space. Sugar wrapper around `reserve` for inserting 168 /// values. Note that on success, an old value stored in the backing slice will be overwritten. 169 /// Use `reserve` directly if it is vital that no old value is dropped. insert(&mut self, value: T) -> Option<Key>170 pub fn insert(&mut self, value: T) -> Option<Key> { 171 // Insertion must work but we don't care about the value. 172 let (index, element) = self.reserve()?; 173 *element = value; 174 Some(index) 175 } 176 177 /// Remove an element. 178 /// 179 /// If successful, return a mutable reference to the removed element so that the caller can 180 /// swap it with a logically empty value. Returns `None` if the provided index did not refer to 181 /// an element that could be freed. remove(&mut self, index: Key) -> Option<&mut T>182 pub fn remove(&mut self, index: Key) -> Option<&mut T> { 183 if self.get(index).is_none() { 184 return None 185 } 186 187 // The slot can be freed. 188 let free = FreeIndex(index.idx); 189 let slot = self.slots.get_mut(index.idx).unwrap(); 190 assert!(slot.generation_id.generation().is_ok()); 191 192 let offset = self.indices.free_list_offset(free, self.free_top); 193 slot.generation_id = offset.into(); 194 self.free_top = index.idx; 195 196 Some(&mut self.elements[index.idx]) 197 } 198 199 /// Get the next free slot. next_free_slot(&mut self) -> Option<FreeIndex>200 fn next_free_slot(&mut self) -> Option<FreeIndex> { 201 // If free_top is one-past-the-end marker one of those is going to fail. Note that this 202 // also means extracting one of these statements out of the function may change the 203 // semantics if `elements.len() != slots.len()`. 204 205 // Ensure the index refers to an element within the slice or try to allocate a new slot 206 // wherein we can fit the element. 207 let free = match self.slots.get_mut(self.free_top) { 208 // There is a free element in our free list. 209 Some(_) => { 210 // Ensure that there is also a real element there. 211 let _= self.elements.get_mut(self.free_top)?; 212 FreeIndex(self.free_top) 213 }, 214 // Need to try an get a new element from the slot slice. 215 None => { // Try to get the next one 216 // Will not actually wrap if pushing is successful. 217 let new_index = self.slots.len(); 218 // Ensure there is an element where we want to push to. 219 let _ = self.elements.get_mut(new_index)?; 220 221 let free_slot = self.slots.try_reserve()?; 222 let free_index = FreeIndex(new_index); 223 // New top is the new one-past-the-end. 224 let new_top = new_index.checked_add(1).unwrap(); 225 226 let offset = self.indices.free_list_offset(free_index, new_top); 227 free_slot.generation_id = offset.into(); 228 self.free_top = free_index.0; 229 230 free_index 231 } 232 }; 233 234 235 // index refers to elements within the slices 236 Some(free) 237 } 238 } 239 240 impl<'a, T> SlotMap<'a, T> { 241 /// Create a slot map. 242 /// 243 /// The capacity is the minimum of the capacity of the element and slot slices. new(elements: Slice<'a, T>, slots: Slice<'a, Slot>) -> Self244 pub fn new(elements: Slice<'a, T>, slots: Slice<'a, Slot>) -> Self { 245 let capacity = elements.len().min(slots.len()); 246 SlotMap { 247 elements, 248 slots: Partial { 249 slice: slots, 250 next_idx: 0, 251 }, 252 generation: Generation::default(), 253 free_top: 0, 254 indices: IndexComputer::from_capacity(capacity), 255 } 256 } 257 } 258 259 impl<'a, T> Partial<'a, T> { get(&self, idx: usize) -> Option<&T>260 fn get(&self, idx: usize) -> Option<&T> { 261 if idx >= self.next_idx { 262 None 263 } else { 264 Some(&self.slice[idx]) 265 } 266 } 267 get_mut(&mut self, idx: usize) -> Option<&mut T>268 fn get_mut(&mut self, idx: usize) -> Option<&mut T> { 269 if idx >= self.next_idx { 270 None 271 } else { 272 Some(&mut self.slice[idx]) 273 } 274 } 275 len(&self) -> usize276 fn len(&self) -> usize { 277 self.next_idx 278 } 279 try_reserve(&mut self) -> Option<&mut T>280 fn try_reserve(&mut self) -> Option<&mut T> { 281 if self.next_idx == self.slice.len() { 282 None 283 } else { 284 let idx = self.next_idx; 285 self.next_idx += 1; 286 Some(&mut self.slice[idx]) 287 } 288 } 289 } 290 291 impl GenerationOrFreelink { free_link(self) -> Result<Offset, Generation>292 pub(crate) fn free_link(self) -> Result<Offset, Generation> { 293 if self.0 > 0 { 294 Err(Generation(self.0)) 295 } else { 296 Ok(Offset(self.0)) 297 } 298 } 299 generation(self) -> Result<Generation, Offset>300 pub(crate) fn generation(self) -> Result<Generation, Offset> { 301 match self.free_link() { 302 Ok(offset) => Err(offset), 303 Err(generation) => Ok(generation), 304 } 305 } 306 } 307 308 impl IndexComputer { from_capacity(capacity: usize) -> Self309 pub(crate) fn from_capacity(capacity: usize) -> Self { 310 assert!(capacity < isize::max_value() as usize); 311 IndexComputer(capacity) 312 } 313 314 /// Get the next free list entry. 315 /// This applies the offset to the base index, wrapping around if required. 316 /// 317 /// This is the reverse of `free_list_offset`. free_list_next(&self, FreeIndex(base): FreeIndex, offset: Offset) -> usize318 fn free_list_next(&self, FreeIndex(base): FreeIndex, offset: Offset) 319 -> usize 320 { 321 let capacity = self.0; 322 let offset = offset.int_offset(); 323 324 assert!(base < capacity); 325 assert!(offset <= capacity); 326 let base = base + 1; 327 328 if capacity - offset >= base { 329 offset + base // Fine within the range 330 } else { 331 // Mathematically, capacity < offset + base < 2*capacity 332 // Wrap once, mod (capacity + 1), result again in range 333 offset 334 .wrapping_add(base) 335 .wrapping_sub(capacity + 1) 336 } 337 } 338 339 /// Get the offset difference between the index and the next element. 340 /// Computes a potentially wrapping positive offset where zero is the element following the 341 /// base. 342 /// 343 /// This is the reverse of `free_list_next`. free_list_offset(&self, FreeIndex(base): FreeIndex, to: usize) -> Offset344 fn free_list_offset(&self, FreeIndex(base): FreeIndex, to: usize) 345 -> Offset 346 { 347 let capacity = self.0; 348 349 assert!(base != to, "Cant offset element to itself"); 350 assert!(base < capacity, "Should never have to offset the end-of-list marker"); 351 assert!(to <= capacity, "Can only offset to the end-of-list marker"); 352 let base = base + 1; 353 354 Offset::from_int_offset(if base <= to { 355 to - base 356 } else { 357 // Wrap once, mod (capacity + 1), result again in range 358 to 359 .wrapping_add(capacity + 1) 360 .wrapping_sub(base) 361 }) 362 } 363 } 364 365 impl Generation { advance(&mut self)366 pub(crate) fn advance(&mut self) { 367 assert!(self.0 > 0); 368 self.0 = self.0.wrapping_add(1).max(1) 369 } 370 } 371 372 impl Offset { from_int_offset(offset: usize) -> Self373 pub(crate) fn from_int_offset(offset: usize) -> Self { 374 assert!(offset < isize::max_value() as usize); 375 Offset((offset as isize).checked_neg().unwrap()) 376 } 377 int_offset(self) -> usize378 pub(crate) fn int_offset(self) -> usize { 379 self.0.checked_neg().unwrap() as usize 380 } 381 } 382 383 impl Default for Generation { default() -> Self384 fn default() -> Self { 385 Generation(1) 386 } 387 } 388 389 impl From<Generation> for GenerationOrFreelink { from(gen: Generation) -> GenerationOrFreelink390 fn from(gen: Generation) -> GenerationOrFreelink { 391 GenerationOrFreelink(gen.0) 392 } 393 } 394 395 impl From<Offset> for GenerationOrFreelink { from(offset: Offset) -> GenerationOrFreelink396 fn from(offset: Offset) -> GenerationOrFreelink { 397 GenerationOrFreelink(offset.0) 398 } 399 } 400 401 #[cfg(test)] 402 mod tests { 403 use super::*; 404 use crate::slice::ManagedSlice as Slice; 405 406 #[test] simple()407 fn simple() { 408 let mut elements = [0u32; 2]; 409 let mut slots = [Slot::default(); 2]; 410 411 let mut map = SlotMap::new( 412 Slice::Borrowed(&mut elements[..]), 413 Slice::Borrowed(&mut slots[..])); 414 let key42 = map.insert(42).unwrap(); 415 let keylo = map.insert('K' as _).unwrap(); 416 417 assert_eq!(map.insert(0x9999), None); 418 assert_eq!(map.get(key42).cloned(), Some(42)); 419 assert_eq!(map.get(keylo).cloned(), Some('K' as _)); 420 421 assert!(map.remove(key42).is_some()); 422 assert_eq!(map.get(key42), None); 423 424 let lastkey = map.insert(0x9999).unwrap(); 425 assert_eq!(map.get(lastkey).cloned(), Some(0x9999)); 426 427 *map.remove(keylo).unwrap() = 0; 428 assert_eq!(map.get(lastkey).cloned(), Some(0x9999)); 429 assert!(map.remove(lastkey).is_some()); 430 } 431 432 #[test] retained()433 fn retained() { 434 let mut elements = [0u32; 1]; 435 let mut slots = [Slot::default(); 1]; 436 437 let mut map = SlotMap::new( 438 Slice::Borrowed(&mut elements[..]), 439 Slice::Borrowed(&mut slots[..])); 440 let key = map.insert(0xde).unwrap(); 441 map.remove(key).unwrap(); 442 assert_eq!(map.get(key), None); 443 444 let new_key = map.insert(0xad).unwrap(); 445 446 assert_eq!(map.get(key), None); 447 assert_eq!(map.get(new_key).cloned(), Some(0xad)); 448 449 assert_eq!(map.remove(key), None); 450 map.remove(new_key).unwrap(); 451 452 assert_eq!(map.get(key), None); 453 assert_eq!(map.get(new_key), None); 454 } 455 456 #[test] non_simple_free_list()457 fn non_simple_free_list() { 458 // Check the free list implementation 459 let mut elements = [0u32; 3]; 460 let mut slots = [Slot::default(); 3]; 461 462 let mut map = SlotMap::new( 463 Slice::Borrowed(&mut elements[..]), 464 Slice::Borrowed(&mut slots[..])); 465 466 let key0 = map.insert(0).unwrap(); 467 let key1 = map.insert(1).unwrap(); 468 let key2 = map.insert(2).unwrap(); 469 470 *map.remove(key1).unwrap() = 0xF; 471 assert_eq!(map.free_top, 1); 472 assert_eq!(map.get(key0).cloned(), Some(0)); 473 assert_eq!(map.get(key2).cloned(), Some(2)); 474 475 *map.remove(key2).unwrap() = 0xF; 476 assert_eq!(map.free_top, 2); 477 assert_eq!(map.get(key0).cloned(), Some(0)); 478 479 *map.remove(key0).unwrap() = 0xF; 480 assert_eq!(map.free_top, 0); 481 482 let key0 = map.insert(0).unwrap(); 483 assert_eq!(map.free_top, 2); 484 let key1 = map.insert(1).unwrap(); 485 let key2 = map.insert(2).unwrap(); 486 assert_eq!(map.get(key0).cloned(), Some(0)); 487 assert_eq!(map.get(key1).cloned(), Some(1)); 488 assert_eq!(map.get(key2).cloned(), Some(2)); 489 } 490 } 491