1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9 
10 //! Heterogeneous immutable arrays.
11 
12 pub use core_foundation_sys::array::*;
13 pub use core_foundation_sys::base::CFIndex;
14 use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault};
15 use std::mem;
16 use std::marker::PhantomData;
17 use std::os::raw::c_void;
18 use std::ptr;
19 use ConcreteCFType;
20 
21 use base::{CFIndexConvertible, TCFType, CFRange};
22 use base::{FromVoid, ItemRef};
23 
24 /// A heterogeneous immutable array.
25 pub struct CFArray<T = *const c_void>(CFArrayRef, PhantomData<T>);
26 
27 impl<T> Drop for CFArray<T> {
drop(&mut self)28     fn drop(&mut self) {
29         unsafe { CFRelease(self.as_CFTypeRef()) }
30     }
31 }
32 
33 pub struct CFArrayIterator<'a, T: 'a> {
34     array: &'a CFArray<T>,
35     index: CFIndex,
36     len: CFIndex,
37 }
38 
39 impl<'a, T: FromVoid> Iterator for CFArrayIterator<'a, T> {
40     type Item = ItemRef<'a, T>;
41 
next(&mut self) -> Option<ItemRef<'a, T>>42     fn next(&mut self) -> Option<ItemRef<'a, T>> {
43         if self.index >= self.len {
44             None
45         } else {
46             let value = unsafe { self.array.get_unchecked(self.index) };
47             self.index += 1;
48             Some(value)
49         }
50     }
51 }
52 
53 impl<'a, T: FromVoid> ExactSizeIterator for CFArrayIterator<'a, T> {
len(&self) -> usize54     fn len(&self) -> usize {
55         (self.array.len() - self.index) as usize
56     }
57 }
58 
59 impl_TCFType!(CFArray<T>, CFArrayRef, CFArrayGetTypeID);
60 impl_CFTypeDescription!(CFArray<T>);
61 
62 unsafe impl ConcreteCFType for CFArray<*const c_void> {}
63 
64 impl<T> CFArray<T> {
65     /// Creates a new `CFArray` with the given elements, which must implement `Copy`.
from_copyable(elems: &[T]) -> CFArray<T> where T: Copy66     pub fn from_copyable(elems: &[T]) -> CFArray<T> where T: Copy {
67         unsafe {
68             let array_ref = CFArrayCreate(kCFAllocatorDefault,
69                                           elems.as_ptr() as *const *const c_void,
70                                           elems.len().to_CFIndex(),
71                                           ptr::null());
72             TCFType::wrap_under_create_rule(array_ref)
73         }
74     }
75 
76     /// Creates a new `CFArray` with the given elements, which must be `CFType` objects.
from_CFTypes(elems: &[T]) -> CFArray<T> where T: TCFType77     pub fn from_CFTypes(elems: &[T]) -> CFArray<T> where T: TCFType {
78         unsafe {
79             let elems: Vec<CFTypeRef> = elems.iter().map(|elem| elem.as_CFTypeRef()).collect();
80             let array_ref = CFArrayCreate(kCFAllocatorDefault,
81                                           elems.as_ptr(),
82                                           elems.len().to_CFIndex(),
83                                           &kCFTypeArrayCallBacks);
84             TCFType::wrap_under_create_rule(array_ref)
85         }
86     }
87 
88     #[inline]
to_untyped(&self) -> CFArray89     pub fn to_untyped(&self) -> CFArray {
90         unsafe { CFArray::wrap_under_get_rule(self.0) }
91     }
92 
93     /// Returns the same array, but with the type reset to void pointers.
94     /// Equal to `to_untyped`, but is faster since it does not increment the retain count.
95     #[inline]
into_untyped(self) -> CFArray96     pub fn into_untyped(self) -> CFArray {
97         let reference = self.0;
98         mem::forget(self);
99         unsafe { CFArray::wrap_under_create_rule(reference) }
100     }
101 
102     /// Iterates over the elements of this `CFArray`.
103     ///
104     /// Careful; the loop body must wrap the reference properly. Generally, when array elements are
105     /// Core Foundation objects (not always true), they need to be wrapped with
106     /// `TCFType::wrap_under_get_rule()`.
107     #[inline]
iter<'a>(&'a self) -> CFArrayIterator<'a, T>108     pub fn iter<'a>(&'a self) -> CFArrayIterator<'a, T> {
109         CFArrayIterator {
110             array: self,
111             index: 0,
112             len: self.len(),
113         }
114     }
115 
116     #[inline]
len(&self) -> CFIndex117     pub fn len(&self) -> CFIndex {
118         unsafe {
119             CFArrayGetCount(self.0)
120         }
121     }
122 
123     #[inline]
get_unchecked<'a>(&'a self, index: CFIndex) -> ItemRef<'a, T> where T: FromVoid124     pub unsafe fn get_unchecked<'a>(&'a self, index: CFIndex) -> ItemRef<'a, T> where T: FromVoid {
125         T::from_void(CFArrayGetValueAtIndex(self.0, index))
126     }
127 
128     #[inline]
get<'a>(&'a self, index: CFIndex) -> Option<ItemRef<'a, T>> where T: FromVoid129     pub fn get<'a>(&'a self, index: CFIndex) -> Option<ItemRef<'a, T>> where T: FromVoid {
130         if index < self.len() {
131             Some(unsafe { T::from_void(CFArrayGetValueAtIndex(self.0, index)) } )
132         } else {
133             None
134         }
135     }
136 
get_values(&self, range: CFRange) -> Vec<*const c_void>137     pub fn get_values(&self, range: CFRange) -> Vec<*const c_void> {
138         let mut vec = Vec::with_capacity(range.length as usize);
139         unsafe {
140             CFArrayGetValues(self.0, range, vec.as_mut_ptr());
141             vec.set_len(range.length as usize);
142             vec
143         }
144     }
145 
get_all_values(&self) -> Vec<*const c_void>146     pub fn get_all_values(&self) -> Vec<*const c_void> {
147         self.get_values(CFRange {
148             location: 0,
149             length: self.len()
150         })
151     }
152 }
153 
154 impl<'a, T: FromVoid> IntoIterator for &'a CFArray<T> {
155     type Item = ItemRef<'a, T>;
156     type IntoIter = CFArrayIterator<'a, T>;
157 
into_iter(self) -> CFArrayIterator<'a, T>158     fn into_iter(self) -> CFArrayIterator<'a, T> {
159         self.iter()
160     }
161 }
162 
163 #[cfg(test)]
164 mod tests {
165     use super::*;
166     use std::mem;
167     use base::CFType;
168 
169     #[test]
to_untyped_correct_retain_count()170     fn to_untyped_correct_retain_count() {
171         let array = CFArray::<CFType>::from_CFTypes(&[]);
172         assert_eq!(array.retain_count(), 1);
173 
174         let untyped_array = array.to_untyped();
175         assert_eq!(array.retain_count(), 2);
176         assert_eq!(untyped_array.retain_count(), 2);
177 
178         mem::drop(array);
179         assert_eq!(untyped_array.retain_count(), 1);
180     }
181 
182     #[test]
into_untyped()183     fn into_untyped() {
184         let array = CFArray::<CFType>::from_CFTypes(&[]);
185         let array2 = array.to_untyped();
186         assert_eq!(array.retain_count(), 2);
187 
188         let untyped_array = array.into_untyped();
189         assert_eq!(untyped_array.retain_count(), 2);
190 
191         mem::drop(array2);
192         assert_eq!(untyped_array.retain_count(), 1);
193     }
194 
195     #[test]
borrow()196     fn borrow() {
197         use string::CFString;
198 
199         let string = CFString::from_static_string("bar");
200         assert_eq!(string.retain_count(), 1);
201         let x;
202         {
203             let arr: CFArray<CFString> = CFArray::from_CFTypes(&[string]);
204             {
205                 let p = arr.get(0).unwrap();
206                 assert_eq!(p.retain_count(), 1);
207             }
208             {
209                 x = arr.get(0).unwrap().clone();
210                 assert_eq!(x.retain_count(), 2);
211                 assert_eq!(x.to_string(), "bar");
212             }
213         }
214         assert_eq!(x.retain_count(), 1);
215     }
216 
217     #[test]
iter_untyped_array()218     fn iter_untyped_array() {
219         use string::{CFString, CFStringRef};
220         use base::TCFTypeRef;
221 
222         let cf_string = CFString::from_static_string("bar");
223         let array: CFArray = CFArray::from_CFTypes(&[cf_string.clone()]).into_untyped();
224 
225         let cf_strings = array.iter().map(|ptr| {
226             unsafe { CFString::wrap_under_get_rule(CFStringRef::from_void_ptr(*ptr)) }
227         }).collect::<Vec<_>>();
228         let strings = cf_strings.iter().map(|s| s.to_string()).collect::<Vec<_>>();
229         assert_eq!(cf_string.retain_count(), 3);
230         assert_eq!(&strings[..], &["bar"]);
231     }
232 
233     #[test]
should_box_and_unbox()234     fn should_box_and_unbox() {
235         use number::CFNumber;
236 
237         let n0 = CFNumber::from(0);
238         let n1 = CFNumber::from(1);
239         let n2 = CFNumber::from(2);
240         let n3 = CFNumber::from(3);
241         let n4 = CFNumber::from(4);
242         let n5 = CFNumber::from(5);
243 
244         let arr = CFArray::from_CFTypes(&[
245             n0.as_CFType(),
246             n1.as_CFType(),
247             n2.as_CFType(),
248             n3.as_CFType(),
249             n4.as_CFType(),
250             n5.as_CFType(),
251         ]);
252 
253         assert_eq!(
254             arr.get_all_values(),
255             &[
256                 n0.as_CFTypeRef(),
257                 n1.as_CFTypeRef(),
258                 n2.as_CFTypeRef(),
259                 n3.as_CFTypeRef(),
260                 n4.as_CFTypeRef(),
261                 n5.as_CFTypeRef()
262             ]
263         );
264 
265         let mut sum = 0;
266 
267         let mut iter = arr.iter();
268         assert_eq!(iter.len(), 6);
269         assert!(iter.next().is_some());
270         assert_eq!(iter.len(), 5);
271 
272         for elem in iter {
273             let number: CFNumber = elem.downcast::<CFNumber>().unwrap();
274             sum += number.to_i64().unwrap()
275         }
276 
277         assert_eq!(sum, 15);
278 
279         for elem in arr.iter() {
280             let number: CFNumber = elem.downcast::<CFNumber>().unwrap();
281             sum += number.to_i64().unwrap()
282         }
283 
284         assert_eq!(sum, 30);
285     }
286 }
287