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