objc_rs/
encode.rs

1use std::ffi::CStr;
2use std::fmt;
3use std::os::raw::{c_char, c_void};
4use std::str;
5use malloc_buf::MallocBuffer;
6
7use runtime::{Class, Object, Sel};
8
9const QUALIFIERS: &'static [char] = &[
10    'r', // const
11    'n', // in
12    'N', // inout
13    'o', // out
14    'O', // bycopy
15    'R', // byref
16    'V', // oneway
17];
18
19#[cfg(target_pointer_width = "64")]
20const CODE_INLINE_CAP: usize = 30;
21
22#[cfg(target_pointer_width = "32")]
23const CODE_INLINE_CAP: usize = 14;
24
25enum Code {
26    Slice(&'static str),
27    Owned(String),
28    Inline(u8, [u8; CODE_INLINE_CAP]),
29    Malloc(MallocBuffer<u8>)
30}
31
32/// An Objective-C type encoding.
33///
34/// For more information, see Apple's documentation:
35/// <https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html>
36pub struct Encoding {
37    code: Code,
38}
39
40impl Encoding {
41    /// Constructs an `Encoding` from its string representation.
42    /// Unsafe because the caller must ensure the string is a valid encoding.
43    pub unsafe fn from_str(code: &str) -> Encoding {
44        from_str(code)
45    }
46
47    /// Returns self as a `str`.
48    pub fn as_str(&self) -> &str {
49        match self.code {
50            Code::Slice(code) => code,
51            Code::Owned(ref code) => code,
52            Code::Inline(len, ref bytes) => unsafe {
53                str::from_utf8_unchecked(&bytes[..len as usize])
54            },
55            Code::Malloc(ref buf) => unsafe {
56                str::from_utf8_unchecked(&buf[..buf.len() - 1])
57            },
58        }
59    }
60}
61
62impl Clone for Encoding {
63    fn clone(&self) -> Encoding {
64        if let Code::Slice(code) = self.code {
65            from_static_str(code)
66        } else {
67            from_str(self.as_str())
68        }
69    }
70}
71
72impl PartialEq for Encoding {
73    fn eq(&self, other: &Encoding) -> bool {
74        // strip qualifiers when comparing
75        let s = self.as_str().trim_start_matches(QUALIFIERS);
76        let o = other.as_str().trim_start_matches(QUALIFIERS);
77        s == o
78    }
79}
80
81impl fmt::Debug for Encoding {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        write!(f, "{}", self.as_str())
84    }
85}
86
87pub fn from_static_str(code: &'static str) -> Encoding {
88    Encoding { code: Code::Slice(code) }
89}
90
91pub fn from_str(code: &str) -> Encoding {
92    if code.len() > CODE_INLINE_CAP {
93        Encoding { code: Code::Owned(code.to_owned()) }
94    } else {
95        let mut bytes = [0; CODE_INLINE_CAP];
96        for (dst, byte) in bytes.iter_mut().zip(code.bytes()) {
97            *dst = byte;
98        }
99        Encoding { code: Code::Inline(code.len() as u8, bytes) }
100    }
101}
102
103pub unsafe fn from_malloc_str(ptr: *mut c_char) -> Encoding {
104    let s = CStr::from_ptr(ptr);
105    let bytes = s.to_bytes_with_nul();
106    assert!(str::from_utf8(bytes).is_ok());
107    let buf = MallocBuffer::new(ptr as *mut u8, bytes.len()).unwrap();
108    Encoding { code: Code::Malloc(buf) }
109}
110
111/// Types that have an Objective-C type encoding.
112///
113/// Unsafe because Objective-C will make assumptions about the type (like its
114/// size and alignment) from its encoding, so the implementer must verify that
115/// the encoding is accurate.
116pub unsafe trait Encode {
117    /// Returns the Objective-C type encoding for Self.
118    fn encode() -> Encoding;
119}
120
121macro_rules! encode_impls {
122    ($($t:ty : $s:expr,)*) => ($(
123        unsafe impl Encode for $t {
124            fn encode() -> Encoding { from_static_str($s) }
125        }
126    )*);
127}
128
129encode_impls!(
130    i8: "c",
131    i16: "s",
132    i32: "i",
133    i64: "q",
134    u8: "C",
135    u16: "S",
136    u32: "I",
137    u64: "Q",
138    f32: "f",
139    f64: "d",
140    bool: "B",
141    (): "v",
142    *mut c_char: "*",
143    *const c_char: "r*",
144    *mut c_void: "^v",
145    *const c_void: "r^v",
146    Sel: ":",
147);
148
149unsafe impl Encode for isize {
150    #[cfg(target_pointer_width = "32")]
151    fn encode() -> Encoding { i32::encode() }
152
153    #[cfg(target_pointer_width = "64")]
154    fn encode() -> Encoding { i64::encode() }
155}
156
157unsafe impl Encode for usize {
158    #[cfg(target_pointer_width = "32")]
159    fn encode() -> Encoding { u32::encode() }
160
161    #[cfg(target_pointer_width = "64")]
162    fn encode() -> Encoding { u64::encode() }
163}
164
165macro_rules! encode_message_impl {
166    ($code:expr, $name:ident) => (
167        encode_message_impl!($code, $name,);
168    );
169    ($code:expr, $name:ident, $($t:ident),*) => (
170        unsafe impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
171            fn encode() -> Encoding { from_static_str($code) }
172        }
173
174        unsafe impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
175            fn encode() -> Encoding { from_static_str($code) }
176        }
177
178        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
179            fn encode() -> Encoding { from_static_str($code) }
180        }
181
182        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
183            fn encode() -> Encoding { from_static_str($code) }
184        }
185
186        unsafe impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
187            fn encode() -> Encoding { from_static_str($code) }
188        }
189
190        unsafe impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
191            fn encode() -> Encoding { from_static_str($code) }
192        }
193    );
194}
195
196encode_message_impl!("@", Object);
197
198encode_message_impl!("#", Class);
199
200/// Types that represent a group of arguments, where each has an Objective-C
201/// type encoding.
202pub trait EncodeArguments {
203    /// The type as which the encodings for Self will be returned.
204    type Encs: AsRef<[Encoding]>;
205
206    /// Returns the Objective-C type encodings for Self.
207    fn encodings() -> Self::Encs;
208}
209
210macro_rules! count_idents {
211    () => (0);
212    ($a:ident) => (1);
213    ($a:ident, $($b:ident),+) => (1 + count_idents!($($b),*));
214}
215
216macro_rules! encode_args_impl {
217    ($($t:ident),*) => (
218        impl<$($t: Encode),*> EncodeArguments for ($($t,)*) {
219            type Encs = [Encoding; count_idents!($($t),*)];
220
221            fn encodings() -> Self::Encs {
222                [
223                    $($t::encode()),*
224                ]
225            }
226        }
227    );
228}
229
230encode_args_impl!();
231encode_args_impl!(A);
232encode_args_impl!(A, B);
233encode_args_impl!(A, B, C);
234encode_args_impl!(A, B, C, D);
235encode_args_impl!(A, B, C, D, E);
236encode_args_impl!(A, B, C, D, E, F);
237encode_args_impl!(A, B, C, D, E, F, G);
238encode_args_impl!(A, B, C, D, E, F, G, H);
239encode_args_impl!(A, B, C, D, E, F, G, H, I);
240encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
241encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
242encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
243
244#[cfg(test)]
245mod tests {
246    use runtime::{Class, Object, Sel};
247    use super::{Encode, Encoding};
248
249    #[test]
250    fn test_encode() {
251        assert!(u32::encode().as_str() == "I");
252        assert!(<()>::encode().as_str() == "v");
253        assert!(<&Object>::encode().as_str() == "@");
254        assert!(<*mut Object>::encode().as_str() == "@");
255        assert!(<&Class>::encode().as_str() == "#");
256        assert!(Sel::encode().as_str() == ":");
257    }
258
259    #[test]
260    fn test_inline_encoding() {
261        let enc = unsafe { Encoding::from_str("C") };
262        assert!(enc.as_str() == "C");
263
264        let enc2 = enc.clone();
265        assert!(enc2 == enc);
266        assert!(enc2.as_str() == "C");
267    }
268
269    #[test]
270    fn test_owned_encoding() {
271        let s = "{Test=CCCCCCCCCCCCCCCCCCCCCCCCC}";
272        let enc = unsafe { Encoding::from_str(s) };
273        assert!(enc.as_str() == s);
274
275        let enc2 = enc.clone();
276        assert!(enc2 == enc);
277        assert!(enc2.as_str() == s);
278    }
279}