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 use std;
11 use std::fmt;
12 use std::marker::PhantomData;
13 use std::mem;
14 use std::mem::ManuallyDrop;
15 use std::ops::{Deref, DerefMut};
16 use std::os::raw::c_void;
17 
18 pub use core_foundation_sys::base::*;
19 
20 use string::CFString;
21 use ConcreteCFType;
22 
23 pub trait CFIndexConvertible {
24     /// Always use this method to construct a `CFIndex` value. It performs bounds checking to
25     /// ensure the value is in range.
to_CFIndex(self) -> CFIndex26     fn to_CFIndex(self) -> CFIndex;
27 }
28 
29 impl CFIndexConvertible for usize {
30     #[inline]
to_CFIndex(self) -> CFIndex31     fn to_CFIndex(self) -> CFIndex {
32         let max_CFIndex = CFIndex::max_value();
33         if self > (max_CFIndex as usize) {
34             panic!("value out of range")
35         }
36         self as CFIndex
37     }
38 }
39 
40 declare_TCFType!{
41     /// Superclass of all Core Foundation objects.
42     CFType, CFTypeRef
43 }
44 
45 impl CFType {
46     /// Try to downcast the `CFType` to a subclass. Checking if the instance is the
47     /// correct subclass happens at runtime and `None` is returned if it is not the correct type.
48     /// Works similar to [`Box::downcast`] and [`CFPropertyList::downcast`].
49     ///
50     /// # Examples
51     ///
52     /// ```
53     /// # use core_foundation::string::CFString;
54     /// # use core_foundation::boolean::CFBoolean;
55     /// # use core_foundation::base::{CFType, TCFType};
56     /// #
57     /// // Create a string.
58     /// let string: CFString = CFString::from_static_string("FooBar");
59     /// // Cast it up to a CFType.
60     /// let cf_type: CFType = string.as_CFType();
61     /// // Cast it down again.
62     /// assert_eq!(cf_type.downcast::<CFString>().unwrap().to_string(), "FooBar");
63     /// // Casting it to some other type will yield `None`
64     /// assert!(cf_type.downcast::<CFBoolean>().is_none());
65     /// ```
66     ///
67     /// ```compile_fail
68     /// # use core_foundation::array::CFArray;
69     /// # use core_foundation::base::TCFType;
70     /// # use core_foundation::boolean::CFBoolean;
71     /// # use core_foundation::string::CFString;
72     /// #
73     /// let boolean_array = CFArray::from_CFTypes(&[CFBoolean::true_value()]).into_CFType();
74     ///
75     /// // This downcast is not allowed and causes compiler error, since it would cause undefined
76     /// // behavior to access the elements of the array as a CFString:
77     /// let invalid_string_array = boolean_array
78     ///     .downcast_into::<CFArray<CFString>>()
79     ///     .unwrap();
80     /// ```
81     ///
82     /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast
83     /// [`CFPropertyList::downcast`]: ../propertylist/struct.CFPropertyList.html#method.downcast
84     #[inline]
downcast<T: ConcreteCFType>(&self) -> Option<T>85     pub fn downcast<T: ConcreteCFType>(&self) -> Option<T> {
86         if self.instance_of::<T>() {
87             unsafe {
88                 let reference = T::Ref::from_void_ptr(self.0);
89                 Some(T::wrap_under_get_rule(reference))
90             }
91         } else {
92             None
93         }
94     }
95 
96     /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count.
97     ///
98     /// [`downcast`]: #method.downcast
99     #[inline]
downcast_into<T: ConcreteCFType>(self) -> Option<T>100     pub fn downcast_into<T: ConcreteCFType>(self) -> Option<T> {
101         if self.instance_of::<T>() {
102             unsafe {
103                 let reference = T::Ref::from_void_ptr(self.0);
104                 mem::forget(self);
105                 Some(T::wrap_under_create_rule(reference))
106             }
107         } else {
108             None
109         }
110     }
111 }
112 
113 impl fmt::Debug for CFType {
114    /// Formats the value using [`CFCopyDescription`].
115    ///
116    /// [`CFCopyDescription`]: https://developer.apple.com/documentation/corefoundation/1521252-cfcopydescription?language=objc
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result117     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118         let desc = unsafe {
119             CFString::wrap_under_create_rule(CFCopyDescription(self.0))
120         };
121         desc.fmt(f)
122     }
123 }
124 
125 impl Clone for CFType {
126     #[inline]
clone(&self) -> CFType127     fn clone(&self) -> CFType {
128         unsafe {
129             TCFType::wrap_under_get_rule(self.0)
130         }
131     }
132 }
133 
134 impl PartialEq for CFType {
135     #[inline]
eq(&self, other: &CFType) -> bool136     fn eq(&self, other: &CFType) -> bool {
137         unsafe {
138             CFEqual(self.as_CFTypeRef(), other.as_CFTypeRef()) != 0
139         }
140     }
141 }
142 
143 declare_TCFType!(CFAllocator, CFAllocatorRef);
144 impl_TCFType!(CFAllocator, CFAllocatorRef, CFAllocatorGetTypeID);
145 
146 impl CFAllocator {
147     #[inline]
new(mut context: CFAllocatorContext) -> CFAllocator148     pub fn new(mut context: CFAllocatorContext) -> CFAllocator {
149         unsafe {
150             let allocator_ref = CFAllocatorCreate(kCFAllocatorDefault, &mut context);
151             TCFType::wrap_under_create_rule(allocator_ref)
152         }
153     }
154 }
155 
156 
157 /// All Core Foundation types implement this trait. The associated type `Ref` specifies the
158 /// associated Core Foundation type: e.g. for `CFType` this is `CFTypeRef`; for `CFArray` this is
159 /// `CFArrayRef`.
160 ///
161 /// Most structs that implement this trait will do so via the [`impl_TCFType`] macro.
162 ///
163 /// [`impl_TCFType`]: ../macro.impl_TCFType.html
164 pub trait TCFType {
165     /// The reference type wrapped inside this type.
166     type Ref: TCFTypeRef;
167 
168     /// Returns the object as its concrete TypeRef.
as_concrete_TypeRef(&self) -> Self::Ref169     fn as_concrete_TypeRef(&self) -> Self::Ref;
170 
171     /// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this
172     /// when following Core Foundation's "Create Rule". The reference count is *not* bumped.
wrap_under_create_rule(obj: Self::Ref) -> Self173     unsafe fn wrap_under_create_rule(obj: Self::Ref) -> Self;
174 
175     /// Returns the type ID for this class.
type_id() -> CFTypeID176     fn type_id() -> CFTypeID;
177 
178     /// Returns the object as a wrapped `CFType`. The reference count is incremented by one.
179     #[inline]
as_CFType(&self) -> CFType180     fn as_CFType(&self) -> CFType {
181         unsafe {
182             TCFType::wrap_under_get_rule(self.as_CFTypeRef())
183         }
184     }
185 
186     /// Returns the object as a wrapped `CFType`. Consumes self and avoids changing the reference
187     /// count.
188     #[inline]
into_CFType(self) -> CFType where Self: Sized,189     fn into_CFType(self) -> CFType
190     where
191         Self: Sized,
192     {
193         let reference = self.as_CFTypeRef();
194         mem::forget(self);
195         unsafe { TCFType::wrap_under_create_rule(reference) }
196     }
197 
198     /// Returns the object as a raw `CFTypeRef`. The reference count is not adjusted.
as_CFTypeRef(&self) -> CFTypeRef199     fn as_CFTypeRef(&self) -> CFTypeRef;
200 
201     /// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this
202     /// when following Core Foundation's "Get Rule". The reference count *is* bumped.
wrap_under_get_rule(reference: Self::Ref) -> Self203     unsafe fn wrap_under_get_rule(reference: Self::Ref) -> Self;
204 
205     /// Returns the reference count of the object. It is unwise to do anything other than test
206     /// whether the return value of this method is greater than zero.
207     #[inline]
retain_count(&self) -> CFIndex208     fn retain_count(&self) -> CFIndex {
209         unsafe {
210             CFGetRetainCount(self.as_CFTypeRef())
211         }
212     }
213 
214     /// Returns the type ID of this object.
215     #[inline]
type_of(&self) -> CFTypeID216     fn type_of(&self) -> CFTypeID {
217         unsafe {
218             CFGetTypeID(self.as_CFTypeRef())
219         }
220     }
221 
222     /// Writes a debugging version of this object on standard error.
show(&self)223     fn show(&self) {
224         unsafe {
225             CFShow(self.as_CFTypeRef())
226         }
227     }
228 
229     /// Returns true if this value is an instance of another type.
230     #[inline]
instance_of<OtherCFType: TCFType>(&self) -> bool231     fn instance_of<OtherCFType: TCFType>(&self) -> bool {
232         self.type_of() == OtherCFType::type_id()
233     }
234 }
235 
236 impl TCFType for CFType {
237     type Ref = CFTypeRef;
238 
239     #[inline]
as_concrete_TypeRef(&self) -> CFTypeRef240     fn as_concrete_TypeRef(&self) -> CFTypeRef {
241         self.0
242     }
243 
244     #[inline]
wrap_under_get_rule(reference: CFTypeRef) -> CFType245     unsafe fn wrap_under_get_rule(reference: CFTypeRef) -> CFType {
246         let reference: CFTypeRef = CFRetain(reference);
247         TCFType::wrap_under_create_rule(reference)
248     }
249 
250     #[inline]
as_CFTypeRef(&self) -> CFTypeRef251     fn as_CFTypeRef(&self) -> CFTypeRef {
252         self.as_concrete_TypeRef()
253     }
254 
255     #[inline]
wrap_under_create_rule(obj: CFTypeRef) -> CFType256     unsafe fn wrap_under_create_rule(obj: CFTypeRef) -> CFType {
257         CFType(obj)
258     }
259 
260     #[inline]
type_id() -> CFTypeID261     fn type_id() -> CFTypeID {
262         // FIXME(pcwalton): Is this right?
263         0
264     }
265 }
266 
267 /// A reference to an element inside a container
268 pub struct ItemRef<'a, T: 'a>(ManuallyDrop<T>, PhantomData<&'a T>);
269 
270 impl<'a, T> Deref for ItemRef<'a, T> {
271     type Target = T;
272 
deref(&self) -> &T273     fn deref(&self) -> &T {
274         &self.0
275     }
276 }
277 
278 impl<'a, T: fmt::Debug> fmt::Debug for ItemRef<'a, T> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>279     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
280         self.0.fmt(f)
281     }
282 }
283 
284 impl<'a, T: PartialEq> PartialEq for ItemRef<'a, T> {
eq(&self, other: &Self) -> bool285     fn eq(&self, other: &Self) -> bool {
286         self.0.eq(&other.0)
287     }
288 }
289 
290 /// A reference to a mutable element inside a container
291 pub struct ItemMutRef<'a, T: 'a>(ManuallyDrop<T>, PhantomData<&'a T>);
292 
293 impl<'a, T> Deref for ItemMutRef<'a, T> {
294     type Target = T;
295 
deref(&self) -> &T296     fn deref(&self) -> &T {
297         &self.0
298     }
299 }
300 
301 impl<'a, T> DerefMut for ItemMutRef<'a, T> {
deref_mut(&mut self) -> &mut T302     fn deref_mut(&mut self) -> &mut T {
303         &mut self.0
304     }
305 }
306 
307 impl<'a, T: fmt::Debug> fmt::Debug for ItemMutRef<'a, T> {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>308     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
309         self.0.fmt(f)
310     }
311 }
312 
313 impl<'a, T: PartialEq> PartialEq for ItemMutRef<'a, T> {
eq(&self, other: &Self) -> bool314     fn eq(&self, other: &Self) -> bool {
315         self.0.eq(&other.0)
316     }
317 }
318 
319 /// A trait describing how to convert from the stored *mut c_void to the desired T
320 pub unsafe trait FromMutVoid {
from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> where Self: std::marker::Sized321     unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> where Self: std::marker::Sized;
322 }
323 
324 unsafe impl FromMutVoid for u32 {
from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self>325     unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> {
326         ItemMutRef(ManuallyDrop::new(x as u32), PhantomData)
327     }
328 }
329 
330 unsafe impl FromMutVoid for *const c_void {
from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self>331     unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> {
332         ItemMutRef(ManuallyDrop::new(x), PhantomData)
333     }
334 }
335 
336 unsafe impl<T: TCFType> FromMutVoid for T {
from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self>337     unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> {
338         ItemMutRef(ManuallyDrop::new(TCFType::wrap_under_create_rule(T::Ref::from_void_ptr(x))), PhantomData)
339     }
340 }
341 
342 /// A trait describing how to convert from the stored *const c_void to the desired T
343 pub unsafe trait FromVoid {
from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> where Self: std::marker::Sized344     unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> where Self: std::marker::Sized;
345 }
346 
347 unsafe impl FromVoid for u32 {
from_void<'a>(x: *const c_void) -> ItemRef<'a, Self>348     unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> {
349         // Functions like CGFontCopyTableTags treat the void*'s as u32's
350         // so we convert by casting directly
351         ItemRef(ManuallyDrop::new(x as u32), PhantomData)
352     }
353 }
354 
355 unsafe impl FromVoid for *const c_void {
from_void<'a>(x: *const c_void) -> ItemRef<'a, Self>356     unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> {
357         ItemRef(ManuallyDrop::new(x), PhantomData)
358     }
359 }
360 
361 unsafe impl<T: TCFType> FromVoid for T {
from_void<'a>(x: *const c_void) -> ItemRef<'a, Self>362     unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> {
363         ItemRef(ManuallyDrop::new(TCFType::wrap_under_create_rule(T::Ref::from_void_ptr(x))), PhantomData)
364     }
365 }
366 
367 /// A trait describing how to convert from the stored *const c_void to the desired T
368 pub unsafe trait ToVoid<T> {
to_void(&self) -> *const c_void369     fn to_void(&self) -> *const c_void;
370 }
371 
372 unsafe impl ToVoid<*const c_void> for *const c_void {
to_void(&self) -> *const c_void373     fn to_void(&self) -> *const c_void {
374         *self
375     }
376 }
377 
378 unsafe impl<'a> ToVoid<CFType> for &'a CFType {
to_void(&self) -> *const ::std::os::raw::c_void379     fn to_void(&self) -> *const ::std::os::raw::c_void {
380         self.as_concrete_TypeRef().as_void_ptr()
381     }
382 }
383 
384 unsafe impl ToVoid<CFType> for CFType {
to_void(&self) -> *const ::std::os::raw::c_void385     fn to_void(&self) -> *const ::std::os::raw::c_void {
386         self.as_concrete_TypeRef().as_void_ptr()
387     }
388 }
389 
390 unsafe impl ToVoid<CFType> for CFTypeRef {
to_void(&self) -> *const ::std::os::raw::c_void391     fn to_void(&self) -> *const ::std::os::raw::c_void {
392         self.as_void_ptr()
393     }
394 }
395 
396 
397 #[cfg(test)]
398 mod tests {
399     use super::*;
400     use std::mem;
401     use boolean::CFBoolean;
402 
403     #[test]
cftype_instance_of()404     fn cftype_instance_of() {
405         let string = CFString::from_static_string("foo");
406         let cftype = string.as_CFType();
407 
408         assert!(cftype.instance_of::<CFString>());
409         assert!(!cftype.instance_of::<CFBoolean>());
410     }
411 
412     #[test]
as_cftype_retain_count()413     fn as_cftype_retain_count() {
414         let string = CFString::from_static_string("bar");
415         assert_eq!(string.retain_count(), 1);
416         let cftype = string.as_CFType();
417         assert_eq!(cftype.retain_count(), 2);
418         mem::drop(string);
419         assert_eq!(cftype.retain_count(), 1);
420     }
421 
422     #[test]
into_cftype_retain_count()423     fn into_cftype_retain_count() {
424         let string = CFString::from_static_string("bar");
425         assert_eq!(string.retain_count(), 1);
426         let cftype = string.into_CFType();
427         assert_eq!(cftype.retain_count(), 1);
428     }
429 
430     #[test]
as_cftype_and_downcast()431     fn as_cftype_and_downcast() {
432         let string = CFString::from_static_string("bar");
433         let cftype = string.as_CFType();
434         let string2 = cftype.downcast::<CFString>().unwrap();
435         assert_eq!(string2.to_string(), "bar");
436 
437         assert_eq!(string.retain_count(), 3);
438         assert_eq!(cftype.retain_count(), 3);
439         assert_eq!(string2.retain_count(), 3);
440     }
441 
442     #[test]
into_cftype_and_downcast_into()443     fn into_cftype_and_downcast_into() {
444         let string = CFString::from_static_string("bar");
445         let cftype = string.into_CFType();
446         let string2 = cftype.downcast_into::<CFString>().unwrap();
447         assert_eq!(string2.to_string(), "bar");
448         assert_eq!(string2.retain_count(), 1);
449     }
450 }
451