mtl_foundation/enumerator.rs
1//! Enumerator types for Foundation.
2//!
3//! Corresponds to `Foundation/NSEnumerator.hpp`.
4//!
5//! # C++ Equivalent
6//!
7//! ```cpp
8//! namespace NS {
9//! struct FastEnumerationState {
10//! unsigned long state;
11//! Object** itemsPtr;
12//! unsigned long* mutationsPtr;
13//! unsigned long extra[5];
14//! } _NS_PACKED;
15//!
16//! class FastEnumeration : public Referencing<FastEnumeration> {
17//! public:
18//! NS::UInteger countByEnumerating(FastEnumerationState* pState, Object** pBuffer, NS::UInteger len);
19//! };
20//!
21//! template <class _ObjectType>
22//! class Enumerator : public Referencing<Enumerator<_ObjectType>, FastEnumeration> {
23//! public:
24//! _ObjectType* nextObject();
25//! class Array* allObjects();
26//! };
27//! }
28//! ```
29
30use std::ffi::c_void;
31use std::marker::PhantomData;
32use std::ptr::NonNull;
33
34use mtl_sys::{msg_send_0, msg_send_3, sel};
35
36use crate::object::{Object, Referencing};
37use crate::types::UInteger;
38
39/// Fast enumeration state structure.
40///
41/// C++ equivalent: `NS::FastEnumerationState`
42#[repr(C, packed)]
43#[derive(Copy, Clone, Debug)]
44pub struct FastEnumerationState {
45 /// Enumeration state value.
46 pub state: std::ffi::c_ulong,
47 /// Pointer to items buffer.
48 pub items_ptr: *mut *mut Object,
49 /// Pointer to mutations counter.
50 pub mutations_ptr: *mut std::ffi::c_ulong,
51 /// Extra storage for implementation use.
52 pub extra: [std::ffi::c_ulong; 5],
53}
54
55impl Default for FastEnumerationState {
56 fn default() -> Self {
57 Self {
58 state: 0,
59 items_ptr: std::ptr::null_mut(),
60 mutations_ptr: std::ptr::null_mut(),
61 extra: [0; 5],
62 }
63 }
64}
65
66/// An object that supports fast enumeration.
67///
68/// C++ equivalent: `NS::FastEnumeration`
69#[repr(transparent)]
70#[derive(Clone)]
71pub struct FastEnumeration(NonNull<c_void>);
72
73impl FastEnumeration {
74 /// Count objects by enumerating.
75 ///
76 /// C++ equivalent: `NS::UInteger countByEnumerating(FastEnumerationState* pState, Object** pBuffer, NS::UInteger len)`
77 #[inline]
78 pub fn count_by_enumerating(
79 &self,
80 state: *mut FastEnumerationState,
81 buffer: *mut *mut Object,
82 len: UInteger,
83 ) -> UInteger {
84 unsafe {
85 msg_send_3(
86 self.as_ptr(),
87 sel!(countByEnumeratingWithState:objects:count:),
88 state,
89 buffer,
90 len,
91 )
92 }
93 }
94
95 /// Create a FastEnumeration from a raw pointer.
96 ///
97 /// # Safety
98 ///
99 /// The pointer must be a valid Objective-C object that supports fast enumeration.
100 #[inline]
101 pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
102 NonNull::new(ptr).map(Self)
103 }
104}
105
106impl Referencing for FastEnumeration {
107 #[inline]
108 fn as_ptr(&self) -> *const c_void {
109 self.0.as_ptr()
110 }
111}
112
113// SAFETY: Objective-C objects are thread-safe for reference counting
114unsafe impl Send for FastEnumeration {}
115unsafe impl Sync for FastEnumeration {}
116
117/// An enumerator for a collection.
118///
119/// C++ equivalent: `NS::Enumerator<_ObjectType>`
120#[repr(transparent)]
121pub struct Enumerator<T> {
122 inner: NonNull<c_void>,
123 _marker: PhantomData<T>,
124}
125
126impl<T> Clone for Enumerator<T> {
127 fn clone(&self) -> Self {
128 Self {
129 inner: self.inner,
130 _marker: PhantomData,
131 }
132 }
133}
134
135impl<T> Enumerator<T> {
136 /// Get the next object in the enumeration.
137 ///
138 /// C++ equivalent: `_ObjectType* nextObject()`
139 #[inline]
140 pub fn next_object(&self) -> *mut T {
141 unsafe { msg_send_0(self.as_ptr(), sel!(nextObject)) }
142 }
143
144 /// Get all remaining objects as an array.
145 ///
146 /// C++ equivalent: `class Array* allObjects()`
147 #[inline]
148 pub fn all_objects(&self) -> *mut c_void {
149 // Returns NSArray* but we return raw pointer to avoid circular dep
150 unsafe { msg_send_0(self.as_ptr(), sel!(allObjects)) }
151 }
152
153 /// Create an Enumerator from a raw pointer.
154 ///
155 /// # Safety
156 ///
157 /// The pointer must be a valid Objective-C NSEnumerator object.
158 #[inline]
159 pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
160 NonNull::new(ptr).map(|inner| Self {
161 inner,
162 _marker: PhantomData,
163 })
164 }
165}
166
167impl<T> Referencing for Enumerator<T> {
168 #[inline]
169 fn as_ptr(&self) -> *const c_void {
170 self.inner.as_ptr()
171 }
172}
173
174// SAFETY: Objective-C objects are thread-safe for reference counting
175unsafe impl<T: Send> Send for Enumerator<T> {}
176unsafe impl<T: Sync> Sync for Enumerator<T> {}
177
178impl<T> std::fmt::Debug for Enumerator<T> {
179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
180 f.debug_struct("Enumerator")
181 .field("ptr", &self.inner)
182 .finish()
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_fast_enumeration_state_size() {
192 // FastEnumerationState should have the expected size
193 // state (8) + itemsPtr (8) + mutationsPtr (8) + extra[5] (40) = 64 bytes
194 assert_eq!(
195 std::mem::size_of::<FastEnumerationState>(),
196 std::mem::size_of::<std::ffi::c_ulong>() * 8
197 );
198 }
199}