/* * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/swamp-render * Licensed under the MIT License. See LICENSE in the project root for license information. */ use std::cmp::Ordering; use std::fmt::{Debug, Display}; use std::hash::Hash; #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct Id { index: u16, generation: u16, } impl PartialOrd for Id { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Id { fn cmp(&self, other: &Self) -> Ordering { self.index .cmp(&other.index) .then(self.generation.cmp(&other.generation)) } } impl Id { const fn new(index: u16, generation: u16) -> Self { Self { index, generation } } } impl Display for Id { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}-{}", self.index, self.generation) } } #[derive(Debug)] struct Entry { item: T, generation: u16, } pub struct SlotMap { items: Vec>>, next_free: Vec, } impl Default for SlotMap { fn default() -> Self { Self::new() } } impl SlotMap { #[must_use] pub const fn new() -> Self { Self { items: Vec::new(), next_free: Vec::new(), } } /// # Panics /// /// Panics if slot map gets to big (> `u16::MAX`). #[must_use] pub fn insert(&mut self, item: T) -> Id { let index = if let Some(free_index) = self.next_free.pop() { free_index as usize } else { self.items.push(None); self.items.len() - 1 }; let generation = self.items[index] .as_ref() .map_or(0u16, |entry| entry.generation); self.items[index] = Some(Entry { item, generation }); Id::new( u16::try_from(index).expect("slot map is too big"), generation, ) } #[must_use] pub fn get(&self, handle: &Id) -> Option<&T> { self.items.get(handle.index as usize).and_then(|entry| { entry.as_ref().and_then(|entry| { if entry.generation == handle.generation { Some(&entry.item) } else { None } }) }) } #[must_use] pub fn get_mut(&mut self, handle: &Id) -> Option<&mut T> { self.items.get_mut(handle.index as usize).and_then(|entry| { entry.as_mut().and_then(|entry| { if entry.generation == handle.generation { Some(&mut entry.item) } else { None } }) }) } pub fn remove(&mut self, handle: &Id) -> bool { if let Some(Some(entry)) = self.items.get_mut(handle.index as usize) { if entry.generation == handle.generation { entry.generation += 1; // Invalidate the handle self.next_free.push(handle.index); self.items[handle.index as usize] = None; return true; } } false } // Get an iterator over the items in the SlotMap, including their Id pub fn iter(&self) -> impl Iterator { self.items .iter() .enumerate() .filter_map(|(index, opt_entry)| { opt_entry .as_ref() .map(|entry| (Id::new(index as u16, entry.generation), &entry.item)) }) } // Get a mutable iterator over the items in the SlotMap, including their Id pub fn iter_mut(&mut self) -> impl Iterator { self.items .iter_mut() .enumerate() .filter_map(|(index, opt_entry)| { opt_entry .as_mut() .map(|entry| (Id::new(index as u16, entry.generation), &mut entry.item)) }) } #[must_use] pub fn len(&self) -> usize { self.items.len() } #[must_use] pub fn is_empty(&self) -> bool { self.items.is_empty() } }