use core::cell::Cell;
use core::fmt;
use core::mem::ManuallyDrop;
use core::ptr::NonNull;
use super::TypeInfo;
const EXCLUSIVE: usize = 1usize.rotate_right(2);
const MOVED: usize = 1usize.rotate_right(1);
const MASK: usize = EXCLUSIVE | MOVED;
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
#[non_exhaustive]
pub struct AccessError {
kind: AccessErrorKind,
}
impl AccessError {
#[inline]
pub(crate) const fn not_owned(type_info: TypeInfo) -> Self {
Self {
kind: AccessErrorKind::NotAccessibleOwned(type_info),
}
}
#[inline]
pub(crate) const fn new(kind: AccessErrorKind) -> Self {
Self { kind }
}
}
impl fmt::Display for AccessError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind {
AccessErrorKind::NotAccessibleRef(s) => write!(f, "Cannot read, value is {s}"),
AccessErrorKind::NotAccessibleMut(s) => write!(f, "Cannot write, value is {s}"),
AccessErrorKind::NotAccessibleTake(s) => write!(f, "Cannot take, value is {s}"),
AccessErrorKind::NotAccessibleOwned(type_info) => {
write!(f, "Cannot use owned operations for {type_info}")
}
}
}
}
impl core::error::Error for AccessError {}
impl From<AccessErrorKind> for AccessError {
#[inline]
fn from(kind: AccessErrorKind) -> Self {
AccessError::new(kind)
}
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) enum AccessErrorKind {
NotAccessibleRef(Snapshot),
NotAccessibleMut(Snapshot),
NotAccessibleTake(Snapshot),
NotAccessibleOwned(TypeInfo),
}
#[derive(PartialEq)]
#[repr(transparent)]
pub(crate) struct Snapshot(usize);
impl Snapshot {
pub(crate) fn is_readable(&self) -> bool {
self.0 & MASK == 0
}
pub(crate) fn is_writable(&self) -> bool {
self.0 & MASK == 0
}
pub(crate) fn is_exclusive(&self) -> bool {
self.0 & MASK != 0
}
pub(crate) fn shared(&self) -> usize {
self.0 & !MASK
}
}
impl fmt::Display for Snapshot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0 & MOVED != 0 {
write!(f, "M")?;
} else {
write!(f, "-")?;
}
if self.0 & EXCLUSIVE != 0 {
write!(f, "X")?;
} else {
write!(f, "-")?;
}
write!(f, "{:06}", self.shared())?;
Ok(())
}
}
impl fmt::Debug for Snapshot {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Snapshot({self})")
}
}
#[repr(transparent)]
pub(crate) struct Access(Cell<usize>);
impl Access {
pub(crate) const fn new() -> Self {
Self(Cell::new(0))
}
#[inline(always)]
pub(crate) fn is_shared(&self) -> bool {
self.0.get() & MASK == 0
}
#[inline(always)]
pub(crate) fn is_exclusive(&self) -> bool {
self.0.get() == 0
}
#[inline(always)]
pub(crate) fn is_taken(&self) -> bool {
self.0.get() & MOVED != 0
}
pub(crate) fn shared(&self) -> Result<AccessGuard<'_>, AccessError> {
self.try_shared()?;
Ok(AccessGuard(self))
}
#[inline(always)]
pub(crate) fn try_shared(&self) -> Result<(), AccessError> {
let state = self.0.get();
if state & MASK != 0 {
debug_assert_eq!(
state & !MASK,
0,
"count should be zero, but was {}",
Snapshot(state)
);
return Err(AccessError::new(AccessErrorKind::NotAccessibleRef(
Snapshot(state),
)));
}
if state == !MASK {
crate::alloc::abort();
}
self.0.set(state + 1);
Ok(())
}
#[inline(always)]
pub(crate) fn exclusive(&self) -> Result<AccessGuard<'_>, AccessError> {
self.try_exclusive()?;
Ok(AccessGuard(self))
}
#[inline(always)]
pub(crate) fn try_exclusive(&self) -> Result<(), AccessError> {
let state = self.0.get();
if state != 0 {
return Err(AccessError::new(AccessErrorKind::NotAccessibleMut(
Snapshot(state),
)));
}
self.0.set(state | EXCLUSIVE);
Ok(())
}
#[inline(always)]
pub(crate) fn try_take(&self) -> Result<(), AccessError> {
let state = self.0.get();
if state != 0 {
return Err(AccessError::new(AccessErrorKind::NotAccessibleTake(
Snapshot(state),
)));
}
self.0.set(state | MOVED);
Ok(())
}
#[inline(always)]
pub(crate) fn take(&self) {
let state = self.0.get();
self.0.set(state | MOVED);
}
#[inline(always)]
pub(super) fn release(&self) {
let b = self.0.get();
let b = if b & EXCLUSIVE != 0 {
b & !EXCLUSIVE
} else {
debug_assert_ne!(b & !MASK, 0, "count should be zero but was {}", Snapshot(b));
b - 1
};
self.0.set(b);
}
#[inline(always)]
pub(super) fn snapshot(&self) -> Snapshot {
Snapshot(self.0.get())
}
}
impl fmt::Debug for Access {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Snapshot(self.0.get()))
}
}
#[repr(transparent)]
pub(crate) struct AccessGuard<'a>(&'a Access);
impl AccessGuard<'_> {
pub(crate) unsafe fn into_raw(self) -> RawAccessGuard {
RawAccessGuard(NonNull::from(ManuallyDrop::new(self).0))
}
}
impl Drop for AccessGuard<'_> {
fn drop(&mut self) {
self.0.release();
}
}
#[repr(transparent)]
pub(crate) struct RawAccessGuard(NonNull<Access>);
impl Drop for RawAccessGuard {
fn drop(&mut self) {
unsafe { self.0.as_ref().release() }
}
}