// 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. use std; use std::fmt; use std::marker::PhantomData; use std::mem; use std::mem::ManuallyDrop; use std::ops::{Deref, DerefMut}; use std::os::raw::c_void; pub use core_foundation_sys::base::*; use string::CFString; use ConcreteCFType; pub trait CFIndexConvertible { /// Always use this method to construct a `CFIndex` value. It performs bounds checking to /// ensure the value is in range. fn to_CFIndex(self) -> CFIndex; } impl CFIndexConvertible for usize { #[inline] fn to_CFIndex(self) -> CFIndex { let max_CFIndex = CFIndex::max_value(); if self > (max_CFIndex as usize) { panic!("value out of range") } self as CFIndex } } declare_TCFType!{ /// Superclass of all Core Foundation objects. CFType, CFTypeRef } impl CFType { /// Try to downcast the `CFType` to a subclass. Checking if the instance is the /// correct subclass happens at runtime and `None` is returned if it is not the correct type. /// Works similar to [`Box::downcast`] and [`CFPropertyList::downcast`]. /// /// # Examples /// /// ``` /// # use core_foundation::string::CFString; /// # use core_foundation::boolean::CFBoolean; /// # use core_foundation::base::{CFType, TCFType}; /// # /// // Create a string. /// let string: CFString = CFString::from_static_string("FooBar"); /// // Cast it up to a CFType. /// let cf_type: CFType = string.as_CFType(); /// // Cast it down again. /// assert_eq!(cf_type.downcast::().unwrap().to_string(), "FooBar"); /// // Casting it to some other type will yield `None` /// assert!(cf_type.downcast::().is_none()); /// ``` /// /// ```compile_fail /// # use core_foundation::array::CFArray; /// # use core_foundation::base::TCFType; /// # use core_foundation::boolean::CFBoolean; /// # use core_foundation::string::CFString; /// # /// let boolean_array = CFArray::from_CFTypes(&[CFBoolean::true_value()]).into_CFType(); /// /// // This downcast is not allowed and causes compiler error, since it would cause undefined /// // behavior to access the elements of the array as a CFString: /// let invalid_string_array = boolean_array /// .downcast_into::>() /// .unwrap(); /// ``` /// /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast /// [`CFPropertyList::downcast`]: ../propertylist/struct.CFPropertyList.html#method.downcast #[inline] pub fn downcast(&self) -> Option { if self.instance_of::() { unsafe { let reference = T::Ref::from_void_ptr(self.0); Some(T::wrap_under_get_rule(reference)) } } else { None } } /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count. /// /// [`downcast`]: #method.downcast #[inline] pub fn downcast_into(self) -> Option { if self.instance_of::() { unsafe { let reference = T::Ref::from_void_ptr(self.0); mem::forget(self); Some(T::wrap_under_create_rule(reference)) } } else { None } } } impl fmt::Debug for CFType { /// Formats the value using [`CFCopyDescription`]. /// /// [`CFCopyDescription`]: https://developer.apple.com/documentation/corefoundation/1521252-cfcopydescription?language=objc fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let desc = unsafe { CFString::wrap_under_create_rule(CFCopyDescription(self.0)) }; desc.fmt(f) } } impl Clone for CFType { #[inline] fn clone(&self) -> CFType { unsafe { TCFType::wrap_under_get_rule(self.0) } } } impl PartialEq for CFType { #[inline] fn eq(&self, other: &CFType) -> bool { unsafe { CFEqual(self.as_CFTypeRef(), other.as_CFTypeRef()) != 0 } } } declare_TCFType!(CFAllocator, CFAllocatorRef); impl_TCFType!(CFAllocator, CFAllocatorRef, CFAllocatorGetTypeID); impl CFAllocator { #[inline] pub fn new(mut context: CFAllocatorContext) -> CFAllocator { unsafe { let allocator_ref = CFAllocatorCreate(kCFAllocatorDefault, &mut context); TCFType::wrap_under_create_rule(allocator_ref) } } } /// All Core Foundation types implement this trait. The associated type `Ref` specifies the /// associated Core Foundation type: e.g. for `CFType` this is `CFTypeRef`; for `CFArray` this is /// `CFArrayRef`. /// /// Most structs that implement this trait will do so via the [`impl_TCFType`] macro. /// /// [`impl_TCFType`]: ../macro.impl_TCFType.html pub trait TCFType { /// The reference type wrapped inside this type. type Ref: TCFTypeRef; /// Returns the object as its concrete TypeRef. fn as_concrete_TypeRef(&self) -> Self::Ref; /// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this /// when following Core Foundation's "Create Rule". The reference count is *not* bumped. unsafe fn wrap_under_create_rule(obj: Self::Ref) -> Self; /// Returns the type ID for this class. fn type_id() -> CFTypeID; /// Returns the object as a wrapped `CFType`. The reference count is incremented by one. #[inline] fn as_CFType(&self) -> CFType { unsafe { TCFType::wrap_under_get_rule(self.as_CFTypeRef()) } } /// Returns the object as a wrapped `CFType`. Consumes self and avoids changing the reference /// count. #[inline] fn into_CFType(self) -> CFType where Self: Sized, { let reference = self.as_CFTypeRef(); mem::forget(self); unsafe { TCFType::wrap_under_create_rule(reference) } } /// Returns the object as a raw `CFTypeRef`. The reference count is not adjusted. fn as_CFTypeRef(&self) -> CFTypeRef; /// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this /// when following Core Foundation's "Get Rule". The reference count *is* bumped. unsafe fn wrap_under_get_rule(reference: Self::Ref) -> Self; /// Returns the reference count of the object. It is unwise to do anything other than test /// whether the return value of this method is greater than zero. #[inline] fn retain_count(&self) -> CFIndex { unsafe { CFGetRetainCount(self.as_CFTypeRef()) } } /// Returns the type ID of this object. #[inline] fn type_of(&self) -> CFTypeID { unsafe { CFGetTypeID(self.as_CFTypeRef()) } } /// Writes a debugging version of this object on standard error. fn show(&self) { unsafe { CFShow(self.as_CFTypeRef()) } } /// Returns true if this value is an instance of another type. #[inline] fn instance_of(&self) -> bool { self.type_of() == OtherCFType::type_id() } } impl TCFType for CFType { type Ref = CFTypeRef; #[inline] fn as_concrete_TypeRef(&self) -> CFTypeRef { self.0 } #[inline] unsafe fn wrap_under_get_rule(reference: CFTypeRef) -> CFType { assert!(!reference.is_null(), "Attempted to create a NULL object."); let reference: CFTypeRef = CFRetain(reference); TCFType::wrap_under_create_rule(reference) } #[inline] fn as_CFTypeRef(&self) -> CFTypeRef { self.as_concrete_TypeRef() } #[inline] unsafe fn wrap_under_create_rule(obj: CFTypeRef) -> CFType { assert!(!obj.is_null(), "Attempted to create a NULL object."); CFType(obj) } #[inline] fn type_id() -> CFTypeID { // FIXME(pcwalton): Is this right? 0 } } /// 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) } } impl<'a, T: PartialEq> PartialEq for ItemRef<'a, T> { fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) } } /// A reference to a mutable element inside a container pub struct ItemMutRef<'a, T: 'a>(ManuallyDrop, PhantomData<&'a T>); impl<'a, T> Deref for ItemMutRef<'a, T> { type Target = T; fn deref(&self) -> &T { &self.0 } } impl<'a, T> DerefMut for ItemMutRef<'a, T> { fn deref_mut(&mut self) -> &mut T { &mut self.0 } } impl<'a, T: fmt::Debug> fmt::Debug for ItemMutRef<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.0.fmt(f) } } impl<'a, T: PartialEq> PartialEq for ItemMutRef<'a, T> { fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) } } /// A trait describing how to convert from the stored *mut c_void to the desired T pub unsafe trait FromMutVoid { unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> where Self: std::marker::Sized; } unsafe impl FromMutVoid for u32 { unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> { ItemMutRef(ManuallyDrop::new(x as u32), PhantomData) } } unsafe impl FromMutVoid for *const c_void { unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> { ItemMutRef(ManuallyDrop::new(x), PhantomData) } } unsafe impl FromMutVoid for T { unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> { ItemMutRef(ManuallyDrop::new(TCFType::wrap_under_create_rule(T::Ref::from_void_ptr(x))), 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<'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) } } /// 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::*; use std::mem; use boolean::CFBoolean; #[test] fn cftype_instance_of() { let string = CFString::from_static_string("foo"); let cftype = string.as_CFType(); assert!(cftype.instance_of::()); assert!(!cftype.instance_of::()); } #[test] fn as_cftype_retain_count() { let string = CFString::from_static_string("bar"); assert_eq!(string.retain_count(), 1); let cftype = string.as_CFType(); assert_eq!(cftype.retain_count(), 2); mem::drop(string); assert_eq!(cftype.retain_count(), 1); } #[test] fn into_cftype_retain_count() { let string = CFString::from_static_string("bar"); assert_eq!(string.retain_count(), 1); let cftype = string.into_CFType(); assert_eq!(cftype.retain_count(), 1); } #[test] fn as_cftype_and_downcast() { let string = CFString::from_static_string("bar"); let cftype = string.as_CFType(); let string2 = cftype.downcast::().unwrap(); assert_eq!(string2.to_string(), "bar"); assert_eq!(string.retain_count(), 3); assert_eq!(cftype.retain_count(), 3); assert_eq!(string2.retain_count(), 3); } #[test] fn into_cftype_and_downcast_into() { let string = CFString::from_static_string("bar"); let cftype = string.into_CFType(); let string2 = cftype.downcast_into::().unwrap(); assert_eq!(string2.to_string(), "bar"); assert_eq!(string2.retain_count(), 1); } }