Skip to main content

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}