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                                                     keys.as_ptr(),
48                                                     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 a `CFMutableDictionary` pointing to the same underlying dictionary as this immutable one.
62     /// This should only be used when the underlying dictionary is mutable.
63     #[inline]
to_mutable(&self) -> CFMutableDictionary<K, V>64     pub unsafe fn to_mutable(&self) -> CFMutableDictionary<K, V> {
65         CFMutableDictionary::wrap_under_get_rule(self.0 as CFMutableDictionaryRef)
66     }
67 
68     /// Returns the same dictionary, but with the types reset to void pointers.
69     /// Equal to `to_untyped`, but is faster since it does not increment the retain count.
70     #[inline]
into_untyped(self) -> CFDictionary71     pub fn into_untyped(self) -> CFDictionary {
72         let reference = self.0;
73         mem::forget(self);
74         unsafe { CFDictionary::wrap_under_create_rule(reference) }
75     }
76 
77     #[inline]
len(&self) -> usize78     pub fn len(&self) -> usize {
79         unsafe {
80             CFDictionaryGetCount(self.0) as usize
81         }
82     }
83 
84     #[inline]
is_empty(&self) -> bool85     pub fn is_empty(&self) -> bool {
86         self.len() == 0
87     }
88 
89     #[inline]
contains_key(&self, key: &K) -> bool where K: ToVoid<K>90     pub fn contains_key(&self, key: &K) -> bool where K: ToVoid<K> {
91         unsafe { CFDictionaryContainsKey(self.0, key.to_void()) != 0 }
92     }
93 
94     #[inline]
find<'a, T: ToVoid<K>>(&'a self, key: T) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K>95     pub fn find<'a, T: ToVoid<K>>(&'a self, key: T) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> {
96         unsafe {
97             let mut value: *const c_void = ptr::null();
98             if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
99                 Some(V::from_void(value))
100             } else {
101                 None
102             }
103         }
104     }
105 
106     /// # Panics
107     ///
108     /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
109     /// of panicking.
110     #[inline]
get<'a, T: ToVoid<K>>(&'a self, key: T) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K>111     pub fn get<'a, T: ToVoid<K>>(&'a self, key: T) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> {
112         let ptr = key.to_void();
113         self.find(key).unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
114     }
115 
get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>)116     pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
117         let length = self.len();
118         let mut keys = Vec::with_capacity(length);
119         let mut values = Vec::with_capacity(length);
120 
121         unsafe {
122             CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
123             keys.set_len(length);
124             values.set_len(length);
125         }
126 
127         (keys, values)
128     }
129 }
130 
131 // consume the type parameters with PhantomDatas
132 pub struct CFMutableDictionary<K = *const c_void, V = *const c_void>(CFMutableDictionaryRef, PhantomData<K>, PhantomData<V>);
133 
134 impl<K, V> Drop for CFMutableDictionary<K, V> {
drop(&mut self)135     fn drop(&mut self) {
136         unsafe { CFRelease(self.as_CFTypeRef()) }
137     }
138 }
139 
140 impl_TCFType!(CFMutableDictionary<K, V>, CFMutableDictionaryRef, CFDictionaryGetTypeID);
141 impl_CFTypeDescription!(CFMutableDictionary);
142 
143 impl<K, V> CFMutableDictionary<K, V> {
new() -> Self144     pub fn new() -> Self {
145         Self::with_capacity(0)
146     }
147 
with_capacity(capacity: isize) -> Self148     pub fn with_capacity(capacity: isize) -> Self {
149         unsafe {
150             let dictionary_ref = CFDictionaryCreateMutable(kCFAllocatorDefault,
151                                                            capacity as _,
152                                                            &kCFTypeDictionaryKeyCallBacks,
153                                                            &kCFTypeDictionaryValueCallBacks);
154             TCFType::wrap_under_create_rule(dictionary_ref)
155         }
156     }
157 
copy_with_capacity(&self, capacity: isize) -> Self158     pub fn copy_with_capacity(&self, capacity: isize) -> Self {
159         unsafe {
160             let dictionary_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity as _, self.0);
161             TCFType::wrap_under_get_rule(dictionary_ref)
162         }
163     }
164 
from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary<K, V> where K: ToVoid<K>, V: ToVoid<V>165     pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary<K, V> where K: ToVoid<K>, V: ToVoid<V> {
166         let mut result = Self::with_capacity(pairs.len() as _);
167         for &(ref key, ref value) in pairs {
168             result.add(key, value);
169         }
170         result
171     }
172 
173     #[inline]
to_untyped(&self) -> CFMutableDictionary174     pub fn to_untyped(&self) -> CFMutableDictionary {
175         unsafe { CFMutableDictionary::wrap_under_get_rule(self.0) }
176     }
177 
178     /// Returns the same dictionary, but with the types reset to void pointers.
179     /// Equal to `to_untyped`, but is faster since it does not increment the retain count.
180     #[inline]
into_untyped(self) -> CFMutableDictionary181     pub fn into_untyped(self) -> CFMutableDictionary {
182         let reference = self.0;
183         mem::forget(self);
184         unsafe { CFMutableDictionary::wrap_under_create_rule(reference) }
185     }
186 
187     /// Returns a `CFDictionary` pointing to the same underlying dictionary as this mutable one.
188     #[inline]
to_immutable(&self) -> CFDictionary<K, V>189     pub fn to_immutable(&self) -> CFDictionary<K, V> {
190         unsafe { CFDictionary::wrap_under_get_rule(self.0) }
191     }
192 
193     // Immutable interface
194 
195     #[inline]
len(&self) -> usize196     pub fn len(&self) -> usize {
197         unsafe {
198             CFDictionaryGetCount(self.0) as usize
199         }
200     }
201 
202     #[inline]
is_empty(&self) -> bool203     pub fn is_empty(&self) -> bool {
204         self.len() == 0
205     }
206 
207     #[inline]
contains_key(&self, key: *const c_void) -> bool208     pub fn contains_key(&self, key: *const c_void) -> bool {
209         unsafe {
210             CFDictionaryContainsKey(self.0, key) != 0
211         }
212     }
213 
214     #[inline]
find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K>215     pub fn find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>> where V: FromVoid, K: ToVoid<K> {
216         unsafe {
217             let mut value: *const c_void = ptr::null();
218             if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
219                 Some(V::from_void(value))
220             } else {
221                 None
222             }
223         }
224     }
225 
226     /// # Panics
227     ///
228     /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
229     /// of panicking.
230     #[inline]
get<'a>(&'a self, key: &K) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K>231     pub fn get<'a>(&'a self, key: &K) -> ItemRef<'a, V> where V: FromVoid, K: ToVoid<K> {
232         let ptr = key.to_void();
233         self.find(&key).unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
234     }
235 
get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>)236     pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
237         let length = self.len();
238         let mut keys = Vec::with_capacity(length);
239         let mut values = Vec::with_capacity(length);
240 
241         unsafe {
242             CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
243             keys.set_len(length);
244             values.set_len(length);
245         }
246 
247         (keys, values)
248     }
249 
250     // Mutable interface
251 
252     /// Adds the key-value pair to the dictionary if no such key already exist.
253     #[inline]
add(&mut self, key: &K, value: &V) where K: ToVoid<K>, V: ToVoid<V>254     pub fn add(&mut self, key: &K, value: &V) where K: ToVoid<K>, V: ToVoid<V> {
255         unsafe { CFDictionaryAddValue(self.0, key.to_void(), value.to_void()) }
256     }
257 
258     /// Sets the value of the key in the dictionary.
259     #[inline]
set(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V>260     pub fn set(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> {
261         unsafe { CFDictionarySetValue(self.0, key.to_void(), value.to_void()) }
262     }
263 
264     /// Replaces the value of the key in the dictionary.
265     #[inline]
replace(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V>266     pub fn replace(&mut self, key: K, value: V) where K: ToVoid<K>, V: ToVoid<V> {
267         unsafe { CFDictionaryReplaceValue(self.0, key.to_void(), value.to_void()) }
268     }
269 
270     /// Removes the value of the key from the dictionary.
271     #[inline]
remove(&mut self, key: K) where K: ToVoid<K>272     pub fn remove(&mut self, key: K) where K: ToVoid<K> {
273         unsafe { CFDictionaryRemoveValue(self.0, key.to_void()) }
274     }
275 
276     #[inline]
remove_all(&mut self)277     pub fn remove_all(&mut self) {
278         unsafe { CFDictionaryRemoveAllValues(self.0) }
279     }
280 }
281 
282 impl<K, V> Default for CFMutableDictionary<K, V> {
default() -> Self283     fn default() -> Self {
284         Self::new()
285     }
286 }
287 
288 impl<'a, K, V> From<&'a CFDictionary<K, V>> for CFMutableDictionary<K, V> {
289     /// Creates a new mutable dictionary with the key-value pairs from another dictionary.
290     /// The capacity of the new mutable dictionary is not limited.
from(dict: &'a CFDictionary<K, V>) -> Self291     fn from(dict: &'a CFDictionary<K, V>) -> Self {
292         unsafe {
293             let mut_dict_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict.0);
294             TCFType::wrap_under_create_rule(mut_dict_ref)
295         }
296     }
297 }
298 
299 
300 #[cfg(test)]
301 pub mod test {
302     use super::*;
303     use base::{CFType, TCFType};
304     use boolean::CFBoolean;
305     use number::CFNumber;
306     use string::CFString;
307 
308 
309     #[test]
dictionary()310     fn dictionary() {
311         let bar = CFString::from_static_string("Bar");
312         let baz = CFString::from_static_string("Baz");
313         let boo = CFString::from_static_string("Boo");
314         let foo = CFString::from_static_string("Foo");
315         let tru = CFBoolean::true_value();
316         let n42 = CFNumber::from(42);
317 
318         let d = CFDictionary::from_CFType_pairs(&[
319             (bar.as_CFType(), boo.as_CFType()),
320             (baz.as_CFType(), tru.as_CFType()),
321             (foo.as_CFType(), n42.as_CFType()),
322         ]);
323 
324         let (v1, v2) = d.get_keys_and_values();
325         assert_eq!(v1, &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
326         assert_eq!(v2, &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
327     }
328 
329     #[test]
mutable_dictionary()330     fn mutable_dictionary() {
331         let bar = CFString::from_static_string("Bar");
332         let baz = CFString::from_static_string("Baz");
333         let boo = CFString::from_static_string("Boo");
334         let foo = CFString::from_static_string("Foo");
335         let tru = CFBoolean::true_value();
336         let n42 = CFNumber::from(42);
337 
338         let mut d = CFMutableDictionary::<CFString, CFType>::new();
339         d.add(&bar, &boo.as_CFType());
340         d.add(&baz, &tru.as_CFType());
341         d.add(&foo, &n42.as_CFType());
342         assert_eq!(d.len(), 3);
343 
344         let (v1, v2) = d.get_keys_and_values();
345         assert_eq!(v1, &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]);
346         assert_eq!(v2, &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]);
347 
348         d.remove(baz);
349         assert_eq!(d.len(), 2);
350 
351         let (v1, v2) = d.get_keys_and_values();
352         assert_eq!(v1, &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]);
353         assert_eq!(v2, &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]);
354 
355         d.remove_all();
356         assert_eq!(d.len(), 0)
357     }
358 
359     #[test]
dict_find_and_contains_key()360     fn dict_find_and_contains_key() {
361         let dict = CFDictionary::from_CFType_pairs(&[
362             (
363                 CFString::from_static_string("hello"),
364                 CFBoolean::true_value(),
365             ),
366         ]);
367         let key = CFString::from_static_string("hello");
368         let invalid_key = CFString::from_static_string("foobar");
369 
370         assert!(dict.contains_key(&key));
371         assert!(!dict.contains_key(&invalid_key));
372 
373         let value = dict.find(&key).unwrap().clone();
374         assert_eq!(value, CFBoolean::true_value());
375         assert_eq!(dict.find(&invalid_key), None);
376     }
377 
378     #[test]
convert_immutable_to_mutable_dict()379     fn convert_immutable_to_mutable_dict() {
380         let dict: CFDictionary<CFString, CFBoolean> = CFDictionary::from_CFType_pairs(&[
381             (CFString::from_static_string("Foo"), CFBoolean::true_value()),
382         ]);
383         let mut mut_dict = CFMutableDictionary::from(&dict);
384         assert_eq!(dict.retain_count(), 1);
385         assert_eq!(mut_dict.retain_count(), 1);
386 
387         assert_eq!(mut_dict.len(), 1);
388         assert_eq!(*mut_dict.get(&CFString::from_static_string("Foo")), CFBoolean::true_value());
389 
390         mut_dict.add(&CFString::from_static_string("Bar"), &CFBoolean::false_value());
391         assert_eq!(dict.len(), 1);
392         assert_eq!(mut_dict.len(), 2);
393     }
394 
395     #[test]
mutable_dictionary_as_immutable()396     fn mutable_dictionary_as_immutable() {
397         let mut mut_dict: CFMutableDictionary<CFString, CFBoolean> = CFMutableDictionary::new();
398         mut_dict.add(&CFString::from_static_string("Bar"), &CFBoolean::false_value());
399         assert_eq!(mut_dict.retain_count(), 1);
400 
401         let dict = mut_dict.to_immutable();
402         assert_eq!(mut_dict.retain_count(), 2);
403         assert_eq!(dict.retain_count(), 2);
404         assert_eq!(*dict.get(&CFString::from_static_string("Bar")), CFBoolean::false_value());
405 
406         mem::drop(dict);
407         assert_eq!(mut_dict.retain_count(), 1);
408     }
409 }
410