#![no_std]
#![warn(elided_lifetimes_in_paths)]
#![warn(missing_docs)]
#![deny(non_ascii_idents)]
#![warn(unreachable_pub)]
#![deny(unsafe_op_in_unsafe_fn)]
#![doc(html_root_url = "https://docs.rs/block2/0.2.0-alpha.4")]
extern crate std;
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
extern "C" {}
use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop};
use core::ops::Deref;
use core::ptr;
use std::os::raw::c_ulong;
pub use block_sys as ffi;
use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode};
#[macro_use]
mod global;
pub use global::GlobalBlock;
pub trait BlockArguments: Sized {
unsafe fn call_block<R>(self, block: *mut Block<Self, R>) -> R;
}
macro_rules! block_args_impl {
($($a:ident : $t:ident),*) => (
impl<$($t),*> BlockArguments for ($($t,)*) {
unsafe fn call_block<R>(self, block: *mut Block<Self, R>) -> R {
let layout = unsafe { block.cast::<ffi::Block_layout>().as_ref().unwrap_unchecked() };
let invoke: unsafe extern "C" fn() = layout.invoke.unwrap();
let invoke: unsafe extern "C" fn(*mut Block<Self, R>, $($t),*) -> R =
unsafe { mem::transmute(invoke) }
;
let ($($a,)*) = self;
unsafe { invoke(block, $($a),*) }
}
}
);
}
block_args_impl!();
block_args_impl!(a: A);
block_args_impl!(a: A, b: B);
block_args_impl!(a: A, b: B, c: C);
block_args_impl!(a: A, b: B, c: C, d: D);
block_args_impl!(a: A, b: B, c: C, d: D, e: E);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J,
k: K
);
block_args_impl!(
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J,
k: K,
l: L
);
#[repr(C)]
pub struct Block<A, R> {
_inner: [u8; 0],
p: PhantomData<(ffi::Block_layout, fn(A) -> R)>,
}
unsafe impl<A: BlockArguments + EncodeArguments, R: Encode> RefEncode for Block<A, R> {
const ENCODING_REF: Encoding<'static> = Encoding::Block;
}
impl<A: BlockArguments + EncodeArguments, R: Encode> Block<A, R> {
pub unsafe fn call(&self, args: A) -> R {
unsafe { args.call_block(self as *const Self as *mut Self) }
}
}
pub struct RcBlock<A, R> {
ptr: *mut Block<A, R>,
}
impl<A, R> RcBlock<A, R> {
pub unsafe fn new(ptr: *mut Block<A, R>) -> Self {
RcBlock { ptr }
}
pub unsafe fn copy(ptr: *mut Block<A, R>) -> Self {
let ptr: *mut Block<A, R> = unsafe { ffi::_Block_copy(ptr.cast()) }.cast();
unsafe { Self::new(ptr) }
}
}
impl<A, R> Clone for RcBlock<A, R> {
fn clone(&self) -> RcBlock<A, R> {
unsafe { RcBlock::copy(self.ptr) }
}
}
impl<A, R> Deref for RcBlock<A, R> {
type Target = Block<A, R>;
fn deref(&self) -> &Block<A, R> {
unsafe { self.ptr.as_ref().unwrap_unchecked() }
}
}
impl<A, R> Drop for RcBlock<A, R> {
fn drop(&mut self) {
unsafe { ffi::_Block_release(self.ptr.cast()) };
}
}
pub trait IntoConcreteBlock<A: BlockArguments + EncodeArguments>: Sized {
type Ret: Encode;
fn into_concrete_block(self) -> ConcreteBlock<A, Self::Ret, Self>;
}
macro_rules! concrete_block_impl {
($f:ident) => (
concrete_block_impl!($f,);
);
($f:ident, $($a:ident : $t:ident),*) => (
impl<$($t: Encode,)* R: Encode, X> IntoConcreteBlock<($($t,)*)> for X
where
X: Fn($($t,)*) -> R,
{
type Ret = R;
fn into_concrete_block(self) -> ConcreteBlock<($($t,)*), R, X> {
extern "C" fn $f<$($t,)* R, X>(
block: &ConcreteBlock<($($t,)*), R, X>,
$($a: $t,)*
) -> R
where
X: Fn($($t,)*) -> R,
{
(block.closure)($($a),*)
}
let f: extern "C" fn(&ConcreteBlock<($($t,)*), R, X>, $($a: $t,)*) -> R = $f;
let f: unsafe extern "C" fn() = unsafe { mem::transmute(f) };
unsafe { ConcreteBlock::with_invoke(f, self) }
}
}
);
}
concrete_block_impl!(concrete_block_invoke_args0);
concrete_block_impl!(concrete_block_invoke_args1, a: A);
concrete_block_impl!(concrete_block_invoke_args2, a: A, b: B);
concrete_block_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
concrete_block_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
concrete_block_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
concrete_block_impl!(
concrete_block_invoke_args6,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F
);
concrete_block_impl!(
concrete_block_invoke_args7,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G
);
concrete_block_impl!(
concrete_block_invoke_args8,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H
);
concrete_block_impl!(
concrete_block_invoke_args9,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I
);
concrete_block_impl!(
concrete_block_invoke_args10,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J
);
concrete_block_impl!(
concrete_block_invoke_args11,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J,
k: K
);
concrete_block_impl!(
concrete_block_invoke_args12,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J,
k: K,
l: L
);
#[repr(C)]
pub struct ConcreteBlock<A, R, F> {
p: PhantomData<Block<A, R>>,
layout: ffi::Block_layout,
closure: F,
}
unsafe impl<A: BlockArguments + EncodeArguments, R: Encode, F> RefEncode
for ConcreteBlock<A, R, F>
{
const ENCODING_REF: Encoding<'static> = Encoding::Block;
}
impl<A, R, F> ConcreteBlock<A, R, F>
where
A: BlockArguments + EncodeArguments,
R: Encode,
F: IntoConcreteBlock<A, Ret = R>,
{
pub fn new(closure: F) -> Self {
closure.into_concrete_block()
}
}
impl<A, R, F> ConcreteBlock<A, R, F> {
const FLAGS: ffi::block_flags = ffi::BLOCK_HAS_COPY_DISPOSE;
const DESCRIPTOR: ffi::Block_descriptor = ffi::Block_descriptor {
header: ffi::Block_descriptor_header {
reserved: 0,
size: mem::size_of::<Self>() as c_ulong,
},
copy: Some(block_context_copy::<Self>),
dispose: Some(block_context_dispose::<Self>),
};
unsafe fn with_invoke(invoke: unsafe extern "C" fn(), closure: F) -> Self {
let layout = ffi::Block_layout {
isa: unsafe { &ffi::_NSConcreteStackBlock },
flags: Self::FLAGS,
reserved: 0,
invoke: Some(invoke),
descriptor: &Self::DESCRIPTOR as *const ffi::Block_descriptor as *mut c_void,
};
Self {
p: PhantomData,
layout,
closure,
}
}
}
impl<A, R, F: 'static> ConcreteBlock<A, R, F> {
pub fn copy(self) -> RcBlock<A, R> {
let mut block = ManuallyDrop::new(self);
let ptr: *mut Self = &mut *block;
unsafe { RcBlock::copy(ptr.cast()) }
}
}
impl<A, R, F: Clone> Clone for ConcreteBlock<A, R, F> {
fn clone(&self) -> Self {
unsafe { Self::with_invoke(self.layout.invoke.unwrap(), self.closure.clone()) }
}
}
impl<A, R, F> Deref for ConcreteBlock<A, R, F> {
type Target = Block<A, R>;
fn deref(&self) -> &Self::Target {
let ptr: *const Self = self;
let ptr: *const Block<A, R> = ptr.cast();
unsafe { ptr.as_ref().unwrap_unchecked() }
}
}
unsafe extern "C" fn block_context_dispose<B>(block: *mut c_void) {
unsafe { ptr::drop_in_place(block.cast::<B>()) };
}
unsafe extern "C" fn block_context_copy<B>(_dst: *mut c_void, _src: *mut c_void) {
}
#[cfg(test)]
mod tests {
}