use crate::util::check_size_and_alignment;
use std::default::Default;
use std::marker::PhantomData;
use std::mem;
#[non_exhaustive]
pub struct Unboxed<RType: Sized, CType: Sized> {
_phantom: PhantomData<(RType, CType)>,
}
impl<RType: Sized, CType: Sized> Unboxed<RType, CType> {
pub unsafe fn take(cval: CType) -> RType {
unsafe { Self::from_ctype(cval) }
}
pub unsafe fn take_ptr_nonnull(cptr: *mut CType) -> RType {
check_size_and_alignment::<CType, RType>();
if cptr.is_null() {
panic!("NULL value not allowed");
}
let rref = unsafe { &mut *(cptr as *mut mem::MaybeUninit<RType>) };
let mut owned = mem::MaybeUninit::<RType>::zeroed();
mem::swap(rref, &mut owned);
unsafe { owned.assume_init() }
}
pub unsafe fn with_ref_nonnull<T, F: FnOnce(&RType) -> T>(cptr: *const CType, f: F) -> T {
check_size_and_alignment::<CType, RType>();
if cptr.is_null() {
panic!("NULL value not allowed");
}
f(unsafe { &*(cptr as *const RType) })
}
pub unsafe fn with_ref_mut_nonnull<T, F: FnOnce(&mut RType) -> T>(cptr: *mut CType, f: F) -> T {
check_size_and_alignment::<CType, RType>();
if cptr.is_null() {
panic!("NULL value not allowed");
}
f(unsafe { &mut *(cptr as *mut RType) })
}
pub unsafe fn return_val(rval: RType) -> CType {
Self::into_ctype(rval)
}
pub unsafe fn to_out_param(rval: RType, arg_out: *mut CType) {
if !arg_out.is_null() {
unsafe { *arg_out = Self::into_ctype(rval) };
}
}
pub unsafe fn to_out_param_nonnull(rval: RType, arg_out: *mut CType) {
if arg_out.is_null() {
panic!("out param pointer is NULL");
}
unsafe { *arg_out = Self::into_ctype(rval) };
}
fn into_ctype(rval: RType) -> CType {
check_size_and_alignment::<CType, RType>();
let mut cval = mem::MaybeUninit::<CType>::uninit();
let cptr = &mut cval as *mut mem::MaybeUninit<CType>;
let selfptr = (&mem::MaybeUninit::<RType>::new(rval)) as *const mem::MaybeUninit<RType>;
let dest = unsafe { cptr as *mut mem::MaybeUninit<RType> };
unsafe { std::ptr::copy(selfptr, dest, 1) };
unsafe { cval.assume_init() }
}
unsafe fn from_ctype(cval: CType) -> RType {
check_size_and_alignment::<CType, RType>();
let cval = mem::MaybeUninit::new(cval);
unsafe { mem::transmute_copy(&cval) }
}
}
impl<RType: Sized + Default, CType: Sized> Unboxed<RType, CType> {
pub unsafe fn with_ref<T, F: FnOnce(&RType) -> T>(cptr: *const CType, f: F) -> T {
check_size_and_alignment::<CType, RType>();
if cptr.is_null() {
let nullval = RType::default();
return f(&nullval);
}
f(unsafe { &*(cptr as *const RType) })
}
pub unsafe fn with_ref_mut<T, F: FnOnce(&mut RType) -> T>(cptr: *mut CType, f: F) -> T {
check_size_and_alignment::<CType, RType>();
if cptr.is_null() {
let mut nullval = RType::default();
return f(&mut nullval);
}
f(unsafe { &mut *(cptr as *mut RType) })
}
pub unsafe fn take_ptr(cptr: *mut CType) -> RType {
check_size_and_alignment::<CType, RType>();
if cptr.is_null() {
return RType::default();
}
let rref = unsafe { &mut *(cptr as *mut mem::MaybeUninit<RType>) };
let mut owned = mem::MaybeUninit::<RType>::zeroed();
mem::swap(rref, &mut owned);
unsafe { owned.assume_init() }
}
}
#[cfg(test)]
mod test {
mod size_panic {
use super::super::*;
struct TwoInts(u64, u64);
struct OneInt(u64);
type UnboxedTwoInts = Unboxed<TwoInts, OneInt>;
#[test]
#[should_panic]
fn test() {
let cval = OneInt(10);
unsafe {
UnboxedTwoInts::with_ref_nonnull(&cval as *const OneInt, |_rval| {});
}
}
}
mod align_panic {
use super::super::*;
struct OneInt(u64);
struct EightBytes([u8; 8]);
type UnboxedOneInt = Unboxed<OneInt, EightBytes>;
#[test]
#[should_panic]
fn test() {
let cval = EightBytes([0u8; 8]);
unsafe {
UnboxedOneInt::with_ref_nonnull(&cval as *const EightBytes, |_rval| {});
}
}
}
use super::*;
#[derive(Default)]
struct RType(u32, u64);
struct CType([u64; 3]);
type UnboxedTuple = Unboxed<RType, CType>;
#[test]
fn intialize_and_with_methods() {
unsafe {
let mut cval = mem::MaybeUninit::<CType>::uninit();
UnboxedTuple::to_out_param(RType(10, 20), cval.as_mut_ptr());
let mut cval = cval.assume_init();
UnboxedTuple::with_ref_nonnull(&cval, |rref| {
assert_eq!(rref.0, 10);
assert_eq!(rref.1, 20);
});
UnboxedTuple::with_ref_mut_nonnull(&mut cval, |rref| {
assert_eq!(rref.0, 10);
assert_eq!(rref.1, 20);
rref.0 = 30;
});
UnboxedTuple::with_ref_mut(&mut cval, |rref| {
assert_eq!(rref.0, 30);
rref.0 += 1;
assert_eq!(rref.1, 20);
rref.1 += 1;
});
UnboxedTuple::with_ref(&cval, |rref| {
assert_eq!(rref.0, 31);
assert_eq!(rref.1, 21);
});
let rval = UnboxedTuple::take(cval);
assert_eq!(rval.0, 31);
assert_eq!(rval.1, 21);
let mut cval = mem::MaybeUninit::<CType>::uninit();
UnboxedTuple::to_out_param_nonnull(RType(100, 200), cval.as_mut_ptr());
let cval = cval.assume_init();
let rval = UnboxedTuple::take(cval);
assert_eq!(rval.0, 100);
assert_eq!(rval.1, 200);
}
}
#[test]
fn with_null_ptrs() {
unsafe {
UnboxedTuple::with_ref_mut(std::ptr::null_mut(), |rref| {
assert_eq!(rref.0, 0);
assert_eq!(rref.1, 0);
rref.1 += 1;
});
UnboxedTuple::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 {
UnboxedTuple::with_ref_nonnull(std::ptr::null(), |_| {});
}
}
#[test]
#[should_panic]
fn with_ref_mut_nonnull_null() {
unsafe {
UnboxedTuple::with_ref_mut_nonnull(std::ptr::null_mut(), |_| {});
}
}
#[test]
fn to_out_param_null() {
unsafe {
UnboxedTuple::to_out_param(RType(10, 20), std::ptr::null_mut());
}
}
#[test]
#[should_panic]
fn to_out_param_nonnull_null() {
unsafe {
UnboxedTuple::to_out_param_nonnull(RType(10, 20), std::ptr::null_mut());
}
}
#[test]
fn return_val() {
unsafe {
let cval = UnboxedTuple::return_val(RType(10, 20));
let rval = UnboxedTuple::take(cval);
assert_eq!(rval.0, 10);
assert_eq!(rval.1, 20);
}
}
fn take_ptr_test(nonnull: bool) {
unsafe {
let cval = Box::new(mem::MaybeUninit::<CType>::uninit());
let cvalptr = Box::into_raw(cval) as *mut CType;
UnboxedTuple::to_out_param(RType(10, 20), cvalptr);
let rval = if nonnull {
UnboxedTuple::take_ptr_nonnull(cvalptr)
} else {
UnboxedTuple::take_ptr(cvalptr)
};
assert_eq!(rval.0, 10);
assert_eq!(rval.1, 20);
let zeroedref = unsafe { &*(cvalptr as *const RType) };
assert_eq!(zeroedref.0, 0);
assert_eq!(zeroedref.1, 0);
unsafe { Box::from_raw(cvalptr as *mut mem::MaybeUninit<CType>) };
}
}
#[test]
fn take_ptr() {
take_ptr_test(false);
}
#[test]
fn take_ptr_null() {
unsafe {
let rval = UnboxedTuple::take_ptr(std::ptr::null_mut());
assert_eq!(rval.0, 0);
assert_eq!(rval.1, 0);
}
}
#[test]
fn take_ptr_nonnull() {
take_ptr_test(true);
}
#[test]
#[should_panic]
fn take_ptr_nonnull_null() {
unsafe {
UnboxedTuple::take_ptr_nonnull(std::ptr::null_mut());
}
}
}