diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/core-foundation/src | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/core-foundation/src')
21 files changed, 3629 insertions, 0 deletions
diff --git a/third_party/rust/core-foundation/src/array.rs b/third_party/rust/core-foundation/src/array.rs new file mode 100644 index 0000000000..d66ffc5b82 --- /dev/null +++ b/third_party/rust/core-foundation/src/array.rs @@ -0,0 +1,286 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Heterogeneous immutable arrays. + +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::marker::PhantomData; +use std::os::raw::c_void; +use std::ptr; +use ConcreteCFType; + +use base::{CFIndexConvertible, TCFType, CFRange}; +use base::{FromVoid, ItemRef}; + +/// A heterogeneous immutable array. +pub struct CFArray<T = *const c_void>(CFArrayRef, PhantomData<T>); + +impl<T> Drop for CFArray<T> { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} + +pub struct CFArrayIterator<'a, T: 'a> { + array: &'a CFArray<T>, + index: CFIndex, + len: CFIndex, +} + +impl<'a, T: FromVoid> Iterator for CFArrayIterator<'a, T> { + type Item = ItemRef<'a, T>; + + fn next(&mut self) -> Option<ItemRef<'a, T>> { + if self.index >= self.len { + None + } else { + let value = unsafe { self.array.get_unchecked(self.index) }; + self.index += 1; + Some(value) + } + } +} + +impl<'a, T: FromVoid> ExactSizeIterator for CFArrayIterator<'a, T> { + fn len(&self) -> usize { + (self.array.len() - self.index) as usize + } +} + +impl_TCFType!(CFArray<T>, CFArrayRef, CFArrayGetTypeID); +impl_CFTypeDescription!(CFArray<T>); + +unsafe impl ConcreteCFType for CFArray<*const c_void> {} + +impl<T> CFArray<T> { + /// Creates a new `CFArray` with the given elements, which must implement `Copy`. + pub fn from_copyable(elems: &[T]) -> CFArray<T> where T: Copy { + unsafe { + let array_ref = CFArrayCreate(kCFAllocatorDefault, + elems.as_ptr() as *const *const c_void, + elems.len().to_CFIndex(), + ptr::null()); + TCFType::wrap_under_create_rule(array_ref) + } + } + + /// Creates a new `CFArray` with the given elements, which must be `CFType` objects. + pub fn from_CFTypes(elems: &[T]) -> CFArray<T> where T: TCFType { + unsafe { + let elems: Vec<CFTypeRef> = elems.iter().map(|elem| elem.as_CFTypeRef()).collect(); + let array_ref = CFArrayCreate(kCFAllocatorDefault, + elems.as_ptr(), + elems.len().to_CFIndex(), + &kCFTypeArrayCallBacks); + TCFType::wrap_under_create_rule(array_ref) + } + } + + #[inline] + pub fn to_untyped(&self) -> CFArray { + unsafe { CFArray::wrap_under_get_rule(self.0) } + } + + /// Returns the same array, but with the type reset to void pointers. + /// Equal to `to_untyped`, but is faster since it does not increment the retain count. + #[inline] + pub fn into_untyped(self) -> CFArray { + let reference = self.0; + mem::forget(self); + unsafe { CFArray::wrap_under_create_rule(reference) } + } + + /// Iterates over the elements of this `CFArray`. + /// + /// Careful; the loop body must wrap the reference properly. Generally, when array elements are + /// Core Foundation objects (not always true), they need to be wrapped with + /// `TCFType::wrap_under_get_rule()`. + #[inline] + pub fn iter<'a>(&'a self) -> CFArrayIterator<'a, T> { + CFArrayIterator { + array: self, + index: 0, + len: self.len(), + } + } + + #[inline] + pub fn len(&self) -> CFIndex { + unsafe { + CFArrayGetCount(self.0) + } + } + + #[inline] + pub unsafe fn get_unchecked<'a>(&'a self, index: CFIndex) -> ItemRef<'a, T> where T: FromVoid { + T::from_void(CFArrayGetValueAtIndex(self.0, index)) + } + + #[inline] + pub fn get<'a>(&'a self, index: CFIndex) -> Option<ItemRef<'a, T>> where T: FromVoid { + if index < self.len() { + Some(unsafe { T::from_void(CFArrayGetValueAtIndex(self.0, index)) } ) + } else { + None + } + } + + pub fn get_values(&self, range: CFRange) -> Vec<*const c_void> { + let mut vec = Vec::with_capacity(range.length as usize); + unsafe { + CFArrayGetValues(self.0, range, vec.as_mut_ptr()); + vec.set_len(range.length as usize); + vec + } + } + + pub fn get_all_values(&self) -> Vec<*const c_void> { + self.get_values(CFRange { + location: 0, + length: self.len() + }) + } +} + +impl<'a, T: FromVoid> IntoIterator for &'a CFArray<T> { + type Item = ItemRef<'a, T>; + type IntoIter = CFArrayIterator<'a, T>; + + fn into_iter(self) -> CFArrayIterator<'a, T> { + self.iter() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::mem; + use base::CFType; + + #[test] + fn to_untyped_correct_retain_count() { + let array = CFArray::<CFType>::from_CFTypes(&[]); + assert_eq!(array.retain_count(), 1); + + let untyped_array = array.to_untyped(); + assert_eq!(array.retain_count(), 2); + assert_eq!(untyped_array.retain_count(), 2); + + mem::drop(array); + assert_eq!(untyped_array.retain_count(), 1); + } + + #[test] + fn into_untyped() { + let array = CFArray::<CFType>::from_CFTypes(&[]); + let array2 = array.to_untyped(); + assert_eq!(array.retain_count(), 2); + + let untyped_array = array.into_untyped(); + assert_eq!(untyped_array.retain_count(), 2); + + mem::drop(array2); + assert_eq!(untyped_array.retain_count(), 1); + } + + #[test] + fn borrow() { + use string::CFString; + + let string = CFString::from_static_string("bar"); + assert_eq!(string.retain_count(), 1); + let x; + { + let arr: CFArray<CFString> = CFArray::from_CFTypes(&[string]); + { + let p = arr.get(0).unwrap(); + assert_eq!(p.retain_count(), 1); + } + { + x = arr.get(0).unwrap().clone(); + assert_eq!(x.retain_count(), 2); + assert_eq!(x.to_string(), "bar"); + } + } + assert_eq!(x.retain_count(), 1); + } + + #[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(); + + let cf_strings = array.iter().map(|ptr| { + unsafe { CFString::wrap_under_get_rule(CFStringRef::from_void_ptr(*ptr)) } + }).collect::<Vec<_>>(); + let strings = cf_strings.iter().map(|s| s.to_string()).collect::<Vec<_>>(); + assert_eq!(cf_string.retain_count(), 3); + assert_eq!(&strings[..], &["bar"]); + } + + #[test] + fn should_box_and_unbox() { + use number::CFNumber; + + let n0 = CFNumber::from(0); + let n1 = CFNumber::from(1); + let n2 = CFNumber::from(2); + let n3 = CFNumber::from(3); + let n4 = CFNumber::from(4); + let n5 = CFNumber::from(5); + + let arr = CFArray::from_CFTypes(&[ + n0.as_CFType(), + n1.as_CFType(), + n2.as_CFType(), + n3.as_CFType(), + n4.as_CFType(), + n5.as_CFType(), + ]); + + assert_eq!( + arr.get_all_values(), + &[ + n0.as_CFTypeRef(), + n1.as_CFTypeRef(), + n2.as_CFTypeRef(), + n3.as_CFTypeRef(), + n4.as_CFTypeRef(), + n5.as_CFTypeRef() + ] + ); + + let mut sum = 0; + + let mut iter = arr.iter(); + assert_eq!(iter.len(), 6); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 5); + + for elem in iter { + let number: CFNumber = elem.downcast::<CFNumber>().unwrap(); + sum += number.to_i64().unwrap() + } + + assert_eq!(sum, 15); + + for elem in arr.iter() { + let number: CFNumber = elem.downcast::<CFNumber>().unwrap(); + sum += number.to_i64().unwrap() + } + + assert_eq!(sum, 30); + } +} diff --git a/third_party/rust/core-foundation/src/attributed_string.rs b/third_party/rust/core-foundation/src/attributed_string.rs new file mode 100644 index 0000000000..d4a467946d --- /dev/null +++ b/third_party/rust/core-foundation/src/attributed_string.rs @@ -0,0 +1,98 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use core_foundation_sys::attributed_string::*; + +use base::TCFType; +use core_foundation_sys::base::{CFIndex, CFRange, kCFAllocatorDefault}; +use std::ptr::null; +use string::{CFString, CFStringRef}; + +declare_TCFType!{ + CFAttributedString, CFAttributedStringRef +} +impl_TCFType!(CFAttributedString, CFAttributedStringRef, CFAttributedStringGetTypeID); + +impl CFAttributedString { + #[inline] + pub fn new(string: &CFString) -> Self { + unsafe { + let astr_ref = CFAttributedStringCreate( + kCFAllocatorDefault, string.as_concrete_TypeRef(), null()); + + CFAttributedString::wrap_under_create_rule(astr_ref) + } + } + + #[inline] + pub fn char_len(&self) -> CFIndex { + unsafe { + CFAttributedStringGetLength(self.0) + } + } +} + +declare_TCFType!{ + CFMutableAttributedString, CFMutableAttributedStringRef +} +impl_TCFType!(CFMutableAttributedString, CFMutableAttributedStringRef, CFAttributedStringGetTypeID); + +impl CFMutableAttributedString { + #[inline] + pub fn new() -> Self { + unsafe { + let astr_ref = CFAttributedStringCreateMutable( + kCFAllocatorDefault, 0); + + CFMutableAttributedString::wrap_under_create_rule(astr_ref) + } + } + + #[inline] + pub fn char_len(&self) -> CFIndex { + unsafe { + CFAttributedStringGetLength(self.0) + } + } + + #[inline] + pub fn replace_str(&mut self, string: &CFString, range: CFRange) { + unsafe { + CFAttributedStringReplaceString( + self.0, range, string.as_concrete_TypeRef()); + } + } + + #[inline] + pub fn set_attribute<T: TCFType>(&mut self, range: CFRange, name: CFStringRef, value: &T) { + unsafe { + CFAttributedStringSetAttribute( + self.0, range, name, value.as_CFTypeRef()); + } + } +} + +impl Default for CFMutableAttributedString { + fn default() -> Self { + Self::new() + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn attributed_string_type_id_comparison() { + // CFMutableAttributedString TypeID must be equal to CFAttributedString TypeID. + // Compilation must not fail. + assert_eq!(<CFAttributedString as TCFType>::type_id(), <CFMutableAttributedString as TCFType>::type_id()); + } +}
\ No newline at end of file diff --git a/third_party/rust/core-foundation/src/base.rs b/third_party/rust/core-foundation/src/base.rs new file mode 100644 index 0000000000..f08f2b2e85 --- /dev/null +++ b/third_party/rust/core-foundation/src/base.rs @@ -0,0 +1,452 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::<CFString>().unwrap().to_string(), "FooBar"); + /// // Casting it to some other type will yield `None` + /// assert!(cf_type.downcast::<CFBoolean>().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::<CFArray<CFString>>() + /// .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<T: ConcreteCFType>(&self) -> Option<T> { + if self.instance_of::<T>() { + 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<T: ConcreteCFType>(self) -> Option<T> { + if self.instance_of::<T>() { + 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<OtherCFType: TCFType>(&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<T>, 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<T>, 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<T: TCFType> 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<T: TCFType> 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<T> { + 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<CFType> for &'a CFType { + fn to_void(&self) -> *const ::std::os::raw::c_void { + self.as_concrete_TypeRef().as_void_ptr() + } +} + +unsafe impl ToVoid<CFType> for CFType { + fn to_void(&self) -> *const ::std::os::raw::c_void { + self.as_concrete_TypeRef().as_void_ptr() + } +} + +unsafe impl ToVoid<CFType> 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::<CFString>()); + assert!(!cftype.instance_of::<CFBoolean>()); + } + + #[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::<CFString>().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::<CFString>().unwrap(); + assert_eq!(string2.to_string(), "bar"); + assert_eq!(string2.retain_count(), 1); + } +} diff --git a/third_party/rust/core-foundation/src/boolean.rs b/third_party/rust/core-foundation/src/boolean.rs new file mode 100644 index 0000000000..8c13b907da --- /dev/null +++ b/third_party/rust/core-foundation/src/boolean.rs @@ -0,0 +1,70 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A Boolean type. + +pub use core_foundation_sys::number::{CFBooleanRef, CFBooleanGetTypeID, kCFBooleanTrue, kCFBooleanFalse}; + +use base::TCFType; + + +declare_TCFType!{ + /// A Boolean type. + /// + /// FIXME(pcwalton): Should be a newtype struct, but that fails due to a Rust compiler bug. + CFBoolean, CFBooleanRef +} +impl_TCFType!(CFBoolean, CFBooleanRef, CFBooleanGetTypeID); +impl_CFTypeDescription!(CFBoolean); + +impl CFBoolean { + pub fn true_value() -> CFBoolean { + unsafe { + TCFType::wrap_under_get_rule(kCFBooleanTrue) + } + } + + pub fn false_value() -> CFBoolean { + unsafe { + TCFType::wrap_under_get_rule(kCFBooleanFalse) + } + } +} + +impl From<bool> for CFBoolean { + fn from(value: bool) -> CFBoolean { + if value { + CFBoolean::true_value() + } else { + CFBoolean::false_value() + } + } +} + +impl From<CFBoolean> for bool { + fn from(value: CFBoolean) -> bool { + value.0 == unsafe { kCFBooleanTrue } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn to_and_from_bool() { + let b_false = CFBoolean::from(false); + let b_true = CFBoolean::from(true); + assert_ne!(b_false, b_true); + assert_eq!(b_false, CFBoolean::false_value()); + assert_eq!(b_true, CFBoolean::true_value()); + assert!(!bool::from(b_false)); + assert!(bool::from(b_true)); + } +} diff --git a/third_party/rust/core-foundation/src/bundle.rs b/third_party/rust/core-foundation/src/bundle.rs new file mode 100644 index 0000000000..b9ab1f65f6 --- /dev/null +++ b/third_party/rust/core-foundation/src/bundle.rs @@ -0,0 +1,190 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Core Foundation Bundle Type + +use core_foundation_sys::base::kCFAllocatorDefault; +pub use core_foundation_sys::bundle::*; +use core_foundation_sys::url::kCFURLPOSIXPathStyle; +use std::path::PathBuf; + +use base::{CFType, TCFType}; +use url::CFURL; +use dictionary::CFDictionary; +use std::os::raw::c_void; +use string::CFString; + +declare_TCFType!{ + /// A Bundle type. + CFBundle, CFBundleRef +} +impl_TCFType!(CFBundle, CFBundleRef, CFBundleGetTypeID); + +impl CFBundle { + pub fn new(bundleURL: CFURL) -> Option<CFBundle> { + unsafe { + let bundle_ref = CFBundleCreate(kCFAllocatorDefault, bundleURL.as_concrete_TypeRef()); + if bundle_ref.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(bundle_ref)) + } + } + } + + pub fn bundle_with_identifier(identifier: CFString) -> Option<CFBundle> { + unsafe { + let bundle_ref = CFBundleGetBundleWithIdentifier(identifier.as_concrete_TypeRef()); + if bundle_ref.is_null() { + None + } else { + Some(TCFType::wrap_under_get_rule(bundle_ref)) + } + } + } + + pub fn function_pointer_for_name(&self, function_name: CFString) -> *const c_void { + unsafe { + CFBundleGetFunctionPointerForName(self.as_concrete_TypeRef(), + function_name.as_concrete_TypeRef()) + } + } + + pub fn main_bundle() -> CFBundle { + unsafe { + let bundle_ref = CFBundleGetMainBundle(); + TCFType::wrap_under_get_rule(bundle_ref) + } + } + + pub fn info_dictionary(&self) -> CFDictionary<CFString, CFType> { + unsafe { + let info_dictionary = CFBundleGetInfoDictionary(self.0); + TCFType::wrap_under_get_rule(info_dictionary) + } + } + + pub fn executable_url(&self) -> Option<CFURL> { + unsafe { + let exe_url = CFBundleCopyExecutableURL(self.0); + if exe_url.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(exe_url)) + } + } + } + + /// Bundle's own location + pub fn bundle_url(&self) -> Option<CFURL> { + unsafe { + let bundle_url = CFBundleCopyBundleURL(self.0); + if bundle_url.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(bundle_url)) + } + } + } + + /// Bundle's own location + pub fn path(&self) -> Option<PathBuf> { + let url = self.bundle_url()?; + Some(PathBuf::from(url.get_file_system_path(kCFURLPOSIXPathStyle).to_string())) + } + + /// Bundle's resources location + pub fn bundle_resources_url(&self) -> Option<CFURL> { + unsafe { + let bundle_url = CFBundleCopyResourcesDirectoryURL(self.0); + if bundle_url.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(bundle_url)) + } + } + } + + /// Bundle's resources location + pub fn resources_path(&self) -> Option<PathBuf> { + let url = self.bundle_resources_url()?; + Some(PathBuf::from(url.get_file_system_path(kCFURLPOSIXPathStyle).to_string())) + } + + pub fn private_frameworks_url(&self) -> Option<CFURL> { + unsafe { + let fw_url = CFBundleCopyPrivateFrameworksURL(self.0); + if fw_url.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(fw_url)) + } + } + } + + pub fn shared_support_url(&self) -> Option<CFURL> { + unsafe { + let fw_url = CFBundleCopySharedSupportURL(self.0); + if fw_url.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(fw_url)) + } + } + } +} + + +#[test] +fn safari_executable_url() { + use string::CFString; + use url::{CFURL, kCFURLPOSIXPathStyle}; + + let cfstr_path = CFString::from_static_string("/Applications/Safari.app"); + let cfurl_path = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true); + let cfurl_executable = CFBundle::new(cfurl_path) + .expect("Safari not present") + .executable_url(); + assert!(cfurl_executable.is_some()); + assert_eq!(cfurl_executable + .unwrap() + .absolute() + .get_file_system_path(kCFURLPOSIXPathStyle) + .to_string(), + "/Applications/Safari.app/Contents/MacOS/Safari"); +} + +#[test] +fn safari_private_frameworks_url() { + use string::CFString; + use url::{CFURL, kCFURLPOSIXPathStyle}; + + let cfstr_path = CFString::from_static_string("/Applications/Safari.app"); + let cfurl_path = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true); + let cfurl_executable = CFBundle::new(cfurl_path) + .expect("Safari not present") + .private_frameworks_url(); + assert!(cfurl_executable.is_some()); + assert_eq!(cfurl_executable + .unwrap() + .absolute() + .get_file_system_path(kCFURLPOSIXPathStyle) + .to_string(), + "/Applications/Safari.app/Contents/Frameworks"); +} + +#[test] +fn non_existant_bundle() { + use string::CFString; + use url::{CFURL, kCFURLPOSIXPathStyle}; + + let cfstr_path = CFString::from_static_string("/usr/local/foo"); + let cfurl_path = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true); + assert!(CFBundle::new(cfurl_path).is_none()); +} diff --git a/third_party/rust/core-foundation/src/characterset.rs b/third_party/rust/core-foundation/src/characterset.rs new file mode 100644 index 0000000000..d1b9439d6e --- /dev/null +++ b/third_party/rust/core-foundation/src/characterset.rs @@ -0,0 +1,21 @@ +// Copyright 2019 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A set of Unicode compliant characters. + +pub use core_foundation_sys::characterset::*; + +use base::TCFType; + +declare_TCFType!{ + /// An immutable set of Unicde characters. + CFCharacterSet, CFCharacterSetRef +} +impl_TCFType!(CFCharacterSet, CFCharacterSetRef, CFCharacterSetGetTypeID); +impl_CFTypeDescription!(CFCharacterSet); diff --git a/third_party/rust/core-foundation/src/data.rs b/third_party/rust/core-foundation/src/data.rs new file mode 100644 index 0000000000..c510c7434d --- /dev/null +++ b/third_party/rust/core-foundation/src/data.rs @@ -0,0 +1,144 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Core Foundation byte buffers. + +pub use core_foundation_sys::data::*; +use core_foundation_sys::base::CFIndex; +use core_foundation_sys::base::{kCFAllocatorDefault}; +use std::ops::Deref; +use std::slice; +use std::sync::Arc; + + +use base::{CFIndexConvertible, TCFType}; + + +declare_TCFType!{ + /// A byte buffer. + CFData, CFDataRef +} +impl_TCFType!(CFData, CFDataRef, CFDataGetTypeID); +impl_CFTypeDescription!(CFData); + +impl CFData { + /// Creates a CFData around a copy `buffer` + pub fn from_buffer(buffer: &[u8]) -> CFData { + unsafe { + let data_ref = CFDataCreate(kCFAllocatorDefault, + buffer.as_ptr(), + buffer.len().to_CFIndex()); + TCFType::wrap_under_create_rule(data_ref) + } + } + + /// Creates a CFData referencing `buffer` without creating a copy + pub fn from_arc<T: AsRef<[u8]> + Sync + Send>(buffer: Arc<T>) -> Self { + use std::os::raw::c_void; + use crate::base::{CFAllocator, CFAllocatorContext}; + + unsafe { + let ptr = (*buffer).as_ref().as_ptr() as *const _; + let len = (*buffer).as_ref().len().to_CFIndex(); + let info = Arc::into_raw(buffer) as *mut c_void; + + extern "C" fn deallocate<T>(_: *mut c_void, info: *mut c_void) { + unsafe { + drop(Arc::from_raw(info as *mut T)); + } + } + + // Use a separate allocator for each allocation because + // we need `info` to do the deallocation vs. `ptr` + let allocator = CFAllocator::new(CFAllocatorContext { + info, + version: 0, + retain: None, + reallocate: None, + release: None, + copyDescription: None, + allocate: None, + deallocate: Some(deallocate::<T>), + preferredSize: None, + }); + let data_ref = + CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, len, allocator.as_CFTypeRef()); + TCFType::wrap_under_create_rule(data_ref) + } + } + + /// Returns a pointer to the underlying bytes in this data. Note that this byte buffer is + /// read-only. + #[inline] + pub fn bytes<'a>(&'a self) -> &'a [u8] { + unsafe { + slice::from_raw_parts(CFDataGetBytePtr(self.0), self.len() as usize) + } + } + + /// Returns the length of this byte buffer. + #[inline] + pub fn len(&self) -> CFIndex { + unsafe { + CFDataGetLength(self.0) + } + } +} + +impl Deref for CFData { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.bytes() + } +} + +#[cfg(test)] +mod test { + use super::CFData; + use std::sync::Arc; + + #[test] + fn test_data_provider() { + let l = vec![5]; + CFData::from_arc(Arc::new(l)); + + let l = vec![5]; + CFData::from_arc(Arc::new(l.into_boxed_slice())); + + // Make sure the buffer is actually dropped + use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; + struct VecWrapper { + inner: Vec<u8>, + dropped: Arc<AtomicBool>, + } + + impl Drop for VecWrapper { + fn drop(&mut self) { + self.dropped.store(true, SeqCst) + } + } + + impl std::convert::AsRef<[u8]> for VecWrapper { + fn as_ref(&self) -> &[u8] { + &self.inner + } + } + + let dropped = Arc::new(AtomicBool::default()); + let l = Arc::new(VecWrapper {inner: vec![5], dropped: dropped.clone() }); + let m = l.clone(); + let dp = CFData::from_arc(l); + drop(m); + assert!(!dropped.load(SeqCst)); + drop(dp); + assert!(dropped.load(SeqCst)) + } +} diff --git a/third_party/rust/core-foundation/src/date.rs b/third_party/rust/core-foundation/src/date.rs new file mode 100644 index 0000000000..57ee7211e6 --- /dev/null +++ b/third_party/rust/core-foundation/src/date.rs @@ -0,0 +1,130 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Core Foundation date objects. + +pub use core_foundation_sys::date::*; +use core_foundation_sys::base::kCFAllocatorDefault; + +use base::TCFType; + +#[cfg(feature = "with-chrono")] +use chrono::NaiveDateTime; + + +declare_TCFType!{ + /// A date. + CFDate, CFDateRef +} +impl_TCFType!(CFDate, CFDateRef, CFDateGetTypeID); +impl_CFTypeDescription!(CFDate); +impl_CFComparison!(CFDate, CFDateCompare); + +impl CFDate { + #[inline] + pub fn new(time: CFAbsoluteTime) -> CFDate { + unsafe { + let date_ref = CFDateCreate(kCFAllocatorDefault, time); + TCFType::wrap_under_create_rule(date_ref) + } + } + + #[inline] + pub fn now() -> CFDate { + CFDate::new(unsafe { CFAbsoluteTimeGetCurrent() }) + } + + #[inline] + pub fn abs_time(&self) -> CFAbsoluteTime { + unsafe { + CFDateGetAbsoluteTime(self.0) + } + } + + #[cfg(feature = "with-chrono")] + pub fn naive_utc(&self) -> NaiveDateTime { + let ts = unsafe { + self.abs_time() + kCFAbsoluteTimeIntervalSince1970 + }; + let (secs, nanos) = if ts.is_sign_positive() { + (ts.trunc() as i64, ts.fract()) + } else { + // nanoseconds can't be negative in NaiveDateTime + (ts.trunc() as i64 - 1, 1.0 - ts.fract().abs()) + }; + NaiveDateTime::from_timestamp(secs, (nanos * 1e9).floor() as u32) + } + + #[cfg(feature = "with-chrono")] + pub fn from_naive_utc(time: NaiveDateTime) -> CFDate { + let secs = time.timestamp(); + let nanos = time.timestamp_subsec_nanos(); + let ts = unsafe { + secs as f64 + (nanos as f64 / 1e9) - kCFAbsoluteTimeIntervalSince1970 + }; + CFDate::new(ts) + } +} + +#[cfg(test)] +mod test { + use super::CFDate; + use std::cmp::Ordering; + + #[cfg(feature = "with-chrono")] + use chrono::NaiveDateTime; + + #[cfg(feature = "with-chrono")] + fn approx_eq(a: f64, b: f64) -> bool { + use std::f64; + + let same_sign = a.is_sign_positive() == b.is_sign_positive(); + let equal = ((a - b).abs() / f64::min(a.abs() + b.abs(), f64::MAX)) < f64::EPSILON; + (same_sign && equal) + } + + #[test] + fn date_comparison() { + let now = CFDate::now(); + let past = CFDate::new(now.abs_time() - 1.0); + assert_eq!(now.cmp(&past), Ordering::Greater); + assert_eq!(now.cmp(&now), Ordering::Equal); + assert_eq!(past.cmp(&now), Ordering::Less); + } + + #[test] + fn date_equality() { + let now = CFDate::now(); + let same_time = CFDate::new(now.abs_time()); + assert_eq!(now, same_time); + } + + #[test] + #[cfg(feature = "with-chrono")] + fn date_chrono_conversion_positive() { + let date = CFDate::now(); + let datetime = date.naive_utc(); + let converted = CFDate::from_naive_utc(datetime); + assert!(approx_eq(date.abs_time(), converted.abs_time())); + } + + #[test] + #[cfg(feature = "with-chrono")] + fn date_chrono_conversion_negative() { + use super::kCFAbsoluteTimeIntervalSince1970; + + let ts = unsafe { + kCFAbsoluteTimeIntervalSince1970 - 420.0 + }; + let date = CFDate::new(ts); + let datetime: NaiveDateTime = date.naive_utc(); + let converted = CFDate::from_naive_utc(datetime); + assert!(approx_eq(date.abs_time(), converted.abs_time())); + } +} diff --git a/third_party/rust/core-foundation/src/dictionary.rs b/third_party/rust/core-foundation/src/dictionary.rs new file mode 100644 index 0000000000..efcbba1178 --- /dev/null +++ b/third_party/rust/core-foundation/src/dictionary.rs @@ -0,0 +1,409 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Dictionaries of key-value pairs. + +pub use core_foundation_sys::dictionary::*; + +use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault}; +use std::mem; +use std::os::raw::c_void; +use std::ptr; +use std::marker::PhantomData; + +use base::{ItemRef, FromVoid, ToVoid}; +use base::{CFIndexConvertible, TCFType}; +use ConcreteCFType; + +// consume the type parameters with PhantomDatas +pub struct CFDictionary<K = *const c_void, V = *const c_void>(CFDictionaryRef, PhantomData<K>, PhantomData<V>); + +impl<K, V> Drop for CFDictionary<K, V> { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} + +impl_TCFType!(CFDictionary<K, V>, CFDictionaryRef, CFDictionaryGetTypeID); +impl_CFTypeDescription!(CFDictionary<K, V>); + +unsafe impl ConcreteCFType for CFDictionary<*const c_void, *const c_void> {} + +impl<K, V> CFDictionary<K, V> { + pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFDictionary<K, V> where K: TCFType, V: TCFType { + let (keys, values): (Vec<CFTypeRef>, Vec<CFTypeRef>) = pairs + .iter() + .map(|&(ref key, ref value)| (key.as_CFTypeRef(), value.as_CFTypeRef())) + .unzip(); + + unsafe { + let dictionary_ref = CFDictionaryCreate(kCFAllocatorDefault, + keys.as_ptr(), + values.as_ptr(), + keys.len().to_CFIndex(), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + TCFType::wrap_under_create_rule(dictionary_ref) + } + } + + #[inline] + pub fn to_untyped(&self) -> CFDictionary { + unsafe { CFDictionary::wrap_under_get_rule(self.0) } + } + + /// Returns a `CFMutableDictionary` pointing to the same underlying dictionary as this immutable one. + /// This should only be used when the underlying dictionary is mutable. + #[inline] + pub unsafe fn to_mutable(&self) -> CFMutableDictionary<K, V> { + CFMutableDictionary::wrap_under_get_rule(self.0 as CFMutableDictionaryRef) + } + + /// Returns the same dictionary, but with the types reset to void pointers. + /// Equal to `to_untyped`, but is faster since it does not increment the retain count. + #[inline] + pub fn into_untyped(self) -> CFDictionary { + let reference = self.0; + mem::forget(self); + unsafe { CFDictionary::wrap_under_create_rule(reference) } + } + + #[inline] + pub fn len(&self) -> usize { + unsafe { + CFDictionaryGetCount(self.0) as usize + } + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + pub fn contains_key(&self, key: &K) -> bool where K: ToVoid<K> { + unsafe { CFDictionaryContainsKey(self.0, key.to_void()) != 0 } + } + + #[inline] + pub fn find<'a, T: ToVoid<K>>(&'a self, key: T) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> { + unsafe { + let mut value: *const c_void = ptr::null(); + if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 { + Some(V::from_void(value)) + } else { + None + } + } + } + + /// # Panics + /// + /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead + /// of panicking. + #[inline] + pub fn get<'a, T: ToVoid<K>>(&'a self, key: T) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> { + let ptr = key.to_void(); + self.find(key).unwrap_or_else(|| panic!("No entry found for key {:p}", ptr)) + } + + pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) { + let length = self.len(); + let mut keys = Vec::with_capacity(length); + let mut values = Vec::with_capacity(length); + + unsafe { + CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr()); + keys.set_len(length); + values.set_len(length); + } + + (keys, values) + } +} + +// consume the type parameters with PhantomDatas +pub struct CFMutableDictionary<K = *const c_void, V = *const c_void>(CFMutableDictionaryRef, PhantomData<K>, PhantomData<V>); + +impl<K, V> Drop for CFMutableDictionary<K, V> { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} + +impl_TCFType!(CFMutableDictionary<K, V>, CFMutableDictionaryRef, CFDictionaryGetTypeID); +impl_CFTypeDescription!(CFMutableDictionary); + +impl<K, V> CFMutableDictionary<K, V> { + pub fn new() -> Self { + Self::with_capacity(0) + } + + pub fn with_capacity(capacity: isize) -> Self { + unsafe { + let dictionary_ref = CFDictionaryCreateMutable(kCFAllocatorDefault, + capacity as _, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + TCFType::wrap_under_create_rule(dictionary_ref) + } + } + + pub fn copy_with_capacity(&self, capacity: isize) -> Self { + unsafe { + let dictionary_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity as _, self.0); + TCFType::wrap_under_get_rule(dictionary_ref) + } + } + + pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary<K, V> where K: ToVoid<K>, V: ToVoid<V> { + let mut result = Self::with_capacity(pairs.len() as _); + for &(ref key, ref value) in pairs { + result.add(key, value); + } + result + } + + #[inline] + pub fn to_untyped(&self) -> CFMutableDictionary { + unsafe { CFMutableDictionary::wrap_under_get_rule(self.0) } + } + + /// Returns the same dictionary, but with the types reset to void pointers. + /// Equal to `to_untyped`, but is faster since it does not increment the retain count. + #[inline] + pub fn into_untyped(self) -> CFMutableDictionary { + let reference = self.0; + mem::forget(self); + unsafe { CFMutableDictionary::wrap_under_create_rule(reference) } + } + + /// Returns a `CFDictionary` pointing to the same underlying dictionary as this mutable one. + #[inline] + pub fn to_immutable(&self) -> CFDictionary<K, V> { + unsafe { CFDictionary::wrap_under_get_rule(self.0) } + } + + // Immutable interface + + #[inline] + pub fn len(&self) -> usize { + unsafe { + CFDictionaryGetCount(self.0) as usize + } + } + + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[inline] + pub fn contains_key(&self, key: *const c_void) -> bool { + unsafe { + CFDictionaryContainsKey(self.0, key) != 0 + } + } + + #[inline] + pub fn find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> { + unsafe { + let mut value: *const c_void = ptr::null(); + if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 { + Some(V::from_void(value)) + } else { + None + } + } + } + + /// # Panics + /// + /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead + /// of panicking. + #[inline] + pub fn get<'a>(&'a self, key: &K) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> { + let ptr = key.to_void(); + self.find(&key).unwrap_or_else(|| panic!("No entry found for key {:p}", ptr)) + } + + pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) { + let length = self.len(); + let mut keys = Vec::with_capacity(length); + let mut values = Vec::with_capacity(length); + + unsafe { + CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr()); + keys.set_len(length); + values.set_len(length); + } + + (keys, values) + } + + // Mutable interface + + /// Adds the key-value pair to the dictionary if no such key already exist. + #[inline] + pub fn add(&mut self, key: &K, value: &V) where K: ToVoid<K>, V: ToVoid<V> { + unsafe { CFDictionaryAddValue(self.0, key.to_void(), value.to_void()) } + } + + /// Sets the value of the key in the dictionary. + #[inline] + pub fn set(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> { + unsafe { CFDictionarySetValue(self.0, key.to_void(), value.to_void()) } + } + + /// Replaces the value of the key in the dictionary. + #[inline] + pub fn replace(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> { + unsafe { CFDictionaryReplaceValue(self.0, key.to_void(), value.to_void()) } + } + + /// Removes the value of the key from the dictionary. + #[inline] + pub fn remove(&mut self, key: K) where K: ToVoid<K> { + unsafe { CFDictionaryRemoveValue(self.0, key.to_void()) } + } + + #[inline] + pub fn remove_all(&mut self) { + unsafe { CFDictionaryRemoveAllValues(self.0) } + } +} + +impl<K, V> Default for CFMutableDictionary<K, V> { + fn default() -> Self { + Self::new() + } +} + +impl<'a, K, V> From<&'a CFDictionary<K, V>> for CFMutableDictionary<K, V> { + /// Creates a new mutable dictionary with the key-value pairs from another dictionary. + /// The capacity of the new mutable dictionary is not limited. + fn from(dict: &'a CFDictionary<K, V>) -> Self { + unsafe { + let mut_dict_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict.0); + TCFType::wrap_under_create_rule(mut_dict_ref) + } + } +} + + +#[cfg(test)] +pub mod test { + use super::*; + use base::{CFType, TCFType}; + use boolean::CFBoolean; + use number::CFNumber; + use string::CFString; + + + #[test] + fn dictionary() { + let bar = CFString::from_static_string("Bar"); + let baz = CFString::from_static_string("Baz"); + let boo = CFString::from_static_string("Boo"); + let foo = CFString::from_static_string("Foo"); + let tru = CFBoolean::true_value(); + let n42 = CFNumber::from(42); + + let d = CFDictionary::from_CFType_pairs(&[ + (bar.as_CFType(), boo.as_CFType()), + (baz.as_CFType(), tru.as_CFType()), + (foo.as_CFType(), n42.as_CFType()), + ]); + + let (v1, v2) = d.get_keys_and_values(); + assert_eq!(v1, &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]); + assert_eq!(v2, &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]); + } + + #[test] + fn mutable_dictionary() { + let bar = CFString::from_static_string("Bar"); + let baz = CFString::from_static_string("Baz"); + let boo = CFString::from_static_string("Boo"); + let foo = CFString::from_static_string("Foo"); + let tru = CFBoolean::true_value(); + let n42 = CFNumber::from(42); + + let mut d = CFMutableDictionary::<CFString, CFType>::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_eq!(v1, &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]); + assert_eq!(v2, &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]); + + d.remove(baz); + assert_eq!(d.len(), 2); + + let (v1, v2) = d.get_keys_and_values(); + assert_eq!(v1, &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]); + assert_eq!(v2, &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]); + + d.remove_all(); + assert_eq!(d.len(), 0) + } + + #[test] + fn dict_find_and_contains_key() { + let dict = CFDictionary::from_CFType_pairs(&[ + ( + CFString::from_static_string("hello"), + CFBoolean::true_value(), + ), + ]); + let key = CFString::from_static_string("hello"); + let invalid_key = CFString::from_static_string("foobar"); + + assert!(dict.contains_key(&key)); + assert!(!dict.contains_key(&invalid_key)); + + let value = dict.find(&key).unwrap().clone(); + assert_eq!(value, CFBoolean::true_value()); + assert_eq!(dict.find(&invalid_key), None); + } + + #[test] + fn convert_immutable_to_mutable_dict() { + let dict: CFDictionary<CFString, CFBoolean> = CFDictionary::from_CFType_pairs(&[ + (CFString::from_static_string("Foo"), CFBoolean::true_value()), + ]); + let mut mut_dict = CFMutableDictionary::from(&dict); + assert_eq!(dict.retain_count(), 1); + assert_eq!(mut_dict.retain_count(), 1); + + assert_eq!(mut_dict.len(), 1); + assert_eq!(*mut_dict.get(&CFString::from_static_string("Foo")), CFBoolean::true_value()); + + mut_dict.add(&CFString::from_static_string("Bar"), &CFBoolean::false_value()); + assert_eq!(dict.len(), 1); + assert_eq!(mut_dict.len(), 2); + } + + #[test] + fn mutable_dictionary_as_immutable() { + let mut mut_dict: CFMutableDictionary<CFString, CFBoolean> = CFMutableDictionary::new(); + mut_dict.add(&CFString::from_static_string("Bar"), &CFBoolean::false_value()); + assert_eq!(mut_dict.retain_count(), 1); + + let dict = mut_dict.to_immutable(); + assert_eq!(mut_dict.retain_count(), 2); + assert_eq!(dict.retain_count(), 2); + assert_eq!(*dict.get(&CFString::from_static_string("Bar")), CFBoolean::false_value()); + + mem::drop(dict); + assert_eq!(mut_dict.retain_count(), 1); + } +} diff --git a/third_party/rust/core-foundation/src/error.rs b/third_party/rust/core-foundation/src/error.rs new file mode 100644 index 0000000000..f100171bc9 --- /dev/null +++ b/third_party/rust/core-foundation/src/error.rs @@ -0,0 +1,71 @@ +// Copyright 2016 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Core Foundation errors. + +pub use core_foundation_sys::error::*; + +use std::error::Error; +use std::fmt; + +use base::{CFIndex, TCFType}; +use string::CFString; + + +declare_TCFType!{ + /// An error value. + CFError, CFErrorRef +} +impl_TCFType!(CFError, CFErrorRef, CFErrorGetTypeID); + +impl fmt::Debug for CFError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("CFError") + .field("domain", &self.domain()) + .field("code", &self.code()) + .field("description", &self.description()) + .finish() + } +} + +impl fmt::Display for CFError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}", self.description()) + } +} + +impl Error for CFError { + fn description(&self) -> &str { + "a Core Foundation error" + } +} + +impl CFError { + /// Returns a string identifying the domain with which this error is + /// associated. + pub fn domain(&self) -> CFString { + unsafe { + let s = CFErrorGetDomain(self.0); + CFString::wrap_under_get_rule(s) + } + } + + /// Returns the code identifying this type of error. + pub fn code(&self) -> CFIndex { + unsafe { CFErrorGetCode(self.0) } + } + + /// Returns a human-presentable description of the error. + pub fn description(&self) -> CFString { + unsafe { + let s = CFErrorCopyDescription(self.0); + CFString::wrap_under_create_rule(s) + } + } +} diff --git a/third_party/rust/core-foundation/src/filedescriptor.rs b/third_party/rust/core-foundation/src/filedescriptor.rs new file mode 100644 index 0000000000..e153c70b2f --- /dev/null +++ b/third_party/rust/core-foundation/src/filedescriptor.rs @@ -0,0 +1,194 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use core_foundation_sys::filedescriptor::*; + +use core_foundation_sys::base::{Boolean, CFIndex}; +use core_foundation_sys::base::{kCFAllocatorDefault, CFOptionFlags}; + +use base::TCFType; +use runloop::CFRunLoopSource; + +use std::mem::MaybeUninit; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::ptr; + +declare_TCFType!{ + CFFileDescriptor, CFFileDescriptorRef +} +impl_TCFType!(CFFileDescriptor, CFFileDescriptorRef, CFFileDescriptorGetTypeID); + +impl CFFileDescriptor { + pub fn new(fd: RawFd, + closeOnInvalidate: bool, + callout: CFFileDescriptorCallBack, + context: Option<&CFFileDescriptorContext>) -> Option<CFFileDescriptor> { + let context = context.map_or(ptr::null(), |c| c as *const _); + unsafe { + let fd_ref = CFFileDescriptorCreate(kCFAllocatorDefault, + fd, + closeOnInvalidate as Boolean, + callout, + context); + if fd_ref.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(fd_ref)) + } + } + } + + pub fn context(&self) -> CFFileDescriptorContext { + unsafe { + let mut context = MaybeUninit::<CFFileDescriptorContext>::uninit(); + CFFileDescriptorGetContext(self.0, context.as_mut_ptr()); + context.assume_init() + } + } + + pub fn enable_callbacks(&self, callback_types: CFOptionFlags) { + unsafe { + CFFileDescriptorEnableCallBacks(self.0, callback_types) + } + } + + pub fn disable_callbacks(&self, callback_types: CFOptionFlags) { + unsafe { + CFFileDescriptorDisableCallBacks(self.0, callback_types) + } + } + + pub fn valid(&self) -> bool { + unsafe { + CFFileDescriptorIsValid(self.0) != 0 + } + } + + pub fn invalidate(&self) { + unsafe { + CFFileDescriptorInvalidate(self.0) + } + } + + pub fn to_run_loop_source(&self, order: CFIndex) -> Option<CFRunLoopSource> { + unsafe { + let source_ref = CFFileDescriptorCreateRunLoopSource( + kCFAllocatorDefault, + self.0, + order + ); + if source_ref.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(source_ref)) + } + } + } +} + +impl AsRawFd for CFFileDescriptor { + fn as_raw_fd(&self) -> RawFd { + unsafe { + CFFileDescriptorGetNativeDescriptor(self.0) + } + } +} + + +#[cfg(test)] +mod test { + extern crate libc; + + use super::*; + use std::ffi::CString; + use std::os::raw::c_void; + use core_foundation_sys::base::{CFOptionFlags}; + use core_foundation_sys::runloop::{kCFRunLoopDefaultMode}; + use libc::O_RDWR; + use runloop::{CFRunLoop}; + + #[test] + fn test_unconsumed() { + let path = CString::new("/dev/null").unwrap(); + let raw_fd = unsafe { libc::open(path.as_ptr(), O_RDWR, 0) }; + let cf_fd = CFFileDescriptor::new(raw_fd, false, never_callback, None); + assert!(cf_fd.is_some()); + let cf_fd = cf_fd.unwrap(); + + assert!(cf_fd.valid()); + cf_fd.invalidate(); + assert!(!cf_fd.valid()); + + // close() should succeed + assert_eq!(unsafe { libc::close(raw_fd) }, 0); + } + + extern "C" fn never_callback(_f: CFFileDescriptorRef, + _callback_types: CFOptionFlags, + _info_ptr: *mut c_void) { + unreachable!(); + } + + struct TestInfo { + value: CFOptionFlags + } + + #[test] + fn test_callback() { + let mut info = TestInfo { value: 0 }; + let context = CFFileDescriptorContext { + version: 0, + info: &mut info as *mut _ as *mut c_void, + retain: None, + release: None, + copyDescription: None + }; + + let path = CString::new("/dev/null").unwrap(); + let raw_fd = unsafe { libc::open(path.as_ptr(), O_RDWR, 0) }; + let cf_fd = CFFileDescriptor::new(raw_fd, true, callback, Some(&context)); + assert!(cf_fd.is_some()); + let cf_fd = cf_fd.unwrap(); + + assert!(cf_fd.valid()); + + let run_loop = CFRunLoop::get_current(); + let source = CFRunLoopSource::from_file_descriptor(&cf_fd, 0); + assert!(source.is_some()); + unsafe { + run_loop.add_source(&source.unwrap(), kCFRunLoopDefaultMode); + } + + info.value = 0; + cf_fd.enable_callbacks(kCFFileDescriptorReadCallBack); + CFRunLoop::run_current(); + assert_eq!(info.value, kCFFileDescriptorReadCallBack); + + info.value = 0; + cf_fd.enable_callbacks(kCFFileDescriptorWriteCallBack); + CFRunLoop::run_current(); + assert_eq!(info.value, kCFFileDescriptorWriteCallBack); + + info.value = 0; + cf_fd.disable_callbacks(kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack); + + cf_fd.invalidate(); + assert!(!cf_fd.valid()); + } + + extern "C" fn callback(_f: CFFileDescriptorRef, callback_types: CFOptionFlags, info_ptr: *mut c_void) { + assert!(!info_ptr.is_null()); + + let info: *mut TestInfo = info_ptr as *mut TestInfo; + + unsafe { (*info).value = callback_types }; + + CFRunLoop::get_current().stop(); + } +} diff --git a/third_party/rust/core-foundation/src/lib.rs b/third_party/rust/core-foundation/src/lib.rs new file mode 100644 index 0000000000..b935938996 --- /dev/null +++ b/third_party/rust/core-foundation/src/lib.rs @@ -0,0 +1,236 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_snake_case)] + +//! This crate provides wrappers around the underlying CoreFoundation +//! types and functions that are available on Apple's operating systems. +//! +//! It also provides a framework for other crates to use when wrapping +//! other frameworks that use the CoreFoundation framework. + +extern crate core_foundation_sys; +extern crate libc; + +#[cfg(feature = "with-chrono")] +extern crate chrono; + +use base::TCFType; + +pub unsafe trait ConcreteCFType: TCFType {} + +/// Declare a Rust type that wraps an underlying CoreFoundation type. +/// +/// This will provide an implementation of `Drop` using [`CFRelease`]. +/// The type must have an implementation of the [`TCFType`] trait, usually +/// provided using the [`impl_TCFType`] macro. +/// +/// ``` +/// #[macro_use] extern crate core_foundation; +/// // Make sure that the `TCFType` trait is in scope. +/// use core_foundation::base::{CFTypeID, TCFType}; +/// +/// extern "C" { +/// // We need a function that returns the `CFTypeID`. +/// pub fn ShrubberyGetTypeID() -> CFTypeID; +/// } +/// +/// pub struct __Shrubbery {} +/// // The ref type must be a pointer to the underlying struct. +/// pub type ShrubberyRef = *const __Shrubbery; +/// +/// declare_TCFType!(Shrubbery, ShrubberyRef); +/// impl_TCFType!(Shrubbery, ShrubberyRef, ShrubberyGetTypeID); +/// # fn main() {} +/// ``` +/// +/// [`CFRelease`]: https://developer.apple.com/documentation/corefoundation/1521153-cfrelease +/// [`TCFType`]: base/trait.TCFType.html +/// [`impl_TCFType`]: macro.impl_TCFType.html +#[macro_export] +macro_rules! declare_TCFType { + ( + $(#[$doc:meta])* + $ty:ident, $raw:ident + ) => { + $(#[$doc])* + pub struct $ty($raw); + + impl Drop for $ty { + fn drop(&mut self) { + unsafe { $crate::base::CFRelease(self.as_CFTypeRef()) } + } + } + } +} + +/// Provide an implementation of the [`TCFType`] trait for the Rust +/// wrapper type around an underlying CoreFoundation type. +/// +/// See [`declare_TCFType`] for details. +/// +/// [`declare_TCFType`]: macro.declare_TCFType.html +/// [`TCFType`]: base/trait.TCFType.html +#[macro_export] +macro_rules! impl_TCFType { + ($ty:ident, $ty_ref:ident, $ty_id:ident) => { + impl_TCFType!($ty<>, $ty_ref, $ty_id); + unsafe impl $crate::ConcreteCFType for $ty { } + }; + + ($ty:ident<$($p:ident $(: $bound:path)*),*>, $ty_ref:ident, $ty_id:ident) => { + impl<$($p $(: $bound)*),*> $crate::base::TCFType for $ty<$($p),*> { + type Ref = $ty_ref; + + #[inline] + fn as_concrete_TypeRef(&self) -> $ty_ref { + self.0 + } + + #[inline] + unsafe fn wrap_under_get_rule(reference: $ty_ref) -> Self { + assert!(!reference.is_null(), "Attempted to create a NULL object."); + let reference = $crate::base::CFRetain(reference as *const ::std::os::raw::c_void) as $ty_ref; + $crate::base::TCFType::wrap_under_create_rule(reference) + } + + #[inline] + fn as_CFTypeRef(&self) -> $crate::base::CFTypeRef { + self.as_concrete_TypeRef() as $crate::base::CFTypeRef + } + + #[inline] + unsafe fn wrap_under_create_rule(reference: $ty_ref) -> Self { + assert!(!reference.is_null(), "Attempted to create a NULL object."); + // we need one PhantomData for each type parameter so call ourselves + // again with @Phantom $p to produce that + $ty(reference $(, impl_TCFType!(@Phantom $p))*) + } + + #[inline] + fn type_id() -> $crate::base::CFTypeID { + unsafe { + $ty_id() + } + } + } + + impl Clone for $ty { + #[inline] + fn clone(&self) -> $ty { + unsafe { + $ty::wrap_under_get_rule(self.0) + } + } + } + + impl PartialEq for $ty { + #[inline] + fn eq(&self, other: &$ty) -> bool { + self.as_CFType().eq(&other.as_CFType()) + } + } + + 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 }; +} + + +/// Implement `std::fmt::Debug` for the given type. +/// +/// This will invoke the implementation of `Debug` for [`CFType`] +/// which invokes [`CFCopyDescription`]. +/// +/// The type must have an implementation of the [`TCFType`] trait, usually +/// provided using the [`impl_TCFType`] macro. +/// +/// [`CFType`]: base/struct.CFType.html#impl-Debug +/// [`CFCopyDescription`]: https://developer.apple.com/documentation/corefoundation/1521252-cfcopydescription?language=objc +/// [`TCFType`]: base/trait.TCFType.html +/// [`impl_TCFType`]: macro.impl_TCFType.html +#[macro_export] +macro_rules! impl_CFTypeDescription { + ($ty:ident) => { + // it's fine to use an empty <> list + impl_CFTypeDescription!($ty<>); + }; + ($ty:ident<$($p:ident $(: $bound:path)*),*>) => { + impl<$($p $(: $bound)*),*> ::std::fmt::Debug for $ty<$($p),*> { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + self.as_CFType().fmt(f) + } + } + } +} + +#[macro_export] +macro_rules! impl_CFComparison { + ($ty:ident, $compare:ident) => { + impl PartialOrd for $ty { + #[inline] + fn partial_cmp(&self, other: &$ty) -> Option<::std::cmp::Ordering> { + unsafe { + Some($compare(self.as_concrete_TypeRef(), other.as_concrete_TypeRef(), ::std::ptr::null_mut()).into()) + } + } + } + + impl Ord for $ty { + #[inline] + fn cmp(&self, other: &$ty) -> ::std::cmp::Ordering { + self.partial_cmp(other).unwrap() + } + } + } +} + +pub mod array; +pub mod attributed_string; +pub mod base; +pub mod boolean; +pub mod characterset; +pub mod data; +pub mod date; +pub mod dictionary; +pub mod error; +pub mod filedescriptor; +pub mod number; +pub mod set; +pub mod string; +pub mod url; +pub mod bundle; +pub mod propertylist; +pub mod runloop; +pub mod timezone; +pub mod uuid; +pub mod mach_port; diff --git a/third_party/rust/core-foundation/src/mach_port.rs b/third_party/rust/core-foundation/src/mach_port.rs new file mode 100644 index 0000000000..6112e3aae0 --- /dev/null +++ b/third_party/rust/core-foundation/src/mach_port.rs @@ -0,0 +1,28 @@ +use base::TCFType; +use core_foundation_sys::base::kCFAllocatorDefault; +use runloop::CFRunLoopSource; +pub use core_foundation_sys::mach_port::*; + + +declare_TCFType! { + /// An immutable numeric value. + CFMachPort, CFMachPortRef +} +impl_TCFType!(CFMachPort, CFMachPortRef, CFMachPortGetTypeID); +impl_CFTypeDescription!(CFMachPort); + +impl CFMachPort { + pub fn create_runloop_source( + &self, + order: CFIndex, + ) -> Result<CFRunLoopSource, ()> { + unsafe { + let runloop_source_ref = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.0, order); + if runloop_source_ref.is_null() { + Err(()) + } else { + Ok(CFRunLoopSource::wrap_under_create_rule(runloop_source_ref)) + } + } + } +} diff --git a/third_party/rust/core-foundation/src/number.rs b/third_party/rust/core-foundation/src/number.rs new file mode 100644 index 0000000000..a4b2affaa7 --- /dev/null +++ b/third_party/rust/core-foundation/src/number.rs @@ -0,0 +1,120 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Immutable numbers. + +use core_foundation_sys::base::kCFAllocatorDefault; +pub use core_foundation_sys::number::*; +use std::os::raw::c_void; + +use base::TCFType; + + +declare_TCFType!{ + /// An immutable numeric value. + CFNumber, CFNumberRef +} +impl_TCFType!(CFNumber, CFNumberRef, CFNumberGetTypeID); +impl_CFTypeDescription!(CFNumber); +impl_CFComparison!(CFNumber, CFNumberCompare); + +impl CFNumber { + + #[inline] + pub fn to_i32(&self) -> Option<i32> { + unsafe { + let mut value: i32 = 0; + let ok = CFNumberGetValue(self.0, kCFNumberSInt32Type, &mut value as *mut i32 as *mut c_void); + if ok { Some(value) } else { None } + } + } + + #[inline] + pub fn to_i64(&self) -> Option<i64> { + unsafe { + let mut value: i64 = 0; + let ok = CFNumberGetValue(self.0, kCFNumberSInt64Type, &mut value as *mut i64 as *mut c_void); + if ok { Some(value) } else { None } + } + } + + #[inline] + pub fn to_f32(&self) -> Option<f32> { + unsafe { + let mut value: f32 = 0.0; + let ok = CFNumberGetValue(self.0, kCFNumberFloat32Type, &mut value as *mut f32 as *mut c_void); + if ok { Some(value) } else { None } + } + } + + #[inline] + pub fn to_f64(&self) -> Option<f64> { + unsafe { + let mut value: f64 = 0.0; + let ok = CFNumberGetValue(self.0, kCFNumberFloat64Type, &mut value as *mut f64 as *mut c_void); + if ok { Some(value) } else { None } + } + } +} + +impl From<i32> for CFNumber { + #[inline] + fn from(value: i32) -> Self { + unsafe { + let number_ref = CFNumberCreate( + kCFAllocatorDefault, + kCFNumberSInt32Type, + &value as *const i32 as *const c_void, + ); + TCFType::wrap_under_create_rule(number_ref) + } + } +} + +impl From<i64> for CFNumber { + #[inline] + fn from(value: i64) -> Self { + unsafe { + let number_ref = CFNumberCreate( + kCFAllocatorDefault, + kCFNumberSInt64Type, + &value as *const i64 as *const c_void, + ); + TCFType::wrap_under_create_rule(number_ref) + } + } +} + +impl From<f32> for CFNumber { + #[inline] + fn from(value: f32) -> Self { + unsafe { + let number_ref = CFNumberCreate( + kCFAllocatorDefault, + kCFNumberFloat32Type, + &value as *const f32 as *const c_void, + ); + TCFType::wrap_under_create_rule(number_ref) + } + } +} + +impl From<f64> for CFNumber { + #[inline] + fn from(value: f64) -> Self { + unsafe { + let number_ref = CFNumberCreate( + kCFAllocatorDefault, + kCFNumberFloat64Type, + &value as *const f64 as *const c_void, + ); + TCFType::wrap_under_create_rule(number_ref) + } + } +} diff --git a/third_party/rust/core-foundation/src/propertylist.rs b/third_party/rust/core-foundation/src/propertylist.rs new file mode 100644 index 0000000000..e8fceac58d --- /dev/null +++ b/third_party/rust/core-foundation/src/propertylist.rs @@ -0,0 +1,329 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Core Foundation property lists + +use std::ptr; +use std::mem; +use std::os::raw::c_void; + +use error::CFError; +use data::CFData; +use base::{CFType, TCFType, TCFTypeRef}; + +pub use core_foundation_sys::propertylist::*; +use core_foundation_sys::error::CFErrorRef; +use core_foundation_sys::base::{CFGetRetainCount, CFGetTypeID, CFIndex, CFRetain, + CFShow, CFTypeID, kCFAllocatorDefault}; + +pub fn create_with_data(data: CFData, + options: CFPropertyListMutabilityOptions) + -> Result<(*const c_void, CFPropertyListFormat), CFError> { + unsafe { + let mut error: CFErrorRef = ptr::null_mut(); + let mut format: CFPropertyListFormat = 0; + let property_list = CFPropertyListCreateWithData(kCFAllocatorDefault, + data.as_concrete_TypeRef(), + options, + &mut format, + &mut error); + if property_list.is_null() { + Err(TCFType::wrap_under_create_rule(error)) + } else { + Ok((property_list, format)) + } + } +} + +pub fn create_data(property_list: *const c_void, format: CFPropertyListFormat) -> Result<CFData, CFError> { + unsafe { + let mut error: CFErrorRef = ptr::null_mut(); + let data_ref = CFPropertyListCreateData(kCFAllocatorDefault, + property_list, + format, + 0, + &mut error); + if data_ref.is_null() { + Err(TCFType::wrap_under_create_rule(error)) + } else { + Ok(TCFType::wrap_under_create_rule(data_ref)) + } + } +} + + +/// Trait for all subclasses of [`CFPropertyList`]. +/// +/// [`CFPropertyList`]: struct.CFPropertyList.html +pub trait CFPropertyListSubClass: TCFType { + /// Create an instance of the superclass type [`CFPropertyList`] for this instance. + /// + /// [`CFPropertyList`]: struct.CFPropertyList.html + #[inline] + fn to_CFPropertyList(&self) -> CFPropertyList { + unsafe { CFPropertyList::wrap_under_get_rule(self.as_concrete_TypeRef().as_void_ptr()) } + } + + /// Equal to [`to_CFPropertyList`], but consumes self and avoids changing the reference count. + /// + /// [`to_CFPropertyList`]: #method.to_CFPropertyList + #[inline] + fn into_CFPropertyList(self) -> CFPropertyList + where + Self: Sized, + { + let reference = self.as_concrete_TypeRef().as_void_ptr(); + mem::forget(self); + unsafe { CFPropertyList::wrap_under_create_rule(reference) } + } +} + +impl CFPropertyListSubClass for ::data::CFData {} +impl CFPropertyListSubClass for ::string::CFString {} +impl CFPropertyListSubClass for ::array::CFArray {} +impl CFPropertyListSubClass for ::dictionary::CFDictionary {} +impl CFPropertyListSubClass for ::date::CFDate {} +impl CFPropertyListSubClass for ::boolean::CFBoolean {} +impl CFPropertyListSubClass for ::number::CFNumber {} + + +declare_TCFType!{ + /// A CFPropertyList struct. This is superclass to [`CFData`], [`CFString`], [`CFArray`], + /// [`CFDictionary`], [`CFDate`], [`CFBoolean`], and [`CFNumber`]. + /// + /// This superclass type does not have its own `CFTypeID`, instead each instance has the `CFTypeID` + /// of the subclass it is an instance of. Thus, this type cannot implement the [`TCFType`] trait, + /// since it cannot implement the static [`TCFType::type_id()`] method. + /// + /// [`CFData`]: ../data/struct.CFData.html + /// [`CFString`]: ../string/struct.CFString.html + /// [`CFArray`]: ../array/struct.CFArray.html + /// [`CFDictionary`]: ../dictionary/struct.CFDictionary.html + /// [`CFDate`]: ../date/struct.CFDate.html + /// [`CFBoolean`]: ../boolean/struct.CFBoolean.html + /// [`CFNumber`]: ../number/struct.CFNumber.html + /// [`TCFType`]: ../base/trait.TCFType.html + /// [`TCFType::type_id()`]: ../base/trait.TCFType.html#method.type_of + CFPropertyList, CFPropertyListRef +} + +impl_CFTypeDescription!(CFPropertyList); + +impl CFPropertyList { + #[inline] + pub fn as_concrete_TypeRef(&self) -> CFPropertyListRef { + self.0 + } + + #[inline] + pub unsafe fn wrap_under_get_rule(reference: CFPropertyListRef) -> CFPropertyList { + assert!(!reference.is_null(), "Attempted to create a NULL object."); + let reference = CFRetain(reference); + CFPropertyList(reference) + } + + #[inline] + pub fn as_CFType(&self) -> CFType { + unsafe { CFType::wrap_under_get_rule(self.as_CFTypeRef()) } + } + + #[inline] + pub fn into_CFType(self) -> CFType + where + Self: Sized, + { + let reference = self.as_CFTypeRef(); + mem::forget(self); + unsafe { TCFType::wrap_under_create_rule(reference) } + } + + #[inline] + pub fn as_CFTypeRef(&self) -> ::core_foundation_sys::base::CFTypeRef { + self.as_concrete_TypeRef() + } + + #[inline] + pub unsafe fn wrap_under_create_rule(obj: CFPropertyListRef) -> CFPropertyList { + assert!(!obj.is_null(), "Attempted to create a NULL object."); + CFPropertyList(obj) + } + + /// 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] + pub fn retain_count(&self) -> CFIndex { + unsafe { CFGetRetainCount(self.as_CFTypeRef()) } + } + + /// Returns the type ID of this object. Will be one of CFData, CFString, CFArray, CFDictionary, + /// CFDate, CFBoolean, or CFNumber. + #[inline] + pub fn type_of(&self) -> CFTypeID { + unsafe { CFGetTypeID(self.as_CFTypeRef()) } + } + + /// Writes a debugging version of this object on standard error. + pub fn show(&self) { + unsafe { CFShow(self.as_CFTypeRef()) } + } + + /// Returns true if this value is an instance of another type. + #[inline] + pub fn instance_of<OtherCFType: TCFType>(&self) -> bool { + self.type_of() == OtherCFType::type_id() + } +} + +impl Clone for CFPropertyList { + #[inline] + fn clone(&self) -> CFPropertyList { + unsafe { CFPropertyList::wrap_under_get_rule(self.0) } + } +} + +impl PartialEq for CFPropertyList { + #[inline] + fn eq(&self, other: &CFPropertyList) -> bool { + self.as_CFType().eq(&other.as_CFType()) + } +} + +impl Eq for CFPropertyList {} + +impl CFPropertyList { + /// Try to downcast the [`CFPropertyList`] 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 [`CFType::downcast`]. + /// + /// # Examples + /// + /// ``` + /// # use core_foundation::string::CFString; + /// # use core_foundation::propertylist::{CFPropertyList, CFPropertyListSubClass}; + /// # + /// // Create a string. + /// let string: CFString = CFString::from_static_string("FooBar"); + /// // Cast it up to a property list. + /// let propertylist: CFPropertyList = string.to_CFPropertyList(); + /// // Cast it down again. + /// assert_eq!(propertylist.downcast::<CFString>().unwrap().to_string(), "FooBar"); + /// ``` + /// + /// [`CFPropertyList`]: struct.CFPropertyList.html + /// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast + pub fn downcast<T: CFPropertyListSubClass>(&self) -> Option<T> { + if self.instance_of::<T>() { + unsafe { + let subclass_ref = T::Ref::from_void_ptr(self.0); + Some(T::wrap_under_get_rule(subclass_ref)) + } + } else { + None + } + } + + /// Similar to [`downcast`], but consumes self and can thus avoid touching the retain count. + /// + /// [`downcast`]: #method.downcast + pub fn downcast_into<T: CFPropertyListSubClass>(self) -> Option<T> { + if self.instance_of::<T>() { + unsafe { + let subclass_ref = T::Ref::from_void_ptr(self.0); + mem::forget(self); + Some(T::wrap_under_create_rule(subclass_ref)) + } + } else { + None + } + } +} + + + +#[cfg(test)] +pub mod test { + use super::*; + use string::CFString; + use boolean::CFBoolean; + + #[test] + fn test_property_list_serialization() { + use base::{TCFType, CFEqual}; + use boolean::CFBoolean; + use number::CFNumber; + use dictionary::CFDictionary; + use string::CFString; + use super::*; + + let bar = CFString::from_static_string("Bar"); + let baz = CFString::from_static_string("Baz"); + let boo = CFString::from_static_string("Boo"); + let foo = CFString::from_static_string("Foo"); + let tru = CFBoolean::true_value(); + let n42 = CFNumber::from(1i64<<33); + + let dict1 = CFDictionary::from_CFType_pairs(&[(bar.as_CFType(), boo.as_CFType()), + (baz.as_CFType(), tru.as_CFType()), + (foo.as_CFType(), n42.as_CFType())]); + + let data = create_data(dict1.as_CFTypeRef(), kCFPropertyListXMLFormat_v1_0).unwrap(); + let (dict2, _) = create_with_data(data, kCFPropertyListImmutable).unwrap(); + unsafe { + assert_eq!(CFEqual(dict1.as_CFTypeRef(), dict2), 1); + } + } + + #[test] + fn to_propertylist_retain_count() { + let string = CFString::from_static_string("Bar"); + assert_eq!(string.retain_count(), 1); + + let propertylist = string.to_CFPropertyList(); + assert_eq!(string.retain_count(), 2); + assert_eq!(propertylist.retain_count(), 2); + + mem::drop(string); + assert_eq!(propertylist.retain_count(), 1); + } + + #[test] + fn downcast_string() { + let propertylist = CFString::from_static_string("Bar").to_CFPropertyList(); + assert_eq!(propertylist.downcast::<CFString>().unwrap().to_string(), "Bar"); + assert!(propertylist.downcast::<CFBoolean>().is_none()); + } + + #[test] + fn downcast_boolean() { + let propertylist = CFBoolean::true_value().to_CFPropertyList(); + assert!(propertylist.downcast::<CFBoolean>().is_some()); + assert!(propertylist.downcast::<CFString>().is_none()); + } + + #[test] + fn downcast_into_fail() { + let string = CFString::from_static_string("Bar"); + let propertylist = string.to_CFPropertyList(); + assert_eq!(string.retain_count(), 2); + + assert!(propertylist.downcast_into::<CFBoolean>().is_none()); + assert_eq!(string.retain_count(), 1); + } + + #[test] + fn downcast_into() { + let string = CFString::from_static_string("Bar"); + let propertylist = string.to_CFPropertyList(); + assert_eq!(string.retain_count(), 2); + + let string2 = propertylist.downcast_into::<CFString>().unwrap(); + assert_eq!(string2.to_string(), "Bar"); + assert_eq!(string2.retain_count(), 2); + } +} diff --git a/third_party/rust/core-foundation/src/runloop.rs b/third_party/rust/core-foundation/src/runloop.rs new file mode 100644 index 0000000000..be06f4ec7f --- /dev/null +++ b/third_party/rust/core-foundation/src/runloop.rs @@ -0,0 +1,224 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +pub use core_foundation_sys::runloop::*; +use core_foundation_sys::base::CFIndex; +use core_foundation_sys::base::{kCFAllocatorDefault, CFOptionFlags}; +use core_foundation_sys::string::CFStringRef; + +use base::{TCFType}; +use date::{CFAbsoluteTime, CFTimeInterval}; +use filedescriptor::CFFileDescriptor; +use string::{CFString}; + +pub type CFRunLoopMode = CFStringRef; + + +declare_TCFType!(CFRunLoop, CFRunLoopRef); +impl_TCFType!(CFRunLoop, CFRunLoopRef, CFRunLoopGetTypeID); +impl_CFTypeDescription!(CFRunLoop); + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum CFRunLoopRunResult { + Finished = 1, + Stopped = 2, + TimedOut = 3, + HandledSource = 4, +} + +impl CFRunLoop { + pub fn get_current() -> CFRunLoop { + unsafe { + let run_loop_ref = CFRunLoopGetCurrent(); + TCFType::wrap_under_get_rule(run_loop_ref) + } + } + + pub fn get_main() -> CFRunLoop { + unsafe { + let run_loop_ref = CFRunLoopGetMain(); + TCFType::wrap_under_get_rule(run_loop_ref) + } + } + + pub fn run_current() { + unsafe { + CFRunLoopRun(); + } + } + + pub fn run_in_mode( + mode: CFStringRef, + duration: std::time::Duration, + return_after_source_handled: bool, + ) -> CFRunLoopRunResult { + let seconds = duration.as_secs_f64(); + let return_after_source_handled = if return_after_source_handled { 1 } else { 0 }; + + unsafe { + match CFRunLoopRunInMode(mode, seconds, return_after_source_handled) { + 2 => CFRunLoopRunResult::Stopped, + 3 => CFRunLoopRunResult::TimedOut, + 4 => CFRunLoopRunResult::HandledSource, + _ => CFRunLoopRunResult::Finished, + } + } + } + + pub fn stop(&self) { + unsafe { + CFRunLoopStop(self.0); + } + } + + pub fn current_mode(&self) -> Option<String> { + unsafe { + let string_ref = CFRunLoopCopyCurrentMode(self.0); + if string_ref.is_null() { + return None; + } + + let cf_string: CFString = TCFType::wrap_under_create_rule(string_ref); + Some(cf_string.to_string()) + } + } + + pub fn contains_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) -> bool { + unsafe { + CFRunLoopContainsTimer(self.0, timer.0, mode) != 0 + } + } + + pub fn add_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) { + unsafe { + CFRunLoopAddTimer(self.0, timer.0, mode); + } + } + + pub fn remove_timer(&self, timer: &CFRunLoopTimer, mode: CFRunLoopMode) { + unsafe { + CFRunLoopRemoveTimer(self.0, timer.0, mode); + } + } + + pub fn contains_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) -> bool { + unsafe { + CFRunLoopContainsSource(self.0, source.0, mode) != 0 + } + } + + pub fn add_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) { + unsafe { + CFRunLoopAddSource(self.0, source.0, mode); + } + } + + pub fn remove_source(&self, source: &CFRunLoopSource, mode: CFRunLoopMode) { + unsafe { + CFRunLoopRemoveSource(self.0, source.0, mode); + } + } + + pub fn contains_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) -> bool { + unsafe { + CFRunLoopContainsObserver(self.0, observer.0, mode) != 0 + } + } + + pub fn add_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) { + unsafe { + CFRunLoopAddObserver(self.0, observer.0, mode); + } + } + + pub fn remove_observer(&self, observer: &CFRunLoopObserver, mode: CFRunLoopMode) { + unsafe { + CFRunLoopRemoveObserver(self.0, observer.0, mode); + } + } + +} + + +declare_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef); +impl_TCFType!(CFRunLoopTimer, CFRunLoopTimerRef, CFRunLoopTimerGetTypeID); + +impl CFRunLoopTimer { + pub fn new(fireDate: CFAbsoluteTime, interval: CFTimeInterval, flags: CFOptionFlags, order: CFIndex, callout: CFRunLoopTimerCallBack, context: *mut CFRunLoopTimerContext) -> CFRunLoopTimer { + unsafe { + let timer_ref = CFRunLoopTimerCreate(kCFAllocatorDefault, fireDate, interval, flags, order, callout, context); + TCFType::wrap_under_create_rule(timer_ref) + } + } +} + + +declare_TCFType!(CFRunLoopSource, CFRunLoopSourceRef); +impl_TCFType!(CFRunLoopSource, CFRunLoopSourceRef, CFRunLoopSourceGetTypeID); + +impl CFRunLoopSource { + pub fn from_file_descriptor(fd: &CFFileDescriptor, order: CFIndex) -> Option<CFRunLoopSource> { + fd.to_run_loop_source(order) + } +} + +declare_TCFType!(CFRunLoopObserver, CFRunLoopObserverRef); +impl_TCFType!(CFRunLoopObserver, CFRunLoopObserverRef, CFRunLoopObserverGetTypeID); + +#[cfg(test)] +mod test { + use super::*; + use date::{CFDate, CFAbsoluteTime}; + use std::mem; + use std::os::raw::c_void; + use std::sync::mpsc; + + #[test] + fn wait_200_milliseconds() { + let run_loop = CFRunLoop::get_current(); + + let now = CFDate::now().abs_time(); + let (elapsed_tx, elapsed_rx) = mpsc::channel(); + let mut info = Info { + start_time: now, + elapsed_tx, + }; + let mut context = CFRunLoopTimerContext { + version: 0, + info: &mut info as *mut _ as *mut c_void, + retain: None, + release: None, + copyDescription: None, + }; + + let run_loop_timer = CFRunLoopTimer::new(now + 0.20f64, 0f64, 0, 0, timer_popped, &mut context); + unsafe { + run_loop.add_timer(&run_loop_timer, kCFRunLoopDefaultMode); + } + CFRunLoop::run_current(); + let elapsed = elapsed_rx.try_recv().unwrap(); + println!("wait_200_milliseconds, elapsed: {}", elapsed); + assert!(elapsed > 0.19 && elapsed < 0.35); + } + + struct Info { + start_time: CFAbsoluteTime, + elapsed_tx: mpsc::Sender<f64>, + } + + extern "C" fn timer_popped(_timer: CFRunLoopTimerRef, raw_info: *mut c_void) { + let info: *mut Info = unsafe { mem::transmute(raw_info) }; + let now = CFDate::now().abs_time(); + let elapsed = now - unsafe { (*info).start_time }; + let _ = unsafe { (*info).elapsed_tx.send(elapsed) }; + CFRunLoop::get_current().stop(); + } +} diff --git a/third_party/rust/core-foundation/src/set.rs b/third_party/rust/core-foundation/src/set.rs new file mode 100644 index 0000000000..eb1d357a03 --- /dev/null +++ b/third_party/rust/core-foundation/src/set.rs @@ -0,0 +1,53 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An immutable bag of elements. + +pub use core_foundation_sys::set::*; +use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault}; + +use base::{CFIndexConvertible, TCFType}; + +use std::os::raw::c_void; +use std::marker::PhantomData; + +/// An immutable bag of elements. +pub struct CFSet<T = *const c_void>(CFSetRef, PhantomData<T>); + +impl<T> Drop for CFSet<T> { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} + +impl_TCFType!(CFSet<T>, CFSetRef, CFSetGetTypeID); +impl_CFTypeDescription!(CFSet); + +impl CFSet { + /// Creates a new set from a list of `CFType` instances. + pub fn from_slice<T>(elems: &[T]) -> CFSet<T> where T: TCFType { + unsafe { + let elems: Vec<CFTypeRef> = elems.iter().map(|elem| elem.as_CFTypeRef()).collect(); + let set_ref = CFSetCreate(kCFAllocatorDefault, + elems.as_ptr(), + elems.len().to_CFIndex(), + &kCFTypeSetCallBacks); + TCFType::wrap_under_create_rule(set_ref) + } + } +} + +impl<T> CFSet<T> { + /// Get the number of elements in the CFSet + pub fn len(&self) -> usize { + unsafe { + CFSetGetCount(self.0) as usize + } + } +} diff --git a/third_party/rust/core-foundation/src/string.rs b/third_party/rust/core-foundation/src/string.rs new file mode 100644 index 0000000000..3f5994bc5a --- /dev/null +++ b/third_party/rust/core-foundation/src/string.rs @@ -0,0 +1,197 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Immutable strings. + +pub use core_foundation_sys::string::*; + +use base::{CFIndexConvertible, TCFType}; + +use core_foundation_sys::base::{Boolean, CFIndex, CFRange}; +use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull}; +use std::borrow::Cow; +use std::fmt; +use std::str::{self, FromStr}; +use std::ptr; +use std::ffi::CStr; + + +declare_TCFType!{ + /// An immutable string in one of a variety of encodings. + CFString, CFStringRef +} +impl_TCFType!(CFString, CFStringRef, CFStringGetTypeID); + +impl FromStr for CFString { + type Err = (); + + /// See also CFString::new for a variant of this which does not return a Result + #[inline] + fn from_str(string: &str) -> Result<CFString, ()> { + Ok(CFString::new(string)) + } +} + +impl<'a> From<&'a str> for CFString { + #[inline] + fn from(string: &'a str) -> CFString { + CFString::new(string) + } +} + +impl<'a> From<&'a CFString> for Cow<'a, str> { + fn from(cf_str: &'a CFString) -> Cow<'a, str> { + unsafe { + // Do this without allocating if we can get away with it + let c_string = CFStringGetCStringPtr(cf_str.0, kCFStringEncodingUTF8); + if !c_string.is_null() { + let c_str = CStr::from_ptr(c_string); + Cow::Borrowed(str::from_utf8_unchecked(c_str.to_bytes())) + } else { + let char_len = cf_str.char_len(); + + // First, ask how big the buffer ought to be. + let mut bytes_required: CFIndex = 0; + CFStringGetBytes(cf_str.0, + CFRange { location: 0, length: char_len }, + kCFStringEncodingUTF8, + 0, + false as Boolean, + ptr::null_mut(), + 0, + &mut bytes_required); + + // Then, allocate the buffer and actually copy. + let mut buffer = vec![b'\x00'; bytes_required as usize]; + + let mut bytes_used: CFIndex = 0; + let chars_written = CFStringGetBytes(cf_str.0, + CFRange { location: 0, length: char_len }, + kCFStringEncodingUTF8, + 0, + false as Boolean, + buffer.as_mut_ptr(), + buffer.len().to_CFIndex(), + &mut bytes_used); + assert_eq!(chars_written, char_len); + + // This is dangerous; we over-allocate and null-terminate the string (during + // initialization). + assert_eq!(bytes_used, buffer.len().to_CFIndex()); + Cow::Owned(String::from_utf8_unchecked(buffer)) + } + } + } +} + +impl fmt::Display for CFString { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(&Cow::from(self)) + } +} + +impl fmt::Debug for CFString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "\"{}\"", self) + } +} + + +impl CFString { + /// Creates a new `CFString` instance from a Rust string. + #[inline] + pub fn new(string: &str) -> CFString { + unsafe { + let string_ref = CFStringCreateWithBytes(kCFAllocatorDefault, + string.as_ptr(), + string.len().to_CFIndex(), + kCFStringEncodingUTF8, + false as Boolean); + CFString::wrap_under_create_rule(string_ref) + } + } + + /// Like `CFString::new`, but references a string that can be used as a backing store + /// by virtue of being statically allocated. + #[inline] + pub fn from_static_string(string: &'static str) -> CFString { + unsafe { + let string_ref = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, + string.as_ptr(), + string.len().to_CFIndex(), + kCFStringEncodingUTF8, + false as Boolean, + kCFAllocatorNull); + TCFType::wrap_under_create_rule(string_ref) + } + } + + /// Returns the number of characters in the string. + #[inline] + pub fn char_len(&self) -> CFIndex { + unsafe { + CFStringGetLength(self.0) + } + } +} + +impl<'a> PartialEq<&'a str> for CFString { + fn eq(&self, other: &&str) -> bool { + unsafe { + let temp = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, + other.as_ptr(), + other.len().to_CFIndex(), + kCFStringEncodingUTF8, + false as Boolean, + kCFAllocatorNull); + self.eq(&CFString::wrap_under_create_rule(temp)) + } + } +} + +impl<'a> PartialEq<CFString> for &'a str { + #[inline] + fn eq(&self, other: &CFString) -> bool { + other.eq(self) + } +} + +impl PartialEq<CFString> for String { + #[inline] + fn eq(&self, other: &CFString) -> bool { + other.eq(&self.as_str()) + } +} + +impl PartialEq<String> for CFString { + #[inline] + fn eq(&self, other: &String) -> bool { + self.eq(&other.as_str()) + } +} + +#[test] +fn str_cmp() { + let cfstr = CFString::new("hello"); + assert_eq!("hello", cfstr); + assert_eq!(cfstr, "hello"); + assert_ne!(cfstr, "wrong"); + assert_ne!("wrong", cfstr); + let hello = String::from("hello"); + assert_eq!(hello, cfstr); + assert_eq!(cfstr, hello); +} + +#[test] +fn string_and_back() { + let original = "The quick brown fox jumped over the slow lazy dog."; + let cfstr = CFString::from_static_string(original); + let converted = cfstr.to_string(); + assert_eq!(converted, original); +} diff --git a/third_party/rust/core-foundation/src/timezone.rs b/third_party/rust/core-foundation/src/timezone.rs new file mode 100644 index 0000000000..a8bb2ed1d2 --- /dev/null +++ b/third_party/rust/core-foundation/src/timezone.rs @@ -0,0 +1,104 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Core Foundation time zone objects. + +pub use core_foundation_sys::timezone::*; +use core_foundation_sys::base::kCFAllocatorDefault; + +use base::TCFType; +use date::{CFDate, CFTimeInterval}; +use string::CFString; + +#[cfg(feature = "with-chrono")] +use chrono::{FixedOffset, NaiveDateTime}; + + +declare_TCFType!{ + /// A time zone. + CFTimeZone, CFTimeZoneRef +} +impl_TCFType!(CFTimeZone, CFTimeZoneRef, CFTimeZoneGetTypeID); +impl_CFTypeDescription!(CFTimeZone); + +impl Default for CFTimeZone { + fn default() -> CFTimeZone { + unsafe { + let tz_ref = CFTimeZoneCopyDefault(); + TCFType::wrap_under_create_rule(tz_ref) + } + } +} + +impl CFTimeZone { + #[inline] + pub fn new(interval: CFTimeInterval) -> CFTimeZone { + unsafe { + let tz_ref = CFTimeZoneCreateWithTimeIntervalFromGMT(kCFAllocatorDefault, interval); + TCFType::wrap_under_create_rule(tz_ref) + } + } + + #[inline] + pub fn system() -> CFTimeZone { + unsafe { + let tz_ref = CFTimeZoneCopySystem(); + TCFType::wrap_under_create_rule(tz_ref) + } + } + + pub fn seconds_from_gmt(&self, date: CFDate) -> CFTimeInterval { + unsafe { + CFTimeZoneGetSecondsFromGMT(self.0, date.abs_time()) + } + } + + #[cfg(feature = "with-chrono")] + pub fn offset_at_date(&self, date: NaiveDateTime) -> FixedOffset { + let date = CFDate::from_naive_utc(date); + FixedOffset::east(self.seconds_from_gmt(date) as i32) + } + + #[cfg(feature = "with-chrono")] + pub fn from_offset(offset: FixedOffset) -> CFTimeZone { + CFTimeZone::new(offset.local_minus_utc() as f64) + } + + /// The timezone database ID that identifies the time zone. E.g. "America/Los_Angeles" or + /// "Europe/Paris". + pub fn name(&self) -> CFString { + unsafe { + CFString::wrap_under_get_rule(CFTimeZoneGetName(self.0)) + } + } +} + +#[cfg(test)] +mod test { + use super::CFTimeZone; + + #[cfg(feature = "with-chrono")] + use chrono::{NaiveDateTime, FixedOffset}; + + #[test] + fn timezone_comparison() { + let system = CFTimeZone::system(); + let default = CFTimeZone::default(); + assert_eq!(system, default); + } + + #[test] + #[cfg(feature = "with-chrono")] + fn timezone_chrono_conversion() { + let offset = FixedOffset::west(28800); + let tz = CFTimeZone::from_offset(offset); + let converted = tz.offset_at_date(NaiveDateTime::from_timestamp(0, 0)); + assert_eq!(offset, converted); + } +} diff --git a/third_party/rust/core-foundation/src/url.rs b/third_party/rust/core-foundation/src/url.rs new file mode 100644 index 0000000000..064dd7b5e0 --- /dev/null +++ b/third_party/rust/core-foundation/src/url.rs @@ -0,0 +1,155 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A URL type for Core Foundation. + +pub use core_foundation_sys::url::*; + +use base::{TCFType, CFIndex}; +use string::{CFString}; + +use core_foundation_sys::base::{kCFAllocatorDefault, Boolean}; +use std::fmt; +use std::ptr; +use std::path::{Path, PathBuf}; + +use libc::{c_char, strlen, PATH_MAX}; + +#[cfg(unix)] +use std::os::unix::ffi::OsStrExt; +#[cfg(unix)] +use std::ffi::OsStr; + + +declare_TCFType!(CFURL, CFURLRef); +impl_TCFType!(CFURL, CFURLRef, CFURLGetTypeID); + +impl fmt::Debug for CFURL { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let string: CFString = TCFType::wrap_under_get_rule(CFURLGetString(self.0)); + write!(f, "{}", string.to_string()) + } + } +} + +impl CFURL { + pub fn from_path<P: AsRef<Path>>(path: P, isDirectory: bool) -> Option<CFURL> { + let path_bytes; + #[cfg(unix)] + { + path_bytes = path.as_ref().as_os_str().as_bytes() + } + #[cfg(not(unix))] + { + // XXX: Getting non-valid UTF8 paths into CoreFoundation on Windows is going to be unpleasant + // CFURLGetWideFileSystemRepresentation might help + path_bytes = match path.as_ref().to_str() { + Some(path) => path, + None => return None, + } + } + + unsafe { + let url_ref = CFURLCreateFromFileSystemRepresentation(ptr::null_mut(), path_bytes.as_ptr(), path_bytes.len() as CFIndex, isDirectory as u8); + if url_ref.is_null() { + return None; + } + Some(TCFType::wrap_under_create_rule(url_ref)) + } + } + + pub fn from_file_system_path(filePath: CFString, pathStyle: CFURLPathStyle, isDirectory: bool) -> CFURL { + unsafe { + let url_ref = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath.as_concrete_TypeRef(), pathStyle, isDirectory as u8); + TCFType::wrap_under_create_rule(url_ref) + } + } + + #[cfg(unix)] + pub fn to_path(&self) -> Option<PathBuf> { + // implementing this on Windows is more complicated because of the different OsStr representation + unsafe { + let mut buf = [0u8; PATH_MAX as usize]; + let result = CFURLGetFileSystemRepresentation(self.0, true as Boolean, buf.as_mut_ptr(), buf.len() as CFIndex); + if result == false as Boolean { + return None; + } + let len = strlen(buf.as_ptr() as *const c_char); + let path = OsStr::from_bytes(&buf[0..len]); + Some(PathBuf::from(path)) + } + } + + pub fn get_string(&self) -> CFString { + unsafe { + TCFType::wrap_under_get_rule(CFURLGetString(self.0)) + } + } + + pub fn get_file_system_path(&self, pathStyle: CFURLPathStyle) -> CFString { + unsafe { + TCFType::wrap_under_create_rule(CFURLCopyFileSystemPath(self.as_concrete_TypeRef(), pathStyle)) + } + } + + pub fn absolute(&self) -> CFURL { + unsafe { + TCFType::wrap_under_create_rule(CFURLCopyAbsoluteURL(self.as_concrete_TypeRef())) + } + } +} + +#[test] +fn file_url_from_path() { + let path = "/usr/local/foo/"; + let cfstr_path = CFString::from_static_string(path); + let cfurl = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true); + assert_eq!(cfurl.get_string().to_string(), "file:///usr/local/foo/"); +} + +#[cfg(unix)] +#[test] +fn non_utf8() { + use std::ffi::OsStr; + let path = Path::new(OsStr::from_bytes(b"/\xC0/blame")); + let cfurl = CFURL::from_path(path, false).unwrap(); + assert_eq!(cfurl.to_path().unwrap(), path); + let len = unsafe { CFURLGetBytes(cfurl.as_concrete_TypeRef(), ptr::null_mut(), 0) }; + assert_eq!(len, 17); +} + +#[test] +fn absolute_file_url() { + use core_foundation_sys::url::CFURLCreateWithFileSystemPathRelativeToBase; + use std::path::PathBuf; + + let path = "/usr/local/foo"; + let file = "bar"; + + let cfstr_path = CFString::from_static_string(path); + let cfstr_file = CFString::from_static_string(file); + let cfurl_base = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true); + let cfurl_relative: CFURL = unsafe { + let url_ref = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorDefault, + cfstr_file.as_concrete_TypeRef(), + kCFURLPOSIXPathStyle, + false as u8, + cfurl_base.as_concrete_TypeRef()); + TCFType::wrap_under_create_rule(url_ref) + }; + + let mut absolute_path = PathBuf::from(path); + absolute_path.push(file); + + assert_eq!(cfurl_relative.get_file_system_path(kCFURLPOSIXPathStyle).to_string(), file); + assert_eq!(cfurl_relative.absolute().get_file_system_path(kCFURLPOSIXPathStyle).to_string(), + absolute_path.to_str().unwrap()); +} diff --git a/third_party/rust/core-foundation/src/uuid.rs b/third_party/rust/core-foundation/src/uuid.rs new file mode 100644 index 0000000000..6be734dabc --- /dev/null +++ b/third_party/rust/core-foundation/src/uuid.rs @@ -0,0 +1,118 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Core Foundation UUID objects. + +#[cfg(feature = "with-uuid")] +extern crate uuid; + +pub use core_foundation_sys::uuid::*; +use core_foundation_sys::base::kCFAllocatorDefault; + +use base::TCFType; + +#[cfg(feature = "with-uuid")] +use self::uuid::Uuid; + + +declare_TCFType! { + /// A UUID. + CFUUID, CFUUIDRef +} +impl_TCFType!(CFUUID, CFUUIDRef, CFUUIDGetTypeID); +impl_CFTypeDescription!(CFUUID); + +impl CFUUID { + #[inline] + pub fn new() -> CFUUID { + unsafe { + let uuid_ref = CFUUIDCreate(kCFAllocatorDefault); + TCFType::wrap_under_create_rule(uuid_ref) + } + } +} + +impl Default for CFUUID { + fn default() -> Self { + Self::new() + } +} + +#[cfg(feature = "with-uuid")] +impl Into<Uuid> for CFUUID { + fn into(self) -> Uuid { + let b = unsafe { + CFUUIDGetUUIDBytes(self.0) + }; + let bytes = [ + b.byte0, + b.byte1, + b.byte2, + b.byte3, + b.byte4, + b.byte5, + b.byte6, + b.byte7, + b.byte8, + b.byte9, + b.byte10, + b.byte11, + b.byte12, + b.byte13, + b.byte14, + b.byte15, + ]; + Uuid::from_bytes(&bytes).unwrap() + } +} + +#[cfg(feature = "with-uuid")] +impl From<Uuid> for CFUUID { + fn from(uuid: Uuid) -> CFUUID { + let b = uuid.as_bytes(); + let bytes = CFUUIDBytes { + byte0: b[0], + byte1: b[1], + byte2: b[2], + byte3: b[3], + byte4: b[4], + byte5: b[5], + byte6: b[6], + byte7: b[7], + byte8: b[8], + byte9: b[9], + byte10: b[10], + byte11: b[11], + byte12: b[12], + byte13: b[13], + byte14: b[14], + byte15: b[15], + }; + unsafe { + let uuid_ref = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, bytes); + TCFType::wrap_under_create_rule(uuid_ref) + } + } +} + + +#[cfg(test)] +#[cfg(feature = "with-uuid")] +mod test { + use super::CFUUID; + use uuid::Uuid; + + #[test] + fn uuid_conversion() { + let cf_uuid = CFUUID::new(); + let uuid: Uuid = cf_uuid.clone().into(); + let converted = CFUUID::from(uuid); + assert_eq!(cf_uuid, converted); + } +} |