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 a value previously returned by `into_raw`, if
160    /// that value is well-formed.
161    ///
162    /// A well-formed `Id` is not necessarily one that any slab has handed out;
163    /// lookups are still checked.
164    #[inline]
165    pub fn try_from_raw(raw: u32) -> Option<Self> {
166        let raw = usize::try_from(raw).ok()?;
167        if raw > Slab::<()>::MAX_CAPACITY {
168            return None;
169        }
170        Some(Self(EntryIndex::new(raw)))
171    }
172}
173
174/// A simple, uni-typed slab arena.
175pub struct Slab<T> {
176    /// The slab's entries, each is either occupied and holding a `T` or vacant
177    /// and is a link the free list.
178    entries: TryVec<Entry<T>>,
179
180    /// The index of the first free entry in the free list.
181    free: Option<EntryIndex>,
182
183    /// The number of occupied entries is this slab.
184    len: u32,
185}
186
187impl<T> fmt::Debug for Slab<T>
188where
189    T: fmt::Debug,
190{
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        f.debug_map().entries(self.iter()).finish()
193    }
194}
195
196enum Entry<T> {
197    /// An occupied entry holding a `T`.
198    Occupied(T),
199
200    /// A vacant entry.
201    Free {
202        /// A link in the slab's free list, pointing to the next free entry, if
203        /// any.
204        next_free: Option<EntryIndex>,
205    },
206}
207
208#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
209#[repr(transparent)]
210struct EntryIndex(NonZeroU32);
211
212impl EntryIndex {
213    #[inline]
214    fn new(index: usize) -> Self {
215        assert!(index <= Slab::<()>::MAX_CAPACITY);
216        let x = u32::try_from(index + 1).unwrap();
217        Self(NonZeroU32::new(x).unwrap())
218    }
219
220    #[inline]
221    fn index(&self) -> usize {
222        let index = self.0.get() - 1;
223        usize::try_from(index).unwrap()
224    }
225}
226
227impl<T> Default for Slab<T> {
228    #[inline]
229    fn default() -> Self {
230        Self {
231            entries: TryVec::default(),
232            free: None,
233            len: 0,
234        }
235    }
236}
237
238impl<T> core::ops::Index<Id> for Slab<T> {
239    type Output = T;
240
241    #[inline]
242    fn index(&self, id: Id) -> &Self::Output {
243        self.get(id)
244            .expect("id from different slab or value was deallocated")
245    }
246}
247
248impl<T> core::ops::IndexMut<Id> for Slab<T> {
249    #[inline]
250    fn index_mut(&mut self, id: Id) -> &mut Self::Output {
251        self.get_mut(id)
252            .expect("id from different slab or value was deallocated")
253    }
254}
255
256impl<T> Slab<T> {
257    /// The maximum capacity any `Slab` can have: `u32::MAX - 1`.
258    pub const MAX_CAPACITY: usize = (u32::MAX - 1) as usize;
259
260    /// Construct a new, empty slab.
261    #[inline]
262    pub fn new() -> Self {
263        Slab::default()
264    }
265
266    /// Construct a new, empty slab, pre-reserving space for at least `capacity`
267    /// elements.
268    #[inline]
269    pub fn with_capacity(capacity: usize) -> Result<Self, OutOfMemory> {
270        let mut slab = Self::new();
271        slab.reserve(capacity)?;
272        Ok(slab)
273    }
274
275    /// Ensure that there is space for at least `additional` elements in this
276    /// slab.
277    ///
278    /// # Panics
279    ///
280    /// Panics if the new capacity exceeds `Self::MAX_CAPACITY`.
281    pub fn reserve(&mut self, additional: usize) -> Result<(), OutOfMemory> {
282        let cap = self.capacity();
283        let len = self.len();
284        assert!(cap >= len);
285        if cap - len >= additional {
286            // Already have `additional` capacity available.
287            return Ok(());
288        }
289
290        self.entries.reserve(additional)?;
291
292        // Maintain the invariant that `i <= MAX_CAPACITY` for all indices `i`
293        // in `self.entries`.
294        assert!(self.entries.capacity() <= Self::MAX_CAPACITY);
295
296        Ok(())
297    }
298
299    fn double_capacity(&mut self) -> Result<(), OutOfMemory> {
300        // Double our capacity to amortize the cost of resizing. But make sure
301        // we add some amount of minimum additional capacity, since doubling
302        // zero capacity isn't useful.
303        const MIN_CAPACITY: usize = 16;
304        let additional = core::cmp::max(self.entries.capacity(), MIN_CAPACITY);
305        self.reserve(additional)
306    }
307
308    /// What is the capacity of this slab? That is, how many entries can it
309    /// contain within its current underlying storage?
310    #[inline]
311    pub fn capacity(&self) -> usize {
312        self.entries.capacity()
313    }
314
315    /// How many values are currently allocated within this slab?
316    #[inline]
317    pub fn len(&self) -> usize {
318        usize::try_from(self.len).unwrap()
319    }
320
321    /// Are there zero allocated values within this slab?
322    #[inline]
323    pub fn is_empty(&self) -> bool {
324        self.len() == 0
325    }
326
327    /// Try to allocate a `T` value within this slab.
328    ///
329    /// If there is no available capacity, ownership of the given value is
330    /// returned via `Err(value)`.
331    #[inline]
332    pub fn try_alloc(&mut self, value: T) -> Result<Id, T> {
333        if let Some(index) = self.try_alloc_index() {
334            let next_free = match self.entries[index.index()] {
335                Entry::Free { next_free } => next_free,
336                Entry::Occupied { .. } => unreachable!(),
337            };
338            self.free = next_free;
339            self.entries[index.index()] = Entry::Occupied(value);
340            self.len += 1;
341            Ok(Id(index))
342        } else {
343            Err(value)
344        }
345    }
346
347    #[inline]
348    fn try_alloc_index(&mut self) -> Option<EntryIndex> {
349        self.free.take().or_else(|| {
350            if self.entries.len() < self.entries.capacity() {
351                let index = EntryIndex::new(self.entries.len());
352                self.entries
353                    .push(Entry::Free { next_free: None })
354                    .expect("have capacity");
355                Some(index)
356            } else {
357                None
358            }
359        })
360    }
361
362    /// Allocate a `T` value within this slab, allocating additional underlying
363    /// storage if there is no available capacity.
364    ///
365    /// # Panics
366    ///
367    /// Panics if allocating this value requires reallocating the underlying
368    /// storage, and the new capacity exceeds `Slab::MAX_CAPACITY`.
369    #[inline]
370    pub fn alloc(&mut self, value: T) -> Result<Id, OutOfMemory> {
371        self.try_alloc(value)
372            .or_else(|value| self.alloc_slow(value))
373    }
374
375    /// Get the `Id` that will be returned for the next allocation in this slab.
376    #[inline]
377    pub fn next_id(&self) -> Id {
378        let index = self.free.unwrap_or_else(|| EntryIndex::new(self.len()));
379        Id(index)
380    }
381
382    #[inline(never)]
383    #[cold]
384    fn alloc_slow(&mut self, value: T) -> Result<Id, OutOfMemory> {
385        // Reserve additional capacity, since we didn't have space for the
386        // allocation.
387        self.double_capacity()?;
388        // After which the allocation will succeed.
389        let id = self.try_alloc(value).ok().unwrap();
390        Ok(id)
391    }
392
393    /// Get a shared borrow of the value associated with `id`.
394    ///
395    /// Returns `None` if the value has since been deallocated.
396    ///
397    /// If `id` comes from a different `Slab` instance, this method may return
398    /// `None`, or return an arbitrary value.
399    #[inline]
400    pub fn get(&self, id: Id) -> Option<&T> {
401        match self.entries.get(id.0.index())? {
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 return
412    /// `None`, or return an arbitrary value.
413    #[inline]
414    pub fn get_mut(&mut self, id: Id) -> Option<&mut T> {
415        match self.entries.get_mut(id.0.index())? {
416            Entry::Occupied(x) => Some(x),
417            Entry::Free { .. } => None,
418        }
419    }
420
421    /// Does this slab contain an allocated value for `id`?
422    #[inline]
423    pub fn contains(&self, id: Id) -> bool {
424        match self.entries.get(id.0.index()) {
425            Some(Entry::Occupied(_)) => true,
426            None | Some(Entry::Free { .. }) => false,
427        }
428    }
429
430    /// Deallocate the value associated with the given `id`.
431    ///
432    /// If `id` comes from a different `Slab` instance, this method may panic or
433    /// deallocate an arbitrary value.
434    #[inline]
435    pub fn dealloc(&mut self, id: Id) -> T {
436        let entry = core::mem::replace(
437            self.entries
438                .get_mut(id.0.index())
439                .expect("id from a different slab"),
440            Entry::Free { next_free: None },
441        );
442        match entry {
443            Entry::Free { .. } => panic!("attempt to deallocate an entry that is already vacant"),
444            Entry::Occupied(value) => {
445                let next_free = core::mem::replace(&mut self.free, Some(id.0));
446                self.entries[id.0.index()] = Entry::Free { next_free };
447                self.len -= 1;
448                value
449            }
450        }
451    }
452
453    /// Iterate over all values currently allocated within this `Slab`.
454    ///
455    /// Yields pairs of an `Id` and the `Id`'s associated value.
456    ///
457    /// Iteration order is undefined.
458    #[inline]
459    pub fn iter(&self) -> impl Iterator<Item = (Id, &T)> + '_ {
460        assert!(self.entries.len() <= Self::MAX_CAPACITY);
461        self.entries
462            .iter()
463            .enumerate()
464            .filter_map(|(i, e)| match e {
465                Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
466                Entry::Free { .. } => None,
467            })
468    }
469
470    /// Mutably iterate over all values currently allocated within this `Slab`.
471    ///
472    /// Yields pairs of an `Id` and the `Id`'s associated value.
473    ///
474    /// Iteration order is undefined.
475    #[inline]
476    pub fn iter_mut(&mut self) -> impl Iterator<Item = (Id, &mut T)> + '_ {
477        assert!(self.entries.len() <= Self::MAX_CAPACITY);
478        self.entries
479            .iter_mut()
480            .enumerate()
481            .filter_map(|(i, e)| match e {
482                Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
483                Entry::Free { .. } => None,
484            })
485    }
486
487    /// Iterate over and remove all entries in this slab.
488    ///
489    /// The slab will be empty after calling this method.
490    ///
491    /// Yields pairs of an `Id` and the `Id`'s associated value.
492    ///
493    /// Iteration order is undefined.
494    #[inline]
495    pub fn drain(&mut self) -> impl Iterator<Item = (Id, T)> + '_ {
496        assert!(self.entries.len() <= Self::MAX_CAPACITY);
497        self.len = 0;
498        self.free = None;
499        self.entries
500            .drain(..)
501            .enumerate()
502            .filter_map(|(i, e)| match e {
503                Entry::Occupied(x) => Some((Id(EntryIndex::new(i)), x)),
504                Entry::Free { .. } => None,
505            })
506    }
507
508    /// Clear all of the values from inside this slab.
509    ///
510    /// Does not deallocate internal storage.
511    #[inline]
512    pub fn clear(&mut self) {
513        self.len = 0;
514        self.free = None;
515        self.entries.clear();
516    }
517}