From 8e943b19b517680d73dd3c4328a18c6ab91be3e2 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Wed, 7 Feb 2018 10:50:28 -0500 Subject: [PATCH 1/2] Move FromVoid and ItemRef to base This will let us reuse them in dictionary.rs --- core-foundation/src/array.rs | 50 +++--------------------------------- core-foundation/src/base.rs | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/core-foundation/src/array.rs b/core-foundation/src/array.rs index 3db2e93..516b1da 100644 --- a/core-foundation/src/array.rs +++ b/core-foundation/src/array.rs @@ -13,61 +13,16 @@ 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::mem::ManuallyDrop; use std::marker::PhantomData; -use std; -use std::ops::Deref; use std::os::raw::c_void; -use std::fmt::{Debug, Formatter}; use ConcreteCFType; -use base::{CFIndexConvertible, TCFType, TCFTypeRef, CFRange}; +use base::{CFIndexConvertible, TCFType, CFRange}; +use base::{FromVoid, ItemRef}; /// A heterogeneous immutable array. pub struct CFArray(CFArrayRef, PhantomData); -/// 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 - } -} - -impl<'a, T: Debug> Debug for ItemRef<'a, T> { - fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { - self.0.fmt(f) - } -} - -/// 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 *const c_void { - unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> { - ItemRef(ManuallyDrop::new(x), PhantomData) - } -} - -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) - } -} - impl Drop for CFArray { fn drop(&mut self) { unsafe { CFRelease(self.as_CFTypeRef()) } @@ -250,6 +205,7 @@ mod tests { #[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(); diff --git a/core-foundation/src/base.rs b/core-foundation/src/base.rs index facdc18..d291962 100644 --- a/core-foundation/src/base.rs +++ b/core-foundation/src/base.rs @@ -7,8 +7,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std; use std::fmt; +use std::marker::PhantomData; use std::mem; +use std::mem::ManuallyDrop; +use std::ops::Deref; +use std::os::raw::c_void; + pub use core_foundation_sys::base::*; @@ -237,6 +243,47 @@ impl TCFType for CFType { } } +/// A reference to an element inside a container +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 + } +} + +impl<'a, T: fmt::Debug> fmt::Debug for ItemRef<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// 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 *const c_void { + unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> { + ItemRef(ManuallyDrop::new(x), PhantomData) + } +} + +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) + } +} #[cfg(test)] mod tests { From 97ec75c14de106e0d5ec2a31c02ed90a69dcf9ab Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Sat, 3 Feb 2018 12:18:19 -0500 Subject: [PATCH 2/2] Redo the interface of CFDictionary and CFMutableDictionary This is a pretty big change to take advantge of the type parameters. It simplifies things and makes them more safe. --- core-foundation/src/base.rs | 37 +++++- core-foundation/src/dictionary.rs | 181 +++++++++--------------------- core-foundation/src/lib.rs | 21 ++++ core-text/src/font_descriptor.rs | 8 +- 4 files changed, 111 insertions(+), 136 deletions(-) diff --git a/core-foundation/src/base.rs b/core-foundation/src/base.rs index d291962..7687510 100644 --- a/core-foundation/src/base.rs +++ b/core-foundation/src/base.rs @@ -15,7 +15,6 @@ use std::mem::ManuallyDrop; use std::ops::Deref; use std::os::raw::c_void; - pub use core_foundation_sys::base::*; use string::CFString; @@ -260,6 +259,12 @@ impl<'a, T: fmt::Debug> fmt::Debug for ItemRef<'a, T> { } } +impl<'a, T: PartialEq> PartialEq for ItemRef<'a, T> { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + /// 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; @@ -285,6 +290,36 @@ unsafe impl FromVoid for T { } } +/// A trait describing how to convert from the stored *const c_void to the desired T +pub unsafe trait ToVoid { + fn to_void(&self) -> *const c_void; +} + +unsafe impl ToVoid<*const c_void> for *const c_void { + fn to_void(&self) -> *const c_void { + *self + } +} + +unsafe impl<'a> ToVoid for &'a CFType { + fn to_void(&self) -> *const ::std::os::raw::c_void { + self.as_concrete_TypeRef().as_void_ptr() + } +} + +unsafe impl ToVoid for CFType { + fn to_void(&self) -> *const ::std::os::raw::c_void { + self.as_concrete_TypeRef().as_void_ptr() + } +} + +unsafe impl ToVoid for CFTypeRef { + fn to_void(&self) -> *const ::std::os::raw::c_void { + self.as_void_ptr() + } +} + + #[cfg(test)] mod tests { use super::*; diff --git a/core-foundation/src/dictionary.rs b/core-foundation/src/dictionary.rs index cf51788..10b9158 100644 --- a/core-foundation/src/dictionary.rs +++ b/core-foundation/src/dictionary.rs @@ -17,8 +17,8 @@ use std::os::raw::c_void; use std::ptr; use std::marker::PhantomData; - -use base::{CFType, CFIndexConvertible, TCFType, TCFTypeRef}; +use base::{ItemRef, FromVoid, ToVoid}; +use base::{CFIndexConvertible, TCFType}; // consume the type parameters with PhantomDatas pub struct CFDictionary(CFDictionaryRef, PhantomData, PhantomData); @@ -63,50 +63,30 @@ impl CFDictionary { } #[inline] - pub fn contains_key(&self, key: *const c_void) -> bool { - unsafe { CFDictionaryContainsKey(self.0, key) != 0 } - } - - /// Similar to `contains_key` but acts on a higher level, automatically converting from any - /// `TCFType` to the raw pointer of its concrete TypeRef. - #[inline] - pub fn contains_key2(&self, key: &K) -> bool where K: TCFType { - self.contains_key(key.as_concrete_TypeRef().as_void_ptr()) + pub fn contains_key(&self, key: &K) -> bool where K: ToVoid { + unsafe { CFDictionaryContainsKey(self.0, key.to_void()) != 0 } } #[inline] - pub fn find(&self, key: *const c_void) -> Option<*const c_void> { + pub fn find<'a, T: ToVoid>(&'a self, key: T) -> Option> where V: FromVoid, K: ToVoid { unsafe { let mut value: *const c_void = ptr::null(); - if CFDictionaryGetValueIfPresent(self.0, key, &mut value) != 0 { - Some(value) + if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 { + Some(V::from_void(value)) } else { None } } } - /// Similar to `find` but acts on a higher level, automatically converting from any `TCFType` - /// to the raw pointer of its concrete TypeRef. - #[inline] - pub fn find2(&self, key: &K) -> Option<*const c_void> where K: TCFType { - self.find(key.as_concrete_TypeRef().as_void_ptr()) - } - /// # Panics /// /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead /// of panicking. #[inline] - pub fn get(&self, key: *const c_void) -> *const c_void { - self.find(key).expect(&format!("No entry found for key {:p}", key)) - } - - /// A convenience function to retrieve `CFType` instances. - #[inline] - pub unsafe fn get_CFType(&self, key: *const c_void) -> CFType { - let value: CFTypeRef = mem::transmute(self.get(key)); - TCFType::wrap_under_get_rule(value) + pub fn get<'a, T: ToVoid>(&'a self, key: T) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid { + let ptr = key.to_void(); + self.find(key).expect(&format!("No entry found for key {:p}", ptr)) } pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) { @@ -124,15 +104,19 @@ impl CFDictionary { } } -declare_TCFType!{ - /// An mutable dictionary of key-value pairs. - CFMutableDictionary, CFMutableDictionaryRef +// consume the type parameters with PhantomDatas +pub struct CFMutableDictionary(CFMutableDictionaryRef, PhantomData, PhantomData); + +impl Drop for CFMutableDictionary { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } } -impl_TCFType!(CFMutableDictionary, CFMutableDictionaryRef, CFDictionaryGetTypeID); +impl_TCFType!(CFMutableDictionary, CFMutableDictionaryRef, CFDictionaryGetTypeID); impl_CFTypeDescription!(CFMutableDictionary); -impl CFMutableDictionary { +impl CFMutableDictionary { pub fn new() -> Self { Self::with_capacity(0) } @@ -154,12 +138,10 @@ impl CFMutableDictionary { } } - pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary { - let result = Self::with_capacity(pairs.len() as _); - unsafe { - for &(ref key, ref value) in pairs { - result.add(key.as_CFTypeRef(), value.as_CFTypeRef()); - } + pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary where K: ToVoid, V: ToVoid { + let mut result = Self::with_capacity(pairs.len() as _); + for &(ref key, ref value) in pairs { + result.add(key, value); } result } @@ -185,46 +167,26 @@ impl CFMutableDictionary { } } - /// Similar to `contains_key` but acts on a higher level, automatically converting from any - /// `TCFType` to the raw pointer of its concrete TypeRef. #[inline] - pub fn contains_key2(&self, key: &K) -> bool { - self.contains_key(key.as_concrete_TypeRef().as_void_ptr()) - } - - #[inline] - pub fn find(&self, key: *const c_void) -> Option<*const c_void> { + pub fn find<'a>(&'a self, key: &K) -> Option> where V: FromVoid, K: ToVoid { unsafe { let mut value: *const c_void = ptr::null(); - if CFDictionaryGetValueIfPresent(self.0, key, &mut value) != 0 { - Some(value) + if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 { + Some(V::from_void(value)) } else { None } } } - /// Similar to `find` but acts on a higher level, automatically converting from any `TCFType` - /// to the raw pointer of its concrete TypeRef. - #[inline] - pub fn find2(&self, key: &K) -> Option<*const c_void> { - self.find(key.as_concrete_TypeRef().as_void_ptr()) - } - /// # Panics /// /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead /// of panicking. #[inline] - pub fn get(&self, key: *const c_void) -> *const c_void { - self.find(key).expect(&format!("No entry found for key {:p}", key)) - } - - /// A convenience function to retrieve `CFType` instances. - #[inline] - pub unsafe fn get_CFType(&self, key: *const c_void) -> CFType { - let value: CFTypeRef = mem::transmute(self.get(key)); - TCFType::wrap_under_get_rule(value) + pub fn get<'a>(&'a self, key: &K) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid { + let ptr = key.to_void(); + self.find(&key).expect(&format!("No entry found for key {:p}", ptr)) } pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) { @@ -245,73 +207,30 @@ impl CFMutableDictionary { /// Adds the key-value pair to the dictionary if no such key already exist. #[inline] - pub unsafe fn add(&self, key: *const c_void, value: *const c_void) { - CFDictionaryAddValue(self.0, key, value) - } - - /// Similar to `add` but acts on a higher level, automatically converting from any `TCFType` - /// to the raw pointer of its concrete TypeRef. - #[inline] - pub fn add2(&self, key: &K, value: &V) { - unsafe { - self.add( - key.as_concrete_TypeRef().as_void_ptr(), - value.as_concrete_TypeRef().as_void_ptr(), - ) - } + pub fn add(&mut self, key: &K, value: &V) where K: ToVoid, V: ToVoid { + unsafe { CFDictionaryAddValue(self.0, key.to_void(), value.to_void()) } } /// Sets the value of the key in the dictionary. #[inline] - pub unsafe fn set(&self, key: *const c_void, value: *const c_void) { - CFDictionarySetValue(self.0, key, value) - } - - /// Similar to `set` but acts on a higher level, automatically converting from any `TCFType` - /// to the raw pointer of its concrete TypeRef. - #[inline] - pub fn set2(&self, key: &K, value: &V) { - unsafe { - self.set( - key.as_concrete_TypeRef().as_void_ptr(), - value.as_concrete_TypeRef().as_void_ptr(), - ) - } + pub fn set(&mut self, key: K, value: V) where K: ToVoid, V: ToVoid { + unsafe { CFDictionarySetValue(self.0, key.to_void(), value.to_void()) } } /// Replaces the value of the key in the dictionary. #[inline] - pub unsafe fn replace(&self, key: *const c_void, value: *const c_void) { - CFDictionaryReplaceValue(self.0, key, value) - } - - /// Similar to `replace` but acts on a higher level, automatically converting from any `TCFType` - /// to the raw pointer of its concrete TypeRef. - #[inline] - pub fn replace2(&self, key: &K, value: &V) { - unsafe { - self.replace( - key.as_concrete_TypeRef().as_void_ptr(), - value.as_concrete_TypeRef().as_void_ptr(), - ) - } + pub fn replace(&mut self, key: K, value: V) where K: ToVoid, V: ToVoid { + unsafe { CFDictionaryReplaceValue(self.0, key.to_void(), value.to_void()) } } /// Removes the value of the key from the dictionary. #[inline] - pub unsafe fn remove(&self, key: *const c_void) { - CFDictionaryRemoveValue(self.0, key); - } - - /// Similar to `remove` but acts on a higher level, automatically converting from any `TCFType` - /// to the raw pointer of its concrete TypeRef. - #[inline] - pub fn remove2(&self, key: &K) { - unsafe { self.remove(key.as_concrete_TypeRef().as_void_ptr()) } + pub fn remove(&mut self, key: K) where K: ToVoid { + unsafe { CFDictionaryRemoveValue(self.0, key.to_void()) } } #[inline] - pub fn remove_all(&self) { + pub fn remove_all(&mut self) { unsafe { CFDictionaryRemoveAllValues(self.0) } } } @@ -320,8 +239,8 @@ impl CFMutableDictionary { #[cfg(test)] pub mod test { use super::*; - use base::TCFType; - use boolean::{CFBoolean, CFBooleanRef}; + use base::{CFType, TCFType}; + use boolean::CFBoolean; use number::CFNumber; use string::CFString; @@ -355,17 +274,17 @@ pub mod test { let tru = CFBoolean::true_value(); let n42 = CFNumber::from(42); - let d = CFMutableDictionary::new(); - d.add2(&bar, &boo); - d.add2(&baz, &tru); - d.add2(&foo, &n42); + let mut d = CFMutableDictionary::::new(); + d.add(&bar, &boo.as_CFType()); + d.add(&baz, &tru.as_CFType()); + d.add(&foo, &n42.as_CFType()); assert_eq!(d.len(), 3); let (v1, v2) = d.get_keys_and_values(); assert!(v1 == &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]); assert!(v2 == &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]); - d.remove2(&baz); + d.remove(baz); assert_eq!(d.len(), 2); let (v1, v2) = d.get_keys_and_values(); @@ -377,7 +296,7 @@ pub mod test { } #[test] - fn dict_find2_and_contains_key2() { + fn dict_find_and_contains_key() { let dict = CFDictionary::from_CFType_pairs(&[ ( CFString::from_static_string("hello"), @@ -387,11 +306,11 @@ pub mod test { let key = CFString::from_static_string("hello"); let invalid_key = CFString::from_static_string("foobar"); - assert!(dict.contains_key2(&key)); - assert!(!dict.contains_key2(&invalid_key)); + assert!(dict.contains_key(&key)); + assert!(!dict.contains_key(&invalid_key)); - let value = unsafe { CFBoolean::wrap_under_get_rule(dict.find2(&key).unwrap() as CFBooleanRef) }; + let value = dict.find(&key).unwrap().clone(); assert_eq!(value, CFBoolean::true_value()); - assert_eq!(dict.find2(&invalid_key), None); + assert_eq!(dict.find(&invalid_key), None); } } diff --git a/core-foundation/src/lib.rs b/core-foundation/src/lib.rs index ef9f53e..34cc3fc 100644 --- a/core-foundation/src/lib.rs +++ b/core-foundation/src/lib.rs @@ -98,6 +98,27 @@ macro_rules! impl_TCFType { impl Eq for $ty { } + unsafe impl<'a> $crate::base::ToVoid<$ty> for &'a $ty { + fn to_void(&self) -> *const ::std::os::raw::c_void { + use $crate::base::TCFTypeRef; + self.as_concrete_TypeRef().as_void_ptr() + } + } + + unsafe impl $crate::base::ToVoid<$ty> for $ty { + fn to_void(&self) -> *const ::std::os::raw::c_void { + use $crate::base::TCFTypeRef; + self.as_concrete_TypeRef().as_void_ptr() + } + } + + unsafe impl $crate::base::ToVoid<$ty> for $ty_ref { + fn to_void(&self) -> *const ::std::os::raw::c_void { + use $crate::base::TCFTypeRef; + self.as_void_ptr() + } + } + }; (@Phantom $x:ident) => { ::std::marker::PhantomData }; diff --git a/core-text/src/font_descriptor.rs b/core-text/src/font_descriptor.rs index 904e802..7996e8e 100644 --- a/core-text/src/font_descriptor.rs +++ b/core-text/src/font_descriptor.rs @@ -115,7 +115,7 @@ impl StylisticClassAccessors for CTFontStylisticClass { pub type CTFontAttributes = CFDictionary; -pub type CTFontTraits = CFDictionary; +pub type CTFontTraits = CFDictionary; pub trait TraitAccessors { fn symbolic_traits(&self) -> CTFontSymbolicTraits; @@ -125,12 +125,12 @@ pub trait TraitAccessors { } trait TraitAccessorPrivate { - unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber; + fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber; } impl TraitAccessorPrivate for CTFontTraits { - unsafe fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber { - let cftype = self.get_CFType(mem::transmute(key)); + fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber { + let cftype = self.get(key); cftype.downcast::().unwrap() }