use {
crate::{error::Error, layout::Layout},
::core::{
ffi::c_void,
mem::{ManuallyDrop, MaybeUninit},
ops::FnOnce,
ptr::NonNull,
result::Result::{self, Err, Ok}
}
};
#[cfg(feature = "catch_unwind")]
::std::thread_local! {
static UNWIND: ::core::cell::RefCell<bool> =
::core::cell::RefCell::new(false);
}
pub unsafe fn with_alloca<R, F: FnOnce(NonNull<u8>, *mut R)>(
layout: Layout,
f: F
) -> Result<R, Error> {
if layout.size() == 0 {
return Err(Error::ZeroSizedLayout);
}
let mut ret = MaybeUninit::uninit();
let mut closure = ManuallyDrop::new(f);
unsafe {
c_alloca(
layout.size(),
layout.align(),
c_call_callback::<R, F>,
(&mut closure as *mut ManuallyDrop<F>).cast::<c_void>(),
(&mut ret as *mut MaybeUninit<R>).cast::<c_void>()
);
}
#[cfg(feature = "catch_unwind")]
if UNWIND.with(|v| v.replace(false)) {
return Err(Error::CaughtUnwind);
}
Ok(unsafe { ret.assume_init() })
}
macro_rules! c_cb {
($verdef:ident, $ffi:literal) => {
#[::rustversion::$verdef(1.71)]
pub unsafe extern $ffi fn c_call_callback<R, F: FnOnce(NonNull<u8>, *mut R)>(
callback: *mut c_void,
ptr: *mut u8,
out: *mut c_void
) {
let run = || {
ManuallyDrop::take(&mut *callback.cast::<ManuallyDrop<F>>())(
NonNull::new_unchecked(ptr),
out.cast()
);
};
#[cfg(not(feature = "catch_unwind"))]
run();
#[cfg(feature = "catch_unwind")]
if $ffi == "C-unwind" {
if ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(run)).is_err() {
UNWIND.with(|v| *v.borrow_mut() = true);
}
} else {
run();
}
}
};
}
macro_rules! c_ext {
($verdef:ident, $ffi:literal) => {
#[::rustversion::$verdef(1.71)]
extern $ffi {
pub fn c_alloca(
size: usize,
align: usize,
cb: unsafe extern $ffi fn(*mut c_void, *mut u8, *mut c_void),
closure: *mut c_void,
out: *mut c_void
);
}
};
}
c_cb! { before, "C" }
c_cb! { since, "C-unwind" }
c_ext! { before, "C" }
c_ext! { since, "C-unwind" }