diff options
Diffstat (limited to 'third_party/rust/objc/src/encode.rs')
-rw-r--r-- | third_party/rust/objc/src/encode.rs | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/third_party/rust/objc/src/encode.rs b/third_party/rust/objc/src/encode.rs new file mode 100644 index 0000000000..921479da98 --- /dev/null +++ b/third_party/rust/objc/src/encode.rs @@ -0,0 +1,279 @@ +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<u8>) +} + +/// An Objective-C type encoding. +/// +/// For more information, see Apple's documentation: +/// <https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html> +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); + } +} |