core_foundation/
dictionary.rs

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
12pub use core_foundation_sys::dictionary::*;
13
14use core_foundation_sys::base::{kCFAllocatorDefault, CFRelease, CFTypeRef};
15use std::marker::PhantomData;
16use std::mem;
17use std::os::raw::c_void;
18use std::ptr;
19
20use crate::base::{CFIndexConvertible, TCFType};
21use crate::base::{FromVoid, ItemRef, ToVoid};
22use crate::ConcreteCFType;
23
24// consume the type parameters with `PhantomData`
25pub struct CFDictionary<K = *const c_void, V = *const c_void>(
26    CFDictionaryRef,
27    PhantomData<K>,
28    PhantomData<V>,
29);
30
31impl<K, V> Drop for CFDictionary<K, V> {
32    fn drop(&mut self) {
33        unsafe { CFRelease(self.as_CFTypeRef()) }
34    }
35}
36
37impl_TCFType!(CFDictionary<K, V>, CFDictionaryRef, CFDictionaryGetTypeID);
38impl_CFTypeDescription!(CFDictionary<K, V>);
39
40unsafe impl ConcreteCFType for CFDictionary<*const c_void, *const c_void> {}
41
42impl<K, V> CFDictionary<K, V> {
43    pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFDictionary<K, V>
44    where
45        K: TCFType,
46        V: TCFType,
47    {
48        let (keys, values): (Vec<CFTypeRef>, Vec<CFTypeRef>) = pairs
49            .iter()
50            .map(|(key, value)| (key.as_CFTypeRef(), value.as_CFTypeRef()))
51            .unzip();
52
53        unsafe {
54            let dictionary_ref = CFDictionaryCreate(
55                kCFAllocatorDefault,
56                keys.as_ptr(),
57                values.as_ptr(),
58                keys.len().to_CFIndex(),
59                &kCFTypeDictionaryKeyCallBacks,
60                &kCFTypeDictionaryValueCallBacks,
61            );
62            TCFType::wrap_under_create_rule(dictionary_ref)
63        }
64    }
65
66    #[inline]
67    pub fn to_untyped(&self) -> CFDictionary {
68        unsafe { CFDictionary::wrap_under_get_rule(self.0) }
69    }
70
71    /// Returns a `CFMutableDictionary` pointing to the same underlying dictionary as this immutable one.
72    /// This should only be used when the underlying dictionary is mutable.
73    #[inline]
74    pub unsafe fn to_mutable(&self) -> CFMutableDictionary<K, V> {
75        CFMutableDictionary::wrap_under_get_rule(self.0 as CFMutableDictionaryRef)
76    }
77
78    /// Returns the same dictionary, but with the types reset to void pointers.
79    /// Equal to `to_untyped`, but is faster since it does not increment the retain count.
80    #[inline]
81    pub fn into_untyped(self) -> CFDictionary {
82        let reference = self.0;
83        mem::forget(self);
84        unsafe { CFDictionary::wrap_under_create_rule(reference) }
85    }
86
87    #[inline]
88    pub fn len(&self) -> usize {
89        unsafe { CFDictionaryGetCount(self.0) as usize }
90    }
91
92    #[inline]
93    pub fn is_empty(&self) -> bool {
94        self.len() == 0
95    }
96
97    #[inline]
98    pub fn contains_key(&self, key: &K) -> bool
99    where
100        K: ToVoid<K>,
101    {
102        unsafe { CFDictionaryContainsKey(self.0, key.to_void()) != 0 }
103    }
104
105    #[inline]
106    pub fn find<T: ToVoid<K>>(&self, key: T) -> Option<ItemRef<'_, V>>
107    where
108        V: FromVoid,
109        K: ToVoid<K>,
110    {
111        unsafe {
112            let mut value: *const c_void = ptr::null();
113            if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
114                Some(V::from_void(value))
115            } else {
116                None
117            }
118        }
119    }
120
121    /// # Panics
122    ///
123    /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
124    /// of panicking.
125    #[inline]
126    pub fn get<T: ToVoid<K>>(&self, key: T) -> ItemRef<'_, V>
127    where
128        V: FromVoid,
129        K: ToVoid<K>,
130    {
131        let ptr = key.to_void();
132        self.find(key)
133            .unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
134    }
135
136    pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
137        let length = self.len();
138        let mut keys = Vec::with_capacity(length);
139        let mut values = Vec::with_capacity(length);
140
141        unsafe {
142            CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
143            keys.set_len(length);
144            values.set_len(length);
145        }
146
147        (keys, values)
148    }
149}
150
151// consume the type parameters with `PhantomData`
152pub struct CFMutableDictionary<K = *const c_void, V = *const c_void>(
153    CFMutableDictionaryRef,
154    PhantomData<K>,
155    PhantomData<V>,
156);
157
158impl<K, V> Drop for CFMutableDictionary<K, V> {
159    fn drop(&mut self) {
160        unsafe { CFRelease(self.as_CFTypeRef()) }
161    }
162}
163
164impl_TCFType!(CFMutableDictionary<K, V>, CFMutableDictionaryRef, CFDictionaryGetTypeID);
165impl_CFTypeDescription!(CFMutableDictionary);
166
167impl<K, V> CFMutableDictionary<K, V> {
168    pub fn new() -> Self {
169        Self::with_capacity(0)
170    }
171
172    pub fn with_capacity(capacity: isize) -> Self {
173        unsafe {
174            let dictionary_ref = CFDictionaryCreateMutable(
175                kCFAllocatorDefault,
176                capacity as _,
177                &kCFTypeDictionaryKeyCallBacks,
178                &kCFTypeDictionaryValueCallBacks,
179            );
180            TCFType::wrap_under_create_rule(dictionary_ref)
181        }
182    }
183
184    pub fn copy_with_capacity(&self, capacity: isize) -> Self {
185        unsafe {
186            let dictionary_ref =
187                CFDictionaryCreateMutableCopy(kCFAllocatorDefault, capacity as _, self.0);
188            TCFType::wrap_under_get_rule(dictionary_ref)
189        }
190    }
191
192    pub fn from_CFType_pairs(pairs: &[(K, V)]) -> CFMutableDictionary<K, V>
193    where
194        K: ToVoid<K>,
195        V: ToVoid<V>,
196    {
197        let mut result = Self::with_capacity(pairs.len() as _);
198        for (key, value) in pairs {
199            result.add(key, value);
200        }
201        result
202    }
203
204    #[inline]
205    pub fn to_untyped(&self) -> CFMutableDictionary {
206        unsafe { CFMutableDictionary::wrap_under_get_rule(self.0) }
207    }
208
209    /// Returns the same dictionary, but with the types reset to void pointers.
210    /// Equal to `to_untyped`, but is faster since it does not increment the retain count.
211    #[inline]
212    pub fn into_untyped(self) -> CFMutableDictionary {
213        let reference = self.0;
214        mem::forget(self);
215        unsafe { CFMutableDictionary::wrap_under_create_rule(reference) }
216    }
217
218    /// Returns a `CFDictionary` pointing to the same underlying dictionary as this mutable one.
219    #[inline]
220    pub fn to_immutable(&self) -> CFDictionary<K, V> {
221        unsafe { CFDictionary::wrap_under_get_rule(self.0) }
222    }
223
224    // Immutable interface
225
226    #[inline]
227    pub fn len(&self) -> usize {
228        unsafe { CFDictionaryGetCount(self.0) as usize }
229    }
230
231    #[inline]
232    pub fn is_empty(&self) -> bool {
233        self.len() == 0
234    }
235
236    #[inline]
237    pub fn contains_key(&self, key: *const c_void) -> bool {
238        unsafe { CFDictionaryContainsKey(self.0, key) != 0 }
239    }
240
241    #[inline]
242    pub fn find<'a>(&'a self, key: &K) -> Option<ItemRef<'a, V>>
243    where
244        V: FromVoid,
245        K: ToVoid<K>,
246    {
247        unsafe {
248            let mut value: *const c_void = ptr::null();
249            if CFDictionaryGetValueIfPresent(self.0, key.to_void(), &mut value) != 0 {
250                Some(V::from_void(value))
251            } else {
252                None
253            }
254        }
255    }
256
257    /// # Panics
258    ///
259    /// Panics if the key is not present in the dictionary. Use `find` to get an `Option` instead
260    /// of panicking.
261    #[inline]
262    pub fn get<'a>(&'a self, key: &K) -> ItemRef<'a, V>
263    where
264        V: FromVoid,
265        K: ToVoid<K>,
266    {
267        let ptr = key.to_void();
268        self.find(key)
269            .unwrap_or_else(|| panic!("No entry found for key {:p}", ptr))
270    }
271
272    pub fn get_keys_and_values(&self) -> (Vec<*const c_void>, Vec<*const c_void>) {
273        let length = self.len();
274        let mut keys = Vec::with_capacity(length);
275        let mut values = Vec::with_capacity(length);
276
277        unsafe {
278            CFDictionaryGetKeysAndValues(self.0, keys.as_mut_ptr(), values.as_mut_ptr());
279            keys.set_len(length);
280            values.set_len(length);
281        }
282
283        (keys, values)
284    }
285
286    // Mutable interface
287
288    /// Adds the key-value pair to the dictionary if no such key already exist.
289    #[inline]
290    pub fn add(&mut self, key: &K, value: &V)
291    where
292        K: ToVoid<K>,
293        V: ToVoid<V>,
294    {
295        unsafe { CFDictionaryAddValue(self.0, key.to_void(), value.to_void()) }
296    }
297
298    /// Sets the value of the key in the dictionary.
299    #[inline]
300    pub fn set(&mut self, key: K, value: V)
301    where
302        K: ToVoid<K>,
303        V: ToVoid<V>,
304    {
305        unsafe { CFDictionarySetValue(self.0, key.to_void(), value.to_void()) }
306    }
307
308    /// Replaces the value of the key in the dictionary.
309    #[inline]
310    pub fn replace(&mut self, key: K, value: V)
311    where
312        K: ToVoid<K>,
313        V: ToVoid<V>,
314    {
315        unsafe { CFDictionaryReplaceValue(self.0, key.to_void(), value.to_void()) }
316    }
317
318    /// Removes the value of the key from the dictionary.
319    #[inline]
320    pub fn remove(&mut self, key: K)
321    where
322        K: ToVoid<K>,
323    {
324        unsafe { CFDictionaryRemoveValue(self.0, key.to_void()) }
325    }
326
327    #[inline]
328    pub fn remove_all(&mut self) {
329        unsafe { CFDictionaryRemoveAllValues(self.0) }
330    }
331}
332
333impl<K, V> Default for CFMutableDictionary<K, V> {
334    fn default() -> Self {
335        Self::new()
336    }
337}
338
339impl<'a, K, V> From<&'a CFDictionary<K, V>> for CFMutableDictionary<K, V> {
340    /// Creates a new mutable dictionary with the key-value pairs from another dictionary.
341    /// The capacity of the new mutable dictionary is not limited.
342    fn from(dict: &'a CFDictionary<K, V>) -> Self {
343        unsafe {
344            let mut_dict_ref = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict.0);
345            TCFType::wrap_under_create_rule(mut_dict_ref)
346        }
347    }
348}
349
350#[cfg(test)]
351pub mod test {
352    use super::*;
353    use crate::base::{CFType, TCFType};
354    use crate::boolean::CFBoolean;
355    use crate::number::CFNumber;
356    use crate::string::CFString;
357
358    #[test]
359    fn dictionary() {
360        let bar = CFString::from_static_string("Bar");
361        let baz = CFString::from_static_string("Baz");
362        let boo = CFString::from_static_string("Boo");
363        let foo = CFString::from_static_string("Foo");
364        let tru = CFBoolean::true_value();
365        let n42 = CFNumber::from(42);
366
367        let d = CFDictionary::from_CFType_pairs(&[
368            (bar.as_CFType(), boo.as_CFType()),
369            (baz.as_CFType(), tru.as_CFType()),
370            (foo.as_CFType(), n42.as_CFType()),
371        ]);
372
373        let (v1, v2) = d.get_keys_and_values();
374        assert_eq!(
375            v1,
376            &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]
377        );
378        assert_eq!(
379            v2,
380            &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]
381        );
382    }
383
384    #[test]
385    fn mutable_dictionary() {
386        let bar = CFString::from_static_string("Bar");
387        let baz = CFString::from_static_string("Baz");
388        let boo = CFString::from_static_string("Boo");
389        let foo = CFString::from_static_string("Foo");
390        let tru = CFBoolean::true_value();
391        let n42 = CFNumber::from(42);
392
393        let mut d = CFMutableDictionary::<CFString, CFType>::new();
394        d.add(&bar, &boo.as_CFType());
395        d.add(&baz, &tru.as_CFType());
396        d.add(&foo, &n42.as_CFType());
397        assert_eq!(d.len(), 3);
398
399        let (v1, v2) = d.get_keys_and_values();
400        assert_eq!(
401            v1,
402            &[bar.as_CFTypeRef(), baz.as_CFTypeRef(), foo.as_CFTypeRef()]
403        );
404        assert_eq!(
405            v2,
406            &[boo.as_CFTypeRef(), tru.as_CFTypeRef(), n42.as_CFTypeRef()]
407        );
408
409        d.remove(baz);
410        assert_eq!(d.len(), 2);
411
412        let (v1, v2) = d.get_keys_and_values();
413        assert_eq!(v1, &[bar.as_CFTypeRef(), foo.as_CFTypeRef()]);
414        assert_eq!(v2, &[boo.as_CFTypeRef(), n42.as_CFTypeRef()]);
415
416        d.remove_all();
417        assert_eq!(d.len(), 0)
418    }
419
420    #[test]
421    fn dict_find_and_contains_key() {
422        let dict = CFDictionary::from_CFType_pairs(&[(
423            CFString::from_static_string("hello"),
424            CFBoolean::true_value(),
425        )]);
426        let key = CFString::from_static_string("hello");
427        let invalid_key = CFString::from_static_string("foobar");
428
429        assert!(dict.contains_key(&key));
430        assert!(!dict.contains_key(&invalid_key));
431
432        let value = dict.find(&key).unwrap().clone();
433        assert_eq!(value, CFBoolean::true_value());
434        assert_eq!(dict.find(&invalid_key), None);
435    }
436
437    #[test]
438    fn convert_immutable_to_mutable_dict() {
439        let dict: CFDictionary<CFString, CFBoolean> = CFDictionary::from_CFType_pairs(&[(
440            CFString::from_static_string("Foo"),
441            CFBoolean::true_value(),
442        )]);
443        let mut mut_dict = CFMutableDictionary::from(&dict);
444        assert_eq!(dict.retain_count(), 1);
445        assert_eq!(mut_dict.retain_count(), 1);
446
447        assert_eq!(mut_dict.len(), 1);
448        assert_eq!(
449            *mut_dict.get(&CFString::from_static_string("Foo")),
450            CFBoolean::true_value()
451        );
452
453        mut_dict.add(
454            &CFString::from_static_string("Bar"),
455            &CFBoolean::false_value(),
456        );
457        assert_eq!(dict.len(), 1);
458        assert_eq!(mut_dict.len(), 2);
459    }
460
461    #[test]
462    fn mutable_dictionary_as_immutable() {
463        let mut mut_dict: CFMutableDictionary<CFString, CFBoolean> = CFMutableDictionary::new();
464        mut_dict.add(
465            &CFString::from_static_string("Bar"),
466            &CFBoolean::false_value(),
467        );
468        assert_eq!(mut_dict.retain_count(), 1);
469
470        let dict = mut_dict.to_immutable();
471        assert_eq!(mut_dict.retain_count(), 2);
472        assert_eq!(dict.retain_count(), 2);
473        assert_eq!(
474            *dict.get(&CFString::from_static_string("Bar")),
475            CFBoolean::false_value()
476        );
477
478        mem::drop(dict);
479        assert_eq!(mut_dict.retain_count(), 1);
480    }
481}