diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/core-foundation/src/array.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/core-foundation/src/array.rs')
-rw-r--r-- | third_party/rust/core-foundation/src/array.rs | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/third_party/rust/core-foundation/src/array.rs b/third_party/rust/core-foundation/src/array.rs new file mode 100644 index 0000000000..d66ffc5b82 --- /dev/null +++ b/third_party/rust/core-foundation/src/array.rs @@ -0,0 +1,286 @@ +// 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. + +//! Heterogeneous immutable arrays. + +pub use core_foundation_sys::array::*; +pub use core_foundation_sys::base::CFIndex; +use core_foundation_sys::base::{CFTypeRef, CFRelease, kCFAllocatorDefault}; +use std::mem; +use std::marker::PhantomData; +use std::os::raw::c_void; +use std::ptr; +use ConcreteCFType; + +use base::{CFIndexConvertible, TCFType, CFRange}; +use base::{FromVoid, ItemRef}; + +/// A heterogeneous immutable array. +pub struct CFArray<T = *const c_void>(CFArrayRef, PhantomData<T>); + +impl<T> Drop for CFArray<T> { + fn drop(&mut self) { + unsafe { CFRelease(self.as_CFTypeRef()) } + } +} + +pub struct CFArrayIterator<'a, T: 'a> { + array: &'a CFArray<T>, + index: CFIndex, + len: CFIndex, +} + +impl<'a, T: FromVoid> Iterator for CFArrayIterator<'a, T> { + type Item = ItemRef<'a, T>; + + fn next(&mut self) -> Option<ItemRef<'a, T>> { + if self.index >= self.len { + None + } else { + let value = unsafe { self.array.get_unchecked(self.index) }; + self.index += 1; + Some(value) + } + } +} + +impl<'a, T: FromVoid> ExactSizeIterator for CFArrayIterator<'a, T> { + fn len(&self) -> usize { + (self.array.len() - self.index) as usize + } +} + +impl_TCFType!(CFArray<T>, CFArrayRef, CFArrayGetTypeID); +impl_CFTypeDescription!(CFArray<T>); + +unsafe impl ConcreteCFType for CFArray<*const c_void> {} + +impl<T> CFArray<T> { + /// Creates a new `CFArray` with the given elements, which must implement `Copy`. + pub fn from_copyable(elems: &[T]) -> CFArray<T> where T: Copy { + unsafe { + let array_ref = CFArrayCreate(kCFAllocatorDefault, + elems.as_ptr() as *const *const c_void, + elems.len().to_CFIndex(), + ptr::null()); + TCFType::wrap_under_create_rule(array_ref) + } + } + + /// Creates a new `CFArray` with the given elements, which must be `CFType` objects. + pub fn from_CFTypes(elems: &[T]) -> CFArray<T> where T: TCFType { + unsafe { + let elems: Vec<CFTypeRef> = elems.iter().map(|elem| elem.as_CFTypeRef()).collect(); + let array_ref = CFArrayCreate(kCFAllocatorDefault, + elems.as_ptr(), + elems.len().to_CFIndex(), + &kCFTypeArrayCallBacks); + TCFType::wrap_under_create_rule(array_ref) + } + } + + #[inline] + pub fn to_untyped(&self) -> CFArray { + unsafe { CFArray::wrap_under_get_rule(self.0) } + } + + /// Returns the same array, but with the type 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) -> CFArray { + let reference = self.0; + mem::forget(self); + unsafe { CFArray::wrap_under_create_rule(reference) } + } + + /// Iterates over the elements of this `CFArray`. + /// + /// Careful; the loop body must wrap the reference properly. Generally, when array elements are + /// Core Foundation objects (not always true), they need to be wrapped with + /// `TCFType::wrap_under_get_rule()`. + #[inline] + pub fn iter<'a>(&'a self) -> CFArrayIterator<'a, T> { + CFArrayIterator { + array: self, + index: 0, + len: self.len(), + } + } + + #[inline] + pub fn len(&self) -> CFIndex { + unsafe { + CFArrayGetCount(self.0) + } + } + + #[inline] + pub unsafe fn get_unchecked<'a>(&'a self, index: CFIndex) -> ItemRef<'a, T> where T: FromVoid { + T::from_void(CFArrayGetValueAtIndex(self.0, index)) + } + + #[inline] + pub fn get<'a>(&'a self, index: CFIndex) -> Option<ItemRef<'a, T>> where T: FromVoid { + if index < self.len() { + Some(unsafe { T::from_void(CFArrayGetValueAtIndex(self.0, index)) } ) + } else { + None + } + } + + pub fn get_values(&self, range: CFRange) -> Vec<*const c_void> { + let mut vec = Vec::with_capacity(range.length as usize); + unsafe { + CFArrayGetValues(self.0, range, vec.as_mut_ptr()); + vec.set_len(range.length as usize); + vec + } + } + + pub fn get_all_values(&self) -> Vec<*const c_void> { + self.get_values(CFRange { + location: 0, + length: self.len() + }) + } +} + +impl<'a, T: FromVoid> IntoIterator for &'a CFArray<T> { + type Item = ItemRef<'a, T>; + type IntoIter = CFArrayIterator<'a, T>; + + fn into_iter(self) -> CFArrayIterator<'a, T> { + self.iter() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::mem; + use base::CFType; + + #[test] + fn to_untyped_correct_retain_count() { + let array = CFArray::<CFType>::from_CFTypes(&[]); + assert_eq!(array.retain_count(), 1); + + let untyped_array = array.to_untyped(); + assert_eq!(array.retain_count(), 2); + assert_eq!(untyped_array.retain_count(), 2); + + mem::drop(array); + assert_eq!(untyped_array.retain_count(), 1); + } + + #[test] + fn into_untyped() { + let array = CFArray::<CFType>::from_CFTypes(&[]); + let array2 = array.to_untyped(); + assert_eq!(array.retain_count(), 2); + + let untyped_array = array.into_untyped(); + assert_eq!(untyped_array.retain_count(), 2); + + mem::drop(array2); + assert_eq!(untyped_array.retain_count(), 1); + } + + #[test] + fn borrow() { + use string::CFString; + + let string = CFString::from_static_string("bar"); + assert_eq!(string.retain_count(), 1); + let x; + { + let arr: CFArray<CFString> = CFArray::from_CFTypes(&[string]); + { + let p = arr.get(0).unwrap(); + assert_eq!(p.retain_count(), 1); + } + { + x = arr.get(0).unwrap().clone(); + assert_eq!(x.retain_count(), 2); + assert_eq!(x.to_string(), "bar"); + } + } + assert_eq!(x.retain_count(), 1); + } + + #[test] + fn iter_untyped_array() { + use string::{CFString, CFStringRef}; + use base::TCFTypeRef; + + let cf_string = CFString::from_static_string("bar"); + let array: CFArray = CFArray::from_CFTypes(&[cf_string.clone()]).into_untyped(); + + let cf_strings = array.iter().map(|ptr| { + unsafe { CFString::wrap_under_get_rule(CFStringRef::from_void_ptr(*ptr)) } + }).collect::<Vec<_>>(); + let strings = cf_strings.iter().map(|s| s.to_string()).collect::<Vec<_>>(); + assert_eq!(cf_string.retain_count(), 3); + assert_eq!(&strings[..], &["bar"]); + } + + #[test] + fn should_box_and_unbox() { + use number::CFNumber; + + let n0 = CFNumber::from(0); + let n1 = CFNumber::from(1); + let n2 = CFNumber::from(2); + let n3 = CFNumber::from(3); + let n4 = CFNumber::from(4); + let n5 = CFNumber::from(5); + + let arr = CFArray::from_CFTypes(&[ + n0.as_CFType(), + n1.as_CFType(), + n2.as_CFType(), + n3.as_CFType(), + n4.as_CFType(), + n5.as_CFType(), + ]); + + assert_eq!( + arr.get_all_values(), + &[ + n0.as_CFTypeRef(), + n1.as_CFTypeRef(), + n2.as_CFTypeRef(), + n3.as_CFTypeRef(), + n4.as_CFTypeRef(), + n5.as_CFTypeRef() + ] + ); + + let mut sum = 0; + + let mut iter = arr.iter(); + assert_eq!(iter.len(), 6); + assert!(iter.next().is_some()); + assert_eq!(iter.len(), 5); + + for elem in iter { + let number: CFNumber = elem.downcast::<CFNumber>().unwrap(); + sum += number.to_i64().unwrap() + } + + assert_eq!(sum, 15); + + for elem in arr.iter() { + let number: CFNumber = elem.downcast::<CFNumber>().unwrap(); + sum += number.to_i64().unwrap() + } + + assert_eq!(sum, 30); + } +} |