#![doc = include_str!("../examples/encode_core_graphics.rs")]
#![doc = include_str!("../examples/encode_nsuinteger.rs")]
#![doc = include_str!("../examples/encode_nsstring.rs")]
#![doc = include_str!("../examples/encode_opaque_type.rs")]
use core::cell::{Cell, UnsafeCell};
use core::ffi::c_void;
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::num::{
NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32,
NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
};
use core::ptr::NonNull;
use core::sync::atomic;
#[doc(inline)]
pub use objc2_encode::{Encoding, EncodingBox, ParseError};
use crate::runtime::{AnyObject, Imp, Sel};
pub unsafe trait Encode {
const ENCODING: Encoding;
}
pub unsafe trait RefEncode {
const ENCODING_REF: Encoding;
}
pub unsafe trait OptionEncode {}
unsafe impl<T: Encode + OptionEncode> Encode for Option<T> {
const ENCODING: Encoding = {
if mem::size_of::<T>() != mem::size_of::<Option<T>>() {
panic!("invalid OptionEncode + Encode implementation");
}
T::ENCODING
};
}
unsafe impl<T: RefEncode + OptionEncode> RefEncode for Option<T> {
const ENCODING_REF: Encoding = {
if mem::size_of::<T>() != mem::size_of::<Option<T>>() {
panic!("invalid OptionEncode + RefEncode implementation");
}
T::ENCODING_REF
};
}
mod return_private {
pub trait Sealed {}
}
pub unsafe trait EncodeReturn: return_private::Sealed {
const ENCODING_RETURN: Encoding;
}
impl return_private::Sealed for () {}
unsafe impl EncodeReturn for () {
const ENCODING_RETURN: Encoding = Encoding::Void;
}
impl<T: Encode> return_private::Sealed for T {}
unsafe impl<T: Encode> EncodeReturn for T {
const ENCODING_RETURN: Encoding = T::ENCODING;
}
mod argument_private {
pub trait Sealed {}
}
pub unsafe trait EncodeArgument: argument_private::Sealed {
const ENCODING_ARGUMENT: Encoding;
}
impl<T: Encode> argument_private::Sealed for T {}
unsafe impl<T: Encode> EncodeArgument for T {
const ENCODING_ARGUMENT: Encoding = T::ENCODING;
}
mod args_private {
pub trait Sealed {}
}
pub trait EncodeArguments: args_private::Sealed {
const ENCODINGS: &'static [Encoding];
#[doc(hidden)]
unsafe fn __invoke<R: EncodeReturn>(
msg_send_fn: Imp,
receiver: *mut AnyObject,
sel: Sel,
args: Self,
) -> R;
}
macro_rules! encode_args_impl {
($($a:ident: $T: ident),*) => {
impl<$($T: EncodeArgument),*> args_private::Sealed for ($($T,)*) {}
impl<$($T: EncodeArgument),*> EncodeArguments for ($($T,)*) {
const ENCODINGS: &'static [Encoding] = &[
$($T::ENCODING_ARGUMENT),*
];
#[inline]
unsafe fn __invoke<R: EncodeReturn>(msg_send_fn: Imp, receiver: *mut AnyObject, sel: Sel, ($($a,)*): Self) -> R {
let msg_send_fn: unsafe extern "C-unwind" fn(*mut AnyObject, Sel $(, $T)*) -> R = unsafe {
mem::transmute(msg_send_fn)
};
unsafe { msg_send_fn(receiver, sel $(, $a)*) }
}
}
};
}
encode_args_impl!();
encode_args_impl!(a: A);
encode_args_impl!(a: A, b: B);
encode_args_impl!(a: A, b: B, c: C);
encode_args_impl!(a: A, b: B, c: C, d: D);
encode_args_impl!(a: A, b: B, c: C, d: D, e: E);
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
encode_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
encode_args_impl!(
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J,
k: K
);
encode_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
);
encode_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,
m: M
);
encode_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,
m: M,
n: N
);
encode_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,
m: M,
n: N,
o: O
);
encode_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,
m: M,
n: N,
o: O,
p: P
);
macro_rules! encode_impls {
($($t:ty => $e:ident,)*) => ($(
unsafe impl Encode for $t {
const ENCODING: Encoding = Encoding::$e;
}
)*);
}
encode_impls!(
i8 => Char,
i16 => Short,
i32 => Int,
i64 => LongLong,
u8 => UChar,
u16 => UShort,
u32 => UInt,
u64 => ULongLong,
f32 => Float,
f64 => Double,
);
macro_rules! encode_impls_size {
($($t:ty => ($t16:ty, $t32:ty, $t64:ty),)*) => ($(
// SAFETY: `usize` and `isize` is ABI compatible with `uN`/`iN` of the
// same size.
// <https://doc.rust-lang.org/nightly/std/primitive.fn.html#abi-compatibility>
#[doc = concat!("The encoding of [`", stringify!($t), "`] varies based on the target pointer width.")]
unsafe impl Encode for $t {
#[cfg(target_pointer_width = "16")]
const ENCODING: Encoding = <$t16>::ENCODING;
#[cfg(target_pointer_width = "32")]
const ENCODING: Encoding = <$t32>::ENCODING;
#[cfg(target_pointer_width = "64")]
const ENCODING: Encoding = <$t64>::ENCODING;
}
)*);
}
encode_impls_size!(
isize => (i16, i32, i64),
usize => (u16, u32, u64),
);
macro_rules! pointer_refencode_impl {
($($t:ty),*) => ($(
unsafe impl RefEncode for $t {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
)*);
}
pointer_refencode_impl!(i16, i32, i64, isize, u16, u32, u64, usize, f32, f64);
unsafe impl RefEncode for i8 {
const ENCODING_REF: Encoding = Encoding::String;
}
unsafe impl RefEncode for u8 {
const ENCODING_REF: Encoding = Encoding::String;
}
macro_rules! encode_impls_nonzero {
($($nonzero:ident => $type:ty,)*) => ($(
unsafe impl Encode for $nonzero {
const ENCODING: Encoding = <$type>::ENCODING;
}
unsafe impl RefEncode for $nonzero {
const ENCODING_REF: Encoding = <$type>::ENCODING_REF;
}
unsafe impl OptionEncode for $nonzero {}
)*);
}
encode_impls_nonzero!(
NonZeroI8 => i8,
NonZeroI16 => i16,
NonZeroI32 => i32,
NonZeroI64 => i64,
NonZeroIsize => isize,
NonZeroU8 => u8,
NonZeroU16 => u16,
NonZeroU32 => u32,
NonZeroU64 => u64,
NonZeroUsize => usize,
);
macro_rules! encode_atomic_impls {
($(
$(#[$m:meta])*
$atomic:ident => $type:ty,
)*) => ($(
// SAFETY: C11 `_Atomic` types use compatible synchronization
// primitives, and the atomic type is guaranteed to have the same
// in-memory representation as the underlying type.
$(#[$m])*
unsafe impl Encode for atomic::$atomic {
const ENCODING: Encoding = Encoding::Atomic(&<$type>::ENCODING);
}
$(#[$m])*
unsafe impl RefEncode for atomic::$atomic {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
)*);
}
encode_atomic_impls!(
#[cfg(target_has_atomic = "8")]
AtomicI8 => i8,
#[cfg(target_has_atomic = "8")]
AtomicU8 => u8,
#[cfg(target_has_atomic = "16")]
AtomicI16 => i16,
#[cfg(target_has_atomic = "16")]
AtomicU16 => u16,
#[cfg(target_has_atomic = "32")]
AtomicI32 => i32,
#[cfg(target_has_atomic = "32")]
AtomicU32 => u32,
#[cfg(target_has_atomic = "64")]
AtomicI64 => i64,
#[cfg(target_has_atomic = "64")]
AtomicU64 => u64,
#[cfg(target_has_atomic = "ptr")]
AtomicIsize => isize,
#[cfg(target_has_atomic = "ptr")]
AtomicUsize => usize,
);
#[cfg(target_has_atomic = "ptr")]
unsafe impl<T: RefEncode> Encode for atomic::AtomicPtr<T> {
const ENCODING: Encoding = Encoding::Atomic(&T::ENCODING_REF);
}
#[cfg(target_has_atomic = "ptr")]
unsafe impl<T: RefEncode> RefEncode for atomic::AtomicPtr<T> {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl RefEncode for c_void {
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Void);
}
unsafe impl<T: Encode, const LENGTH: usize> Encode for [T; LENGTH] {
const ENCODING: Encoding = Encoding::Array(LENGTH as u64, &T::ENCODING);
}
unsafe impl<T: Encode, const LENGTH: usize> RefEncode for [T; LENGTH] {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
macro_rules! encode_impls_transparent {
($($t:ident<T $(: ?$b:ident)?>,)*) => ($(
unsafe impl<T: Encode $(+ ?$b)?> Encode for $t<T> {
const ENCODING: Encoding = T::ENCODING;
}
unsafe impl<T: RefEncode $(+ ?$b)?> RefEncode for $t<T> {
const ENCODING_REF: Encoding = T::ENCODING_REF;
}
)*);
}
encode_impls_transparent! {
ManuallyDrop<T: ?Sized>,
UnsafeCell<T: ?Sized>,
Cell<T: ?Sized>,
MaybeUninit<T>,
Wrapping<T>,
}
macro_rules! encode_pointer_impls {
(unsafe impl<T: RefEncode> $x:ident for Pointer<T> {
const $c:ident = $e:expr;
}) => (
unsafe impl<T: RefEncode + ?Sized> $x for *const T {
const $c: Encoding = $e;
}
unsafe impl<T: RefEncode + ?Sized> $x for *mut T {
const $c: Encoding = $e;
}
unsafe impl<'a, T: RefEncode + ?Sized> $x for &'a T {
const $c: Encoding = $e;
}
unsafe impl<'a, T: RefEncode + ?Sized> $x for &'a mut T {
const $c: Encoding = $e;
}
unsafe impl<T: RefEncode + ?Sized> $x for NonNull<T> {
const $c: Encoding = $e;
}
);
}
encode_pointer_impls!(
unsafe impl<T: RefEncode> Encode for Pointer<T> {
const ENCODING = T::ENCODING_REF;
}
);
encode_pointer_impls!(
unsafe impl<T: RefEncode> RefEncode for Pointer<T> {
const ENCODING_REF = Encoding::Pointer(&T::ENCODING_REF);
}
);
unsafe impl<T: RefEncode + ?Sized> OptionEncode for &T {}
unsafe impl<T: RefEncode + ?Sized> OptionEncode for &mut T {}
unsafe impl<T: RefEncode + ?Sized> OptionEncode for NonNull<T> {}
macro_rules! encode_fn_pointer_impl {
(@ $FnTy: ty, $($Arg: ident),*) => {
unsafe impl<Ret: EncodeReturn, $($Arg: EncodeArgument),*> Encode for $FnTy {
const ENCODING: Encoding = Encoding::Pointer(&Encoding::Unknown);
}
unsafe impl<Ret: EncodeReturn, $($Arg: EncodeArgument),*> RefEncode for $FnTy {
const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl<Ret: EncodeReturn, $($Arg: EncodeArgument),*> OptionEncode for $FnTy {}
};
(# $abi:literal; $($Arg: ident),+) => {
encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ unsafe extern $abi fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
};
(# $abi:literal; ) => {
encode_fn_pointer_impl!(@ extern $abi fn() -> Ret, );
encode_fn_pointer_impl!(@ unsafe extern $abi fn() -> Ret, );
};
($($Arg: ident),*) => {
encode_fn_pointer_impl!(# "C"; $($Arg),*);
encode_fn_pointer_impl!(# "C-unwind"; $($Arg),*);
};
}
encode_fn_pointer_impl!();
encode_fn_pointer_impl!(A);
encode_fn_pointer_impl!(A, B);
encode_fn_pointer_impl!(A, B, C);
encode_fn_pointer_impl!(A, B, C, D);
encode_fn_pointer_impl!(A, B, C, D, E);
encode_fn_pointer_impl!(A, B, C, D, E, F);
encode_fn_pointer_impl!(A, B, C, D, E, F, G);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
encode_fn_pointer_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
#[cfg(test)]
mod tests {
use super::*;
use core::sync::atomic::*;
#[test]
fn test_c_string() {
assert_eq!(i8::ENCODING, Encoding::Char);
assert_eq!(u8::ENCODING, Encoding::UChar);
assert_eq!(<*const i8>::ENCODING, Encoding::String);
assert_eq!(<&u8>::ENCODING, Encoding::String);
assert_eq!(i8::ENCODING_REF, Encoding::String);
assert_eq!(i8::ENCODING_REF, Encoding::String);
assert_eq!(
<*const *const i8>::ENCODING,
Encoding::Pointer(&Encoding::String)
);
assert_eq!(<&&u8>::ENCODING, Encoding::Pointer(&Encoding::String));
}
#[test]
fn test_i32() {
assert_eq!(i32::ENCODING, Encoding::Int);
assert_eq!(<&i32>::ENCODING, Encoding::Pointer(&Encoding::Int));
assert_eq!(
<&&i32>::ENCODING,
Encoding::Pointer(&Encoding::Pointer(&Encoding::Int))
);
}
#[test]
fn test_atomic() {
assert_eq!(AtomicI32::ENCODING, Encoding::Atomic(&Encoding::Int));
assert_eq!(
AtomicI32::ENCODING_REF,
Encoding::Pointer(&Encoding::Atomic(&Encoding::Int))
);
assert_eq!(
AtomicPtr::<i32>::ENCODING,
Encoding::Atomic(&Encoding::Pointer(&Encoding::Int))
);
assert_eq!(AtomicI8::ENCODING, Encoding::Atomic(&Encoding::Char));
assert_eq!(
AtomicI8::ENCODING_REF,
Encoding::Pointer(&Encoding::Atomic(&Encoding::Char))
);
assert_eq!(
AtomicPtr::<i8>::ENCODING,
Encoding::Atomic(&Encoding::String)
);
}
#[test]
fn test_void() {
assert_eq!(
<*const c_void>::ENCODING,
Encoding::Pointer(&Encoding::Void)
);
assert_eq!(<&c_void>::ENCODING, Encoding::Pointer(&Encoding::Void));
assert_eq!(
<&*const c_void>::ENCODING,
Encoding::Pointer(&Encoding::Pointer(&Encoding::Void))
);
assert_eq!(
<AtomicPtr<c_void>>::ENCODING,
Encoding::Atomic(&Encoding::Pointer(&Encoding::Void))
);
}
#[test]
fn test_transparent() {
assert_eq!(<ManuallyDrop<u8>>::ENCODING, u8::ENCODING);
assert_eq!(<ManuallyDrop<&u8>>::ENCODING, u8::ENCODING_REF);
assert_eq!(<ManuallyDrop<Option<&u8>>>::ENCODING, u8::ENCODING_REF);
assert_eq!(<&ManuallyDrop<Option<&u8>>>::ENCODING, <&&u8>::ENCODING);
assert_eq!(<UnsafeCell<u8>>::ENCODING, u8::ENCODING);
assert_eq!(<UnsafeCell<&u8>>::ENCODING, <&u8>::ENCODING);
assert_eq!(<Cell<u8>>::ENCODING, u8::ENCODING);
assert_eq!(<Cell<&u8>>::ENCODING, <&u8>::ENCODING);
assert_eq!(<MaybeUninit<u8>>::ENCODING, u8::ENCODING);
assert_eq!(<Wrapping<u8>>::ENCODING, u8::ENCODING);
}
#[test]
fn test_extern_fn_pointer() {
assert_eq!(
<extern "C" fn()>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
assert_eq!(
<extern "C" fn(x: i32) -> u32>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
assert_eq!(
<Option<unsafe extern "C" fn()>>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
assert_eq!(
<extern "C-unwind" fn()>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
}
#[test]
fn test_extern_fn_pointer_elided_lifetime() {
fn impls_encode<T: Encode>(_x: T) {}
extern "C" fn my_fn1(_x: &i32) {}
extern "C" fn my_fn2(_x: &i32, _y: &u8) {}
extern "C" fn my_fn3(x: &u8) -> &u8 {
x
}
extern "C" fn my_fn4<'a>(x: &'a u8, _y: &i32) -> &'a u8 {
x
}
impls_encode(my_fn1 as extern "C" fn(_));
impls_encode(my_fn2 as extern "C" fn(_, _));
impls_encode(my_fn3 as extern "C" fn(_) -> _);
impls_encode(my_fn4 as extern "C" fn(_, _) -> _);
}
#[test]
fn test_return() {
assert_eq!(<i32>::ENCODING_RETURN, <i32>::ENCODING);
assert_eq!(<()>::ENCODING_RETURN, Encoding::Void);
}
#[test]
fn test_argument() {
assert_eq!(<i32>::ENCODING_ARGUMENT, <i32>::ENCODING);
}
#[test]
fn test_arguments() {
assert_eq!(<()>::ENCODINGS, &[] as &[Encoding]);
assert_eq!(<(i8,)>::ENCODINGS, &[i8::ENCODING]);
assert_eq!(<(i8, u32)>::ENCODINGS, &[i8::ENCODING, u32::ENCODING]);
}
}