use std::fmt;
use super::{Root, Trace, TARGET};
#[derive(Debug)]
#[repr(u16)]
pub enum Slot<T: fmt::Debug> {
Undefined,
Vacant(usize),
Occupied(u32, T),
Marked(u32, T),
Dropped,
}
impl<T: fmt::Debug> Slot<T> {
pub fn mark(&mut self) {
*self = match std::mem::replace(self, Self::Undefined) {
Self::Occupied(locks, value) => {
log::debug!(target: TARGET,
"Slot: marking at {:p} with {} locks",
std::ptr::addr_of!(value), locks);
Self::Marked(locks, value)
}
slot => slot,
}
}
pub fn unmark(&mut self) {
*self = match std::mem::replace(self, Self::Undefined) {
Self::Marked(locks, value) => {
log::debug!(target: TARGET,
"Slot: unmarking at {:p} with {} locks",
std::ptr::addr_of!(value), locks);
Self::Occupied(locks, value)
}
slot => slot,
}
}
pub fn lock(&mut self) {
let ptr = self as *mut Self;
match self {
Self::Marked(locks, _) | Self::Occupied(locks, _) => {
log::debug!(target: TARGET,
"Slot: locking {:p} with {} locks",
ptr, locks);
*locks = locks.checked_add(1).expect("lock overflow");
}
Self::Dropped => panic!("attempt to lock dropped slot"),
Self::Undefined => panic!("attempt to lock undefined slot"),
Self::Vacant(_) => panic!("attempt to lock vacant slot"),
}
}
pub fn unlock(&mut self) {
let ptr = self as *mut Self;
match self {
Self::Marked(locks, _) | Self::Occupied(locks, _) => {
log::debug!(target: TARGET,
"Slot: unlocking {:p} with {} locks",
ptr, locks);
*locks = locks.checked_sub(1).expect("null-unlock");
}
Self::Dropped => (),
Self::Undefined => panic!("attempt to unlock undefined slot"),
Self::Vacant(_) => panic!("attempt to unlock vacant slot"),
}
}
#[inline]
pub const fn value(&self) -> Option<&T> {
match self {
Self::Marked(_, value) | Self::Occupied(_, value) => Some(value),
_ => None,
}
}
#[inline]
pub fn value_mut(&mut self) -> Option<&mut T> {
match self {
Self::Marked(_, value) | Self::Occupied(_, value) => Some(value),
_ => None,
}
}
#[must_use]
#[inline]
pub const fn is_undefined(&self) -> bool {
matches!(self, Self::Undefined)
}
#[must_use]
#[inline]
pub const fn is_vacant(&self) -> bool {
matches!(self, Self::Vacant(_))
}
#[must_use]
#[inline]
pub const fn is_occupied(&self) -> bool {
matches!(self, Self::Occupied(_, _) | Self::Marked(_, _))
}
#[must_use]
#[inline]
pub const fn is_marked(&self) -> bool {
matches!(self, Self::Marked(_, _))
}
#[must_use]
#[inline]
pub const fn is_locked(&self) -> bool {
matches!(self, Self::Occupied(locks, _) | Self::Marked(locks, _) if *locks > 0)
}
#[must_use]
#[inline]
pub const fn is_collectable(&self) -> bool {
matches!(self, Self::Occupied(locks, _) if *locks == 0)
}
}
impl<'a, T: Trace<'a> + fmt::Debug> Trace<'a> for Slot<T> {
#[inline]
fn trace(&self, traced: &mut Vec<Root<'a>>) {
if let Some(value) = self.value() {
value.trace(traced);
}
}
}
impl<T: fmt::Debug> Default for Slot<T> {
#[inline]
fn default() -> Self {
Self::Undefined
}
}
impl<T: PartialEq + fmt::Debug> PartialEq for Slot<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Undefined, Self::Undefined) => true,
(Self::Vacant(a), Self::Vacant(b)) => a == b,
(Self::Occupied(_, a), Self::Occupied(_, b))
| (Self::Marked(_, a), Self::Marked(_, b)) => a == b,
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display() {
assert_eq!(format!("{:?}", Slot::<i32>::default()), "Undefined");
assert_eq!(format!("{:?}", Slot::<i32>::Vacant(0)), "Vacant(0)");
assert_eq!(format!("{:?}", Slot::Occupied(0, 42)), "Occupied(0, 42)");
assert_eq!(format!("{:?}", Slot::Marked(0, 42)), "Marked(0, 42)");
}
#[allow(clippy::cognitive_complexity)]
#[test]
fn slot() {
let mut slot = Slot::<u32>::default();
assert!(slot.is_undefined());
assert!(!slot.is_vacant());
assert!(!slot.is_occupied());
assert!(!slot.is_marked());
assert!(!slot.is_locked());
assert!(!slot.is_collectable());
slot = Slot::Vacant(0);
assert!(!slot.is_undefined());
assert!(slot.is_vacant());
assert!(!slot.is_occupied());
assert!(!slot.is_marked());
assert!(!slot.is_locked());
assert!(!slot.is_collectable());
slot = Slot::Occupied(0, 42);
assert!(!slot.is_undefined());
assert!(!slot.is_vacant());
assert!(slot.is_occupied());
assert!(!slot.is_marked());
assert!(!slot.is_locked());
assert!(slot.is_collectable());
slot.lock();
assert!(!slot.is_undefined());
assert!(!slot.is_vacant());
assert!(slot.is_occupied());
assert!(!slot.is_marked());
assert!(slot.is_locked());
assert!(!slot.is_collectable());
slot.unlock();
assert!(!slot.is_undefined());
assert!(!slot.is_vacant());
assert!(slot.is_occupied());
assert!(!slot.is_marked());
assert!(!slot.is_locked());
assert!(slot.is_collectable());
slot = Slot::Marked(0, 42);
assert!(!slot.is_undefined());
assert!(!slot.is_vacant());
assert!(slot.is_occupied());
assert!(slot.is_marked());
assert!(!slot.is_locked());
assert!(!slot.is_collectable());
slot.lock();
assert!(!slot.is_undefined());
assert!(!slot.is_vacant());
assert!(slot.is_occupied());
assert!(slot.is_marked());
assert!(slot.is_locked());
assert!(!slot.is_collectable());
slot.unlock();
assert!(!slot.is_undefined());
assert!(!slot.is_vacant());
assert!(slot.is_occupied());
assert!(slot.is_marked());
assert!(!slot.is_locked());
assert!(!slot.is_collectable());
}
}