Skip to main content

wasmtime_internal_core/
slab.rs

1//! A very simple, uniformly-typed slab arena that supports deallocation and
2//! reusing deallocated entries' space.
3//!
4//! > **⚠️ Warning ⚠️**: this crate is an internal-only crate for the Wasmtime
5//! > project and is not intended for general use. APIs are not strictly
6//! > reviewed for safety and usage outside of Wasmtime may have bugs. If
7//! > you're interested in using this feel free to file an issue on the
8//! > Wasmtime repository to start a discussion about doing so, but otherwise
9//! > be aware that your usage of this crate is not supported.
10//!
11//! The free list of vacant entries in the slab are stored inline in the slab's
12//! existing storage.
13//!
14//! # Example
15//!
16//! ```
17//! # fn foo() -> wasmtime_internal_core::error::Result<()> {
18//! use wasmtime_internal_core::slab::Slab;
19//!
20//! let mut slab = Slab::new();
21//!
22//! // Insert some values into the slab.
23//! let rza = slab.alloc("Robert Fitzgerald Diggs")?;
24//! let gza = slab.alloc("Gary Grice")?;
25//! let bill = slab.alloc("Bill Gates")?;
26//!
27//! // Allocated elements can be accessed infallibly via indexing (and missing and
28//! // deallocated entries will panic).
29//! assert_eq!(slab[rza], "Robert Fitzgerald Diggs");
30//!
31//! // Alternatively, the `get` and `get_mut` methods provide fallible lookup.
32//! if let Some(genius) = slab.get(gza) {
33//!     println!("The gza gza genius: {}", genius);
34//! }
35//! if let Some(val) = slab.get_mut(bill) {
36//!     *val = "Bill Gates doesn't belong in this set...";
37//! }
38//!
39//! // We can remove values from the slab.
40//! slab.dealloc(bill);
41//!
42//! // Allocate a new entry.
43//! let bill = slab.alloc("Bill Murray")?;
44//! # Ok(())
45//! # }
46//! # foo().unwrap();
47//! ```
48//!
49//! # Using `Id`s with the Wrong `Slab`
50//!
51//! `Slab` does NOT check that `Id`s used to access previously-allocated values
52//! came from the current `Slab` instance (as opposed to a different `Slab`
53//! instance). Using `Id`s from a different `Slab` is safe, but will yield an
54//! unrelated value, if any at all.
55//!
56//! If you desire checking that an `Id` came from the correct `Slab` instance,
57//! it should be easy to layer that functionality on top of this crate by
58//! wrapping `Slab` and `Id` in types that additionally maintain a slab instance
59//! identifier.
60//!
61//! # The ABA Problem
62//!
63//! This `Slab` type does NOT protect against ABA bugs, such as the following
64//! sequence:
65//!
66//! * Value `A` is allocated into the slab, yielding id `i`.
67//!
68//! * `A` is deallocated, and so `i`'s associated entry is added to the slab's
69//!   free list.
70//!
71//! * Value `B` is allocated into the slab, reusing `i`'s associated entry,
72//!   yielding id `i`.
73//!
74//! * The "original" id `i` is used to access the arena, expecting the
75//!   deallocated value `A`, but getting the new value `B`.
76//!
77//! That is, it does not detect and prevent against the memory-safe version of
78//! use-after-free bugs.
79//!
80//! If you need to protect against ABA bugs, it should be easy to layer that
81//! functionality on top of this crate by wrapping `Slab` with something like
82//! the following:
83//!
84//! ```rust
85//! use wasmtime_internal_core::error::OutOfMemory;
86//!
87//! pub struct GenerationalId {
88//!     id: wasmtime_internal_core::slab::Id,
89//!     generation: u32,
90//! }
91//!
92//! struct GenerationalEntry<T> {
93//!     value: T,
94//!     generation: u32,
95//! }
96//!
97//! pub struct GenerationalSlab<T> {
98//!     slab: wasmtime_internal_core::slab::Slab<GenerationalEntry<T>>,
99//!     generation: u32,
100//! }
101//!
102//! impl<T> GenerationalSlab<T> {
103//!     pub fn alloc(&mut self, value: T) -> Result<GenerationalId, OutOfMemory> {
104//!         let generation = self.generation;
105//!         let id = self.slab.alloc(GenerationalEntry { value, generation })?;
106//!         Ok(GenerationalId { id, generation })
107//!     }
108//!
109//!     pub fn get(&self, id: GenerationalId) -> Option<&T> {
110//!         let entry = self.slab.get(id.id)?;
111//!
112//!         // Check that the entry's generation matches the id's generation,
113//!         // else we have an ABA bug. (Alternatively, return `None` instead
114//!         // of panicking.)
115//!         assert_eq!(id.generation, entry.generation);
116//!
117//!         Some(&entry.value)
118//!     }
119//!
120//!     pub fn dealloc(&mut self, id: GenerationalId) {
121//!         // Check that the entry's generation matches the id's generation,
122//!         // else we have an ABA bug. (Alternatively, silently return on
123//!         // double-free instead of panicking.)
124//!         assert_eq!(id.generation, self.slab[id.id].generation);
125//!
126//!         self.slab.dealloc(id.id);
127//!
128//!         // Increment our generation whenever we deallocate so that any new
129//!         // value placed in this same entry will have a different generation
130//!         // and we can detect ABA bugs.
131//!         self.generation += 1;
132//!     }
133//! }
134//! ```
135
136use crate::alloc::TryVec;
137use crate::error::OutOfMemory;
138use core::fmt;
139use core::num::NonZeroU32;
140
141/// An identifier for an allocated value inside a `slab`.
142#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
143#[repr(transparent)]
144pub struct Id(EntryIndex);
145
146impl fmt::Debug for Id {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        f.debug_tuple("Id").field(&self.0.index()).finish()
149    }
150}
151
152impl Id {
153    /// Get the raw underlying representation of this `Id`.
154    #[inline]
155    pub fn into_raw(self) -> u32 {
156        u32::try_from(self.0.index()).unwrap()
157    }
158
159    /// Construct an `Id` from its raw underlying representation.
160    ///
161    /// `raw` should be a value that was previously created via
162    /// `Id::into_raw`. May panic if given arbitrary values.
163    #[inline]
164    pub fn from_raw(raw: u32) -> Self {
165        let raw = usize::try_from(raw).unwrap();
166        Self(EntryIndex::new(raw))
167    }
168}
169
170/// A simple, uni-typed slab arena.
171pub struct Slab<T> {
172    /// The slab's entries, each is either occupied and holding a `T` or vacant
173    /// and is a link the free list.
174    entries: TryVec<Entry<T>>,
175
176    /// The index of the first free entry in the free list.
177    free: Option<EntryIndex>,
178
179    /// The number of occupied entries is this slab.
180    len: u32,
181}
182
183impl<T> fmt::Debug for Slab<T>
184where
185    T: fmt::Debug,
186{
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        f.debug_map().entries(self.iter()).finish()
189    }
190}
191
192enum Entry<T> {
193    /// An occupied entry holding a `T`.
194    Occupied(T),
195
196    /// A vacant entry.
197    Free {
198        /// A link in the slab's free list, pointing to the next free entry, if
199        /// any.
200        next_free: Option<EntryIndex>,
201    },
202}
203
204#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
205#[repr(transparent)]
206struct EntryIndex(NonZeroU32);
207
208impl EntryIndex {
209    #[inline]
210    fn new(index: usize) -> Self {
211        assert!(index <= Slab::<()>::MAX_CAPACITY);
212        let x = u32::try_from(index + 1).unwrap();
213        Self(NonZeroU32::new(x).unwrap())
214    }
215
216    #[inline]
217    fn index(&self) -> usize {
218        let index = self.0.get() - 1;
219        usize::try_from(index).unwrap()
220    }
221}
222
223impl<T> Default for Slab<T> {
224    #[inline]
225    fn default() -> Self {
226        Self {
227            entries: TryVec::default(),
228            free: None,
229            len: 0,
230        }
231    }
232}
233
234impl<T> core::ops::Index<Id> for Slab<T> {
235    type Output = T;
236
237    #[inline]
238    fn index(&self, id: Id) -> &Self::Output {
239        self.get(id)
240            .expect("id from different slab or value was deallocated")
241    }
242}
243
244impl<T> core::ops::IndexMut<Id> for Slab<T> {
245    #[inline]
246    fn index_mut(&mut self, id: Id) -> &mut Self::Output {
247        self.get_mut(id)
248            .expect("id from different slab or value was deallocated")
249    }
250}
251
252impl<T> Slab<T> {
253    /// The maximum capacity any `Slab` can have: `u32::MAX - 1`.
254    pub const MAX_CAPACITY: usize = (u32::MAX - 1) as usize;
255
256    /// Construct a new, empty slab.
257    #[inline]
258    pub fn new() -> Self {
259        Slab::default()
260    }
261
262    /// Construct a new, empty slab, pre-reserving space for at least `capacity`
263    /// elements.
264    #[inline]
265    pub fn with_capacity(capacity: usize) -> Result<Self, OutOfMemory> {
266        let mut slab = Self::new();
267        slab.reserve(capacity)?;
268        Ok(slab)
269    }
270
271    /// Ensure that there is space for at least `additional` elements in this
272    /// slab.
273    ///
274    /// # Panics
275    ///
276    /// Panics if the new capacity exceeds `Self::MAX_CAPACITY`.
277    pub fn reserve(&mut self, additional: usize) -> Result<(), OutOfMemory> {
278        let cap = self.capacity();
279        let len = self.len();
280        assert!(cap >= len);
281        if cap - len >= additional {
282            // Already have `additional` capacity available.
283            return Ok(());
284        }
285
286        self.entries.reserve(additional)?;
287
288        // Maintain the invariant that `i <= MAX_CAPACITY` for all indices `i`
289        // in `self.entries`.
290        assert!(self.entries.capacity() <= Self::MAX_CAPACITY);
291
292        Ok(())
293    }
294
295    fn double_capacity(&mut self) -> Result<(), OutOfMemory> {
296        // Double our capacity to amortize the cost of resizing. But make sure
297        // we add some amount of minimum additional capacity, since doubling
298        // zero capacity isn't useful.
299        const MIN_CAPACITY: usize = 16;
300        let additional = core::cmp::max(self.entries.capacity(), MIN_CAPACITY);
301        self.reserve(additional)
302    }
303
304    /// What is the capacity of this slab? That is, how many entries can it
305    /// contain within its current underlying storage?
306    #[inline]
307    pub fn capacity(&self) -> usize {
308        self.entries.capacity()
309    }
310
311    /// How many values are currently allocated within this slab?
312    #[inline]
313    pub fn len(&self) -> usize {
314        usize::try_from(self.len).unwrap()
315    }
316
317    /// Are there zero allocated values within this slab?
318    #[inline]
319    pub fn is_empty(&self) -> bool {
320        self.len() == 0
321    }
322
323    /// Try to allocate a `T` value within this slab.
324    ///
325    /// If there is no available capacity, ownership of the given value is
326    /// returned via `Err(value)`.
327    #[inline]
328    pub fn try_alloc(&mut self, value: T) -> Result<Id, T> {
329        if let Some(index) = self.try_alloc_index() {
330            let next_free = match self.entries[index.index()] {
331                Entry::Free { next_free } => next_free,
332                Entry::Occupied { .. } => unreachable!(),
333            };
334            self.free = next_free;
335            self.entries[index.index()] = Entry::Occupied(value);
336            self.len += 1;
337            Ok(Id(index))
338        } else {
339            Err(value)
340        }
341    }
342
343    #[inline]
344    fn try_alloc_index(&mut self) -> Option<EntryIndex> {
345        self.free.take().or_else(|| {
346            if self.entries.len() < self.entries.capacity() {
347                let index = EntryIndex::new(self.entries.len());
348                self.entries
349                    .push(Entry::Free { next_free: None })
350                    .expect("have capacity");
351                Some(index)
352            } else {
353                None
354            }
355        })
356    }
357
358    /// Allocate a `T` value within this slab, allocating additional underlying
359    /// storage if there is no available capacity.
360    ///
361    /// # Panics
362    ///
363    /// Panics if allocating this value requires reallocating the underlying
364    /// storage, and the new capacity exceeds `Slab::MAX_CAPACITY`.
365    #[inline]
366    pub fn alloc(&mut self, value: T) -> Result<Id, OutOfMemory> {
367        self.try_alloc(value)
368            .or_else(|value| self.alloc_slow(value))
369    }
370
371    /// Get the `Id` that will be returned for the next allocation in this slab.
372    #[inline]
373    pub fn next_id(&self) -> Id {
374        let index = self.free.unwrap_or_else(|| EntryIndex::new(self.len()));
375        Id(index)
376    }
377
378    #[inline(never)]
379    #[cold]
380    fn alloc_slow(&mut self, value: T) -> Result<Id, OutOfMemory> {
381        // Reserve additional capacity, since we didn't have space for the
382        // allocation.
383        self.double_capacity()?;
384        // After which the allocation will succeed.
385        let id = self.try_alloc(value).ok().unwrap();
386        Ok(id)
387    }
388
389    /// Get a shared borrow of the value associated with `id`.
390    ///
391    /// Returns `None` if the value has since been deallocated.
392    ///
393    /// If `id` comes from a different `Slab` instance, this method may panic,
394    /// return `None`, or return an arbitrary value.
395    #[inline]
396    pub fn get(&self, id: Id) -> Option<&T> {
397        match self
398            .entries
399            .get(id.0.index())
400            .expect("id from different slab")
401        {
402            Entry::Occupied(x) => Some(x),
403            Entry::Free { .. } => None,
404        }
405    }
406
407    /// Get an exclusive borrow of the value associated with `id`.
408    ///
409    /// Returns `None` if the value has since been deallocated.
410    ///
411    /// If `id` comes from a different `Slab` instance, this method may panic,
412    /// return `None`, or return an arbitrary value.
413    #[inline]
414    pub fn get_mut(&mut self, id: Id) -> Option<&mut T> {
415        match self
416            .entries
417            .get_mut(id.0.index())
418            .expect("id from different slab")
419        {
420            Entry::Occupied(x) => Some(x),
421            Entry::Free { .. } => None,
422        }
423    }
424
425    /// Does this slab contain an allocated value for `id`?
426    #[inline]
427    pub fn contains(&self, id: Id) -> bool {
428        match self.entries.get(id.0.index()) {
429            Some(Entry::Occupied(_)) => true,
430            None | Some(Entry::Free { .. }) => false,
431        }
432    }
433
434    /// Deallocate the value associated with the given `id`.
435    ///
436    /// If `id` comes from a different `Slab` instance, this method may panic or
437    /// deallocate an arbitrary value.
438    #[inline]
439    pub fn dealloc(&mut self, id: Id) -> T {
440        let entry = core::mem::replace(
441            self.entries
442                .get_mut(id.0.index())
443                .expect("id from a different slab"),
444            Entry::Free { next_free: None },
445        );
446        match entry {
447            Entry::Free { .. } => panic!("attempt to deallocate an entry that is already vacant"),
448            Entry::Occupied(value) => {
449                let next_free = core::mem::replace(&mut self.free, Some(id.0));
450                self.entries[id.0.index()] = Entry::Free { next_free };
451                self.len -= 1;
452                value
453            }
454        }
455    }
456
457    /// Iterate over all values currently allocated within this `Slab`.
458    ///
459    /// Yields pairs of an `Id` and the `Id`'s associated value.
460    ///
461    /// Iteration order is undefined.
462    #[inline]
463    pub fn iter(&self) -> impl Iterator<Item = (Id, &T)> + '_ {
464        assert!(self.entries.len() <= Self::MAX_CAPACITY);
465        self.entries
466            .iter()
467            .enumerate()
468            .filter_map(|(i, e)| match e {
469                Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
470                Entry::Free { .. } => None,
471            })
472    }
473
474    /// Mutably iterate over all values currently allocated within this `Slab`.
475    ///
476    /// Yields pairs of an `Id` and the `Id`'s associated value.
477    ///
478    /// Iteration order is undefined.
479    #[inline]
480    pub fn iter_mut(&mut self) -> impl Iterator<Item = (Id, &mut T)> + '_ {
481        assert!(self.entries.len() <= Self::MAX_CAPACITY);
482        self.entries
483            .iter_mut()
484            .enumerate()
485            .filter_map(|(i, e)| match e {
486                Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
487                Entry::Free { .. } => None,
488            })
489    }
490
491    /// Iterate over and remove all entries in this slab.
492    ///
493    /// The slab will be empty after calling this method.
494    ///
495    /// Yields pairs of an `Id` and the `Id`'s associated value.
496    ///
497    /// Iteration order is undefined.
498    #[inline]
499    pub fn drain(&mut self) -> impl Iterator<Item = (Id, T)> + '_ {
500        assert!(self.entries.len() <= Self::MAX_CAPACITY);
501        self.len = 0;
502        self.free = None;
503        self.entries
504            .drain(..)
505            .enumerate()
506            .filter_map(|(i, e)| match e {
507                Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
508                Entry::Free { .. } => None,
509            })
510    }
511
512    /// Clear all of the values from inside this slab.
513    ///
514    /// Does not deallocate internal storage.
515    #[inline]
516    pub fn clear(&mut self) {
517        self.len = 0;
518        self.free = None;
519        self.entries.clear();
520    }
521}