use std::cell::Cell;
use std::fmt;
use std::future::Future;
use std::marker;
use std::ops;
use std::pin::Pin;
use std::task::{Context, Poll};
use thiserror::Error;
const TAKEN: isize = isize::max_value();
#[derive(Debug, Error)]
pub enum AccessError {
#[error("expected data of type `{expected}`, but found `{actual}`")]
UnexpectedType {
expected: &'static str,
actual: &'static str,
},
#[error("{error}")]
NotAccessibleRef {
#[from]
error: NotAccessibleRef,
},
#[error("{error}")]
NotAccessibleMut {
#[from]
error: NotAccessibleMut,
},
#[error("{error}")]
NotAccessibleTake {
#[from]
error: NotAccessibleTake,
},
}
#[derive(Debug, Error)]
#[error("cannot read, value is {0}")]
pub struct NotAccessibleRef(Snapshot);
#[derive(Debug, Error)]
#[error("cannot write, value is {0}")]
pub struct NotAccessibleMut(Snapshot);
#[derive(Debug, Error)]
#[error("cannot take, value is {0}")]
pub struct NotAccessibleTake(Snapshot);
#[derive(Debug)]
#[repr(transparent)]
pub struct Snapshot(isize);
impl fmt::Display for Snapshot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
0 => write!(f, "fully accessible"),
1 => write!(f, "exclusively accessed"),
TAKEN => write!(f, "moved"),
n if n < 0 => write!(f, "shared by {}", -n),
n => write!(f, "invalidly marked ({})", n),
}
}
}
#[derive(Clone)]
pub(crate) struct Access(Cell<isize>);
impl Access {
pub(crate) const fn new() -> Self {
Self(Cell::new(0))
}
#[inline]
pub(crate) fn is_shared(&self) -> bool {
self.0.get().wrapping_sub(1) < 0
}
#[inline]
pub(crate) fn is_exclusive(&self) -> bool {
self.0.get() == 0
}
#[inline]
pub(crate) fn is_taken(&self) -> bool {
self.0.get() == isize::max_value()
}
#[inline]
pub(crate) fn shared(&self) -> Result<RawBorrowedRef, NotAccessibleRef> {
let state = self.0.get();
let n = state.wrapping_sub(1);
if n >= 0 {
return Err(NotAccessibleRef(Snapshot(state)));
}
self.0.set(n);
Ok(RawBorrowedRef { access: self })
}
#[inline]
pub(crate) fn exclusive(&self) -> Result<RawBorrowedMut, NotAccessibleMut> {
let state = self.0.get();
let n = state.wrapping_add(1);
if n != 1 {
return Err(NotAccessibleMut(Snapshot(state)));
}
self.0.set(n);
Ok(RawBorrowedMut { access: self })
}
#[inline]
pub(crate) fn take(&self) -> Result<RawTakeGuard, NotAccessibleTake> {
let state = self.0.get();
if state != 0 {
return Err(NotAccessibleTake(Snapshot(state)));
}
self.0.set(isize::max_value());
Ok(RawTakeGuard { access: self })
}
#[inline]
fn release_shared(&self) {
let b = self.0.get().wrapping_add(1);
debug_assert!(b <= 0);
self.0.set(b);
}
#[inline]
fn release_exclusive(&self) {
let b = self.0.get().wrapping_sub(1);
debug_assert!(b == 0);
self.0.set(b);
}
#[inline]
fn release_take(&self) {
let b = self.0.get();
debug_assert!(b == isize::max_value());
self.0.set(0);
}
}
impl fmt::Debug for Access {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", Snapshot(self.0.get()))
}
}
pub struct RawBorrowedRef {
access: *const Access,
}
impl Drop for RawBorrowedRef {
fn drop(&mut self) {
unsafe { (*self.access).release_shared() };
}
}
pub struct BorrowRef<'a, T: ?Sized + 'a> {
data: *const T,
guard: RawBorrowedRef,
_marker: marker::PhantomData<&'a T>,
}
impl<'a, T: ?Sized> BorrowRef<'a, T> {
pub(crate) unsafe fn from_raw(data: *const T, guard: RawBorrowedRef) -> Self {
Self {
data,
guard,
_marker: marker::PhantomData,
}
}
pub fn try_map<M, U: ?Sized, E>(this: Self, m: M) -> Result<BorrowRef<'a, U>, E>
where
M: FnOnce(&T) -> Result<&U, E>,
{
let data = m(unsafe { &*this.data })?;
let guard = this.guard;
Ok(BorrowRef {
data,
guard,
_marker: marker::PhantomData,
})
}
}
impl<T: ?Sized> ops::Deref for BorrowRef<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.data }
}
}
impl<T: ?Sized> fmt::Debug for BorrowRef<'_, T>
where
T: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, fmt)
}
}
pub struct RawBorrowedMut {
access: *const Access,
}
impl Drop for RawBorrowedMut {
fn drop(&mut self) {
unsafe { (*self.access).release_exclusive() }
}
}
pub(crate) struct RawTakeGuard {
access: *const Access,
}
impl Drop for RawTakeGuard {
fn drop(&mut self) {
unsafe { (*self.access).release_take() }
}
}
pub struct BorrowMut<'a, T: ?Sized> {
data: *mut T,
guard: RawBorrowedMut,
_marker: marker::PhantomData<&'a mut T>,
}
impl<'a, T: ?Sized> BorrowMut<'a, T> {
pub(crate) unsafe fn from_raw(data: *mut T, guard: RawBorrowedMut) -> Self {
Self {
data,
guard,
_marker: marker::PhantomData,
}
}
pub fn try_map<M, U: ?Sized, E>(this: Self, m: M) -> Result<BorrowMut<'a, U>, E>
where
M: FnOnce(&mut T) -> Result<&mut U, E>,
{
let data = m(unsafe { &mut *this.data })?;
let guard = this.guard;
Ok(BorrowMut {
data,
guard,
_marker: marker::PhantomData,
})
}
}
impl<T: ?Sized> ops::Deref for BorrowMut<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.data }
}
}
impl<T: ?Sized> ops::DerefMut for BorrowMut<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.data }
}
}
impl<T: ?Sized> fmt::Debug for BorrowMut<'_, T>
where
T: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, fmt)
}
}
impl<F> Future for BorrowMut<'_, F>
where
F: Unpin + Future,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
Pin::new(&mut **this).poll(cx)
}
}