Skip to main content

objc_rs/
encode.rs

1use malloc_buf::Malloc;
2use std::ffi::CStr;
3use std::fmt;
4use std::os::raw::{c_char, c_void};
5use std::str;
6
7use crate::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(Malloc<[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 { str::from_utf8_unchecked(&buf[..buf.len() - 1]) },
56        }
57    }
58}
59
60impl Clone for Encoding {
61    fn clone(&self) -> Encoding {
62        if let Code::Slice(code) = self.code {
63            from_static_str(code)
64        } else {
65            from_str(self.as_str())
66        }
67    }
68}
69
70impl PartialEq for Encoding {
71    fn eq(&self, other: &Encoding) -> bool {
72        // strip qualifiers when comparing
73        let s = self.as_str().trim_start_matches(QUALIFIERS);
74        let o = other.as_str().trim_start_matches(QUALIFIERS);
75        s == o
76    }
77}
78
79impl fmt::Debug for Encoding {
80    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81        write!(f, "{}", self.as_str())
82    }
83}
84
85pub fn from_static_str(code: &'static str) -> Encoding {
86    Encoding {
87        code: Code::Slice(code),
88    }
89}
90
91pub fn from_str(code: &str) -> Encoding {
92    if code.len() > CODE_INLINE_CAP {
93        Encoding {
94            code: Code::Owned(code.to_owned()),
95        }
96    } else {
97        let mut bytes = [0; CODE_INLINE_CAP];
98        for (dst, byte) in bytes.iter_mut().zip(code.bytes()) {
99            *dst = byte;
100        }
101        Encoding {
102            code: Code::Inline(code.len() as u8, bytes),
103        }
104    }
105}
106
107pub unsafe fn from_malloc_str(ptr: *mut c_char) -> Encoding {
108    let s = unsafe { CStr::from_ptr(ptr) };
109    let bytes = s.to_bytes_with_nul();
110    assert!(str::from_utf8(bytes).is_ok());
111    let buf = unsafe { Malloc::from_array(ptr as *mut u8, bytes.len()) };
112    Encoding {
113        code: Code::Malloc(buf),
114    }
115}
116
117/// Types that have an Objective-C type encoding.
118///
119/// Unsafe because Objective-C will make assumptions about the type (like its
120/// size and alignment) from its encoding, so the implementer must verify that
121/// the encoding is accurate.
122pub unsafe trait Encode {
123    /// Returns the Objective-C type encoding for Self.
124    fn encode() -> Encoding;
125}
126
127macro_rules! encode_impls {
128    ($($t:ty : $s:expr,)*) => ($(
129        unsafe impl Encode for $t {
130            fn encode() -> Encoding { from_static_str($s) }
131        }
132    )*);
133}
134
135encode_impls!(
136    i8: "c",
137    i16: "s",
138    i32: "i",
139    i64: "q",
140    u8: "C",
141    u16: "S",
142    u32: "I",
143    u64: "Q",
144    f32: "f",
145    f64: "d",
146    bool: "B",
147    (): "v",
148    *mut c_char: "*",
149    *const c_char: "r*",
150    *mut c_void: "^v",
151    *const c_void: "r^v",
152    Sel: ":",
153);
154
155unsafe impl Encode for isize {
156    #[cfg(target_pointer_width = "32")]
157    fn encode() -> Encoding {
158        i32::encode()
159    }
160
161    #[cfg(target_pointer_width = "64")]
162    fn encode() -> Encoding {
163        i64::encode()
164    }
165}
166
167unsafe impl Encode for usize {
168    #[cfg(target_pointer_width = "32")]
169    fn encode() -> Encoding {
170        u32::encode()
171    }
172
173    #[cfg(target_pointer_width = "64")]
174    fn encode() -> Encoding {
175        u64::encode()
176    }
177}
178
179macro_rules! encode_message_impl {
180    ($code:expr, $name:ident) => (
181        encode_message_impl!($code, $name,);
182    );
183    ($code:expr, $name:ident, $($t:ident),*) => (
184        unsafe impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
185            fn encode() -> Encoding { from_static_str($code) }
186        }
187
188        unsafe impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
189            fn encode() -> Encoding { from_static_str($code) }
190        }
191
192        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
193            fn encode() -> Encoding { from_static_str($code) }
194        }
195
196        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
197            fn encode() -> Encoding { from_static_str($code) }
198        }
199
200        unsafe impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
201            fn encode() -> Encoding { from_static_str($code) }
202        }
203
204        unsafe impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
205            fn encode() -> Encoding { from_static_str($code) }
206        }
207    );
208}
209
210encode_message_impl!("@", Object);
211
212encode_message_impl!("#", Class);
213
214/// Types that represent a group of arguments, where each has an Objective-C
215/// type encoding.
216pub trait EncodeArguments {
217    /// The type as which the encodings for Self will be returned.
218    type Encs: AsRef<[Encoding]>;
219
220    /// Returns the Objective-C type encodings for Self.
221    fn encodings() -> Self::Encs;
222}
223
224macro_rules! count_idents {
225    () => (0);
226    ($a:ident) => (1);
227    ($a:ident, $($b:ident),+) => (1 + count_idents!($($b),*));
228}
229
230macro_rules! encode_args_impl {
231    ($($t:ident),*) => (
232        impl<$($t: Encode),*> EncodeArguments for ($($t,)*) {
233            type Encs = [Encoding; count_idents!($($t),*)];
234
235            fn encodings() -> Self::Encs {
236                [
237                    $($t::encode()),*
238                ]
239            }
240        }
241    );
242}
243
244encode_args_impl!();
245encode_args_impl!(A);
246encode_args_impl!(A, B);
247encode_args_impl!(A, B, C);
248encode_args_impl!(A, B, C, D);
249encode_args_impl!(A, B, C, D, E);
250encode_args_impl!(A, B, C, D, E, F);
251encode_args_impl!(A, B, C, D, E, F, G);
252encode_args_impl!(A, B, C, D, E, F, G, H);
253encode_args_impl!(A, B, C, D, E, F, G, H, I);
254encode_args_impl!(A, B, C, D, E, F, G, H, I, J);
255encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K);
256encode_args_impl!(A, B, C, D, E, F, G, H, I, J, K, L);
257
258#[cfg(test)]
259mod tests {
260    use super::{Encode, Encoding};
261    use crate::runtime::{Class, Object, Sel};
262
263    #[test]
264    fn test_encode() {
265        assert!(u32::encode().as_str() == "I");
266        assert!(<()>::encode().as_str() == "v");
267        assert!(<&Object>::encode().as_str() == "@");
268        assert!(<*mut Object>::encode().as_str() == "@");
269        assert!(<&Class>::encode().as_str() == "#");
270        assert!(Sel::encode().as_str() == ":");
271    }
272
273    #[test]
274    fn test_inline_encoding() {
275        let enc = unsafe { Encoding::from_str("C") };
276        assert!(enc.as_str() == "C");
277
278        let enc2 = enc.clone();
279        assert!(enc2 == enc);
280        assert!(enc2.as_str() == "C");
281    }
282
283    #[test]
284    fn test_owned_encoding() {
285        let s = "{Test=CCCCCCCCCCCCCCCCCCCCCCCCC}";
286        let enc = unsafe { Encoding::from_str(s) };
287        assert!(enc.as_str() == s);
288
289        let enc2 = enc.clone();
290        assert!(enc2 == enc);
291        assert!(enc2.as_str() == s);
292    }
293}