objc_foundation/
enumerator.rs

1use std::marker::PhantomData;
2use std::mem;
3use std::os::raw::c_ulong;
4use std::ptr;
5use std::slice;
6
7use objc::runtime::Object;
8use objc_id::Id;
9
10use INSObject;
11
12pub struct NSEnumerator<'a, T> where T: INSObject {
13    id: Id<Object>,
14    item: PhantomData<&'a T>,
15}
16
17impl<'a, T> NSEnumerator<'a, T> where T: INSObject {
18    pub unsafe fn from_ptr(ptr: *mut Object) -> NSEnumerator<'a, T> {
19        NSEnumerator { id: Id::from_ptr(ptr), item: PhantomData }
20    }
21}
22
23impl<'a, T> Iterator for NSEnumerator<'a, T> where T: INSObject {
24    type Item = &'a T;
25
26    fn next(&mut self) -> Option<&'a T> {
27        unsafe {
28            let obj: *mut T = msg_send![self.id, nextObject];
29            if obj.is_null() { None } else { Some(&*obj) }
30        }
31    }
32}
33
34pub trait INSFastEnumeration: INSObject {
35    type Item: INSObject;
36
37    fn enumerator(&self) -> NSFastEnumerator<Self> {
38        NSFastEnumerator::new(self)
39    }
40}
41
42#[repr(C)]
43struct NSFastEnumerationState<T> {
44    state: c_ulong,
45    items_ptr: *const *const T,
46    mutations_ptr: *mut c_ulong,
47    extra: [c_ulong; 5],
48}
49
50fn enumerate<'a, 'b: 'a, C: INSFastEnumeration>(object: &'b C,
51        state: &mut NSFastEnumerationState<C::Item>,
52        buf: &'a mut [*const C::Item]) -> Option<&'a [*const C::Item]> {
53    let count: usize = unsafe {
54        // Reborrow state so that we don't move it
55        let state = &mut *state;
56        msg_send![object, countByEnumeratingWithState:state
57                                              objects:buf.as_mut_ptr()
58                                                count:buf.len()]
59    };
60
61    if count > 0 {
62        unsafe { Some(slice::from_raw_parts(state.items_ptr, count)) }
63    } else {
64        None
65    }
66}
67
68const FAST_ENUM_BUF_SIZE: usize = 16;
69
70pub struct NSFastEnumerator<'a, C: 'a + INSFastEnumeration> {
71    object: &'a C,
72
73    ptr: *const *const C::Item,
74    end: *const *const C::Item,
75
76    state: NSFastEnumerationState<C::Item>,
77    buf: [*const C::Item; FAST_ENUM_BUF_SIZE],
78}
79
80impl<'a, C: INSFastEnumeration> NSFastEnumerator<'a, C> {
81    fn new(object: &C) -> NSFastEnumerator<C> {
82        NSFastEnumerator {
83            object: object,
84
85            ptr: ptr::null(),
86            end: ptr::null(),
87
88            state: unsafe { mem::zeroed() },
89            buf: [ptr::null(); FAST_ENUM_BUF_SIZE],
90        }
91    }
92
93    fn update_buf(&mut self) -> bool {
94        // If this isn't our first time enumerating, record the previous value
95        // from the mutations pointer.
96        let mutations = if !self.ptr.is_null() {
97            Some(unsafe { *self.state.mutations_ptr })
98        } else {
99            None
100        };
101
102        let next_buf = enumerate(self.object, &mut self.state, &mut self.buf);
103        if let Some(buf) = next_buf {
104            // Check if the collection was mutated
105            if let Some(mutations) = mutations {
106                assert!(mutations == unsafe { *self.state.mutations_ptr },
107                    "Mutation detected during enumeration of object {:p}",
108                    self.object);
109            }
110
111            self.ptr = buf.as_ptr();
112            self.end = unsafe { self.ptr.offset(buf.len() as isize) };
113            true
114        } else {
115            self.ptr = ptr::null();
116            self.end = ptr::null();
117            false
118        }
119    }
120}
121
122impl<'a, C: INSFastEnumeration> Iterator for NSFastEnumerator<'a, C> {
123    type Item = &'a C::Item;
124
125    fn next(&mut self) -> Option<&'a C::Item> {
126        if self.ptr == self.end && !self.update_buf() {
127            None
128        } else {
129            unsafe {
130                let obj = *self.ptr;
131                self.ptr = self.ptr.offset(1);
132                Some(&*obj)
133            }
134        }
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use {INSArray, INSValue, NSArray, NSValue};
141    use super::INSFastEnumeration;
142
143    #[test]
144    fn test_enumerator() {
145        let vec = (0u32..4).map(NSValue::from_value).collect();
146        let array = NSArray::from_vec(vec);
147
148        let enumerator = array.object_enumerator();
149        assert!(enumerator.count() == 4);
150
151        let enumerator = array.object_enumerator();
152        assert!(enumerator.enumerate().all(|(i, obj)| obj.value() == i as u32));
153    }
154
155    #[test]
156    fn test_fast_enumerator() {
157        let vec = (0u32..4).map(NSValue::from_value).collect();
158        let array = NSArray::from_vec(vec);
159
160        let enumerator = array.enumerator();
161        assert!(enumerator.count() == 4);
162
163        let enumerator = array.enumerator();
164        assert!(enumerator.enumerate().all(|(i, obj)| obj.value() == i as u32));
165    }
166}