|
|
|
pub trait NSFastEnumeration { |
|
type Item; |
|
|
|
/// Responsible for calling [countByEnumeratingWithState:objects:count:] |
|
unsafe fn next( |
|
&self, |
|
state: &mut NSFastEnumerationState, |
|
stackbuf: *mut Self::Item, |
|
count: NSUInteger, |
|
) -> NSUInteger; |
|
} |
|
|
|
impl<T> NSFastEnumeration for &T |
|
where |
|
T: NSFastEnumeration, |
|
{ |
|
type Item = T::Item; |
|
|
|
unsafe fn next( |
|
&self, |
|
state: &mut NSFastEnumerationState, |
|
stackbuf: *mut Self::Item, |
|
count: NSUInteger, |
|
) -> NSUInteger { |
|
T::next(self, state, stackbuf, count) |
|
} |
|
} |
|
|
|
pub enum FastEnumerationPointingTo { |
|
StackVec, |
|
InnerStorage, |
|
} |
|
|
|
pub struct NSFastEnumerationIter<T: NSFastEnumeration> { |
|
collection: T, |
|
state: NSFastEnumerationState, |
|
stackvec: ArrayVec<T::Item, 16>, |
|
mutations_ptr_value: usize, |
|
count: NSUInteger, |
|
index: usize, |
|
point_to: FastEnumerationPointingTo, |
|
} |
|
|
|
impl<'a, T: NSFastEnumeration> NSFastEnumerationIter<T> { |
|
pub fn new(collection: T) -> Self { |
|
let mut stackvec = ArrayVec::<T::Item, 16>::new(); |
|
let mut state = NSFastEnumerationState::default(); |
|
|
|
// SAFETY: the FFI function we are going to call guarantees not to |
|
// modify this raw pointer. |
|
let mut stackbuf = Pin::new(&mut stackvec); |
|
|
|
let count = |
|
unsafe { collection.next(&mut state, stackbuf.as_mut_ptr(), stackbuf.len()) }; |
|
|
|
/*println!( |
|
"count={} ptr={:p} stackptr={:?}", |
|
count, |
|
state.items, |
|
stackbuf.as_ptr() |
|
);*/ |
|
|
|
// SAFETY: stackvec can't move just yet, let's check state.items |
|
let point_to = if stackbuf.as_ptr() as *const fruity::objc::id == state.items { |
|
// The enumerator filled our own vector |
|
unsafe { stackvec.set_len(count) } |
|
FastEnumerationPointingTo::StackVec |
|
} else { |
|
// We have a completely different pointer |
|
FastEnumerationPointingTo::InnerStorage |
|
}; |
|
|
|
let mutations_ptr_value = state.mutations as usize; |
|
|
|
Self { |
|
collection, |
|
state, |
|
stackvec, |
|
mutations_ptr_value, |
|
count, |
|
index: 0, |
|
point_to, |
|
} |
|
} |
|
} |
|
|
|
impl<'a, T: NSFastEnumeration> Iterator for NSFastEnumerationIter<T> { |
|
type Item = T::Item; |
|
|
|
fn next(&mut self) -> Option<Self::Item> { |
|
if self.count == 0 { |
|
return None; |
|
} |
|
|
|
//println!("{} {}", self.index, self.count); |
|
if self.index > self.count - 1 { |
|
self.index = 0; |
|
|
|
// SAFETY: the FFI function we are going to call guarantees not to |
|
// modify the value of the raw pointer, but not the contents. |
|
let mut stackbuf = Pin::new(&mut self.stackvec); |
|
|
|
self.count = unsafe { |
|
self.collection |
|
.next(&mut self.state, stackbuf.as_mut_ptr(), stackbuf.len()) |
|
}; |
|
|
|
if self.state.mutations as usize != self.mutations_ptr_value { |
|
panic!("underlying storage changed during iteration"); |
|
} |
|
|
|
/*println!( |
|
"count={} ptr={:p} stackptr={:?}", |
|
self.count, |
|
self.state.items, |
|
stackbuf.as_ptr() |
|
);*/ |
|
|
|
if self.count == 0 { |
|
return None; |
|
} |
|
|
|
// SAFETY: stackvec can't move just yet, let's check state.items |
|
self.point_to = if stackbuf.as_ptr() as *const fruity::objc::id == self.state.items |
|
{ |
|
// The enumerator filled our own vector |
|
unsafe { self.stackvec.set_len(self.count) } |
|
FastEnumerationPointingTo::StackVec |
|
} else { |
|
FastEnumerationPointingTo::InnerStorage |
|
}; |
|
} |
|
|
|
let element = match self.point_to { |
|
FastEnumerationPointingTo::StackVec => { |
|
let item = unsafe { self.stackvec.get_unchecked(self.index) }; |
|
let item = item.clone(); |
|
Some(item) |
|
} |
|
FastEnumerationPointingTo::InnerStorage => { |
|
let buffer = unsafe { slice::from_raw_parts(self.state.items, self.count) }; |
|
Some(unsafe { |
|
T::Item::from_object(buffer[self.index].clone().as_non_null_ptr()) |
|
}) |
|
} |
|
}; |
|
|
|
self.index += 1; |
|
element |
|
} |
|
} |
|
|
|
#[repr(C)] |
|
pub struct NSFastEnumerationState { |
|
state: u64, |
|
items: *const fruity::objc::id, |
|
mutations: *const usize, |
|
extra: [u64; 5], |
|
} |
|
|
|
impl Default for NSFastEnumerationState { |
|
fn default() -> Self { |
|
Self { |
|
state: Default::default(), |
|
items: std::ptr::null(), |
|
mutations: std::ptr::null(), |
|
extra: Default::default(), |
|
} |
|
} |
|
} |