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