#![cfg_attr(feature = "no_std", no_std)]
#![cfg_attr(feature = "no_std", feature(no_std, core, collections))]
#![warn(bad_style, unused, missing_docs)]
#[cfg(feature = "no_std")]
#[macro_use]
extern crate core;
#[cfg(all(feature = "no_std", not(test)))]
extern crate core as std;
#[cfg(feature = "no_std")]
extern crate collections;
#[cfg(all(feature = "no_std", test))]
extern crate std;
use std::cell::{Cell, UnsafeCell};
use std::clone::Clone;
use std::cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering};
use std::default::Default;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, Drop, FnOnce};
use std::option::Option;
use std::result::Result;
const MUTATING: usize = !0;
pub struct MuCell<T> {
value: UnsafeCell<T>,
borrows: Cell<usize>,
}
impl<T> MuCell<T> {
#[inline]
pub fn new(value: T) -> MuCell<T> {
MuCell {
value: UnsafeCell::new(value),
borrows: Cell::new(0),
}
}
#[inline]
pub fn borrow_mut(&mut self) -> &mut T {
unsafe { &mut *self.value.get() }
}
#[inline]
pub fn borrow(&self) -> Ref<T> {
let borrows = self.borrows.get();
assert!(borrows != MUTATING, "borrow() called inside try_mutate()");
self.borrows.set(borrows + 1);
Ref { _parent: self }
}
#[inline]
pub fn try_mutate<F: FnOnce(&mut T)>(&self, mutator: F) -> bool {
if self.borrows.get() == 0 {
self.borrows.set(MUTATING);
mutator(unsafe { &mut *self.value.get() });
self.borrows.set(0);
true
} else {
false
}
}
}
pub struct Ref<'a, T: 'a> {
_parent: &'a MuCell<T>,
}
impl<'a, T: 'a> Drop for Ref<'a, T> {
fn drop(&mut self) {
self._parent.borrows.set(self._parent.borrows.get() - 1);
}
}
impl<'a, T: 'a> Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self._parent.value.get() }
}
}
impl<T: PartialEq> PartialEq for MuCell<T> {
fn eq(&self, other: &MuCell<T>) -> bool {
*self.borrow() == *other.borrow()
}
}
impl<T: Eq> Eq for MuCell<T> { }
impl<T: PartialOrd> PartialOrd for MuCell<T> {
fn partial_cmp(&self, other: &MuCell<T>) -> Option<Ordering> {
self.borrow().partial_cmp(&*other.borrow())
}
}
impl<T: Ord> Ord for MuCell<T> {
fn cmp(&self, other: &MuCell<T>) -> Ordering {
self.borrow().cmp(&*other.borrow())
}
}
impl<T: Default> Default for MuCell<T> {
fn default() -> MuCell<T> {
MuCell::new(Default::default())
}
}
impl<T: Clone> Clone for MuCell<T> {
fn clone(&self) -> MuCell<T> {
MuCell::new(self.borrow().clone())
}
}
macro_rules! impl_fmt {
($($trait_name:ident)*) => {$(
impl<T: fmt::$trait_name> fmt::$trait_name for MuCell<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.borrow().fmt(f)
}
}
)*}
}
impl_fmt!(Display Debug Octal LowerHex UpperHex Pointer Binary LowerExp UpperExp);
impl<T> Hash for MuCell<T> where T: Hash {
fn hash<H: Hasher>(&self, state: &mut H) {
self.borrow().hash(state)
}
}
#[macro_export]
macro_rules! mucell_ref_type {
(
$(#[$attr:meta])* // suggestions: doc, stability markers
struct $ref_name:ident<'a>($ty:ty),
impl Deref -> $deref:ty,
data: $data_ty:ty = |$data_ident:ident| $data_expr:expr
) => {
$(#[$attr])*
pub struct $ref_name<'a> {
_parent: Ref<'a, $ty>,
_data: $data_ty,
}
impl<'a> $ref_name<'a> {
#[allow(trivial_casts)] fn from(cell: &'a MuCell<$ty>) -> $ref_name<'a> {
let parent = cell.borrow();
let $data_ident: &'a $ty = unsafe { &*(&*parent as *const $ty) };
let data = $data_expr;
$ref_name {
_parent: parent,
_data: data,
}
}
}
impl<'a> ::std::ops::Deref for $ref_name<'a> {
type Target = $deref;
fn deref<'b>(&'b self) -> &'b $deref {
&*self._data
}
}
}
}
#[test]
#[should_panic]
fn test_borrow_in_try_mutate() {
let a = MuCell::new(());
a.try_mutate(|_| { let _ = a.borrow(); });
}
#[test]
fn test_try_mutate_in_try_mutate() {
let a = MuCell::new(());
assert!(a.try_mutate(|_| assert!(!a.try_mutate(|_| unreachable!()))));
}