Skip to content

Add a type parameter to CFArray #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 48 additions & 21 deletions core-foundation/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,56 @@
pub use core_foundation_sys::array::*;
pub use core_foundation_sys::base::{CFIndex, CFRelease};
use core_foundation_sys::base::{CFTypeRef, kCFAllocatorDefault};
use base::CFType;
use libc::c_void;
use std::mem;
use std::marker::PhantomData;

use base::{CFIndexConvertible, TCFType, CFRange};

/// A heterogeneous immutable array.
pub struct CFArray(CFArrayRef);
pub struct CFArray<T = *const c_void>(CFArrayRef, PhantomData<T>);

impl Drop for CFArray {
/// A trait describing how to convert from the stored *const c_void to the desired T
pub trait FromVoid {
fn from_void(x: *const c_void) -> Self;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be unsafe.

}

impl FromVoid for u32 {
fn from_void(x: *const c_void) -> u32 {
x as usize as u32
}
}

impl FromVoid for *const c_void {
fn from_void(x: *const c_void) -> *const c_void {
x
}
}

impl FromVoid for CFType {
fn from_void(x: *const c_void) -> CFType {
unsafe { TCFType::wrap_under_get_rule(mem::transmute(x)) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unsound, one can pass 0xdeadbeef as *const c_void or any other arbitrary and trigger UB.

}
}

impl<T> Drop for CFArray<T> {
fn drop(&mut self) {
unsafe {
CFRelease(self.as_CFTypeRef())
}
}
}

pub struct CFArrayIterator<'a> {
array: &'a CFArray,
pub struct CFArrayIterator<'a, T: 'a> {
array: &'a CFArray<T>,
index: CFIndex,
}

impl<'a> Iterator for CFArrayIterator<'a> {
type Item = *const c_void;
impl<'a, T: FromVoid> Iterator for CFArrayIterator<'a, T> {
type Item = T;

fn next(&mut self) -> Option<*const c_void> {
fn next(&mut self) -> Option<T> {
if self.index >= self.array.len() {
None
} else {
Expand All @@ -47,18 +72,18 @@ impl<'a> Iterator for CFArrayIterator<'a> {
}
}

impl<'a> ExactSizeIterator for CFArrayIterator<'a> {
impl<'a, T: FromVoid> ExactSizeIterator for CFArrayIterator<'a, T> {
fn len(&self) -> usize {
(self.array.len() - self.index) as usize
}
}

impl_TCFType!(CFArray, CFArrayRef, CFArrayGetTypeID);
impl_CFTypeDescription!(CFArray);
impl_TCFTypeGeneric!(CFArray, CFArrayRef, CFArrayGetTypeID);
impl_CFTypeDescriptionGeneric!(CFArray);

impl CFArray {
impl<T> CFArray<T> {
/// Creates a new `CFArray` with the given elements, which must be `CFType` objects.
pub fn from_CFTypes<R, T>(elems: &[T]) -> CFArray where T: TCFType<R> {
pub fn from_CFTypes<R>(elems: &[T]) -> CFArray<T> where T: TCFType<R> {
unsafe {
let elems: Vec<CFTypeRef> = elems.iter().map(|elem| elem.as_CFTypeRef()).collect();
let array_ref = CFArrayCreate(kCFAllocatorDefault,
Expand All @@ -69,13 +94,17 @@ impl CFArray {
}
}

pub fn to_untyped(self) -> CFArray {
CFArray(self.0, PhantomData)
}

/// 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> {
pub fn iter<'a>(&'a self) -> CFArrayIterator<'a, T> {
CFArrayIterator {
array: self,
index: 0
Expand All @@ -90,11 +119,9 @@ impl CFArray {
}

#[inline]
pub fn get(&self, index: CFIndex) -> *const c_void {
pub fn get(&self, index: CFIndex) -> T where T: FromVoid {
assert!(index < self.len());
unsafe {
CFArrayGetValueAtIndex(self.0, index)
}
T::from_void(unsafe { CFArrayGetValueAtIndex(self.0, index) })
}

pub fn get_values(&self, range: CFRange) -> Vec<*const c_void> {
Expand All @@ -114,11 +141,11 @@ impl CFArray {
}
}

impl<'a> IntoIterator for &'a CFArray {
type Item = *const c_void;
type IntoIter = CFArrayIterator<'a>;
impl<'a, T: FromVoid> IntoIterator for &'a CFArray<T> {
type Item = T;
type IntoIter = CFArrayIterator<'a, T>;

fn into_iter(self) -> CFArrayIterator<'a> {
fn into_iter(self) -> CFArrayIterator<'a, T> {
self.iter()
}
}
Expand Down
69 changes: 69 additions & 0 deletions core-foundation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,63 @@ macro_rules! impl_TCFType {
}
}

// This is basically identical to the implementation above. I can't
// think of a clean way to have them share code
#[macro_export]
macro_rules! impl_TCFTypeGeneric {
($ty:ident, $raw:ident, $ty_id:ident) => {
impl<T> $crate::base::TCFType<$raw> for $ty<T> {
#[inline]
fn as_concrete_TypeRef(&self) -> $raw {
self.0
}

#[inline]
unsafe fn wrap_under_get_rule(reference: $raw) -> $ty<T> {
let reference = ::std::mem::transmute(::core_foundation_sys::base::CFRetain(::std::mem::transmute(reference)));
$crate::base::TCFType::wrap_under_create_rule(reference)
}

#[inline]
fn as_CFTypeRef(&self) -> ::core_foundation_sys::base::CFTypeRef {
unsafe {
::std::mem::transmute(self.as_concrete_TypeRef())
}
}

#[inline]
unsafe fn wrap_under_create_rule(obj: $raw) -> $ty<T> {
$ty(obj, PhantomData)
}

#[inline]
fn type_id() -> ::core_foundation_sys::base::CFTypeID {
unsafe {
$ty_id()
}
}
}

impl<T> Clone for $ty<T> {
#[inline]
fn clone(&self) -> $ty<T> {
unsafe {
$ty::wrap_under_get_rule(self.0)
}
}
}

impl<T> PartialEq for $ty<T> {
#[inline]
fn eq(&self, other: &$ty<T>) -> bool {
self.as_CFType().eq(&other.as_CFType())
}
}

impl<T> Eq for $ty<T> { }
}
}

#[macro_export]
macro_rules! impl_CFTypeDescription {
($ty:ident) => {
Expand All @@ -80,6 +137,18 @@ macro_rules! impl_CFTypeDescription {
}
}

// The same as impl_CFTypeDescription but with a type parameter
#[macro_export]
macro_rules! impl_CFTypeDescriptionGeneric {
($ty:ident) => {
impl<T> ::std::fmt::Debug for $ty<T> {
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) => {
Expand Down