use crate::GAlloc;
use core::alloc::{GlobalAlloc, Layout};
use core::cmp::Ordering;
use core::ops::{Deref, DerefMut};
use std::alloc::handle_alloc_error;
use std::borrow::{Borrow, BorrowMut};
use std::hash::{Hash, Hasher};
pub type GBox<T> = TestBox<T, GAlloc>;
#[derive(Debug)]
pub struct TestBox<T, A>
where
A: GlobalAlloc,
{
ptr: *mut T,
alloc: A,
}
impl<T, A> Default for TestBox<T, A>
where
T: Default,
A: Default + GlobalAlloc,
{
fn default() -> Self {
Self::new(T::default(), A::default())
}
}
impl<T, A> From<T> for TestBox<T, A>
where
A: Default + GlobalAlloc,
{
fn from(val: T) -> Self {
Self::new(val, A::default())
}
}
impl<T, A> TestBox<T, A>
where
A: GlobalAlloc,
{
pub fn new(x: T, alloc: A) -> Self {
let layout = Layout::new::<T>();
let ptr = unsafe { alloc.alloc(layout) as *mut T };
if ptr.is_null() {
handle_alloc_error(layout);
}
unsafe { ptr.write(x) };
Self { ptr, alloc }
}
pub unsafe fn from_raw_alloc(ptr: *mut T, alloc: A) -> Self {
Self { ptr, alloc }
}
}
impl<T, A> Clone for TestBox<T, A>
where
T: Clone,
A: Clone + GlobalAlloc,
{
fn clone(&self) -> Self {
Self::new(self.as_ref().clone(), self.alloc.clone())
}
}
impl<T, A> Drop for TestBox<T, A>
where
A: GlobalAlloc,
{
fn drop(&mut self) {
if self.ptr.is_null() {
return;
}
unsafe {
self.ptr.drop_in_place();
let layout = Layout::new::<T>();
self.alloc.dealloc(self.ptr as *mut u8, layout);
}
}
}
impl<T, A> PartialEq<Self> for TestBox<T, A>
where
T: PartialEq,
A: GlobalAlloc,
{
fn eq(&self, rh: &Self) -> bool {
let l: &T = self.borrow();
let r: &T = rh.borrow();
l == r
}
}
impl<T, A> Eq for TestBox<T, A>
where
T: Eq,
A: GlobalAlloc,
{
}
impl<T, A> PartialOrd<Self> for TestBox<T, A>
where
T: PartialOrd,
A: GlobalAlloc,
{
fn partial_cmp(&self, rh: &Self) -> Option<Ordering> {
let l: &T = self.borrow();
let r: &T = rh.borrow();
l.partial_cmp(r)
}
}
impl<T, A> Ord for TestBox<T, A>
where
T: Ord,
A: GlobalAlloc,
{
fn cmp(&self, rh: &Self) -> Ordering {
let l: &T = self.borrow();
let r: &T = rh.borrow();
l.cmp(r)
}
}
impl<T, A> Hash for TestBox<T, A>
where
T: Hash,
A: GlobalAlloc,
{
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
let t: &T = self.borrow();
t.hash(state)
}
}
impl<T, A> TestBox<T, A>
where
A: GlobalAlloc,
{
pub fn leak<'a>(mut tb: Self) -> &'a mut T
where
T: 'a,
{
let ptr = tb.ptr;
tb.ptr = core::ptr::null_mut();
unsafe { &mut *ptr }
}
pub fn into_raw(mut tb: Self) -> *mut T {
let ptr = tb.ptr;
tb.ptr = core::ptr::null_mut();
ptr
}
}
impl<T, A> AsRef<T> for TestBox<T, A>
where
A: GlobalAlloc,
{
fn as_ref(&self) -> &T {
&*self
}
}
impl<T, A> AsMut<T> for TestBox<T, A>
where
A: GlobalAlloc,
{
fn as_mut(&mut self) -> &mut T {
&mut *self
}
}
impl<T, A> Borrow<T> for TestBox<T, A>
where
A: GlobalAlloc,
{
fn borrow(&self) -> &T {
&*self
}
}
impl<T, A> BorrowMut<T> for TestBox<T, A>
where
A: GlobalAlloc,
{
fn borrow_mut(&mut self) -> &mut T {
&mut *self
}
}
impl<T, A> Deref for TestBox<T, A>
where
A: GlobalAlloc,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T, A> DerefMut for TestBox<T, A>
where
A: GlobalAlloc,
{
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constructor() {
let _tb = GBox::from(35);
}
#[test]
fn leak() {
let alloc = GAlloc::default();
let tb = GBox::new("foo".to_string(), alloc.clone());
let s = GBox::leak(tb);
let ptr = s as *mut String;
let layout = Layout::new::<String>();
unsafe {
ptr.drop_in_place();
alloc.dealloc(ptr as *mut u8, layout);
}
}
#[test]
#[should_panic]
fn leak_without_free() {
let tb = GBox::from("foo".to_string());
let s = GBox::leak(tb);
let ptr = s as *mut String;
unsafe { ptr.drop_in_place() };
}
#[test]
fn into_raw() {
let alloc = GAlloc::default();
let tb = GBox::new("foo".to_string(), alloc.clone());
let ptr = GBox::into_raw(tb);
let layout = Layout::new::<String>();
unsafe {
ptr.drop_in_place();
alloc.dealloc(ptr as *mut u8, layout);
}
}
#[test]
#[should_panic]
fn into_raw_without_free() {
let tb = GBox::from("foo".to_string());
let ptr = GBox::into_raw(tb);
unsafe { ptr.drop_in_place() };
}
#[test]
fn clone() {
let tb = GBox::from(35);
let _cloned = tb.clone();
}
}