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', 'n', 'N', 'o', 'O', 'R', 'V', ];
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
32pub struct Encoding {
37 code: Code,
38}
39
40impl Encoding {
41 pub unsafe fn from_str(code: &str) -> Encoding {
44 from_str(code)
45 }
46
47 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 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
117pub unsafe trait Encode {
123 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
214pub trait EncodeArguments {
217 type Encs: AsRef<[Encoding]>;
219
220 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}