410 lines
14 KiB
Rust
410 lines
14 KiB
Rust
// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
|
|
// file at the top-level directory of this distribution.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
|
|
//! Dictionaries of key-value pairs.
|
|
|
|
pub use core_foundation_sys::dictionary::*;
|
|
|
|
use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault};
|
|
use std::mem;
|
|
use std::os::raw::c_void;
|
|
use std::ptr;
|
|
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>);
|
|
|
|
impl<K, V> Drop for CFDictionary<K, V> {
|
|
fn drop(&mut self) {
|
|
unsafe { CFRelease(self.as_CFTypeRef()) }
|
|
}
|
|
}
|
|
|
|
impl_TCFType!(CFDictionary<K, V>, CFDictionaryRef, CFDictionaryGetTypeID);
|
|
impl_CFTypeDescription!(CFDictionary<K, V>);
|
|
|
|
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
|
|
.iter()
|
|
.map(|&(ref key, ref value)| (key.as_CFTypeRef(), value.as_CFTypeRef()))
|
|
.unzip();
|
|
|
|
unsafe {
|
|
let dictionary_ref = CFDictionaryCreate(kCFAllocatorDefault,
|
|
keys.as_ptr(),
|
|
values.as_ptr(),
|
|
keys.len().to_CFIndex(),
|
|
&kCFTypeDictionaryKeyCallBacks,
|
|
&kCFTypeDictionaryValueCallBacks);
|
|
TCFType::wrap_under_create_rule(dictionary_ref)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn to_untyped(&self) -> CFDictionary {
|
|
unsafe { CFDictionary::wrap_under_get_rule(self.0) }
|
|
}
|
|
|
|
/// Returns a `CFMutableDictionary` pointing to the same underlying dictionary as this immutable one.
|
|
/// This should only be used when the underlying dictionary is mutable.
|
|
#[inline]
|
|
pub unsafe fn to_mutable(&self) -> CFMutableDictionary<K, V> {
|
|
CFMutableDictionary::wrap_under_get_rule(self.0 as CFMutableDictionaryRef)
|
|
}
|
|
|
|
/// 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 {
|
|
CFDictionaryGetCount(self.0) as usize
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn is_empty(&self) -> bool {
|
|
self.len() == 0
|
|
}
|
|
|
|
#[inline]
|
|
pub fn contains_key(&self, key: &K) -> bool where K: ToVoid<K> {
|
|
unsafe { CFDictionaryContainsKey(self.0, key.to_void()) != 0 }
|
|
}
|
|
|
|
#[inline]
|
|
pub fn find<'a, T: ToVoid<K>>(&'a self, key: T) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> {
|
|
unsafe {
|
|
let mut value: *const c_void = ptr::null();
|
|
if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
|
|
Some(V::from_void(value))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// # Panics
|
|
///
|
|
/// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
|
|
/// of panicking.
|
|
#[inline]
|
|
pub fn get<'a, T: ToVoid<K>>(&'a self, key: T) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> {
|
|
let ptr = key.to_void();
|
|
self.find(key).unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
// consume the type parameters with PhantomDatas
|
|
pub struct CFMutableDictionary<K = *const c_void, V = *const c_void>(CFMutableDictionaryRef, PhantomData<K>, PhantomData<V>);
|
|
|
|
impl<K, V> Drop for CFMutableDictionary<K, V> {
|
|
fn drop(&mut self) {
|
|
unsafe { CFRelease(self.as_CFTypeRef()) }
|
|
}
|
|
}
|
|
|
|
impl_TCFType!(CFMutableDictionary<K, V>, CFMutableDictionaryRef, CFDictionaryGetTypeID);
|
|
impl_CFTypeDescription!(CFMutableDictionary);
|
|
|
|
impl<K, V> CFMutableDictionary<K, V> {
|
|
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(pairs: &[(K, V)]) -> CFMutableDictionary<K, V> where K: ToVoid<K>, V: ToVoid<V> {
|
|
let mut result = Self::with_capacity(pairs.len() as _);
|
|
for &(ref key, ref value) in pairs {
|
|
result.add(key, value);
|
|
}
|
|
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]
|
|
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
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> {
|
|
unsafe {
|
|
let mut value: *const c_void = ptr::null();
|
|
if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
|
|
Some(V::from_void(value))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// # Panics
|
|
///
|
|
/// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
|
|
/// of panicking.
|
|
#[inline]
|
|
pub fn get<'a>(&'a self, key: &K) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> {
|
|
let ptr = key.to_void();
|
|
self.find(&key).unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
// Mutable interface
|
|
|
|
/// Adds the key-value pair to the dictionary if no such key already exist.
|
|
#[inline]
|
|
pub fn add(&mut self, key: &K, value: &V) where K: ToVoid<K>, V: ToVoid<V> {
|
|
unsafe { CFDictionaryAddValue(self.0, key.to_void(), value.to_void()) }
|
|
}
|
|
|
|
/// Sets the value of the key in the dictionary.
|
|
#[inline]
|
|
pub fn set(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> {
|
|
unsafe { CFDictionarySetValue(self.0, key.to_void(), value.to_void()) }
|
|
}
|
|
|
|
/// Replaces the value of the key in the dictionary.
|
|
#[inline]
|
|
pub fn replace(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> {
|
|
unsafe { CFDictionaryReplaceValue(self.0, key.to_void(), value.to_void()) }
|
|
}
|
|
|
|
/// Removes the value of the key from the dictionary.
|
|
#[inline]
|
|
pub fn remove(&mut self, key: K) where K: ToVoid<K> {
|
|
unsafe { CFDictionaryRemoveValue(self.0, key.to_void()) }
|
|
}
|
|
|
|
#[inline]
|
|
pub fn remove_all(&mut self) {
|
|
unsafe { CFDictionaryRemoveAllValues(self.0) }
|
|
}
|
|
}
|
|
|
|
impl<K, V> Default for CFMutableDictionary<K, V> {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
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 {
|
|
use super::*;
|
|
use base::{CFType, TCFType};
|
|
use boolean::CFBoolean;
|
|
use number::CFNumber;
|
|
use string::CFString;
|
|
|
|
|
|
#[test]
|
|
fn dictionary() {
|
|
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 = CFDictionary::from_CFType_pairs(&[
|
|
(bar.as_CFType(), boo.as_CFType()),
|
|
(baz.as_CFType(), tru.as_CFType()),
|
|
(foo.as_CFType(), n42.as_CFType()),
|
|
]);
|
|
|
|
let (v1, v2) = d.get_keys_and_values();
|
|
assert_eq!(v1, &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
|
|
assert_eq!(v2, &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
|
|
}
|
|
|
|
#[test]
|
|
fn mutable_dictionary() {
|
|
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 mut d = CFMutableDictionary::<CFString, CFType>::new();
|
|
d.add(&bar, &boo.as_CFType());
|
|
d.add(&baz, &tru.as_CFType());
|
|
d.add(&foo, &n42.as_CFType());
|
|
assert_eq!(d.len(), 3);
|
|
|
|
let (v1, v2) = d.get_keys_and_values();
|
|
assert_eq!(v1, &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
|
|
assert_eq!(v2, &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
|
|
|
|
d.remove(baz);
|
|
assert_eq!(d.len(), 2);
|
|
|
|
let (v1, v2) = d.get_keys_and_values();
|
|
assert_eq!(v1, &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]);
|
|
assert_eq!(v2, &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]);
|
|
|
|
d.remove_all();
|
|
assert_eq!(d.len(), 0)
|
|
}
|
|
|
|
#[test]
|
|
fn dict_find_and_contains_key() {
|
|
let dict = CFDictionary::from_CFType_pairs(&[
|
|
(
|
|
CFString::from_static_string("hello"),
|
|
CFBoolean::true_value(),
|
|
),
|
|
]);
|
|
let key = CFString::from_static_string("hello");
|
|
let invalid_key = CFString::from_static_string("foobar");
|
|
|
|
assert!(dict.contains_key(&key));
|
|
assert!(!dict.contains_key(&invalid_key));
|
|
|
|
let value = dict.find(&key).unwrap().clone();
|
|
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);
|
|
}
|
|
}
|