use std::fmt;
use std::mem::ManuallyDrop;
pub(crate) union SlotUnion<T> {
pub(crate) value: ManuallyDrop<T>,
pub(crate) next_free: u32,
}
pub(crate) struct Slot<T> {
pub(crate) u: SlotUnion<T>,
pub(crate) version: crate::Version, }
pub(crate) enum SlotContent<'a, T: 'a> {
Occupied(&'a T),
Vacant(&'a u32),
}
pub(crate) enum SlotContentMut<'a, T: 'a> {
OccupiedMut(&'a mut T),
VacantMut(&'a mut u32),
}
use self::SlotContent::{Occupied, Vacant};
use self::SlotContentMut::{OccupiedMut, VacantMut};
impl<T> Slot<T> {
#[inline(always)]
#[allow(unused)]
pub(crate) fn is_vacant(&self) -> bool {
self.version.is_vacant()
}
#[inline(always)]
pub(crate) fn is_reserved(&self) -> bool {
self.version.is_reserved()
}
#[inline(always)]
pub(crate) fn is_occupied(&self) -> bool {
self.version.is_occupied()
}
#[inline(always)]
pub(crate) fn generation(&self) -> crate::Generation {
self.version.generation()
}
#[inline(always)]
pub(crate) fn get<'a>(&'a self) -> SlotContent<'a, T> {
unsafe {
if self.is_occupied() {
Occupied(&*self.u.value)
} else {
Vacant(&self.u.next_free)
}
}
}
#[inline(always)]
pub(crate) fn get_mut<'a>(&'a mut self) -> SlotContentMut<'a, T> {
unsafe {
if self.is_occupied() {
OccupiedMut(&mut *self.u.value)
} else {
VacantMut(&mut self.u.next_free)
}
}
}
}
impl<T> Drop for Slot<T> {
#[inline]
fn drop(&mut self) {
if std::mem::needs_drop::<T>() && self.is_occupied() {
unsafe {
ManuallyDrop::drop(&mut self.u.value);
}
}
}
}
impl<T: Clone> Clone for Slot<T> {
#[inline]
fn clone(&self) -> Self {
Self {
u: match self.get() {
Occupied(value) => SlotUnion {
value: ManuallyDrop::new(value.clone()),
},
Vacant(&next_free) => SlotUnion { next_free },
},
version: self.version,
}
}
#[inline]
fn clone_from(&mut self, source: &Self) {
match (self.get_mut(), source.get()) {
(OccupiedMut(self_val), Occupied(source_val)) => self_val.clone_from(source_val),
(VacantMut(self_next_free), Vacant(&source_next_free)) => {
*self_next_free = source_next_free
}
(_, Occupied(value)) => {
self.u = SlotUnion {
value: ManuallyDrop::new(value.clone()),
}
}
(_, Vacant(&next_free)) => self.u = SlotUnion { next_free },
}
self.version = source.version;
}
}
impl<T: fmt::Debug> fmt::Debug for Slot<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut builder = fmt.debug_struct("Slot");
builder.field("version", &self.version);
match self.get() {
Occupied(value) => builder.field("value", value).finish(),
Vacant(next_free) => builder.field("next_free", next_free).finish(),
}
}
}
#[cfg(feature = "serde")]
impl<T: serde::Serialize> serde::Serialize for Slot<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut state = serializer.serialize_struct("Slot", 2)?;
state.serialize_field("version", &self.version)?;
#[derive(serde::Serialize)]
enum SlotInner<'a, T> {
Occupied(&'a T),
Vacant(u32),
Reserved,
}
let inner = if self.is_occupied() {
unsafe { SlotInner::Occupied(&*self.u.value) }
} else if self.is_reserved() {
SlotInner::Reserved
} else {
unsafe { SlotInner::Vacant(self.u.next_free) }
};
state.serialize_field("inner", &inner)?;
state.end()
}
}
#[cfg(feature = "serde")]
impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Slot<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize)]
struct SlotHelper<T> {
version: u32,
inner: SlotInnerOwned<T>,
}
#[derive(serde::Deserialize)]
enum SlotInnerOwned<T> {
Occupied(T),
Vacant(u32),
Reserved,
}
let helper = SlotHelper::<T>::deserialize(deserializer)?;
let state_bits = helper.version & 0b11;
match (&helper.inner, state_bits) {
(SlotInnerOwned::Occupied(_), 0b11) => {}
(SlotInnerOwned::Vacant(_), 0b00) => {}
(SlotInnerOwned::Reserved, 0b01) => {}
_ => {
return Err(serde::de::Error::custom(
"Slot version and content mismatch",
));
}
}
let u = match helper.inner {
SlotInnerOwned::Occupied(val) => SlotUnion {
value: ManuallyDrop::new(val),
},
SlotInnerOwned::Vacant(next) => SlotUnion { next_free: next },
SlotInnerOwned::Reserved => SlotUnion { next_free: 0 },
};
Ok(Slot {
u,
version: crate::Version::new(
unsafe {
crate::Generation::new(std::num::NonZeroU32::new_unchecked(helper.version >> 2))
},
helper.version & 0b11,
),
})
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
use super::*;
#[test]
fn test_slot_occupied_serde() {
let slot = Slot {
u: SlotUnion {
value: ManuallyDrop::new(42),
},
version: crate::Version::new(
unsafe { crate::Generation::new(std::num::NonZeroU32::new_unchecked(1)) },
0b11,
),
};
let serialized = serde_json::to_string(&slot).expect("Failed to serialize");
let deserialized: Slot<i32> =
serde_json::from_str(&serialized).expect("Failed to deserialize");
assert!(deserialized.is_occupied());
assert_eq!(deserialized.generation().get(), 1);
if let Occupied(val) = deserialized.get() {
assert_eq!(*val, 42);
} else {
panic!("Expected Occupied");
}
}
#[test]
fn test_slot_vacant_serde() {
let slot: Slot<i32> = Slot {
u: SlotUnion { next_free: 10 },
version: crate::Version::new(
unsafe { crate::Generation::new(std::num::NonZeroU32::new_unchecked(2)) },
0b00,
),
};
let serialized = serde_json::to_string(&slot).expect("Failed to serialize");
let deserialized: Slot<i32> =
serde_json::from_str(&serialized).expect("Failed to deserialize");
assert!(deserialized.is_vacant());
assert_eq!(deserialized.generation().get(), 2);
if let Vacant(next) = deserialized.get() {
assert_eq!(*next, 10);
} else {
panic!("Expected Vacant");
}
}
#[test]
fn test_slot_reserved_serde() {
let slot: Slot<i32> = Slot {
u: SlotUnion { next_free: 0 },
version: crate::Version::new(
unsafe { crate::Generation::new(std::num::NonZeroU32::new_unchecked(3)) },
0b01,
),
};
let serialized = serde_json::to_string(&slot).expect("Failed to serialize");
let deserialized: Slot<i32> =
serde_json::from_str(&serialized).expect("Failed to deserialize");
assert!(deserialized.is_reserved());
assert_eq!(deserialized.generation().get(), 3);
}
}