// Copyright 2013 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Heterogeneous immutable arrays. pub use core_foundation_sys::array::*; pub use core_foundation_sys::base::CFIndex; use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault}; use std::mem; use std::marker::PhantomData; use std::os::raw::c_void; use std::ptr; use ConcreteCFType; use base::{CFIndexConvertible, TCFType, CFRange}; use base::{FromVoid, ItemRef}; /// A heterogeneous immutable array. pub struct CFArray(CFArrayRef, PhantomData); impl Drop for CFArray { fn drop(&mut self) { unsafe { CFRelease(self.as_CFTypeRef()) } } } pub struct CFArrayIterator<'a, T: 'a> { array: &'a CFArray, index: CFIndex, len: CFIndex, } impl<'a, T: FromVoid> Iterator for CFArrayIterator<'a, T> { type Item = ItemRef<'a, T>; fn next(&mut self) -> Option> { if self.index >= self.len { None } else { let value = unsafe { self.array.get_unchecked(self.index) }; self.index += 1; Some(value) } } } impl<'a, T: FromVoid> ExactSizeIterator for CFArrayIterator<'a, T> { fn len(&self) -> usize { (self.array.len() - self.index) as usize } } impl_TCFType!(CFArray, CFArrayRef, CFArrayGetTypeID); impl_CFTypeDescription!(CFArray); unsafe impl ConcreteCFType for CFArray<*const c_void> {} impl CFArray { /// Creates a new `CFArray` with the given elements, which must implement `Copy`. pub fn from_copyable(elems: &[T]) -> CFArray where T: Copy { unsafe { let array_ref = CFArrayCreate(kCFAllocatorDefault, elems.as_ptr() as *const *const c_void, elems.len().to_CFIndex(), ptr::null()); TCFType::wrap_under_create_rule(array_ref) } } /// Creates a new `CFArray` with the given elements, which must be `CFType` objects. pub fn from_CFTypes(elems: &[T]) -> CFArray where T: TCFType { unsafe { let elems: Vec = elems.iter().map(|elem| elem.as_CFTypeRef()).collect(); let array_ref = CFArrayCreate(kCFAllocatorDefault, elems.as_ptr(), elems.len().to_CFIndex(), &kCFTypeArrayCallBacks); TCFType::wrap_under_create_rule(array_ref) } } #[inline] pub fn to_untyped(&self) -> CFArray { unsafe { CFArray::wrap_under_get_rule(self.0) } } /// Returns the same array, but with the type reset to void pointers. /// Equal to `to_untyped`, but is faster since it does not increment the retain count. #[inline] pub fn into_untyped(self) -> CFArray { let reference = self.0; mem::forget(self); unsafe { CFArray::wrap_under_create_rule(reference) } } /// Iterates over the elements of this `CFArray`. /// /// Careful; the loop body must wrap the reference properly. Generally, when array elements are /// Core Foundation objects (not always true), they need to be wrapped with /// `TCFType::wrap_under_get_rule()`. #[inline] pub fn iter<'a>(&'a self) -> CFArrayIterator<'a, T> { CFArrayIterator { array: self, index: 0, len: self.len(), } } #[inline] pub fn len(&self) -> CFIndex { unsafe { CFArrayGetCount(self.0) } } #[inline] pub unsafe fn get_unchecked<'a>(&'a self, index: CFIndex) -> ItemRef<'a, T> where T: FromVoid { T::from_void(CFArrayGetValueAtIndex(self.0, index)) } #[inline] pub fn get<'a>(&'a self, index: CFIndex) -> Option> where T: FromVoid { if index < self.len() { Some(unsafe { T::from_void(CFArrayGetValueAtIndex(self.0, index)) } ) } else { None } } pub fn get_values(&self, range: CFRange) -> Vec<*const c_void> { let mut vec = Vec::with_capacity(range.length as usize); unsafe { CFArrayGetValues(self.0, range, vec.as_mut_ptr()); vec.set_len(range.length as usize); vec } } pub fn get_all_values(&self) -> Vec<*const c_void> { self.get_values(CFRange { location: 0, length: self.len() }) } } impl<'a, T: FromVoid> IntoIterator for &'a CFArray { type Item = ItemRef<'a, T>; type IntoIter = CFArrayIterator<'a, T>; fn into_iter(self) -> CFArrayIterator<'a, T> { self.iter() } } #[cfg(test)] mod tests { use super::*; use std::mem; use base::CFType; #[test] fn to_untyped_correct_retain_count() { let array = CFArray::::from_CFTypes(&[]); assert_eq!(array.retain_count(), 1); let untyped_array = array.to_untyped(); assert_eq!(array.retain_count(), 2); assert_eq!(untyped_array.retain_count(), 2); mem::drop(array); assert_eq!(untyped_array.retain_count(), 1); } #[test] fn into_untyped() { let array = CFArray::::from_CFTypes(&[]); let array2 = array.to_untyped(); assert_eq!(array.retain_count(), 2); let untyped_array = array.into_untyped(); assert_eq!(untyped_array.retain_count(), 2); mem::drop(array2); assert_eq!(untyped_array.retain_count(), 1); } #[test] fn borrow() { use string::CFString; let string = CFString::from_static_string("bar"); assert_eq!(string.retain_count(), 1); let x; { let arr: CFArray = CFArray::from_CFTypes(&[string]); { let p = arr.get(0).unwrap(); assert_eq!(p.retain_count(), 1); } { x = arr.get(0).unwrap().clone(); assert_eq!(x.retain_count(), 2); assert_eq!(x.to_string(), "bar"); } } assert_eq!(x.retain_count(), 1); } #[test] fn iter_untyped_array() { use string::{CFString, CFStringRef}; use base::TCFTypeRef; let cf_string = CFString::from_static_string("bar"); let array: CFArray = CFArray::from_CFTypes(&[cf_string.clone()]).into_untyped(); let cf_strings = array.iter().map(|ptr| { unsafe { CFString::wrap_under_get_rule(CFStringRef::from_void_ptr(*ptr)) } }).collect::>(); let strings = cf_strings.iter().map(|s| s.to_string()).collect::>(); assert_eq!(cf_string.retain_count(), 3); assert_eq!(&strings[..], &["bar"]); } #[test] fn should_box_and_unbox() { use number::CFNumber; let n0 = CFNumber::from(0); let n1 = CFNumber::from(1); let n2 = CFNumber::from(2); let n3 = CFNumber::from(3); let n4 = CFNumber::from(4); let n5 = CFNumber::from(5); let arr = CFArray::from_CFTypes(&[ n0.as_CFType(), n1.as_CFType(), n2.as_CFType(), n3.as_CFType(), n4.as_CFType(), n5.as_CFType(), ]); assert_eq!( arr.get_all_values(), &[ n0.as_CFTypeRef(), n1.as_CFTypeRef(), n2.as_CFTypeRef(), n3.as_CFTypeRef(), n4.as_CFTypeRef(), n5.as_CFTypeRef() ] ); let mut sum = 0; let mut iter = arr.iter(); assert_eq!(iter.len(), 6); assert!(iter.next().is_some()); assert_eq!(iter.len(), 5); for elem in iter { let number: CFNumber = elem.downcast::().unwrap(); sum += number.to_i64().unwrap() } assert_eq!(sum, 15); for elem in arr.iter() { let number: CFNumber = elem.downcast::().unwrap(); sum += number.to_i64().unwrap() } assert_eq!(sum, 30); } }