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