use std::cell::Cell;
pub struct ContextCell<T> {
ptr: Cell<*mut T>,
}
impl<T> ContextCell<T> {
#[inline]
pub const fn new() -> Self {
Self {
ptr: Cell::new(std::ptr::null_mut()),
}
}
#[inline]
pub fn get_ptr(&self) -> *mut T {
self.ptr.get()
}
#[inline]
pub fn replace(&self, ptr: *mut T) -> *mut T {
let prev = self.ptr.get();
self.ptr.set(ptr);
prev
}
#[inline]
pub fn restore(&self, ptr: *mut T) {
self.ptr.set(ptr);
}
}
#[cfg(test)]
impl<T> ContextCell<T> {
#[inline]
pub(crate) fn get(&self) -> Option<*mut T> {
let ptr = self.ptr.get();
if ptr.is_null() { None } else { Some(ptr) }
}
#[inline]
pub(crate) fn clear(&self) -> *mut T {
self.replace(std::ptr::null_mut())
}
#[inline]
pub(crate) unsafe fn deref_mut(&self) -> Option<&'static mut T> {
let ptr = self.ptr.get();
if ptr.is_null() {
None
} else {
Some(unsafe { &mut *ptr })
}
}
}
impl<T> Default for ContextCell<T> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<T> Send for ContextCell<T> {}
unsafe impl<T> Sync for ContextCell<T> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_is_null() {
let cell = ContextCell::<u32>::new();
assert!(cell.get().is_none());
}
#[test]
fn replace_returns_previous() {
let cell = ContextCell::<u32>::new();
let mut a: u32 = 1;
let mut b: u32 = 2;
let ptr_a = &mut a as *mut u32;
let ptr_b = &mut b as *mut u32;
let prev = cell.replace(ptr_a);
assert!(prev.is_null());
assert_eq!(cell.get(), Some(ptr_a));
let prev = cell.replace(ptr_b);
assert_eq!(prev, ptr_a);
assert_eq!(cell.get(), Some(ptr_b));
}
#[test]
fn clear_returns_previous() {
let cell = ContextCell::<u32>::new();
let mut a: u32 = 42;
let ptr_a = &mut a as *mut u32;
cell.replace(ptr_a);
let prev = cell.clear();
assert_eq!(prev, ptr_a);
assert!(cell.get().is_none());
}
#[test]
fn restore_sets_pointer() {
let cell = ContextCell::<u32>::new();
let mut a: u32 = 10;
let ptr_a = &mut a as *mut u32;
cell.restore(ptr_a);
assert_eq!(cell.get(), Some(ptr_a));
}
#[test]
fn deref_mut_returns_reference() {
let cell = ContextCell::<u32>::new();
let mut a: u32 = 99;
let ptr_a = &mut a as *mut u32;
cell.replace(ptr_a);
let r = unsafe { cell.deref_mut() }.unwrap();
assert_eq!(*r, 99);
*r = 100;
assert_eq!(a, 100);
}
#[test]
fn deref_mut_none_when_null() {
let cell = ContextCell::<u32>::new();
assert!(unsafe { cell.deref_mut() }.is_none());
}
#[test]
fn default_is_null() {
let cell = ContextCell::<u32>::default();
assert!(cell.get().is_none());
}
}