use std::default::Default;
use std::marker::PhantomData;
#[non_exhaustive]
pub struct Boxed<RType: Sized> {
_phantom: PhantomData<RType>,
}
impl<RType: Sized> Boxed<RType> {
pub unsafe fn take_nonnull(arg: *mut RType) -> RType {
debug_assert!(!arg.is_null());
unsafe { *(Box::from_raw(arg)) }
}
pub unsafe fn with_ref_nonnull<T, F: FnOnce(&RType) -> T>(arg: *const RType, f: F) -> T {
if arg.is_null() {
panic!("NULL value not allowed");
}
f(unsafe { &*(arg as *const RType) })
}
pub unsafe fn with_ref_mut_nonnull<T, F: FnOnce(&mut RType) -> T>(arg: *mut RType, f: F) -> T {
if arg.is_null() {
panic!("NULL value not allowed");
}
f(unsafe { &mut *arg })
}
pub unsafe fn return_val(rval: RType) -> *mut RType {
unsafe { Self::return_val_boxed(Box::new(rval)) }
}
pub unsafe fn return_val_boxed(rval: Box<RType>) -> *mut RType {
Box::into_raw(rval)
}
pub unsafe fn to_out_param(rval: RType, arg_out: *mut *mut RType) {
if !arg_out.is_null() {
unsafe { *arg_out = Self::return_val(rval) };
}
}
pub unsafe fn to_out_param_nonnull(rval: RType, arg_out: *mut *mut RType) {
if arg_out.is_null() {
panic!("out param pointer is NULL");
}
unsafe { *arg_out = Self::return_val(rval) };
}
}
impl<RType: Sized + Default> Boxed<RType> {
pub unsafe fn take(arg: *mut RType) -> RType {
debug_assert!(!arg.is_null());
unsafe { *(Box::from_raw(arg)) }
}
pub unsafe fn with_ref<T, F: FnOnce(&RType) -> T>(arg: *const RType, f: F) -> T {
if arg.is_null() {
let nullval = RType::default();
return f(&nullval);
}
f(unsafe { &*(arg as *const RType) })
}
pub unsafe fn with_ref_mut<T, F: FnOnce(&mut RType) -> T>(arg: *mut RType, f: F) -> T {
if arg.is_null() {
let mut nullval = RType::default();
return f(&mut nullval);
}
f(unsafe { &mut *arg })
}
}
#[cfg(test)]
mod test {
use super::*;
use std::mem;
#[derive(Default)]
struct RType(u32, u64);
type BoxedTuple = Boxed<RType>;
#[test]
fn intialize_and_with_methods() {
unsafe {
let mut cptr = mem::MaybeUninit::<*mut RType>::uninit();
BoxedTuple::to_out_param(RType(10, 20), cptr.as_mut_ptr());
let cptr = cptr.assume_init();
BoxedTuple::with_ref_nonnull(cptr, |rref| {
assert_eq!(rref.0, 10);
assert_eq!(rref.1, 20);
});
BoxedTuple::with_ref_mut_nonnull(cptr, |rref| {
assert_eq!(rref.0, 10);
assert_eq!(rref.1, 20);
rref.0 = 30;
});
BoxedTuple::with_ref_mut(cptr, |rref| {
assert_eq!(rref.0, 30);
rref.0 += 1;
assert_eq!(rref.1, 20);
rref.1 += 1;
});
BoxedTuple::with_ref(cptr, |rref| {
assert_eq!(rref.0, 31);
assert_eq!(rref.1, 21);
});
let rval = BoxedTuple::take(cptr);
assert_eq!(rval.0, 31);
assert_eq!(rval.1, 21);
let mut cptr = mem::MaybeUninit::<*mut RType>::uninit();
BoxedTuple::to_out_param_nonnull(RType(100, 200), cptr.as_mut_ptr());
let cptr = cptr.assume_init();
let rval = BoxedTuple::take(cptr);
assert_eq!(rval.0, 100);
assert_eq!(rval.1, 200);
}
}
#[test]
fn with_null_ptrs() {
unsafe {
BoxedTuple::with_ref_mut(std::ptr::null_mut(), |rref| {
assert_eq!(rref.0, 0);
assert_eq!(rref.1, 0);
rref.1 += 1;
});
BoxedTuple::with_ref(std::ptr::null(), |rref| {
assert_eq!(rref.0, 0);
assert_eq!(rref.1, 0);
});
}
}
#[test]
#[should_panic]
fn with_ref_nonnull_null() {
unsafe {
BoxedTuple::with_ref_nonnull(std::ptr::null(), |_| {});
}
}
#[test]
#[should_panic]
fn with_ref_mut_nonnull_null() {
unsafe {
BoxedTuple::with_ref_mut_nonnull(std::ptr::null_mut(), |_| {});
}
}
#[test]
fn to_out_param_null() {
unsafe {
BoxedTuple::to_out_param(RType(10, 20), std::ptr::null_mut());
}
}
#[test]
#[should_panic]
fn to_out_param_nonnull_null() {
unsafe {
BoxedTuple::to_out_param_nonnull(RType(10, 20), std::ptr::null_mut());
}
}
#[test]
fn return_val_take() {
unsafe {
let cptr = BoxedTuple::return_val(RType(10, 20));
let rval = BoxedTuple::take(cptr);
assert_eq!(rval.0, 10);
assert_eq!(rval.1, 20);
}
}
#[test]
fn return_val_boxed_take_nonnull() {
unsafe {
let cptr = BoxedTuple::return_val_boxed(Box::new(RType(10, 20)));
let rval = BoxedTuple::take_nonnull(cptr);
assert_eq!(rval.0, 10);
assert_eq!(rval.1, 20);
}
}
#[test]
#[should_panic]
fn take_nnull() {
unsafe {
let rval = BoxedTuple::take(std::ptr::null_mut());
assert_eq!(rval.0, 0);
assert_eq!(rval.1, 0);
}
}
#[test]
#[should_panic]
fn take_nonnull_null() {
unsafe {
BoxedTuple::take_nonnull(std::ptr::null_mut());
}
}
}