core_foundation/
base.rs

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
10use std;
11use std::fmt;
12use std::marker::PhantomData;
13use std::mem;
14use std::mem::ManuallyDrop;
15use std::ops::{Deref, DerefMut};
16use std::os::raw::c_void;
17
18pub use core_foundation_sys::base::*;
19
20use crate::string::CFString;
21use crate::ConcreteCFType;
22
23pub trait CFIndexConvertible {
24    /// Always use this method to construct a `CFIndex` value. It performs bounds checking to
25    /// ensure the value is in range.
26    fn to_CFIndex(self) -> CFIndex;
27}
28
29impl CFIndexConvertible for usize {
30    #[inline]
31    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
40declare_TCFType! {
41    /// Superclass of all Core Foundation objects.
42    CFType, CFTypeRef
43}
44
45impl 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]
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]
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
113impl fmt::Debug for CFType {
114    /// Formats the value using [`CFCopyDescription`].
115    ///
116    /// [`CFCopyDescription`]: https://developer.apple.com/documentation/corefoundation/1521252-cfcopydescription?language=objc
117    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118        let desc = unsafe { CFString::wrap_under_create_rule(CFCopyDescription(self.0)) };
119        desc.fmt(f)
120    }
121}
122
123impl Clone for CFType {
124    #[inline]
125    fn clone(&self) -> CFType {
126        unsafe { TCFType::wrap_under_get_rule(self.0) }
127    }
128}
129
130impl PartialEq for CFType {
131    #[inline]
132    fn eq(&self, other: &CFType) -> bool {
133        unsafe { CFEqual(self.as_CFTypeRef(), other.as_CFTypeRef()) != 0 }
134    }
135}
136
137declare_TCFType!(CFAllocator, CFAllocatorRef);
138impl_TCFType!(CFAllocator, CFAllocatorRef, CFAllocatorGetTypeID);
139
140impl CFAllocator {
141    #[inline]
142    pub fn new(mut context: CFAllocatorContext) -> CFAllocator {
143        unsafe {
144            let allocator_ref = CFAllocatorCreate(kCFAllocatorDefault, &mut context);
145            TCFType::wrap_under_create_rule(allocator_ref)
146        }
147    }
148}
149
150/// All Core Foundation types implement this trait. The associated type `Ref` specifies the
151/// associated Core Foundation type: e.g. for `CFType` this is `CFTypeRef`; for `CFArray` this is
152/// `CFArrayRef`.
153///
154/// Most structs that implement this trait will do so via the [`impl_TCFType`] macro.
155///
156/// [`impl_TCFType`]: ../macro.impl_TCFType.html
157pub trait TCFType {
158    /// The reference type wrapped inside this type.
159    type Ref: TCFTypeRef;
160
161    /// Returns the object as its concrete `TypeRef`.
162    fn as_concrete_TypeRef(&self) -> Self::Ref;
163
164    /// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this
165    /// when following Core Foundation's "Create Rule". The reference count is *not* bumped.
166    unsafe fn wrap_under_create_rule(obj: Self::Ref) -> Self;
167
168    /// Returns the type ID for this class.
169    fn type_id() -> CFTypeID;
170
171    /// Returns the object as a wrapped `CFType`. The reference count is incremented by one.
172    #[inline]
173    fn as_CFType(&self) -> CFType {
174        unsafe { TCFType::wrap_under_get_rule(self.as_CFTypeRef()) }
175    }
176
177    /// Returns the object as a wrapped `CFType`. Consumes self and avoids changing the reference
178    /// count.
179    #[inline]
180    fn into_CFType(self) -> CFType
181    where
182        Self: Sized,
183    {
184        let reference = self.as_CFTypeRef();
185        mem::forget(self);
186        unsafe { TCFType::wrap_under_create_rule(reference) }
187    }
188
189    /// Returns the object as a raw `CFTypeRef`. The reference count is not adjusted.
190    fn as_CFTypeRef(&self) -> CFTypeRef;
191
192    /// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this
193    /// when following Core Foundation's "Get Rule". The reference count *is* bumped.
194    unsafe fn wrap_under_get_rule(reference: Self::Ref) -> Self;
195
196    /// Returns the reference count of the object. It is unwise to do anything other than test
197    /// whether the return value of this method is greater than zero.
198    #[inline]
199    fn retain_count(&self) -> CFIndex {
200        unsafe { CFGetRetainCount(self.as_CFTypeRef()) }
201    }
202
203    /// Returns the type ID of this object.
204    #[inline]
205    fn type_of(&self) -> CFTypeID {
206        unsafe { CFGetTypeID(self.as_CFTypeRef()) }
207    }
208
209    /// Writes a debugging version of this object on standard error.
210    fn show(&self) {
211        unsafe { CFShow(self.as_CFTypeRef()) }
212    }
213
214    /// Returns `true` if this value is an instance of another type.
215    #[inline]
216    fn instance_of<OtherCFType: TCFType>(&self) -> bool {
217        self.type_of() == OtherCFType::type_id()
218    }
219}
220
221impl TCFType for CFType {
222    type Ref = CFTypeRef;
223
224    #[inline]
225    fn as_concrete_TypeRef(&self) -> CFTypeRef {
226        self.0
227    }
228
229    #[inline]
230    unsafe fn wrap_under_get_rule(reference: CFTypeRef) -> CFType {
231        assert!(!reference.is_null(), "Attempted to create a NULL object.");
232        let reference: CFTypeRef = CFRetain(reference);
233        TCFType::wrap_under_create_rule(reference)
234    }
235
236    #[inline]
237    fn as_CFTypeRef(&self) -> CFTypeRef {
238        self.as_concrete_TypeRef()
239    }
240
241    #[inline]
242    unsafe fn wrap_under_create_rule(obj: CFTypeRef) -> CFType {
243        assert!(!obj.is_null(), "Attempted to create a NULL object.");
244        CFType(obj)
245    }
246
247    #[inline]
248    fn type_id() -> CFTypeID {
249        // FIXME(pcwalton): Is this right?
250        0
251    }
252}
253
254/// A reference to an element inside a container
255pub struct ItemRef<'a, T: 'a>(ManuallyDrop<T>, PhantomData<&'a T>);
256
257impl<'a, T> Deref for ItemRef<'a, T> {
258    type Target = T;
259
260    fn deref(&self) -> &T {
261        &self.0
262    }
263}
264
265impl<'a, T: fmt::Debug> fmt::Debug for ItemRef<'a, T> {
266    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
267        self.0.fmt(f)
268    }
269}
270
271impl<'a, T: PartialEq> PartialEq for ItemRef<'a, T> {
272    fn eq(&self, other: &Self) -> bool {
273        self.0.eq(&other.0)
274    }
275}
276
277/// A reference to a mutable element inside a container
278pub struct ItemMutRef<'a, T: 'a>(ManuallyDrop<T>, PhantomData<&'a T>);
279
280impl<'a, T> Deref for ItemMutRef<'a, T> {
281    type Target = T;
282
283    fn deref(&self) -> &T {
284        &self.0
285    }
286}
287
288impl<'a, T> DerefMut for ItemMutRef<'a, T> {
289    fn deref_mut(&mut self) -> &mut T {
290        &mut self.0
291    }
292}
293
294impl<'a, T: fmt::Debug> fmt::Debug for ItemMutRef<'a, T> {
295    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
296        self.0.fmt(f)
297    }
298}
299
300impl<'a, T: PartialEq> PartialEq for ItemMutRef<'a, T> {
301    fn eq(&self, other: &Self) -> bool {
302        self.0.eq(&other.0)
303    }
304}
305
306/// A trait describing how to convert from the stored `*mut c_void` to the desired `T`
307pub unsafe trait FromMutVoid {
308    unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self>
309    where
310        Self: std::marker::Sized;
311}
312
313unsafe impl FromMutVoid for u32 {
314    unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> {
315        ItemMutRef(ManuallyDrop::new(x as u32), PhantomData)
316    }
317}
318
319unsafe impl FromMutVoid for *const c_void {
320    unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> {
321        ItemMutRef(ManuallyDrop::new(x), PhantomData)
322    }
323}
324
325unsafe impl<T: TCFType> FromMutVoid for T {
326    unsafe fn from_mut_void<'a>(x: *mut c_void) -> ItemMutRef<'a, Self> {
327        ItemMutRef(
328            ManuallyDrop::new(TCFType::wrap_under_create_rule(T::Ref::from_void_ptr(x))),
329            PhantomData,
330        )
331    }
332}
333
334/// A trait describing how to convert from the stored `*const c_void` to the desired `T`
335pub unsafe trait FromVoid {
336    unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self>
337    where
338        Self: std::marker::Sized;
339}
340
341unsafe impl FromVoid for u32 {
342    unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> {
343        // Functions like CGFontCopyTableTags treat the void*'s as u32's
344        // so we convert by casting directly
345        ItemRef(ManuallyDrop::new(x as u32), PhantomData)
346    }
347}
348
349unsafe impl FromVoid for *const c_void {
350    unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> {
351        ItemRef(ManuallyDrop::new(x), PhantomData)
352    }
353}
354
355unsafe impl<T: TCFType> FromVoid for T {
356    unsafe fn from_void<'a>(x: *const c_void) -> ItemRef<'a, Self> {
357        ItemRef(
358            ManuallyDrop::new(TCFType::wrap_under_create_rule(T::Ref::from_void_ptr(x))),
359            PhantomData,
360        )
361    }
362}
363
364/// A trait describing how to convert from the stored `*const c_void` to the desired `T`
365pub unsafe trait ToVoid<T> {
366    fn to_void(&self) -> *const c_void;
367}
368
369unsafe impl ToVoid<*const c_void> for *const c_void {
370    fn to_void(&self) -> *const c_void {
371        *self
372    }
373}
374
375unsafe impl<'a> ToVoid<CFType> for &'a CFType {
376    fn to_void(&self) -> *const ::std::os::raw::c_void {
377        self.as_concrete_TypeRef().as_void_ptr()
378    }
379}
380
381unsafe impl ToVoid<CFType> for CFType {
382    fn to_void(&self) -> *const ::std::os::raw::c_void {
383        self.as_concrete_TypeRef().as_void_ptr()
384    }
385}
386
387unsafe impl ToVoid<CFType> for CFTypeRef {
388    fn to_void(&self) -> *const ::std::os::raw::c_void {
389        self.as_void_ptr()
390    }
391}
392
393#[cfg(test)]
394mod tests {
395    use super::*;
396    use crate::boolean::CFBoolean;
397    use std::mem;
398
399    #[test]
400    fn cftype_instance_of() {
401        let string = CFString::from_static_string("foo");
402        let cftype = string.as_CFType();
403
404        assert!(cftype.instance_of::<CFString>());
405        assert!(!cftype.instance_of::<CFBoolean>());
406    }
407
408    #[test]
409    fn as_cftype_retain_count() {
410        let string = CFString::from_static_string("alongerstring");
411        assert_eq!(string.retain_count(), 1);
412        let cftype = string.as_CFType();
413        assert_eq!(cftype.retain_count(), 2);
414        mem::drop(string);
415        assert_eq!(cftype.retain_count(), 1);
416    }
417
418    #[test]
419    fn into_cftype_retain_count() {
420        let string = CFString::from_static_string("alongerstring");
421        assert_eq!(string.retain_count(), 1);
422        let cftype = string.into_CFType();
423        assert_eq!(cftype.retain_count(), 1);
424    }
425
426    #[test]
427    fn as_cftype_and_downcast() {
428        let string = CFString::from_static_string("alongerstring");
429        let cftype = string.as_CFType();
430        let string2 = cftype.downcast::<CFString>().unwrap();
431        assert_eq!(string2.to_string(), "alongerstring");
432
433        assert_eq!(string.retain_count(), 3);
434        assert_eq!(cftype.retain_count(), 3);
435        assert_eq!(string2.retain_count(), 3);
436    }
437
438    #[test]
439    fn into_cftype_and_downcast_into() {
440        let string = CFString::from_static_string("alongerstring");
441        let cftype = string.into_CFType();
442        let string2 = cftype.downcast_into::<CFString>().unwrap();
443        assert_eq!(string2.to_string(), "alongerstring");
444        assert_eq!(string2.retain_count(), 1);
445    }
446}