use super::handle::RawHandle;
#[derive(Clone, Debug)]
struct Slot<T> {
generation: u32,
value: Option<T>,
}
#[derive(Clone, Debug)]
struct SparseSlot<T> {
index: u32,
slot: Slot<T>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum ManifestRowsError {
InconsistentOrder,
RowOutOfRange,
CapacityUnavailable,
}
impl ManifestRowsError {
pub(crate) const fn reason(self) -> &'static str {
match self {
Self::InconsistentOrder => "manifest rows are not strictly increasing",
Self::RowOutOfRange => "manifest row is outside the supported identity range",
Self::CapacityUnavailable => "manifest slot capacity is unavailable",
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct SlotMap<T> {
slots: Vec<Slot<T>>,
sparse: Vec<SparseSlot<T>>,
free: Vec<u32>,
live: usize,
}
impl<T> Default for SlotMap<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> SlotMap<T> {
pub(crate) fn new() -> Self {
Self {
slots: Vec::new(),
sparse: Vec::new(),
free: Vec::new(),
live: 0,
}
}
pub(crate) fn reserve(&mut self, additional: usize) {
self.slots.reserve(additional);
}
pub(crate) fn prepare_manifest_rows<I>(&mut self, rows: I) -> Result<(), ManifestRowsError>
where
I: ExactSizeIterator<Item = u32>,
{
let live_count = rows.len();
if live_count == 0 {
return Ok(());
}
let mut previous = None;
let mut dense_count = 0usize;
let mut next_dense = crate::column::saturating_u32(self.slots.len());
for row in rows {
if row == u32::MAX {
return Err(ManifestRowsError::RowOutOfRange);
}
if previous.is_some_and(|before| row <= before) {
return Err(ManifestRowsError::InconsistentOrder);
}
if row == next_dense {
dense_count = dense_count.saturating_add(1);
next_dense = next_dense.saturating_add(1);
}
previous = Some(row);
}
self.slots
.try_reserve_exact(dense_count)
.map_err(|_| ManifestRowsError::CapacityUnavailable)?;
self.sparse
.try_reserve_exact(live_count.saturating_sub(dense_count))
.map_err(|_| ManifestRowsError::CapacityUnavailable)
}
pub(crate) fn insert(&mut self, value: T) -> RawHandle {
if let Some(index) = self.free.pop()
&& let Some(slot) = self.slot_mut(index)
&& slot.value.is_none()
{
let generation = slot.generation;
slot.value = Some(value);
self.live = self.live.saturating_add(1);
return RawHandle { index, generation };
}
self.densify_sparse_prefix();
let index = crate::column::saturating_u32(self.slots.len());
self.slots.push(Slot {
generation: 0,
value: Some(value),
});
self.live = self.live.saturating_add(1);
RawHandle {
index,
generation: 0,
}
}
pub(crate) fn insert_at(&mut self, handle: RawHandle, value: T) -> Option<()> {
if handle.index == u32::MAX {
return None;
}
let index = usize::try_from(handle.index).ok()?;
if self.slots.len() == index {
self.slots.push(Slot {
generation: handle.generation,
value: Some(value),
});
self.live = self.live.saturating_add(1);
self.densify_sparse_prefix();
return Some(());
}
if index < self.slots.len() {
let slot = self.slots.get_mut(index)?;
if slot.value.is_some() {
return None;
}
slot.generation = handle.generation;
slot.value = Some(value);
self.remove_free(handle.index)?;
self.live = self.live.saturating_add(1);
return Some(());
}
let position = match self
.sparse
.binary_search_by_key(&handle.index, |entry| entry.index)
{
Ok(position) => {
let entry = self.sparse.get_mut(position)?;
if entry.slot.value.is_some() {
return None;
}
entry.slot.generation = handle.generation;
entry.slot.value = Some(value);
self.remove_free(handle.index)?;
self.live = self.live.saturating_add(1);
return Some(());
}
Err(position) => position,
};
self.sparse.insert(
position,
SparseSlot {
index: handle.index,
slot: Slot {
generation: handle.generation,
value: Some(value),
},
},
);
self.live = self.live.saturating_add(1);
Some(())
}
pub(crate) fn get(&self, handle: RawHandle) -> Option<&T> {
let slot = self.slot(handle.index)?;
if slot.generation != handle.generation {
return None;
}
slot.value.as_ref()
}
pub(crate) fn get_index(&self, index: u32) -> Option<(RawHandle, &T)> {
let slot = self.slot(index)?;
Some((
RawHandle {
index,
generation: slot.generation,
},
slot.value.as_ref()?,
))
}
pub(crate) fn get_mut(&mut self, handle: RawHandle) -> Option<&mut T> {
let slot = self.slot_mut(handle.index)?;
if slot.generation != handle.generation {
return None;
}
slot.value.as_mut()
}
pub(crate) fn remove(&mut self, handle: RawHandle) -> Option<T> {
let slot = self.slot_mut(handle.index)?;
if slot.generation != handle.generation {
return None;
}
let value = slot.value.take()?;
slot.generation = slot.generation.wrapping_add(1);
self.free.push(handle.index);
self.live = self.live.saturating_sub(1);
Some(value)
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (RawHandle, &T)> + '_ {
let dense = self.slots.iter().enumerate().filter_map(|(i, slot)| {
let value = slot.value.as_ref()?;
let index = crate::column::saturating_u32(i);
Some((
RawHandle {
index,
generation: slot.generation,
},
value,
))
});
let sparse = self.sparse.iter().filter_map(|entry| {
let value = entry.slot.value.as_ref()?;
Some((
RawHandle {
index: entry.index,
generation: entry.slot.generation,
},
value,
))
});
dense.chain(sparse)
}
pub(crate) fn len(&self) -> usize {
self.live
}
fn slot(&self, index: u32) -> Option<&Slot<T>> {
if let Some(slot) = self.slots.get(index as usize) {
return Some(slot);
}
let position = self
.sparse
.binary_search_by_key(&index, |entry| entry.index)
.ok()?;
self.sparse.get(position).map(|entry| &entry.slot)
}
fn slot_mut(&mut self, index: u32) -> Option<&mut Slot<T>> {
if usize::try_from(index).ok()? < self.slots.len() {
return self.slots.get_mut(index as usize);
}
let position = self
.sparse
.binary_search_by_key(&index, |entry| entry.index)
.ok()?;
self.sparse.get_mut(position).map(|entry| &mut entry.slot)
}
fn remove_free(&mut self, index: u32) -> Option<()> {
let position = self.free.iter().position(|&row| row == index)?;
self.free.swap_remove(position);
Some(())
}
fn densify_sparse_prefix(&mut self) {
let mut count = 0usize;
let mut expected = crate::column::saturating_u32(self.slots.len());
for entry in &self.sparse {
if entry.index != expected {
break;
}
count = count.saturating_add(1);
expected = expected.saturating_add(1);
}
if count == 0 {
return;
}
self.slots
.extend(self.sparse.drain(..count).map(|entry| entry.slot));
}
}
#[cfg(test)]
#[path = "slot_map_tests.rs"]
mod tests;