core_foundation/
array.rs

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