use std::os::raw::{c_int,c_ulong};
use std::ffi::c_void;
use std::marker::PhantomPinned;
use std::mem::MaybeUninit;
#[repr(C)]
#[derive(Debug)]
#[doc(hidden)]
pub struct BlockDescriptorOnce {
pub reserved: c_ulong,
pub size: c_ulong,
}
#[repr(C)]
#[derive(Debug)]
#[doc(hidden)]
pub struct BlockLiteralOnceEscape {
pub isa: *const c_void,
pub flags: c_int,
pub reserved: MaybeUninit<c_int>,
pub invoke: *const c_void,
pub descriptor: *mut BlockDescriptorOnce,
pub closure: *const c_void,
}
pub static mut BLOCK_DESCRIPTOR_ONCE: blocksr::hidden::BlockDescriptorOnce = BlockDescriptorOnce {
reserved: 0, size: std::mem::size_of::<blocksr::hidden::BlockLiteralOnceEscape>() as u64,
};
#[macro_export]
macro_rules! once_escaping(
(
$pub:vis $blockname: ident ($($a:ident : $A:ty),*) -> $R:ty
) => {
#[repr(transparent)]
#[derive(Debug)]
#[allow(non_camel_case_types)] $pub struct $blockname(blocksr::hidden::BlockLiteralOnceEscape);
impl $blockname {
pub unsafe fn new<F>(f: F) -> Self where F: FnOnce($($A),*) -> $R + Send + 'static {
extern "C" fn invoke_thunk<G>(block: *mut blocksr::hidden::BlockLiteralOnceEscape, $($a : $A),*) -> $R where G: FnOnce($($A),*) -> $R + Send {
let typed_ptr: *mut G = unsafe{ (*block).closure as *mut G};
let rust_fn = unsafe{ Box::from_raw(typed_ptr)};
rust_fn($($a),*)
}
let boxed = Box::new(f);
let thunk_fn: *const core::ffi::c_void = invoke_thunk::<F> as *const core::ffi::c_void;
let literal = blocksr::hidden::BlockLiteralOnceEscape {
isa: &blocksr::hidden::_NSConcreteStackBlock,
flags: blocksr::hidden::BLOCK_HAS_STRET,
reserved: std::mem::MaybeUninit::uninit(),
invoke: thunk_fn ,
descriptor: core::ptr::addr_of_mut!(blocksr::hidden::BLOCK_DESCRIPTOR_ONCE),
closure: Box::into_raw(boxed) as *mut core::ffi::c_void,
};
$blockname(literal)
}
}
}
);
#[repr(C)]
#[derive(Debug)]
#[doc(hidden)]
pub struct BlockLiteralNoEscape<C> {
pub isa: *const c_void,
pub flags: c_int,
pub reserved: MaybeUninit<c_int>,
pub invoke: *const c_void,
pub descriptor: *mut BlockDescriptorOnce,
pub inline_descriptor: BlockDescriptorOnce,
pub closure_inline: C,
pub pinned: PhantomPinned,
}
#[macro_export]
macro_rules! once_noescape(
(
$pub:vis $blockname: ident ($($a:ident : $A:ty),*) -> $R:ty
) => {
#[repr(transparent)]
#[derive(Debug)]
#[allow(non_camel_case_types)] $pub struct $blockname<F>(blocksr::hidden::BlockLiteralNoEscape<F>);
impl<F> $blockname<F> {
pub unsafe fn new<'a>(into: core::pin::Pin<&'a mut core::mem::MaybeUninit<Self>>, f: F) -> core::pin::Pin<&'a Self> where F: FnOnce($($A),*) -> $R + Send {
use blocksr::hidden::BlockLiteralNoEscape;
use core::mem::MaybeUninit;
use core::pin::Pin;
extern "C" fn invoke_thunk<G>(block: *mut BlockLiteralNoEscape<G>, $($a : $A),*) -> $R where G: FnOnce($($A),*) -> $R + Send {
let read_owned = unsafe{std::ptr::read(block)};
(read_owned.closure_inline)($($a),*)
}
let thunk_fn: *const core::ffi::c_void = invoke_thunk::<F> as *const core::ffi::c_void;
let mut literal = BlockLiteralNoEscape {
isa: &blocksr::hidden::_NSConcreteStackBlock,
flags: blocksr::hidden::BLOCK_HAS_STRET,
reserved: std::mem::MaybeUninit::uninit(),
invoke: thunk_fn ,
descriptor: std::ptr::null_mut(),
inline_descriptor: blocksr::hidden::BlockDescriptorOnce {
reserved: 0, size: std::mem::size_of::<BlockLiteralNoEscape<F>>() as u64
},
closure_inline: f,
pinned: std::marker::PhantomPinned,
};
literal.descriptor = &mut literal.inline_descriptor;
let magic_ptr = into.get_unchecked_mut();
*magic_ptr = MaybeUninit::new($blockname(literal));
let raw_ptr: *const Self = magic_ptr.assume_init_ref();
Pin::new_unchecked(&*raw_ptr)
}
}
}
);
extern {
#[doc(hidden)]
pub static _NSConcreteStackBlock: c_void;
}
#[doc(hidden)]
pub const BLOCK_HAS_STRET: c_int = 1<<29;
#[doc(hidden)]
pub const BLOCK_HAS_COPY_DISPOSE: c_int = 1 << 25;
#[doc(hidden)]
pub const BLOCK_IS_NOESCAPE: c_int = 1<<23;
#[doc(hidden)]
pub const BLOCK_IS_GLOBAL: c_int = 1<<28;
#[test] fn make_escape() {
once_escaping!(MyBlock (arg: u8) -> u8);
let _f = unsafe{ MyBlock::new(|_arg| {
3
})};
}
#[test] fn make_noescape() {
use core::pin::Pin;
use std::mem::MaybeUninit;
let mut block_value = MaybeUninit::uninit();
let block_value = unsafe{ Pin::new_unchecked(&mut block_value) };
once_noescape!(MyBlock(arg: u8) -> u8);
let _f = unsafe { MyBlock::new(block_value, |_arg| {
3
})
};
}