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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user