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