objc_foundation/
enumerator.rs1use 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 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 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 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}