Auto merge of #135 - djg:dict_mut, r=jdm

Add CFMutableDictionary type.

Added CFMutableDictionary that presents the mutable dictionary
functions of CF. Tries to present a similar interface as CFDictionary.

Fixes issue #93.

<!-- 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/135)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo
2018-01-19 17:25:41 -06:00
committed by GitHub
3 changed files with 252 additions and 5 deletions
+17 -3
View File
@@ -67,11 +67,25 @@ extern {
pub fn CFDictionaryApplyFunction(theDict: CFDictionaryRef,
applier: CFDictionaryApplierFunction,
context: *mut c_void);
pub fn CFDictionarySetValue(theDict: CFMutableDictionaryRef,
key: *const c_void,
value: *const c_void);
pub fn CFDictionaryGetKeysAndValues(theDict: CFDictionaryRef,
keys: *mut *const c_void,
values: *mut *const c_void);
pub fn CFDictionaryCreateMutable(allocator: CFAllocatorRef, capacity: CFIndex,
keyCallbacks: *const CFDictionaryKeyCallBacks,
valueCallbacks: *const CFDictionaryValueCallBacks) -> CFMutableDictionaryRef;
pub fn CFDictionaryCreateMutableCopy(allocator: CFAllocatorRef, capacity: CFIndex,
theDict: CFDictionaryRef) -> CFMutableDictionaryRef;
pub fn CFDictionaryAddValue(theDict: CFMutableDictionaryRef,
key: *const c_void,
value: *const c_void);
pub fn CFDictionarySetValue(theDict: CFMutableDictionaryRef,
key: *const c_void,
value: *const c_void);
pub fn CFDictionaryReplaceValue(theDict: CFMutableDictionaryRef,
key: *const c_void,
value: *const c_void);
pub fn CFDictionaryRemoveValue(theDict: CFMutableDictionaryRef,
key: *const c_void);
pub fn CFDictionaryRemoveAllValues(theDict: CFMutableDictionaryRef);
}
+199 -2
View File
@@ -112,9 +112,129 @@ impl CFDictionary {
TCFType::wrap_under_get_rule(value)
}
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)
}
}
/// An mutable dictionary of key-value pairs.
pub struct CFMutableDictionary(CFMutableDictionaryRef);
impl Drop for CFMutableDictionary {
fn drop(&mut self) {
unsafe {
CFRelease(self.as_CFTypeRef())
}
}
}
impl_TCFType!(CFMutableDictionary, CFMutableDictionaryRef, CFDictionaryGetTypeID);
impl_CFTypeDescription!(CFMutableDictionary);
impl CFMutableDictionary {
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<R1, R2, K, V>(pairs: &[(K, V)]) -> CFMutableDictionary
where K: TCFType<R1>, V: TCFType<R2> {
let result = Self::with_capacity(pairs.len() as _);
unsafe {
for &(ref key, ref value) in pairs {
result.add(key.as_CFTypeRef(), value.as_CFTypeRef());
}
}
result
}
// Immutable interface
#[inline]
pub unsafe fn set_value(&self, key: *const c_void, value: *const c_void) {
CFDictionarySetValue(self.0, key, value)
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
}
}
/// Similar to `contains_key` but acts on a higher level, automatically converting from any
/// `TCFType` to the raw pointer of its concrete TypeRef.
#[inline]
pub fn contains_key2<X, K: TCFType<*const X>>(&self, key: &K) -> bool {
self.contains_key(key.as_concrete_TypeRef() as *const c_void)
}
#[inline]
pub fn find(&self, key: *const c_void) -> Option<*const c_void> {
unsafe {
let mut value: *const c_void = ptr::null();
if CFDictionaryGetValueIfPresent(self.0, key, &mut value) != 0 {
Some(value)
} else {
None
}
}
}
/// Similar to `find` but acts on a higher level, automatically converting from any `TCFType`
/// to the raw pointer of its concrete TypeRef.
#[inline]
pub fn find2<X, K: TCFType<*const X>>(&self, key: &K) -> Option<*const c_void> {
self.find(key.as_concrete_TypeRef() as *const c_void)
}
/// # Panics
///
/// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
/// of panicking.
#[inline]
pub fn get(&self, key: *const c_void) -> *const c_void {
self.find(key).expect(&format!("No entry found for key {:p}", key))
}
/// A convenience function to retrieve `CFType` instances.
#[inline]
pub unsafe fn get_CFType(&self, key: *const c_void) -> CFType {
let value: CFTypeRef = mem::transmute(self.get(key));
TCFType::wrap_under_get_rule(value)
}
pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
@@ -130,4 +250,81 @@ impl CFDictionary {
(keys, values)
}
// Mutable interface
/// Adds the key-value pair to the dictionary if no such key already exist.
#[inline]
pub unsafe fn add(&self, key: *const c_void, value: *const c_void) {
CFDictionaryAddValue(self.0, key, value)
}
/// Similar to `add` but acts on a higher level, automatically converting from any `TCFType`
/// to the raw pointer of its concrete TypeRef.
#[inline]
pub fn add2<RK, RV, K, V>(&self, key: &K, value: &V)
where K: TCFType<*const RK>,
V: TCFType<*const RV> {
unsafe {
self.add(key.as_concrete_TypeRef() as *const _,
value.as_concrete_TypeRef() as *const _)
}
}
/// Sets the value of the key in the dictionary.
#[inline]
pub unsafe fn set(&self, key: *const c_void, value: *const c_void) {
CFDictionarySetValue(self.0, key, value)
}
/// Similar to `set` but acts on a higher level, automatically converting from any `TCFType`
/// to the raw pointer of its concrete TypeRef.
#[inline]
pub fn set2<RK, RV, K, V>(&self, key: &K, value: &V)
where K: TCFType<*const RK>,
V: TCFType<*const RV> {
unsafe {
self.set(key.as_concrete_TypeRef() as *const c_void,
value.as_concrete_TypeRef() as *const c_void)
}
}
/// Replaces the value of the key in the dictionary.
#[inline]
pub unsafe fn replace(&self, key: *const c_void, value: *const c_void) {
CFDictionaryReplaceValue(self.0, key, value)
}
/// Similar to `replace` but acts on a higher level, automatically converting from any `TCFType`
/// to the raw pointer of its concrete TypeRef.
#[inline]
pub fn replace2<RK, RV, K, V>(&self, key: &K, value: &V)
where K: TCFType<*const RK>,
V: TCFType<*const RV> {
unsafe {
self.replace(key.as_concrete_TypeRef() as *const c_void,
value.as_concrete_TypeRef() as *const c_void)
}
}
/// Removes the value of the key from the dictionary.
#[inline]
pub unsafe fn remove(&self, key: *const c_void) {
CFDictionaryRemoveValue(self.0, key);
}
/// Similar to `remove` but acts on a higher level, automatically converting from any `TCFType`
/// to the raw pointer of its concrete TypeRef.
#[inline]
pub fn remove2<RK, K>(&self, key: &K)
where K: TCFType<*const RK> {
unsafe {
self.remove(key.as_concrete_TypeRef() as *const c_void)
}
}
#[inline]
pub fn remove_all(&self) {
unsafe { CFDictionaryRemoveAllValues(self.0) }
}
}
+36
View File
@@ -219,4 +219,40 @@ pub mod test {
assert!(v1 == &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
assert!(v2 == &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
}
#[test]
fn test_mutable_dictionary() {
use base::TCFType;
use boolean::CFBoolean;
use dictionary::CFMutableDictionary;
use number::CFNumber;
use string::CFString;
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 = CFMutableDictionary::new();
d.add2(&bar, &boo);
d.add2(&baz, &tru);
d.add2(&foo, &n42);
assert_eq!(d.len(), 3);
let (v1, v2) = d.get_keys_and_values();
assert!(v1 == &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
assert!(v2 == &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
d.remove2(&baz);
assert_eq!(d.len(), 2);
let (v1, v2) = d.get_keys_and_values();
assert!(v1 == &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]);
assert!(v2 == &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]);
d.remove_all();
assert_eq!(d.len(), 0)
}
}