From df7df6bb52adbc2b52adc0c5a0ac7db49a3fa451 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Mon, 22 Jan 2018 22:37:31 -0500 Subject: [PATCH] Add the ability to borrow values from the array. This uses borrowed values from the array to avoid having to Retain/Release during iteration. It also implements borrowing for all TCFType. --- core-foundation/src/array.rs | 75 +++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/core-foundation/src/array.rs b/core-foundation/src/array.rs index 76984c1..bb8f628 100644 --- a/core-foundation/src/array.rs +++ b/core-foundation/src/array.rs @@ -12,36 +12,52 @@ pub use core_foundation_sys::array::*; pub use core_foundation_sys::base::{CFIndex, CFRelease}; use core_foundation_sys::base::{CFTypeRef, kCFAllocatorDefault}; -use base::CFType; use libc::c_void; use std::mem; +use std::mem::ManuallyDrop; use std::marker::PhantomData; +use std; +use std::ops::Deref; +use std::fmt::{Debug, Formatter}; -use base::{CFIndexConvertible, TCFType, CFRange}; +use base::{CFIndexConvertible, TCFType, TCFTypeRef, CFRange}; /// A heterogeneous immutable array. pub struct CFArray(CFArrayRef, PhantomData); -/// A trait describing how to convert from the stored *const c_void to the desired T -pub unsafe trait FromVoid { - unsafe fn from_void(x: *const c_void) -> Self; +/// A reference to an element inside the array +pub struct ItemRef<'a, T: 'a>(ManuallyDrop, PhantomData<&'a T>); + +impl<'a, T> Deref for ItemRef<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } } -unsafe impl FromVoid for u32 { - unsafe fn from_void(x: *const c_void) -> u32 { - x as usize as u32 +impl<'a, T: Debug> Debug for ItemRef<'a, T> { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + self.0.fmt(f) } } -unsafe impl FromVoid for *const c_void { - unsafe fn from_void(x: *const c_void) -> *const c_void { - x +/// A trait describing how to convert from the stored *const c_void to the desired T +pub unsafe trait FromVoid { + unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> where Self: std::marker::Sized; +} + +unsafe impl FromVoid for u32 { + unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> { + // Functions like CGFontCopyTableTags treat the void*'s as u32's + // so we convert by casting directly + ItemRef(ManuallyDrop::new(x as u32), PhantomData) } } -unsafe impl FromVoid for CFType { - unsafe fn from_void(x: *const c_void) -> CFType { - TCFType::wrap_under_get_rule(mem::transmute(x)) +unsafe impl FromVoid for T { + unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> { + ItemRef(ManuallyDrop::new(TCFType::wrap_under_create_rule(T::Ref::from_void_ptr(x))), PhantomData) } } @@ -57,9 +73,9 @@ pub struct CFArrayIterator<'a, T: 'a> { } impl<'a, T: FromVoid> Iterator for CFArrayIterator<'a, T> { - type Item = T; + type Item = ItemRef<'a, T>; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option> { if self.index >= self.array.len() { None } else { @@ -127,7 +143,7 @@ impl CFArray { } #[inline] - pub fn get(&self, index: CFIndex) -> T where T: FromVoid { + pub fn get<'a>(&'a self, index: CFIndex) -> ItemRef<'a, T> where T: FromVoid { assert!(index < self.len()); unsafe { T::from_void(CFArrayGetValueAtIndex(self.0, index)) } } @@ -150,7 +166,7 @@ impl CFArray { } impl<'a, T: FromVoid> IntoIterator for &'a CFArray { - type Item = T; + type Item = ItemRef<'a, T>; type IntoIter = CFArrayIterator<'a, T>; fn into_iter(self) -> CFArrayIterator<'a, T> { @@ -162,6 +178,7 @@ impl<'a, T: FromVoid> IntoIterator for &'a CFArray { mod tests { use super::*; use std::mem; + use base::CFType; #[test] fn to_untyped_correct_retain_count() { @@ -189,6 +206,28 @@ mod tests { 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); + assert_eq!(p.retain_count(), 1); + } + { + x = arr.get(0).clone(); + assert_eq!(x.retain_count(), 2); + assert_eq!(x.to_string(), "bar"); + } + } + assert_eq!(x.retain_count(), 1); + } + #[test] fn should_box_and_unbox() { use number::CFNumber;