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