#[derive(Debug, Default)]
pub(crate) struct DictAttach<T> {
table: Option<T>,
region_len: usize,
primed: bool,
next_to_update: usize,
}
impl<T: Clone> Clone for DictAttach<T> {
fn clone(&self) -> Self {
Self {
table: self.table.clone(),
region_len: self.region_len,
primed: self.primed,
next_to_update: self.next_to_update,
}
}
fn clone_from(&mut self, source: &Self) {
self.table.clone_from(&source.table);
self.region_len = source.region_len;
self.primed = source.primed;
self.next_to_update = source.next_to_update;
}
}
impl<T> DictAttach<T> {
pub(crate) const fn new() -> Self {
Self {
table: None,
region_len: 0,
primed: false,
next_to_update: 0,
}
}
#[inline]
pub(crate) fn next_to_update(&self) -> usize {
self.next_to_update
}
#[inline]
pub(crate) fn set_next_to_update(&mut self, pos: usize) {
self.next_to_update = pos;
}
#[inline]
pub(crate) fn is_attached(&self) -> bool {
self.table.is_some()
}
#[inline]
pub(crate) fn table(&self) -> Option<&T> {
self.table.as_ref()
}
#[inline]
pub(crate) fn region_len(&self) -> usize {
self.region_len
}
#[inline]
pub(crate) fn set_region_len(&mut self, region_len: usize) {
self.region_len = region_len;
}
#[inline]
pub(crate) fn is_primed(&self) -> bool {
self.primed
}
#[inline]
pub(crate) fn mark_primed(&mut self) {
if self.table.is_some() {
self.primed = true;
}
}
#[inline]
pub(crate) fn table_mut_or_init(&mut self, init: impl FnOnce() -> T) -> &mut T {
self.table.get_or_insert_with(init)
}
fn take_table(&mut self) -> Option<(T, bool, usize, usize)> {
let taken = self
.table
.take()
.map(|table| (table, self.primed, self.region_len, self.next_to_update));
self.invalidate();
taken
}
#[inline]
pub(crate) fn table_mut(&mut self) -> Option<&mut T> {
self.table.as_mut()
}
#[inline]
pub(crate) fn invalidate(&mut self) {
self.table = None;
self.region_len = 0;
self.primed = false;
self.next_to_update = 0;
}
}
#[derive(Debug, Default)]
pub(crate) struct DictKeep<T>(Option<alloc::boxed::Box<Option<Kept<T>>>>);
#[derive(Debug)]
enum Kept<T> {
Parked {
table: T,
region_len: usize,
next_to_update: usize,
},
Spare(T),
}
impl<T> Clone for DictKeep<T> {
fn clone(&self) -> Self {
Self(None)
}
}
impl<T> DictKeep<T> {
pub(crate) const fn new() -> Self {
Self(None)
}
pub(crate) fn table(&self) -> Option<&T> {
match self.kept() {
Some(Kept::Parked { table, .. } | Kept::Spare(table)) => Some(table),
None => None,
}
}
pub(crate) fn slot_bytes(&self) -> usize {
self.0
.as_ref()
.map_or(0, |_| core::mem::size_of::<Option<Kept<T>>>())
}
fn kept(&self) -> Option<&Kept<T>> {
self.0.as_deref().and_then(Option::as_ref)
}
fn is_parked(&self) -> bool {
matches!(self.kept(), Some(Kept::Parked { .. }))
}
fn take(&mut self) -> Option<Kept<T>> {
self.0.as_deref_mut().and_then(Option::take)
}
fn store(&mut self, kept: Option<Kept<T>>) {
match &mut self.0 {
Some(slot) => **slot = kept,
None if kept.is_some() => self.0 = Some(alloc::boxed::Box::new(kept)),
None => {}
}
}
pub(crate) fn park(&mut self, attach: &mut DictAttach<T>) {
match attach.take_table() {
Some((table, true, region_len, next_to_update)) => {
self.store(Some(Kept::Parked {
table,
region_len,
next_to_update,
}));
}
Some((table, false, ..)) if !self.is_parked() => {
self.store(Some(Kept::Spare(table)));
}
_ => {}
}
}
pub(crate) fn revive(
&mut self,
attach: &mut DictAttach<T>,
accept: impl FnOnce(&T, usize) -> bool,
) -> bool {
if !self.is_parked() {
return false;
}
let Some(Kept::Parked {
table,
region_len,
next_to_update,
}) = self.take()
else {
unreachable!("checked just above");
};
if attach.is_attached() || !accept(&table, region_len) {
self.store(Some(Kept::Spare(table)));
return false;
}
attach.table = Some(table);
attach.region_len = region_len;
attach.primed = true;
attach.next_to_update = next_to_update;
true
}
pub(crate) fn forget(&mut self, attach: &mut DictAttach<T>) {
let kept = attach
.take_table()
.map(|(table, ..)| table)
.or_else(|| self.take_any());
self.store(kept.map(Kept::Spare));
}
pub(crate) fn take_spare(&mut self) -> Option<T> {
if !matches!(self.kept(), Some(Kept::Spare(_))) {
return None;
}
match self.take() {
Some(Kept::Spare(table)) => Some(table),
_ => unreachable!("checked just above"),
}
}
pub(crate) fn release(&mut self) {
self.0 = None;
}
fn take_any(&mut self) -> Option<T> {
self.take().map(|kept| match kept {
Kept::Parked { table, .. } | Kept::Spare(table) => table,
})
}
}
#[cfg(test)]
mod tests;