Auto merge of #119 - conradev:nsfastenumeration, r=jdm
Added basic NSFastEnumeration implementation This allows you to turn any Foundation collection into an `Iterator`. The basic implementation is derived from @mikeash's [blog post](https://www.mikeash.com/pyblog/friday-qa-2010-04-16-implementing-fast-enumeration.html) on the subject. Let me know what you think! I have not yet decided how best to handle a change in the mutation pointer – that still remains to be implemented. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/cocoa-rs/119) <!-- Reviewable:end -->
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::ptr;
|
||||
use base::{id, class, BOOL, nil};
|
||||
use libc;
|
||||
use objc;
|
||||
@@ -256,3 +257,76 @@ pub trait NSDate {
|
||||
impl NSDate for id {
|
||||
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct NSFastEnumerationState {
|
||||
pub state: libc::c_ulong,
|
||||
pub items_ptr: *mut id,
|
||||
pub mutations_ptr: *mut libc::c_ulong,
|
||||
pub extra: [libc::c_ulong; 5]
|
||||
}
|
||||
|
||||
const NS_FAST_ENUM_BUF_SIZE: usize = 16;
|
||||
|
||||
pub struct NSFastIterator {
|
||||
state: NSFastEnumerationState,
|
||||
buffer: [id; NS_FAST_ENUM_BUF_SIZE],
|
||||
mut_val: Option<libc::c_ulong>,
|
||||
len: usize,
|
||||
idx: usize,
|
||||
object: id
|
||||
}
|
||||
|
||||
impl Iterator for NSFastIterator {
|
||||
type Item = id;
|
||||
|
||||
fn next(&mut self) -> Option<id> {
|
||||
if self.idx >= self.len {
|
||||
self.len = unsafe {
|
||||
msg_send![self.object, countByEnumeratingWithState:&mut self.state objects:self.buffer.as_mut_ptr() count:NS_FAST_ENUM_BUF_SIZE]
|
||||
};
|
||||
self.idx = 0;
|
||||
}
|
||||
|
||||
let new_mut = unsafe {
|
||||
*self.state.mutations_ptr
|
||||
};
|
||||
|
||||
if let Some(old_mut) = self.mut_val {
|
||||
assert!(old_mut == new_mut, "The collection was mutated while being enumerated");
|
||||
}
|
||||
|
||||
if self.idx < self.len {
|
||||
let object = unsafe {
|
||||
*self.state.items_ptr.offset(self.idx as isize)
|
||||
};
|
||||
self.mut_val = Some(new_mut);
|
||||
self.idx += 1;
|
||||
Some(object)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NSFastEnumeration {
|
||||
unsafe fn iter(self) -> NSFastIterator;
|
||||
}
|
||||
|
||||
impl NSFastEnumeration for id {
|
||||
unsafe fn iter(self) -> NSFastIterator {
|
||||
NSFastIterator {
|
||||
state: NSFastEnumerationState {
|
||||
state: 0,
|
||||
items_ptr: ptr::null_mut(),
|
||||
mutations_ptr: ptr::null_mut(),
|
||||
extra: [0; 5]
|
||||
},
|
||||
buffer: [nil; NS_FAST_ENUM_BUF_SIZE],
|
||||
mut_val: None,
|
||||
len: 0,
|
||||
idx: 0,
|
||||
object: self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
extern crate cocoa;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -56,4 +58,44 @@ mod foundation {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod nsfastenumeration {
|
||||
use std::str;
|
||||
use std::slice;
|
||||
use cocoa::foundation::{NSString, NSFastEnumeration};
|
||||
use cocoa::base::{id, nil};
|
||||
|
||||
#[test]
|
||||
fn test_iter() {
|
||||
unsafe {
|
||||
let string = NSString::alloc(nil).init_str("this is a test string");
|
||||
let separator = NSString::alloc(nil).init_str(" ");
|
||||
let components: id = msg_send![string, componentsSeparatedByString:separator];
|
||||
|
||||
let combined = components.iter()
|
||||
.map(|s| {
|
||||
let bytes = s.UTF8String() as *const u8;
|
||||
str::from_utf8(slice::from_raw_parts(bytes, s.len())).unwrap()
|
||||
})
|
||||
.fold(String::new(), |mut acc, s| { acc.push_str(s); acc });
|
||||
|
||||
assert_eq!(combined, "thisisateststring");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_mutation() {
|
||||
unsafe {
|
||||
let string = NSString::alloc(nil).init_str("this is a test string");
|
||||
let separator = NSString::alloc(nil).init_str(" ");
|
||||
let components: id = msg_send![string, componentsSeparatedByString:separator];
|
||||
let mut_components: id = msg_send![components, mutableCopy];
|
||||
let mut iter = mut_components.iter();
|
||||
iter.next();
|
||||
msg_send![mut_components, removeObjectAtIndex:1];
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user