1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9 
10 //! Dictionaries of key-value pairs.
11 
12 pub use core_foundation_sys::dictionary::*;
13 
14 use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault};
15 use std::mem;
16 use std::os::raw::c_void;
17 use std::ptr;
18 use std::marker::PhantomData;
19 
20 use base::{ItemRef, FromVoid, ToVoid};
21 use base::{CFIndexConvertible, TCFType};
22 use ConcreteCFType;
23 
24 // consume the type parameters with PhantomDatas
25 pub struct CFDictionary<K = *const c_void, V = *const c_void>(CFDictionaryRef, PhantomData<K>, PhantomData<V>);
26 
27 impl<K, V> Drop for CFDictionary<K, V> {
drop(&mut self)28     fn drop(&mut self) {
29         unsafe { CFRelease(self.as_CFTypeRef()) }
30     }
31 }
32 
33 impl_TCFType!(CFDictionary<K, V>, CFDictionaryRef, CFDictionaryGetTypeID);
34 impl_CFTypeDescription!(CFDictionary);
35 
36 unsafe impl ConcreteCFType for CFDictionary<*const c_void, *const c_void> {}
37 
38 impl<K, V> CFDictionary<K, V> {
from_CFType_pairs(pairs: &[(K, V)]) -> CFDictionary<K, V> where K: TCFType, V: TCFType39     pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFDictionary<K, V> where K: TCFType, V: TCFType {
40         let (keys, values): (Vec<CFTypeRef>, Vec<CFTypeRef>) = pairs
41             .iter()
42             .map(|&(ref key, ref value)| (key.as_CFTypeRef(), value.as_CFTypeRef()))
43             .unzip();
44 
45         unsafe {
46             let dictionary_ref = CFDictionaryCreate(kCFAllocatorDefault,
47                                                     mem::transmute(keys.as_ptr()),
48                                                     mem::transmute(values.as_ptr()),
49                                                     keys.len().to_CFIndex(),
50                                                     &kCFTypeDictionaryKeyCallBacks,
51                                                     &kCFTypeDictionaryValueCallBacks);
52             TCFType::wrap_under_create_rule(dictionary_ref)
53         }
54     }
55 
56     #[inline]
to_untyped(&self) -> CFDictionary57     pub fn to_untyped(&self) -> CFDictionary {
58         unsafe { CFDictionary::wrap_under_get_rule(self.0) }
59     }
60 
61     /// Returns the same dictionary, but with the types reset to void pointers.
62     /// Equal to `to_untyped`, but is faster since it does not increment the retain count.
63     #[inline]
into_untyped(self) -> CFDictionary64     pub fn into_untyped(self) -> CFDictionary {
65         let reference = self.0;
66         mem::forget(self);
67         unsafe { CFDictionary::wrap_under_create_rule(reference) }
68     }
69 
70     #[inline]
len(&self) -> usize71     pub fn len(&self) -> usize {
72         unsafe {
73             CFDictionaryGetCount(self.0) as usize
74         }
75     }
76 
77     #[inline]
is_empty(&self) -> bool78     pub fn is_empty(&self) -> bool {
79         self.len() == 0
80     }
81 
82     #[inline]
contains_key(&self, key: &K) -> bool where K: ToVoid<K>83     pub fn contains_key(&self, key: &K) -> bool where K: ToVoid<K> {
84         unsafe { CFDictionaryContainsKey(self.0, key.to_void()) != 0 }
85     }
86 
87     #[inline]
find<'a, T: ToVoid<K>>(&'a self, key: T) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K>88     pub fn find<'a, T: ToVoid<K>>(&'a self, key: T) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> {
89         unsafe {
90             let mut value: *const c_void = ptr::null();
91             if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
92                 Some(V::from_void(value))
93             } else {
94                 None
95             }
96         }
97     }
98 
99     /// # Panics
100     ///
101     /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
102     /// of panicking.
103     #[inline]
get<'a, T: ToVoid<K>>(&'a self, key: T) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K>104     pub fn get<'a, T: ToVoid<K>>(&'a self, key: T) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> {
105         let ptr = key.to_void();
106         self.find(key).expect(&format!("No entry found for key {:p}", ptr))
107     }
108 
get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>)109     pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
110         let length = self.len();
111         let mut keys = Vec::with_capacity(length);
112         let mut values = Vec::with_capacity(length);
113 
114         unsafe {
115             CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
116             keys.set_len(length);
117             values.set_len(length);
118         }
119 
120         (keys, values)
121     }
122 }
123 
124 // consume the type parameters with PhantomDatas
125 pub struct CFMutableDictionary<K = *const c_void, V = *const c_void>(CFMutableDictionaryRef, PhantomData<K>, PhantomData<V>);
126 
127 impl<K, V> Drop for CFMutableDictionary<K, V> {
drop(&mut self)128     fn drop(&mut self) {
129         unsafe { CFRelease(self.as_CFTypeRef()) }
130     }
131 }
132 
133 impl_TCFType!(CFMutableDictionary<K, V>, CFMutableDictionaryRef, CFDictionaryGetTypeID);
134 impl_CFTypeDescription!(CFMutableDictionary);
135 
136 impl<K, V> CFMutableDictionary<K, V> {
new() -> Self137     pub fn new() -> Self {
138         Self::with_capacity(0)
139     }
140 
with_capacity(capacity: isize) -> Self141     pub fn with_capacity(capacity: isize) -> Self {
142         unsafe {
143             let dictionary_ref = CFDictionaryCreateMutable(kCFAllocatorDefault,
144                                                            capacity as _,
145                                                            &kCFTypeDictionaryKeyCallBacks,
146                                                            &kCFTypeDictionaryValueCallBacks);
147             TCFType::wrap_under_create_rule(dictionary_ref)
148         }
149     }
150 
copy_with_capacity(&self, capacity: isize) -> Self151     pub fn copy_with_capacity(&self, capacity: isize) -> Self {
152         unsafe {
153             let dictionary_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity as _, self.0);
154             TCFType::wrap_under_get_rule(dictionary_ref)
155         }
156     }
157 
from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary<K, V> where K: ToVoid<K>, V: ToVoid<V>158     pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary<K, V> where K: ToVoid<K>, V: ToVoid<V> {
159         let mut result = Self::with_capacity(pairs.len() as _);
160         for &(ref key, ref value) in pairs {
161             result.add(key, value);
162         }
163         result
164     }
165 
166     #[inline]
to_untyped(&self) -> CFMutableDictionary167     pub fn to_untyped(&self) -> CFMutableDictionary {
168         unsafe { CFMutableDictionary::wrap_under_get_rule(self.0) }
169     }
170 
171     /// Returns the same dictionary, but with the types reset to void pointers.
172     /// Equal to `to_untyped`, but is faster since it does not increment the retain count.
173     #[inline]
into_untyped(self) -> CFMutableDictionary174     pub fn into_untyped(self) -> CFMutableDictionary {
175         let reference = self.0;
176         mem::forget(self);
177         unsafe { CFMutableDictionary::wrap_under_create_rule(reference) }
178     }
179 
180     /// Returns a `CFDictionary` pointing to the same underlying dictionary as this mutable one.
181     #[inline]
to_immutable(&self) -> CFDictionary<K, V>182     pub fn to_immutable(&self) -> CFDictionary<K, V> {
183         unsafe { CFDictionary::wrap_under_get_rule(self.0) }
184     }
185 
186     // Immutable interface
187 
188     #[inline]
len(&self) -> usize189     pub fn len(&self) -> usize {
190         unsafe {
191             CFDictionaryGetCount(self.0) as usize
192         }
193     }
194 
195     #[inline]
is_empty(&self) -> bool196     pub fn is_empty(&self) -> bool {
197         self.len() == 0
198     }
199 
200     #[inline]
contains_key(&self, key: *const c_void) -> bool201     pub fn contains_key(&self, key: *const c_void) -> bool {
202         unsafe {
203             CFDictionaryContainsKey(self.0, key) != 0
204         }
205     }
206 
207     #[inline]
find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K>208     pub fn find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> {
209         unsafe {
210             let mut value: *const c_void = ptr::null();
211             if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
212                 Some(V::from_void(value))
213             } else {
214                 None
215             }
216         }
217     }
218 
219     /// # Panics
220     ///
221     /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
222     /// of panicking.
223     #[inline]
get<'a>(&'a self, key: &K) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K>224     pub fn get<'a>(&'a self, key: &K) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> {
225         let ptr = key.to_void();
226         self.find(&key).expect(&format!("No entry found for key {:p}", ptr))
227     }
228 
get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>)229     pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
230         let length = self.len();
231         let mut keys = Vec::with_capacity(length);
232         let mut values = Vec::with_capacity(length);
233 
234         unsafe {
235             CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
236             keys.set_len(length);
237             values.set_len(length);
238         }
239 
240         (keys, values)
241     }
242 
243     // Mutable interface
244 
245     /// Adds the key-value pair to the dictionary if no such key already exist.
246     #[inline]
add(&mut self, key: &K, value: &V) where K: ToVoid<K>, V: ToVoid<V>247     pub fn add(&mut self, key: &K, value: &V) where K: ToVoid<K>, V: ToVoid<V> {
248         unsafe { CFDictionaryAddValue(self.0, key.to_void(), value.to_void()) }
249     }
250 
251     /// Sets the value of the key in the dictionary.
252     #[inline]
set(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V>253     pub fn set(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> {
254         unsafe { CFDictionarySetValue(self.0, key.to_void(), value.to_void()) }
255     }
256 
257     /// Replaces the value of the key in the dictionary.
258     #[inline]
replace(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V>259     pub fn replace(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> {
260         unsafe { CFDictionaryReplaceValue(self.0, key.to_void(), value.to_void()) }
261     }
262 
263     /// Removes the value of the key from the dictionary.
264     #[inline]
remove(&mut self, key: K) where K: ToVoid<K>265     pub fn remove(&mut self, key: K) where K: ToVoid<K> {
266         unsafe { CFDictionaryRemoveValue(self.0, key.to_void()) }
267     }
268 
269     #[inline]
remove_all(&mut self)270     pub fn remove_all(&mut self) {
271         unsafe { CFDictionaryRemoveAllValues(self.0) }
272     }
273 }
274 
275 impl<'a, K, V> From<&'a CFDictionary<K, V>> for CFMutableDictionary<K, V> {
276     /// Creates a new mutable dictionary with the key-value pairs from another dictionary.
277     /// The capacity of the new mutable dictionary is not limited.
from(dict: &'a CFDictionary<K, V>) -> Self278     fn from(dict: &'a CFDictionary<K, V>) -> Self {
279         unsafe {
280             let mut_dict_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict.0);
281             TCFType::wrap_under_create_rule(mut_dict_ref)
282         }
283     }
284 }
285 
286 
287 #[cfg(test)]
288 pub mod test {
289     use super::*;
290     use base::{CFType, TCFType};
291     use boolean::CFBoolean;
292     use number::CFNumber;
293     use string::CFString;
294 
295 
296     #[test]
dictionary()297     fn dictionary() {
298         let bar = CFString::from_static_string("Bar");
299         let baz = CFString::from_static_string("Baz");
300         let boo = CFString::from_static_string("Boo");
301         let foo = CFString::from_static_string("Foo");
302         let tru = CFBoolean::true_value();
303         let n42 = CFNumber::from(42);
304 
305         let d = CFDictionary::from_CFType_pairs(&[
306             (bar.as_CFType(), boo.as_CFType()),
307             (baz.as_CFType(), tru.as_CFType()),
308             (foo.as_CFType(), n42.as_CFType()),
309         ]);
310 
311         let (v1, v2) = d.get_keys_and_values();
312         assert!(v1 == &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
313         assert!(v2 == &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
314     }
315 
316     #[test]
mutable_dictionary()317     fn mutable_dictionary() {
318         let bar = CFString::from_static_string("Bar");
319         let baz = CFString::from_static_string("Baz");
320         let boo = CFString::from_static_string("Boo");
321         let foo = CFString::from_static_string("Foo");
322         let tru = CFBoolean::true_value();
323         let n42 = CFNumber::from(42);
324 
325         let mut d = CFMutableDictionary::<CFString, CFType>::new();
326         d.add(&bar, &boo.as_CFType());
327         d.add(&baz, &tru.as_CFType());
328         d.add(&foo, &n42.as_CFType());
329         assert_eq!(d.len(), 3);
330 
331         let (v1, v2) = d.get_keys_and_values();
332         assert!(v1 == &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
333         assert!(v2 == &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
334 
335         d.remove(baz);
336         assert_eq!(d.len(), 2);
337 
338         let (v1, v2) = d.get_keys_and_values();
339         assert!(v1 == &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]);
340         assert!(v2 == &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]);
341 
342         d.remove_all();
343         assert_eq!(d.len(), 0)
344     }
345 
346     #[test]
dict_find_and_contains_key()347     fn dict_find_and_contains_key() {
348         let dict = CFDictionary::from_CFType_pairs(&[
349             (
350                 CFString::from_static_string("hello"),
351                 CFBoolean::true_value(),
352             ),
353         ]);
354         let key = CFString::from_static_string("hello");
355         let invalid_key = CFString::from_static_string("foobar");
356 
357         assert!(dict.contains_key(&key));
358         assert!(!dict.contains_key(&invalid_key));
359 
360         let value = dict.find(&key).unwrap().clone();
361         assert_eq!(value, CFBoolean::true_value());
362         assert_eq!(dict.find(&invalid_key), None);
363     }
364 
365     #[test]
convert_immutable_to_mutable_dict()366     fn convert_immutable_to_mutable_dict() {
367         let dict: CFDictionary<CFString, CFBoolean> = CFDictionary::from_CFType_pairs(&[
368             (CFString::from_static_string("Foo"), CFBoolean::true_value()),
369         ]);
370         let mut mut_dict = CFMutableDictionary::from(&dict);
371         assert_eq!(dict.retain_count(), 1);
372         assert_eq!(mut_dict.retain_count(), 1);
373 
374         assert_eq!(mut_dict.len(), 1);
375         assert_eq!(*mut_dict.get(&CFString::from_static_string("Foo")), CFBoolean::true_value());
376 
377         mut_dict.add(&CFString::from_static_string("Bar"), &CFBoolean::false_value());
378         assert_eq!(dict.len(), 1);
379         assert_eq!(mut_dict.len(), 2);
380     }
381 
382     #[test]
mutable_dictionary_as_immutable()383     fn mutable_dictionary_as_immutable() {
384         let mut mut_dict: CFMutableDictionary<CFString, CFBoolean> = CFMutableDictionary::new();
385         mut_dict.add(&CFString::from_static_string("Bar"), &CFBoolean::false_value());
386         assert_eq!(mut_dict.retain_count(), 1);
387 
388         let dict = mut_dict.to_immutable();
389         assert_eq!(mut_dict.retain_count(), 2);
390         assert_eq!(dict.retain_count(), 2);
391         assert_eq!(*dict.get(&CFString::from_static_string("Bar")), CFBoolean::false_value());
392 
393         mem::drop(dict);
394         assert_eq!(mut_dict.retain_count(), 1);
395     }
396 }
397