use crate::utils::unlikely;
use std::fmt;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct Slot<T> {
value: T,
generation: crate::Generation,
}
impl<T> Slot<T> {
#[inline(always)]
fn new(value: T, generation: crate::Generation) -> Self {
Self { value, generation }
}
#[inline(always)]
fn generation(&self) -> crate::Generation {
self.generation
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone)]
pub struct SecondaryMap<T, K: crate::Key = crate::DefaultKey> {
slots: Vec<Option<Slot<T>>>,
num_elems: usize,
#[cfg(debug_assertions)]
map_id: Option<u64>,
#[cfg_attr(feature = "serde", serde(skip))]
_marker: std::marker::PhantomData<K>,
}
impl<T, K: crate::Key> Default for SecondaryMap<T, K> {
fn default() -> Self {
Self::new()
}
}
impl<T, K: crate::Key> SecondaryMap<T, K> {
#[inline]
pub fn new() -> Self {
Self {
slots: Vec::new(),
num_elems: 0,
#[cfg(debug_assertions)]
map_id: None,
_marker: std::marker::PhantomData,
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
slots: Vec::with_capacity(capacity),
num_elems: 0,
#[cfg(debug_assertions)]
map_id: None,
_marker: std::marker::PhantomData,
}
}
pub fn insert(&mut self, key: K, value: T) -> Option<T> {
#[cfg(debug_assertions)]
{
if let Some(id) = self.map_id {
debug_assert_eq!(
id,
key.map_id(),
"Key used with wrong map instance in SecondaryMap"
);
} else {
self.map_id = Some(key.map_id());
}
}
let index = key.index();
let generation = key.generation();
let index = index as usize;
if index >= self.slots.len() {
let required_additional = index - self.slots.len() + 1;
self.slots.reserve(required_additional);
self.slots.resize_with(index + 1, || None);
}
let slot_opt = unsafe { self.slots.get_unchecked_mut(index) };
match slot_opt {
Some(slot) => {
if slot.generation() == generation {
Some(std::mem::replace(&mut slot.value, value))
} else if slot.generation() < generation {
*slot = Slot::new(value, generation);
None
} else {
None
}
}
None => {
*slot_opt = Some(Slot::new(value, generation));
self.num_elems += 1;
None
}
}
}
pub fn remove(&mut self, key: K) -> Option<T> {
#[cfg(debug_assertions)]
if let Some(id) = self.map_id {
debug_assert_eq!(
id,
key.map_id(),
"Key used with wrong map instance in SecondaryMap"
);
}
let index = key.index();
let generation = key.generation();
let index = index as usize;
if unlikely(index >= self.slots.len()) {
return None;
}
let slot_opt = unsafe { self.slots.get_unchecked_mut(index) };
if let Some(slot) = slot_opt {
if slot.generation() == generation {
self.num_elems -= 1;
return slot_opt.take().map(|s| s.value);
}
}
None
}
#[inline]
pub fn get(&self, key: K) -> Option<&T> {
#[cfg(debug_assertions)]
if let Some(id) = self.map_id {
debug_assert_eq!(
id,
key.map_id(),
"Key used with wrong map instance in SecondaryMap"
);
}
let index = key.index();
let generation = key.generation();
let index = index as usize;
if unlikely(index >= self.slots.len()) {
return None;
}
match unsafe { self.slots.get_unchecked(index) } {
Some(slot) if slot.generation() == generation => Some(&slot.value),
_ => None,
}
}
#[inline]
pub fn get_mut(&mut self, key: K) -> Option<&mut T> {
#[cfg(debug_assertions)]
if let Some(id) = self.map_id {
debug_assert_eq!(
id,
key.map_id(),
"Key used with wrong map instance in SecondaryMap"
);
}
let index = key.index();
let generation = key.generation();
let index = index as usize;
if unlikely(index >= self.slots.len()) {
return None;
}
match unsafe { self.slots.get_unchecked_mut(index) } {
Some(slot) if slot.generation() == generation => Some(&mut slot.value),
_ => None,
}
}
#[inline]
pub fn contains_key(&self, key: K) -> bool {
self.get(key).is_some()
}
#[inline]
pub fn len(&self) -> usize {
self.num_elems
}
#[inline]
pub fn is_empty(&self) -> bool {
self.num_elems == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.slots.capacity()
}
pub fn clear(&mut self) {
self.slots.clear();
self.num_elems = 0;
#[cfg(debug_assertions)]
{
self.map_id = None;
}
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(K, &mut T) -> bool,
{
for (index, slot_opt) in self.slots.iter_mut().enumerate() {
if let Some(slot) = slot_opt {
let key = K::from_parts(
index as u32,
slot.generation,
#[cfg(debug_assertions)]
self.map_id.unwrap_or(0),
);
if !f(key, &mut slot.value) {
*slot_opt = None;
self.num_elems -= 1;
}
}
}
}
pub fn iter(&self) -> impl Iterator<Item = (K, &T)> {
self.slots
.iter()
.enumerate()
.filter_map(move |(index, slot_opt)| {
slot_opt.as_ref().map(|slot| {
let key = K::from_parts(
index as u32,
slot.generation,
#[cfg(debug_assertions)]
self.map_id.unwrap_or(0),
);
(key, &slot.value)
})
})
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (K, &mut T)> {
#[cfg(debug_assertions)]
let map_id = self.map_id;
self.slots
.iter_mut()
.enumerate()
.filter_map(move |(index, slot_opt)| {
slot_opt.as_mut().map(|slot| {
let key = K::from_parts(
index as u32,
slot.generation,
#[cfg(debug_assertions)]
map_id.unwrap_or(0),
);
(key, &mut slot.value)
})
})
}
}
impl<T: fmt::Debug> fmt::Debug for SecondaryMap<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem::size_of;
#[test]
fn test_slot_niche_optimization() {
assert_eq!(size_of::<Slot<u32>>(), 8);
assert_eq!(
size_of::<Option<Slot<u32>>>(),
8,
"Niche optimization failed for Slot<u32>"
);
assert_eq!(size_of::<Slot<u64>>(), 16);
assert_eq!(
size_of::<Option<Slot<u64>>>(),
16,
"Niche optimization failed for Slot<u64>"
);
}
}