use std::ffi::CStr; use std::fmt; use std::os::raw::{c_char, c_void}; use std::str; use malloc_buf::MallocBuffer; use runtime::{Class, Object, Sel}; const QUALIFIERS: &'static [char] = &[ 'r', // const 'n', // in 'N', // inout 'o', // out 'O', // bycopy 'R', // byref 'V', // oneway ]; #[cfg(target_pointer_width = "64")] const CODE_INLINE_CAP: usize = 30; #[cfg(target_pointer_width = "32")] const CODE_INLINE_CAP: usize = 14; enum Code { Slice(&'static str), Owned(String), Inline(u8, [u8; CODE_INLINE_CAP]), Malloc(MallocBuffer) } /// An Objective-C type encoding. /// /// For more information, see Apple's documentation: /// pub struct Encoding { code: Code, } impl Encoding { /// Constructs an `Encoding` from its string representation. /// Unsafe because the caller must ensure the string is a valid encoding. pub unsafe fn from_str(code: &str) -> Encoding { from_str(code) } /// Returns self as a `str`. pub fn as_str(&self) -> &str { match self.code { Code::Slice(code) => code, Code::Owned(ref code) => code, Code::Inline(len, ref bytes) => unsafe { str::from_utf8_unchecked(&bytes[..len as usize]) }, Code::Malloc(ref buf) => unsafe { str::from_utf8_unchecked(&buf[..buf.len() - 1]) }, } } } impl Clone for Encoding { fn clone(&self) -> Encoding { if let Code::Slice(code) = self.code { from_static_str(code) } else { from_str(self.as_str()) } } } impl PartialEq for Encoding { fn eq(&self, other: &Encoding) -> bool { // strip qualifiers when comparing let s = self.as_str().trim_left_matches(QUALIFIERS); let o = other.as_str().trim_left_matches(QUALIFIERS); s == o } } impl fmt::Debug for Encoding { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.as_str()) } } pub fn from_static_str(code: &'static str) -> Encoding { Encoding { code: Code::Slice(code) } } pub fn from_str(code: &str) -> Encoding { if code.len() > CODE_INLINE_CAP { Encoding { code: Code::Owned(code.to_owned()) } } else { let mut bytes = [0; CODE_INLINE_CAP]; for (dst, byte) in bytes.iter_mut().zip(code.bytes()) { *dst = byte; } Encoding { code: Code::Inline(code.len() as u8, bytes) } } } pub unsafe fn from_malloc_str(ptr: *mut c_char) -> Encoding { let s = CStr::from_ptr(ptr); let bytes = s.to_bytes_with_nul(); assert!(str::from_utf8(bytes).is_ok()); let buf = MallocBuffer::new(ptr as *mut u8, bytes.len()).unwrap(); Encoding { code: Code::Malloc(buf) } } /// Types that have an Objective-C type encoding. /// /// Unsafe because Objective-C will make assumptions about the type (like its /// size and alignment) from its encoding, so the implementer must verify that /// the encoding is accurate. pub unsafe trait Encode { /// Returns the Objective-C type encoding for Self. fn encode() -> Encoding; } macro_rules! encode_impls { ($($t:ty : $s:expr,)*) => ($( unsafe impl Encode for $t { fn encode() -> Encoding { from_static_str($s) } } )*); } encode_impls!( i8: "c", i16: "s", i32: "i", i64: "q", u8: "C", u16: "S", u32: "I", u64: "Q", f32: "f", f64: "d", bool: "B", (): "v", *mut c_char: "*", *const c_char: "r*", *mut c_void: "^v", *const c_void: "r^v", Sel: ":", ); unsafe impl Encode for isize { #[cfg(target_pointer_width = "32")] fn encode() -> Encoding { i32::encode() } #[cfg(target_pointer_width = "64")] fn encode() -> Encoding { i64::encode() } } unsafe impl Encode for usize { #[cfg(target_pointer_width = "32")] fn encode() -> Encoding { u32::encode() } #[cfg(target_pointer_width = "64")] fn encode() -> Encoding { u64::encode() } } macro_rules! encode_message_impl { ($code:expr, $name:ident) => ( encode_message_impl!($code, $name,); ); ($code:expr, $name:ident, $($t:ident),*) => ( unsafe impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> { fn encode() -> Encoding { from_static_str($code) } } unsafe impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> { fn encode() -> Encoding { from_static_str($code) } } unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> { fn encode() -> Encoding { from_static_str($code) } } unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> { fn encode() -> Encoding { from_static_str($code) } } unsafe impl<$($t),*> $crate::Encode for *const $name<$($t),*> { fn encode() -> Encoding { from_static_str($code) } } unsafe impl<$($t),*> $crate::Encode for *mut $name<$($t),*> { fn encode() -> Encoding { from_static_str($code) } } ); } encode_message_impl!("@", Object); encode_message_impl!("#", Class); /// Types that represent a group of arguments, where each has an Objective-C /// type encoding. pub trait EncodeArguments { /// The type as which the encodings for Self will be returned. type Encs: AsRef<[Encoding]>; /// Returns the Objective-C type encodings for Self. fn encodings() -> Self::Encs; } macro_rules! count_idents { () => (0); ($a:ident) => (1); ($a:ident, $($b:ident),+) => (1 + count_idents!($($b),*)); } macro_rules! encode_args_impl { ($($t:ident),*) => ( impl<$($t: Encode),*> EncodeArguments for ($($t,)*) { type Encs = [Encoding; count_idents!($($t),*)]; fn encodings() -> Self::Encs { [ $($t::encode()),* ] } } ); } 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 runtime::{Class, Object, Sel}; use super::{Encode, Encoding}; #[test] fn test_encode() { assert!(u32::encode().as_str() == "I"); assert!(<()>::encode().as_str() == "v"); assert!(<&Object>::encode().as_str() == "@"); assert!(<*mut Object>::encode().as_str() == "@"); assert!(<&Class>::encode().as_str() == "#"); assert!(Sel::encode().as_str() == ":"); } #[test] fn test_inline_encoding() { let enc = unsafe { Encoding::from_str("C") }; assert!(enc.as_str() == "C"); let enc2 = enc.clone(); assert!(enc2 == enc); assert!(enc2.as_str() == "C"); } #[test] fn test_owned_encoding() { let s = "{Test=CCCCCCCCCCCCCCCCCCCCCCCCC}"; let enc = unsafe { Encoding::from_str(s) }; assert!(enc.as_str() == s); let enc2 = enc.clone(); assert!(enc2 == enc); assert!(enc2.as_str() == s); } }