use core::ffi::c_void;
use core::mem::ManuallyDrop;
use core::num::{
NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU16, NonZeroU32,
NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
};
use core::pin::Pin;
use core::ptr::NonNull;
use crate::Encoding;
pub unsafe trait Encode {
const ENCODING: Encoding<'static>;
}
pub unsafe trait RefEncode {
const ENCODING_REF: Encoding<'static>;
}
macro_rules! encode_impls {
($($t:ty => $e:ident,)*) => ($(
unsafe impl Encode for $t {
const ENCODING: Encoding<'static> = Encoding::$e;
}
)*);
}
encode_impls!(
i8 => Char,
i16 => Short,
i32 => Int,
i64 => LongLong,
u8 => UChar,
u16 => UShort,
u32 => UInt,
u64 => ULongLong,
f32 => Float,
f64 => Double,
);
unsafe impl Encode for () {
const ENCODING: Encoding<'static> = Encoding::Void;
}
unsafe impl Encode for bool {
const ENCODING: Encoding<'static> = Encoding::Bool;
}
macro_rules! encode_impls_size {
($($t:ty => ($t16:ty, $t32:ty, $t64:ty),)*) => ($(
#[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<'static> = <$t16>::ENCODING;
#[cfg(target_pointer_width = "32")]
const ENCODING: Encoding<'static> = <$t32>::ENCODING;
#[cfg(target_pointer_width = "64")]
const ENCODING: Encoding<'static> = <$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<'static> = Encoding::Pointer(&Self::ENCODING);
}
)*);
}
pointer_refencode_impl!(bool, i16, i32, i64, isize, u16, u32, u64, usize, f32, f64);
unsafe impl RefEncode for i8 {
const ENCODING_REF: Encoding<'static> = Encoding::String;
}
unsafe impl RefEncode for u8 {
const ENCODING_REF: Encoding<'static> = Encoding::String;
}
macro_rules! encode_impls_nonzero {
($($nonzero:ident => $type:ty,)*) => ($(
unsafe impl Encode for $nonzero {
const ENCODING: Encoding<'static> = <$type>::ENCODING;
}
unsafe impl Encode for Option<$nonzero> {
const ENCODING: Encoding<'static> = <$type>::ENCODING;
}
unsafe impl RefEncode for $nonzero {
const ENCODING_REF: Encoding<'static> = <$type>::ENCODING_REF;
}
unsafe impl RefEncode for Option<$nonzero> {
const ENCODING_REF: Encoding<'static> = <$type>::ENCODING_REF;
}
)*);
}
encode_impls_nonzero!(
NonZeroI8 => i8,
NonZeroI16 => i16,
NonZeroI32 => i32,
NonZeroI64 => i64,
NonZeroIsize => isize,
NonZeroU8 => u8,
NonZeroU16 => u16,
NonZeroU32 => u32,
NonZeroU64 => u64,
NonZeroUsize => usize,
);
unsafe impl Encode for *const c_void {
const ENCODING: Encoding<'static> = Encoding::Pointer(&Encoding::Void);
}
unsafe impl RefEncode for *const c_void {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl Encode for *mut c_void {
const ENCODING: Encoding<'static> = Encoding::Pointer(&Encoding::Void);
}
unsafe impl RefEncode for *mut c_void {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl<T: Encode, const LENGTH: usize> Encode for [T; LENGTH] {
const ENCODING: Encoding<'static> = Encoding::Array(LENGTH, &T::ENCODING);
}
unsafe impl<T: Encode, const LENGTH: usize> RefEncode for [T; LENGTH] {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl<T: Encode + ?Sized> Encode for ManuallyDrop<T> {
const ENCODING: Encoding<'static> = T::ENCODING;
}
unsafe impl<T: RefEncode + ?Sized> RefEncode for ManuallyDrop<T> {
const ENCODING_REF: Encoding<'static> = T::ENCODING_REF;
}
unsafe impl<T: Encode> Encode for Pin<T> {
const ENCODING: Encoding<'static> = T::ENCODING;
}
unsafe impl<T: RefEncode> RefEncode for Pin<T> {
const ENCODING_REF: Encoding<'static> = T::ENCODING_REF;
}
unsafe impl<T: Encode> Encode for Wrapping<T> {
const ENCODING: Encoding<'static> = T::ENCODING;
}
unsafe impl<T: RefEncode> RefEncode for Wrapping<T> {
const ENCODING_REF: Encoding<'static> = T::ENCODING_REF;
}
macro_rules! encode_pointer_impls {
(unsafe impl<T: RefEncode> $x:ident for &$t:ident {
const $c:ident = $e:expr;
}) => (
unsafe impl<$t: RefEncode + ?Sized> $x for *const $t {
const $c: Encoding<'static> = $e;
}
unsafe impl<$t: RefEncode + ?Sized> $x for *mut $t {
const $c: Encoding<'static> = $e;
}
unsafe impl<'a, $t: RefEncode + ?Sized> $x for &'a $t {
const $c: Encoding<'static> = $e;
}
unsafe impl<'a, $t: RefEncode + ?Sized> $x for &'a mut $t {
const $c: Encoding<'static> = $e;
}
unsafe impl<T: RefEncode + ?Sized> $x for NonNull<$t> {
const $c: Encoding<'static> = $e;
}
unsafe impl<'a, $t: RefEncode + ?Sized> $x for Option<&'a $t> {
const $c: Encoding<'static> = $e;
}
unsafe impl<'a, $t: RefEncode + ?Sized> $x for Option<&'a mut $t> {
const $c: Encoding<'static> = $e;
}
unsafe impl<T: RefEncode + ?Sized> $x for Option<NonNull<$t>> {
const $c: Encoding<'static> = $e;
}
);
}
encode_pointer_impls!(
unsafe impl<T: RefEncode> Encode for &T {
const ENCODING = T::ENCODING_REF;
}
);
encode_pointer_impls!(
unsafe impl<T: RefEncode> RefEncode for &T {
const ENCODING_REF = Encoding::Pointer(&T::ENCODING_REF);
}
);
macro_rules! encode_fn_pointer_impl {
(@ $FnTy: ty, $($Arg: ident),*) => {
unsafe impl<Ret: Encode, $($Arg: Encode),*> Encode for $FnTy {
const ENCODING: Encoding<'static> = Encoding::Pointer(&Encoding::Unknown);
}
unsafe impl<Ret: Encode, $($Arg: Encode),*> RefEncode for $FnTy {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
unsafe impl<Ret: Encode, $($Arg: Encode),*> Encode for Option<$FnTy> {
const ENCODING: Encoding<'static> = Encoding::Pointer(&Encoding::Unknown);
}
unsafe impl<Ret: Encode, $($Arg: Encode),*> RefEncode for Option<$FnTy> {
const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Self::ENCODING);
}
};
($($Arg: ident),+) => {
encode_fn_pointer_impl!(@ extern "C" fn($($Arg),+) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ unsafe extern "C" fn($($Arg),+) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
encode_fn_pointer_impl!(@ unsafe extern "C" fn($($Arg),+ , ...) -> Ret, $($Arg),+ );
};
() => {
encode_fn_pointer_impl!(@ extern "C" fn() -> Ret, );
encode_fn_pointer_impl!(@ unsafe extern "C" fn() -> Ret, );
};
}
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);
pub unsafe trait EncodeArguments {
const ENCODINGS: &'static [Encoding<'static>];
}
macro_rules! encode_args_impl {
($($Arg: ident),*) => {
unsafe impl<$($Arg: Encode),*> EncodeArguments for ($($Arg,)*) {
const ENCODINGS: &'static [Encoding<'static>] = &[
$($Arg::ENCODING),*
];
}
};
}
encode_args_impl!();
encode_args_impl!(A);
encode_args_impl!(A, B);
encode_args_impl!(A, B, C);
encode_args_impl!(A, B, C, D);
encode_args_impl!(A, B, C, D, E);
encode_args_impl!(A, B, C, D, E, F);
encode_args_impl!(A, B, C, D, E, F, G);
encode_args_impl!(A, B, C, D, E, F, G, H);
encode_args_impl!(A, B, C, D, E, F, G, H, I);
encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
#[cfg(test)]
mod tests {
use super::*;
#[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_void() {
assert_eq!(<()>::ENCODING, Encoding::Void);
assert_eq!(
<*const c_void>::ENCODING,
Encoding::Pointer(&Encoding::Void)
);
assert_eq!(
<&*const c_void>::ENCODING,
Encoding::Pointer(&Encoding::Pointer(&Encoding::Void))
);
}
#[test]
fn test_extern_fn_pointer() {
assert_eq!(
<extern "C" fn()>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
assert_eq!(
<extern "C" fn(x: ()) -> ()>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
assert_eq!(
<Option<unsafe extern "C" fn()>>::ENCODING,
Encoding::Pointer(&Encoding::Unknown)
);
}
}