#![doc = include_str!("../README.md")]
#![warn(clippy::pedantic, clippy::cargo)]
#![no_std]
#[cfg(test)]
extern crate std;
use core::mem::{size_of, ManuallyDrop};
use core::ops::{Deref, DerefMut};
use core::panic::UnwindSafe;
use core::ptr::{self, NonNull};
pub struct LockedBox<T>(NonNull<T>);
impl<T> LockedBox<T> {
pub fn new(contained: T) -> Self {
Self::try_new(contained).expect("allocation too large")
}
pub fn try_new(contained: T) -> Option<Self> {
let memory = unsafe {
let memory = memsec::malloc::<T>()?;
memsec::mlock(memory.as_ptr().cast(), size_of::<T>());
ptr::write(memory.as_ptr(), contained);
memory
};
Some(Self(memory))
}
#[must_use]
pub const fn ptr(boxed: &LockedBox<T>) -> *mut T {
boxed.0.as_ptr()
}
#[must_use]
pub fn unbox(boxed: LockedBox<T>) -> T {
let boxed = ManuallyDrop::new(boxed);
unsafe {
let contained = ptr::read(boxed.0.as_ptr());
memsec::free(boxed.0);
contained
}
}
}
unsafe impl<T> Sync for LockedBox<T> where T: Sync {}
unsafe impl<T> Send for LockedBox<T> where T: Send {}
impl<T> UnwindSafe for LockedBox<T> where T: UnwindSafe {}
impl<T> Deref for LockedBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.0.as_ref() }
}
}
impl<T> DerefMut for LockedBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.0.as_mut() }
}
}
impl<T> Drop for LockedBox<T> {
fn drop(&mut self) {
unsafe {
ptr::drop_in_place(self.0.as_ptr());
memsec::free(self.0);
}
}
}
#[test]
fn doesnt_crash() {
let locked = LockedBox::new(1_u8);
assert_eq!(*locked, 1);
}
#[test]
fn drops_correctly() {
use std::{cell::RefCell, rc::Rc};
#[derive(Default)]
struct Droppable(Rc<RefCell<bool>>);
impl Drop for Droppable {
fn drop(&mut self) {
let mut dropped = (*self.0).borrow_mut();
assert!(!*dropped, "already dropped");
*dropped = true;
}
}
let locked = LockedBox::new(Droppable::default());
let dropped = (*locked).0.clone();
drop(locked);
let mut dropped = Rc::try_unwrap(dropped).expect("Rc has clones");
assert!(*dropped.get_mut());
let locked = LockedBox::new(Droppable::default());
let unboxed = LockedBox::unbox(locked);
drop(unboxed);
}
#[test]
fn allows_zero_sized() {
LockedBox::new(());
}