Auto merge of #265 - faern:dictionary-conversion, r=jdm

Converting between dictionary types

I'm trying to use the `CFDictionary` and `CFMutableDictionary` types more extensively. But run into problems. They are not very compatible with each other. This crate for example does not expose any clean way of using `CFDictionaryCreateMutableCopy` to create a `CFMutableDictionary` from any `CFDictionary`. I added a `From` implementation for this.

It was not possible to, in some safe/simple way, cast a `CFDictionary<K, V>` into the untyped `CFDictionary<*const c_void, *const c_void>` something I added `to_untyped` to be able to do.

When it comes to downcasting, it was not possible to downcast a `CFType` into a dictionary, since they don't implement `ConcreteCFType`. Something they probably should, for the case where the keys and values are just void pointers. `CFArray` does exactly this.

Lastly, what I needed the most was to be able to convert a `CFMutableDictionary` into a `CFDictionary`. This allows me to borrow an immutable `CFDictionary` via the `ItemRef` type basically. It's possible to work around this and actually have owned instances of a `CFDictionary` and a `CFMutableDictionary` pointing to the same actual dictionary by doing this:
```rust
let mut_dict = CFMutableDictionary::new();
let mut_dict2 = mut_dict.clone();
let dict = mut_dict.as_immutable();
// Here one can mutate the dict via `mut_dict2` while `dict` is still in scope/alive.
```
But since we already allow cloning a `CFMutableDictionary` to have two mutable references to the same dictionary I guess someone decided this should be viewed as safe. Both `CFDictionary` and `CFMutableDictionary` does not implement `Send` so we should not end up in any actual thread inconsistencies here.

If the above is indeed considered safe, I could get rid of the `ItemRef` juggling and just make it `fn as_immutable(&self) -> CFDictionary` directly maybe? Would simplify usage.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/core-foundation-rs/265)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo
2018-10-29 08:57:26 -04:00
committed by GitHub
2 changed files with 83 additions and 3 deletions
+80
View File
@@ -19,6 +19,7 @@ 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>);
@@ -32,6 +33,8 @@ impl<K, V> Drop for CFDictionary<K, V> {
impl_TCFType!(CFDictionary<K, V>, CFDictionaryRef, CFDictionaryGetTypeID);
impl_CFTypeDescription!(CFDictionary);
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
@@ -50,6 +53,20 @@ impl<K, V> CFDictionary<K, V> {
}
}
#[inline]
pub fn to_untyped(&self) -> CFDictionary {
unsafe { CFDictionary::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) -> CFDictionary {
let reference = self.0;
mem::forget(self);
unsafe { CFDictionary::wrap_under_create_rule(reference) }
}
#[inline]
pub fn len(&self) -> usize {
unsafe {
@@ -146,6 +163,26 @@ impl<K, V> CFMutableDictionary<K, V> {
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]
@@ -235,6 +272,17 @@ impl<K, V> CFMutableDictionary<K, V> {
}
}
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 {
@@ -313,4 +361,36 @@ pub mod test {
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);
}
}
+3 -3
View File
@@ -97,21 +97,21 @@ macro_rules! impl_TCFType {
unsafe impl<'a> $crate::base::ToVoid<$ty> for &'a $ty {
fn to_void(&self) -> *const ::std::os::raw::c_void {
use $crate::base::TCFTypeRef;
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;
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;
use $crate::base::TCFTypeRef;
self.as_void_ptr()
}
}