1#[cfg(feature = "alloc")]
2use alloc::vec::Vec;
3use core::ffi::c_void;
4use core::{borrow::Borrow, mem};
5
6use crate::{kCFTypeArrayCallBacks, CFArray, CFIndex, CFMutableArray, CFRetained, Type};
7
8#[inline]
9fn get_len<T>(objects: &[T]) -> CFIndex {
10 let len = objects.len();
16 debug_assert!(len < CFIndex::MAX as usize);
17 len as CFIndex
18}
19
20#[cold]
28fn failed_creating_array(len: CFIndex) -> ! {
29 #[cfg(feature = "alloc")]
30 {
31 use alloc::alloc::{handle_alloc_error, Layout};
32 use core::mem::align_of;
33
34 let layout = Layout::array::<*const ()>(len as usize).unwrap_or_else(|_| unsafe {
37 Layout::from_size_align_unchecked(0, align_of::<*const ()>())
38 });
39
40 handle_alloc_error(layout)
41 }
42 #[cfg(not(feature = "alloc"))]
43 {
44 panic!("failed allocating CFArray holding {len} elements")
45 }
46}
47
48impl<T: ?Sized> CFArray<T> {
50 #[inline]
53 #[doc(alias = "CFArray::new")]
54 pub fn empty() -> CFRetained<Self>
55 where
56 T: Type,
57 {
58 Self::from_objects(&[])
62 }
63
64 #[inline]
66 #[doc(alias = "CFArray::new")]
67 pub fn from_objects(objects: &[&T]) -> CFRetained<Self>
68 where
69 T: Type,
70 {
71 let len = get_len(objects);
72 let ptr = objects.as_ptr().cast::<*const c_void>().cast_mut();
74
75 let array = unsafe { CFArray::new(None, ptr, len, &kCFTypeArrayCallBacks) }
81 .unwrap_or_else(|| failed_creating_array(len));
82
83 unsafe { CFRetained::cast_unchecked::<Self>(array) }
85 }
86
87 #[inline]
89 #[allow(non_snake_case)]
90 #[deprecated = "renamed to CFArray::from_objects"]
91 pub fn from_CFTypes(objects: &[&T]) -> CFRetained<Self>
92 where
93 T: Type,
94 {
95 Self::from_objects(objects)
96 }
97
98 #[inline]
100 #[doc(alias = "CFArray::new")]
101 pub fn from_retained_objects(objects: &[CFRetained<T>]) -> CFRetained<Self>
102 where
103 T: Type,
104 {
105 let len = get_len(objects);
106 let ptr = objects.as_ptr().cast::<*const c_void>().cast_mut();
108
109 let array = unsafe { CFArray::new(None, ptr, len, &kCFTypeArrayCallBacks) }
111 .unwrap_or_else(|| failed_creating_array(len));
112
113 unsafe { CFRetained::cast_unchecked::<Self>(array) }
115 }
116}
117
118impl<T: ?Sized> CFMutableArray<T> {
120 #[inline]
122 #[doc(alias = "CFMutableArray::new")]
123 pub fn empty() -> CFRetained<Self>
124 where
125 T: Type,
126 {
127 Self::with_capacity(0)
128 }
129
130 #[inline]
132 #[doc(alias = "CFMutableArray::new")]
133 pub fn with_capacity(capacity: usize) -> CFRetained<Self>
134 where
135 T: Type,
136 {
137 let capacity = capacity.try_into().expect("capacity too high");
139
140 let array = unsafe { CFMutableArray::new(None, capacity, &kCFTypeArrayCallBacks) }
143 .unwrap_or_else(|| failed_creating_array(capacity));
144
145 unsafe { CFRetained::cast_unchecked::<Self>(array) }
148 }
149}
150
151impl<T: ?Sized> CFArray<T> {
175 #[inline]
185 #[doc(alias = "CFArrayGetValueAtIndex")]
186 pub unsafe fn get_unchecked(&self, index: CFIndex) -> &T
187 where
188 T: Type + Sized,
189 {
190 let ptr = unsafe { self.as_opaque().value_at_index(index) };
192 unsafe { &*ptr.cast::<T>() }
198 }
199
200 #[cfg(feature = "alloc")]
209 #[doc(alias = "CFArrayGetValues")]
210 pub unsafe fn to_vec_unchecked(&self) -> Vec<&T>
211 where
212 T: Type,
213 {
214 let len = self.len();
215 let range = crate::CFRange {
216 location: 0,
217 length: len as CFIndex,
219 };
220 let mut vec = Vec::<&T>::with_capacity(len);
221
222 let ptr = vec.as_mut_ptr().cast::<*const c_void>();
224 unsafe { self.as_opaque().values(range, ptr) };
226 unsafe { vec.set_len(len) };
228
229 vec
230 }
231
232 #[inline]
242 pub unsafe fn iter_unchecked(&self) -> CFArrayIterUnchecked<'_, T>
243 where
244 T: Type,
245 {
246 CFArrayIterUnchecked {
247 array: self,
248 index: 0,
249 len: self.len() as CFIndex,
250 }
251 }
252}
253
254impl<T: ?Sized> CFArray<T> {
256 pub fn as_opaque(&self) -> &CFArray {
258 unsafe { mem::transmute::<&CFArray<T>, &CFArray>(self) }
261 }
262
263 #[inline]
265 #[doc(alias = "CFArrayGetCount")]
266 pub fn len(&self) -> usize {
267 self.as_opaque().count() as _
269 }
270
271 #[inline]
273 pub fn is_empty(&self) -> bool {
274 self.len() == 0
275 }
276
277 #[doc(alias = "CFArrayGetValueAtIndex")]
281 pub fn get(&self, index: usize) -> Option<CFRetained<T>>
282 where
283 T: Type + Sized,
284 {
285 if index < self.len() {
286 let index = index as CFIndex;
289 Some(unsafe { self.get_unchecked(index) }.retain())
299 } else {
300 None
301 }
302 }
303
304 #[cfg(feature = "alloc")]
306 #[doc(alias = "CFArrayGetValues")]
307 pub fn to_vec(&self) -> Vec<CFRetained<T>>
308 where
309 T: Type + Sized,
310 {
311 let vec = unsafe { self.to_vec_unchecked() };
314 vec.into_iter().map(T::retain).collect()
315 }
316
317 #[inline]
319 pub fn iter(&self) -> CFArrayIter<'_, T> {
320 CFArrayIter {
321 array: self,
322 index: 0,
323 }
324 }
325}
326
327impl<T: ?Sized> CFMutableArray<T> {
329 pub fn as_opaque(&self) -> &CFMutableArray {
331 unsafe { mem::transmute::<&CFMutableArray<T>, &CFMutableArray>(self) }
333 }
334}
335
336impl<T> CFMutableArray<T> {
338 #[inline]
340 #[doc(alias = "CFArrayAppendValue")]
341 pub fn append(&self, obj: &T) {
342 let ptr: *const T = obj;
343 let ptr: *const c_void = ptr.cast();
344 unsafe { CFMutableArray::append_value(Some(self.as_opaque()), ptr) }
346 }
347
348 #[doc(alias = "CFArrayInsertValueAtIndex")]
354 pub fn insert(&self, index: usize, obj: &T) {
355 let len = self.len();
357 if index <= len {
358 let ptr: *const T = obj;
359 let ptr: *const c_void = ptr.cast();
360 unsafe {
363 CFMutableArray::insert_value_at_index(Some(self.as_opaque()), index as CFIndex, ptr)
364 }
365 } else {
366 panic!(
367 "insertion index (is {}) should be <= len (is {})",
368 index, len
369 );
370 }
371 }
372}
373
374#[derive(Debug)]
376pub struct CFArrayIter<'a, T: ?Sized + 'a> {
377 array: &'a CFArray<T>,
378 index: usize,
379}
380
381impl<T: Type> Iterator for CFArrayIter<'_, T> {
382 type Item = CFRetained<T>;
383
384 fn next(&mut self) -> Option<CFRetained<T>> {
385 let value = self.array.get(self.index)?;
389 self.index += 1;
390 Some(value)
391 }
392
393 fn size_hint(&self) -> (usize, Option<usize>) {
394 let len = self.array.len().saturating_sub(self.index);
395 (len, Some(len))
396 }
397}
398
399impl<T: Type> ExactSizeIterator for CFArrayIter<'_, T> {}
400
401#[derive(Debug)]
409pub struct CFArrayIntoIter<T: ?Sized> {
410 array: CFRetained<CFArray<T>>,
411 index: usize,
412}
413
414impl<T: Type> Iterator for CFArrayIntoIter<T> {
415 type Item = CFRetained<T>;
416
417 fn next(&mut self) -> Option<CFRetained<T>> {
418 let value = self.array.get(self.index)?;
420 self.index += 1;
421 Some(value)
422 }
423
424 fn size_hint(&self) -> (usize, Option<usize>) {
425 let len = self.array.len().saturating_sub(self.index);
426 (len, Some(len))
427 }
428}
429
430impl<T: Type> ExactSizeIterator for CFArrayIntoIter<T> {}
431
432impl<'a, T: Type> IntoIterator for &'a CFArray<T> {
433 type Item = CFRetained<T>;
434 type IntoIter = CFArrayIter<'a, T>;
435
436 #[inline]
437 fn into_iter(self) -> Self::IntoIter {
438 self.iter()
439 }
440}
441
442impl<'a, T: Type> IntoIterator for &'a CFMutableArray<T> {
443 type Item = CFRetained<T>;
444 type IntoIter = CFArrayIter<'a, T>;
445
446 #[inline]
447 fn into_iter(self) -> Self::IntoIter {
448 self.iter()
449 }
450}
451
452impl<T: Type> IntoIterator for CFRetained<CFArray<T>> {
453 type Item = CFRetained<T>;
454 type IntoIter = CFArrayIntoIter<T>;
455
456 #[inline]
457 fn into_iter(self) -> Self::IntoIter {
458 CFArrayIntoIter {
459 array: self,
460 index: 0,
461 }
462 }
463}
464
465impl<T: Type> IntoIterator for CFRetained<CFMutableArray<T>> {
466 type Item = CFRetained<T>;
467 type IntoIter = CFArrayIntoIter<T>;
468
469 #[inline]
470 fn into_iter(self) -> Self::IntoIter {
471 let array = unsafe { CFRetained::cast_unchecked::<CFArray<T>>(self) };
473 CFArrayIntoIter { array, index: 0 }
474 }
475}
476
477#[derive(Debug)]
483pub struct CFArrayIterUnchecked<'a, T: ?Sized + 'a> {
484 array: &'a CFArray<T>,
485 index: CFIndex,
486 len: CFIndex,
487}
488
489impl<'a, T: Type> Iterator for CFArrayIterUnchecked<'a, T> {
490 type Item = &'a T;
491
492 #[inline]
493 fn next(&mut self) -> Option<&'a T> {
494 debug_assert_eq!(
495 self.array.len(),
496 self.len as usize,
497 "array was mutated while iterating"
498 );
499 if self.index < self.len {
500 let value = unsafe { self.array.get_unchecked(self.index) };
507 self.index += 1;
508 Some(value)
509 } else {
510 None
511 }
512 }
513
514 #[inline]
515 fn size_hint(&self) -> (usize, Option<usize>) {
516 let len = (self.len - self.index) as usize;
517 (len, Some(len))
518 }
519}
520
521impl<T: Type> ExactSizeIterator for CFArrayIterUnchecked<'_, T> {}
522
523impl<T: ?Sized + Type> AsRef<CFArray> for CFArray<T> {
526 fn as_ref(&self) -> &CFArray {
527 self.as_opaque()
528 }
529}
530impl<T: ?Sized + Type> AsRef<CFMutableArray> for CFMutableArray<T> {
531 fn as_ref(&self) -> &CFMutableArray {
532 self.as_opaque()
533 }
534}
535
536impl<T: ?Sized + Type> Borrow<CFArray> for CFArray<T> {
538 fn borrow(&self) -> &CFArray {
539 self.as_opaque()
540 }
541}
542impl<T: ?Sized + Type> Borrow<CFMutableArray> for CFMutableArray<T> {
543 fn borrow(&self) -> &CFMutableArray {
544 self.as_opaque()
545 }
546}
547
548#[cfg(test)]
549mod tests {
550 use super::*;
551 #[cfg(feature = "CFString")]
552 use crate::CFString;
553 use core::ptr::null;
554
555 #[test]
556 fn array_with_invalid_pointers() {
557 let ptr = [0 as _, 1 as _, 2 as _, 3 as _, usize::MAX as _].as_mut_ptr();
559 let array = unsafe { CFArray::new(None, ptr, 1, null()) }.unwrap();
560 let value = unsafe { array.value_at_index(0) };
561 assert!(value.is_null());
562 }
563
564 #[test]
565 #[should_panic]
566 #[ignore = "aborts (as expected)"]
567 fn object_array_cannot_contain_null() {
568 let ptr = [null()].as_mut_ptr();
569 let _array = unsafe { CFArray::new(None, ptr, 1, &kCFTypeArrayCallBacks) };
570 }
571
572 #[test]
573 #[cfg(feature = "CFString")]
574 fn correct_retain_count() {
575 let objects = [
576 CFString::from_str("some long string that doesn't get small-string optimized"),
577 CFString::from_str("another long string that doesn't get small-string optimized"),
578 ];
579 let array = CFArray::from_retained_objects(&objects);
580
581 assert_eq!(array.retain_count(), 1);
583 assert_eq!(unsafe { array.get_unchecked(0) }.retain_count(), 2);
584 assert_eq!(unsafe { array.get_unchecked(1) }.retain_count(), 2);
585
586 drop(objects);
587 assert_eq!(unsafe { array.get_unchecked(0) }.retain_count(), 1);
588 assert_eq!(unsafe { array.get_unchecked(1) }.retain_count(), 1);
589
590 let _array2 = array.retain();
592 assert_eq!(unsafe { array.get_unchecked(0) }.retain_count(), 1);
593 assert_eq!(unsafe { array.get_unchecked(1) }.retain_count(), 1);
594
595 assert_eq!(array.get(0).unwrap().retain_count(), 2);
597 }
598
599 #[test]
600 #[cfg(feature = "CFString")]
601 fn iter() {
602 use alloc::vec::Vec;
603
604 let s1 = CFString::from_str("a");
605 let s2 = CFString::from_str("b");
606 let array = CFArray::from_objects(&[&*s1, &*s2, &*s1]);
607
608 assert_eq!(
609 array.iter().collect::<Vec<_>>(),
610 [s1.clone(), s2.clone(), s1.clone()]
611 );
612
613 assert_eq!(
614 unsafe { array.iter_unchecked() }.collect::<Vec<_>>(),
615 [&*s1, &*s2, &*s1]
616 );
617 }
618
619 #[test]
620 #[cfg(feature = "CFString")]
621 fn iter_fused() {
622 let s1 = CFString::from_str("a");
623 let s2 = CFString::from_str("b");
624 let array = CFArray::from_objects(&[&*s1, &*s2]);
625
626 let mut iter = array.iter();
627 assert_eq!(iter.next(), Some(s1.clone()));
628 assert_eq!(iter.next(), Some(s2.clone()));
629 assert_eq!(iter.next(), None);
630 assert_eq!(iter.next(), None);
631 assert_eq!(iter.next(), None);
632 assert_eq!(iter.next(), None);
633 assert_eq!(iter.next(), None);
634 assert_eq!(iter.next(), None);
635 assert_eq!(iter.next(), None);
636
637 let mut iter = unsafe { array.iter_unchecked() };
638 assert_eq!(iter.next(), Some(&*s1));
639 assert_eq!(iter.next(), Some(&*s2));
640 assert_eq!(iter.next(), None);
641 assert_eq!(iter.next(), None);
642 assert_eq!(iter.next(), None);
643 assert_eq!(iter.next(), None);
644 assert_eq!(iter.next(), None);
645 assert_eq!(iter.next(), None);
646 assert_eq!(iter.next(), None);
647 }
648
649 #[test]
650 #[cfg(feature = "CFString")]
651 fn mutate() {
652 let array = CFMutableArray::<CFString>::with_capacity(10);
653 array.insert(0, &CFString::from_str("a"));
654 array.append(&CFString::from_str("c"));
655 array.insert(1, &CFString::from_str("b"));
656 assert_eq!(
657 array.to_vec(),
658 [
659 CFString::from_str("a"),
660 CFString::from_str("b"),
661 CFString::from_str("c"),
662 ]
663 );
664 }
665
666 #[test]
667 #[cfg(feature = "CFString")]
668 #[cfg_attr(
669 not(debug_assertions),
670 ignore = "not detected when debug assertions are off"
671 )]
672 #[should_panic = "array was mutated while iterating"]
673 fn mutate_while_iter_unchecked() {
674 let array = CFMutableArray::<CFString>::with_capacity(10);
675 assert_eq!(array.len(), 0);
676
677 let mut iter = unsafe { array.iter_unchecked() };
678 array.append(&CFString::from_str("a"));
679 let _ = iter.next();
681 }
682}