#![allow(unexpected_cfgs)]
#![allow(clippy::should_implement_trait)]
use core::cell::{Cell, UnsafeCell};
use core::cmp::Ordering;
use core::fmt::{self, Debug, Display};
use core::marker::PhantomData;
use core::mem;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use super::init::{init, Init, UnsafeCellInit};
pub struct RefCell<T: ?Sized> {
borrow: Cell<BorrowFlag>,
#[cfg(feature = "debug_refcell")]
borrowed_at: Cell<Option<&'static crate::panic::Location<'static>>>,
_not_sync: PhantomData<*const ()>,
value: UnsafeCell<T>,
}
#[non_exhaustive]
pub struct BorrowError {
#[cfg(feature = "debug_refcell")]
location: &'static crate::panic::Location<'static>,
}
impl Debug for BorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut builder = f.debug_struct("BorrowError");
#[cfg(feature = "debug_refcell")]
builder.field("location", self.location);
builder.finish()
}
}
impl Display for BorrowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt("already mutably borrowed", f)
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for BorrowError {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "already mutably borrowed")
}
}
#[non_exhaustive]
pub struct BorrowMutError {
#[cfg(feature = "debug_refcell")]
location: &'static crate::panic::Location<'static>,
}
impl Debug for BorrowMutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut builder = f.debug_struct("BorrowMutError");
#[cfg(feature = "debug_refcell")]
builder.field("location", self.location);
builder.finish()
}
}
impl Display for BorrowMutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt("already borrowed", f)
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for BorrowMutError {
fn format(&self, f: defmt::Formatter<'_>) {
defmt::write!(f, "already borrowed")
}
}
#[inline(never)]
#[track_caller]
#[cold]
fn panic_already_borrowed(err: BorrowMutError) -> ! {
panic!("already borrowed: {:?}", err)
}
#[inline(never)]
#[track_caller]
#[cold]
fn panic_already_mutably_borrowed(err: BorrowError) -> ! {
panic!("already mutably borrowed: {:?}", err)
}
type BorrowFlag = isize;
const UNUSED: BorrowFlag = 0;
#[inline(always)]
fn is_writing(x: BorrowFlag) -> bool {
x < UNUSED
}
#[inline(always)]
fn is_reading(x: BorrowFlag) -> bool {
x > UNUSED
}
impl<T> RefCell<T> {
#[inline]
pub const fn new(value: T) -> RefCell<T> {
RefCell {
value: UnsafeCell::new(value),
borrow: Cell::new(UNUSED),
#[cfg(feature = "debug_refcell")]
borrowed_at: Cell::new(None),
_not_sync: PhantomData,
}
}
pub fn init<I: Init<T>>(value: I) -> impl Init<Self> {
init!(Self {
value <- UnsafeCell::init(value),
borrow: Cell::new(UNUSED),
_not_sync: PhantomData,
})
}
#[inline]
pub fn into_inner(self) -> T {
self.value.into_inner()
}
#[inline]
#[track_caller]
pub fn replace(&self, t: T) -> T {
mem::replace(&mut *self.borrow_mut(), t)
}
#[inline]
#[track_caller]
pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T {
let mut_borrow = &mut *self.borrow_mut();
let replacement = f(mut_borrow);
mem::replace(mut_borrow, replacement)
}
#[inline]
pub fn swap(&self, other: &Self) {
mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut())
}
}
impl<T: ?Sized> RefCell<T> {
#[inline]
#[track_caller]
pub fn borrow(&self) -> Ref<'_, T> {
match self.try_borrow() {
Ok(b) => b,
Err(err) => panic_already_mutably_borrowed(err),
}
}
#[inline]
pub fn try_borrow(&self) -> Result<Ref<'_, T>, BorrowError> {
match BorrowRef::new(&self.borrow) {
Some(b) => {
#[cfg(feature = "debug_refcell")]
{
if b.borrow.get() == 1 {
self.borrowed_at.set(Some(crate::panic::Location::caller()));
}
}
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
Ok(Ref { value, borrow: b })
}
None => Err(BorrowError {
#[cfg(feature = "debug_refcell")]
location: unwrap!(self.borrowed_at.get()),
}),
}
}
#[inline]
#[track_caller]
pub fn borrow_mut(&self) -> RefMut<'_, T> {
match self.try_borrow_mut() {
Ok(b) => b,
Err(err) => panic_already_borrowed(err),
}
}
#[inline]
pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
match BorrowRefMut::new(&self.borrow) {
Some(b) => {
#[cfg(feature = "debug_refcell")]
{
self.borrowed_at.set(Some(crate::panic::Location::caller()));
}
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
Ok(RefMut {
value,
borrow: b,
marker: PhantomData,
})
}
None => Err(BorrowMutError {
#[cfg(feature = "debug_refcell")]
location: unwrap!(self.borrowed_at.get()),
}),
}
}
#[inline]
pub fn as_ptr(&self) -> *mut T {
self.value.get()
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
#[inline]
pub unsafe fn try_borrow_unguarded(&self) -> Result<&T, BorrowError> {
if !is_writing(self.borrow.get()) {
Ok(unsafe { &*self.value.get() })
} else {
Err(BorrowError {
#[cfg(feature = "debug_refcell")]
location: unwrap!(self.borrowed_at.get()),
})
}
}
}
impl<T: Default> RefCell<T> {
pub fn take(&self) -> T {
self.replace(Default::default())
}
}
unsafe impl<T: ?Sized> Send for RefCell<T> where T: Send {}
impl<T: Clone> Clone for RefCell<T> {
#[inline]
#[track_caller]
fn clone(&self) -> RefCell<T> {
RefCell::new(self.borrow().clone())
}
#[inline]
#[track_caller]
fn clone_from(&mut self, other: &Self) {
self.get_mut().clone_from(&other.borrow())
}
}
impl<T: Default> Default for RefCell<T> {
#[inline]
fn default() -> RefCell<T> {
RefCell::new(Default::default())
}
}
impl<T: ?Sized + PartialEq> PartialEq for RefCell<T> {
#[inline]
fn eq(&self, other: &RefCell<T>) -> bool {
*self.borrow() == *other.borrow()
}
}
impl<T: ?Sized + Eq> Eq for RefCell<T> {}
impl<T: ?Sized + PartialOrd> PartialOrd for RefCell<T> {
#[inline]
fn partial_cmp(&self, other: &RefCell<T>) -> Option<Ordering> {
self.borrow().partial_cmp(&*other.borrow())
}
#[inline]
fn lt(&self, other: &RefCell<T>) -> bool {
*self.borrow() < *other.borrow()
}
#[inline]
fn le(&self, other: &RefCell<T>) -> bool {
*self.borrow() <= *other.borrow()
}
#[inline]
fn gt(&self, other: &RefCell<T>) -> bool {
*self.borrow() > *other.borrow()
}
#[inline]
fn ge(&self, other: &RefCell<T>) -> bool {
*self.borrow() >= *other.borrow()
}
}
impl<T: ?Sized + Ord> Ord for RefCell<T> {
#[inline]
fn cmp(&self, other: &RefCell<T>) -> Ordering {
self.borrow().cmp(&*other.borrow())
}
}
struct BorrowRef<'b> {
borrow: &'b Cell<BorrowFlag>,
}
impl<'b> BorrowRef<'b> {
#[inline]
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRef<'b>> {
let b = borrow.get().wrapping_add(1);
if !is_reading(b) {
None
} else {
borrow.set(b);
Some(BorrowRef { borrow })
}
}
}
impl Drop for BorrowRef<'_> {
#[inline]
fn drop(&mut self) {
let borrow = self.borrow.get();
debug_assert!(is_reading(borrow));
self.borrow.set(borrow - 1);
}
}
impl Clone for BorrowRef<'_> {
#[inline]
fn clone(&self) -> Self {
let borrow = self.borrow.get();
debug_assert!(is_reading(borrow));
assert!(borrow != BorrowFlag::MAX);
self.borrow.set(borrow + 1);
BorrowRef {
borrow: self.borrow,
}
}
}
pub struct Ref<'b, T: ?Sized + 'b> {
value: NonNull<T>,
borrow: BorrowRef<'b>,
}
impl<T: ?Sized> Deref for Ref<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { self.value.as_ref() }
}
}
impl<'b, T: ?Sized> Ref<'b, T> {
#[must_use]
#[inline]
pub fn clone(orig: &Ref<'b, T>) -> Ref<'b, T> {
Ref {
value: orig.value,
borrow: orig.borrow.clone(),
}
}
#[inline]
pub fn map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Ref<'b, U>
where
F: FnOnce(&T) -> &U,
{
Ref {
value: NonNull::from(f(&*orig)),
borrow: orig.borrow,
}
}
#[inline]
pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Result<Ref<'b, U>, Self>
where
F: FnOnce(&T) -> Option<&U>,
{
match f(&*orig) {
Some(value) => Ok(Ref {
value: NonNull::from(value),
borrow: orig.borrow,
}),
None => Err(orig),
}
}
#[inline]
pub fn map_split<U: ?Sized, V: ?Sized, F>(orig: Ref<'b, T>, f: F) -> (Ref<'b, U>, Ref<'b, V>)
where
F: FnOnce(&T) -> (&U, &V),
{
let (a, b) = f(&*orig);
let borrow = orig.borrow.clone();
(
Ref {
value: NonNull::from(a),
borrow,
},
Ref {
value: NonNull::from(b),
borrow: orig.borrow,
},
)
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for Ref<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<'b, T: ?Sized> RefMut<'b, T> {
#[inline]
pub fn map<U: ?Sized, F>(mut orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
where
F: FnOnce(&mut T) -> &mut U,
{
let value = NonNull::from(f(&mut *orig));
RefMut {
value,
borrow: orig.borrow,
marker: PhantomData,
}
}
#[inline]
pub fn filter_map<U: ?Sized, F>(mut orig: RefMut<'b, T>, f: F) -> Result<RefMut<'b, U>, Self>
where
F: FnOnce(&mut T) -> Option<&mut U>,
{
match f(&mut *orig) {
Some(value) => Ok(RefMut {
value: NonNull::from(value),
borrow: orig.borrow,
marker: PhantomData,
}),
None => Err(orig),
}
}
#[inline]
pub fn map_split<U: ?Sized, V: ?Sized, F>(
mut orig: RefMut<'b, T>,
f: F,
) -> (RefMut<'b, U>, RefMut<'b, V>)
where
F: FnOnce(&mut T) -> (&mut U, &mut V),
{
let borrow = orig.borrow.clone();
let (a, b) = f(&mut *orig);
(
RefMut {
value: NonNull::from(a),
borrow,
marker: PhantomData,
},
RefMut {
value: NonNull::from(b),
borrow: orig.borrow,
marker: PhantomData,
},
)
}
}
struct BorrowRefMut<'b> {
borrow: &'b Cell<BorrowFlag>,
}
impl Drop for BorrowRefMut<'_> {
#[inline]
fn drop(&mut self) {
let borrow = self.borrow.get();
debug_assert!(is_writing(borrow));
self.borrow.set(borrow + 1);
}
}
impl<'b> BorrowRefMut<'b> {
#[inline]
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRefMut<'b>> {
match borrow.get() {
UNUSED => {
borrow.set(UNUSED - 1);
Some(BorrowRefMut { borrow })
}
_ => None,
}
}
#[inline]
fn clone(&self) -> BorrowRefMut<'b> {
let borrow = self.borrow.get();
debug_assert!(is_writing(borrow));
assert!(borrow != BorrowFlag::MIN);
self.borrow.set(borrow - 1);
BorrowRefMut {
borrow: self.borrow,
}
}
}
pub struct RefMut<'b, T: ?Sized + 'b> {
value: NonNull<T>,
borrow: BorrowRefMut<'b>,
marker: PhantomData<&'b mut T>,
}
impl<T: ?Sized> Deref for RefMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { self.value.as_ref() }
}
}
impl<T: ?Sized> DerefMut for RefMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { self.value.as_mut() }
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for RefMut<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}