#![allow(dead_code)]
use crate::error::{Error, ErrorCode};
use crate::{ExtResult, Result};
use libc::{calloc, free, malloc};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut};
use std::ptr::{drop_in_place, NonNull};
use std::{borrow, cmp, mem, slice};
#[derive(Debug)]
#[repr(transparent)]
pub struct CBox<T: ?Sized>(NonNull<T>);
impl<T> CBox<T> {
pub fn new(value: T) -> CBox<T> {
Self::try_new(value).expect("memory allocation failed")
}
pub fn try_new(value: T) -> ExtResult<Self, T> {
let size = cmp::max(mem::size_of::<T>(), 1);
let ptr = unsafe { malloc(size) }.cast();
match NonNull::new(ptr) {
None => Err(Self::buf_err().into_with_payload(value)),
Some(result) => {
unsafe { *ptr = value };
Ok(Self(result))
}
}
}
pub fn new_zeroed() -> CBox<MaybeUninit<T>> {
Self::try_new_zeroed().expect("memory allocation failed")
}
pub fn try_new_zeroed() -> Result<CBox<MaybeUninit<T>>> {
let size = cmp::max(mem::size_of::<T>(), 1);
let ptr = unsafe { calloc(1, size) }.cast();
match CBox::wrap(ptr) {
None => Err(Self::buf_err()),
Some(result) => Ok(result),
}
}
pub fn new_zeroed_slice(len: usize) -> CBox<[MaybeUninit<T>]> {
Self::try_new_zeroed_slice(len).expect("memory allocation failed")
}
pub fn try_new_zeroed_slice(len: usize) -> Result<CBox<[MaybeUninit<T>]>> {
let maxlen = isize::MAX as usize / mem::size_of::<T>();
let size = cmp::max(mem::size_of::<T>(), 1);
if size > isize::MAX as usize || len > maxlen {
return Err(Self::buf_err());
}
let ptr = unsafe { calloc(len, size) }.cast();
match CBox::wrap_slice(ptr, len) {
None => Err(Self::buf_err()),
Some(result) => Ok(result),
}
}
fn wrap_slice(raw: *mut T, len: usize) -> Option<CBox<[T]>> {
let slice = unsafe { slice::from_raw_parts_mut(raw, len) };
CBox::wrap(slice)
}
pub unsafe fn from_raw_slice(raw: *mut T, len: usize) -> CBox<[T]> {
CBox::wrap_slice(raw, len).expect("cannot construct CBox from null pointer")
}
}
impl<T> CBox<T>
where
T: ?Sized,
{
#[inline]
fn wrap(raw: *mut T) -> Option<Self> {
NonNull::new(raw).map(Self)
}
#[cold]
fn buf_err() -> Error {
ErrorCode::BUF_ERR.into()
}
pub unsafe fn from_raw(raw: *mut T) -> CBox<T> {
Self::wrap(raw).expect("cannot construct CBox from null pointer")
}
pub const fn into_raw(b: CBox<T>) -> *mut T {
let ptr: NonNull<T> = b.0;
mem::forget(b);
ptr.as_ptr()
}
}
impl<T> CBox<[T]> {
pub const fn into_raw_unsized(b: CBox<[T]>) -> *mut T {
let ptr: NonNull<_> = b.0;
mem::forget(b);
ptr.as_ptr().cast()
}
}
impl<T> CBox<MaybeUninit<T>> {
pub const unsafe fn assume_init(self) -> CBox<T> {
CBox::<T>(NonNull::new_unchecked(CBox::into_raw(self).cast()))
}
}
impl<T> CBox<[MaybeUninit<T>]> {
pub const unsafe fn assume_all_init(self) -> CBox<[T]> {
CBox::<[T]>(NonNull::new_unchecked(CBox::into_raw(self) as *mut _))
}
}
impl<T: ?Sized> Drop for CBox<T> {
fn drop(&mut self) {
let ptr = self.0.as_ptr();
unsafe {
drop_in_place(ptr);
free(ptr.cast());
};
}
}
impl<T> From<T> for CBox<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: ?Sized> Deref for CBox<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.0.as_ref() }
}
}
impl<T: ?Sized> DerefMut for CBox<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { self.0.as_mut() }
}
}
impl<T: ?Sized + PartialEq> PartialEq for CBox<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
PartialEq::eq(&**self, &**other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd for CBox<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
PartialOrd::partial_cmp(&**self, &**other)
}
}
impl<T: ?Sized + Ord> Ord for CBox<T> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&**self, &**other)
}
}
impl<T: ?Sized + Eq> Eq for CBox<T> {}
impl<T: ?Sized + Hash> Hash for CBox<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<T: ?Sized> borrow::Borrow<T> for CBox<T> {
#[inline]
fn borrow(&self) -> &T {
self
}
}
impl<T: ?Sized> borrow::BorrowMut<T> for CBox<T> {
#[inline]
fn borrow_mut(&mut self) -> &mut T {
self
}
}
impl<T: ?Sized> AsRef<T> for CBox<T> {
#[inline]
fn as_ref(&self) -> &T {
self
}
}
impl<T: ?Sized> AsMut<T> for CBox<T> {
#[inline]
fn as_mut(&mut self) -> &mut T {
self
}
}
unsafe impl<T: ?Sized + Send> Send for CBox<T> {}
unsafe impl<T: ?Sized + Sync> Sync for CBox<T> {}
#[cfg(test)]
mod tests {
use super::*;
use libc::c_void;
use std::mem::size_of;
use std::ptr::null_mut;
#[test]
fn test_sizes() {
assert_eq!(size_of::<CBox<()>>(), size_of::<*const ()>());
assert_eq!(size_of::<CBox<()>>(), size_of::<*mut ()>());
assert_eq!(size_of::<CBox<c_void>>(), size_of::<*mut c_void>());
assert_eq!(size_of::<CBox<[i32]>>(), size_of::<*mut [i32]>());
assert_eq!(size_of::<CBox<[i32; 3]>>(), size_of::<*mut [i32; 3]>());
}
#[test]
fn test_allocation() {
let mut b: CBox<i32> = CBox::new(32);
assert_eq!(*b, 32);
*b = 42;
assert_eq!(*b, 42);
let ptr = CBox::into_raw(b);
assert_eq!(unsafe { *ptr }, 42);
let b = unsafe { CBox::from_raw(ptr) };
drop(b);
}
#[test]
fn test_equality() {
let a: CBox<i32> = 32.into();
let mut b: CBox<i32> = CBox::new(32);
assert_eq!(a, b);
*b = 42;
assert_ne!(a, b);
assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
assert_eq!(a.cmp(&b), Ordering::Less);
}
#[test]
fn test_zeroed() {
let uninit_b = CBox::<u64>::new_zeroed();
let mut b = unsafe { uninit_b.assume_init() };
assert_eq!(*b, 0);
*b = 42;
assert_eq!(*b, 42);
}
#[test]
#[should_panic = "memory allocation failed"]
fn test_toobig() {
let _b = CBox::<u64>::new_zeroed_slice(isize::MAX as usize);
}
#[test]
fn test_debug() {
let b: CBox<()> = CBox::new(());
assert!(format!("{:?}", b).contains("CBox"));
}
#[test]
#[should_panic = "from null pointer"]
fn test_null() {
let _: CBox<()> = unsafe { CBox::from_raw(null_mut()) };
}
#[test]
#[cfg_attr(miri, ignore)]
#[should_panic = "from null pointer"]
fn test_null_slice() {
let _: CBox<[()]> = unsafe { CBox::from_raw_slice(null_mut(), 1) };
}
#[test]
fn test_slice() {
let uninit_b: CBox<[MaybeUninit<u64>]> = CBox::new_zeroed_slice(3);
let mut b = unsafe { uninit_b.assume_all_init() };
assert_eq!(*b, [0, 0, 0]);
b[1] = u64::MAX;
assert_eq!(*b, [0, u64::MAX, 0]);
let ptr: *mut u64 = CBox::into_raw_unsized(b);
let b = unsafe { CBox::from_raw_slice(ptr, 3) };
assert_eq!(*b, [0, u64::MAX, 0]);
}
#[test]
fn test_hash() {
use std::borrow::Borrow;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
fn calc_hash<T: Hash>(t: &T) -> u64 {
let mut s = DefaultHasher::new();
t.hash(&mut s);
s.finish()
}
let b: CBox<()> = CBox::new(());
assert_eq!(calc_hash(&()), calc_hash(&b));
assert_eq!(calc_hash::<()>(b.borrow()), calc_hash(&b));
}
}