diff --git a/src/lib/iter.rs b/src/lib/iter.rs new file mode 100644 index 0000000000000000000000000000000000000000..e99495515884ab3ebd1109c041db496a35de8b7d --- /dev/null +++ b/src/lib/iter.rs @@ -0,0 +1,37 @@ +pub trait Batching<B, I, F> + where I: Iterator, + F: FnMut(&mut I) -> Option<B>, +{ + fn batching(self, func: F) -> BatchingIter<I, F>; +} + +#[derive(Clone)] +pub struct BatchingIter<I, F> { + iter: I, + func: F, +} + +impl<B, I, F> Batching<B, I, F> for I + where I: Iterator, + F: FnMut(&mut I) -> Option<B>, +{ + fn batching(self, func: F) -> BatchingIter<I, F> { + BatchingIter { iter: self, func: func } + } +} + +impl<B, I, F> Iterator for BatchingIter<I, F> + where I: Iterator, + F: FnMut(&mut I) -> Option<B>, +{ + type Item = B; + #[inline] + fn next(&mut self) -> Option<B> { + (self.func)(&mut self.iter) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + (0, None) + } +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs index f79b63a1d5990bf8a2f465f959c17b4b5ab243e5..26d087f01ae0f345fcf0e2877f28d470d8e8402a 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -3,6 +3,7 @@ #![feature(integer_atomics)] #![feature(pointer_methods)] #![feature(getpid)] +#![feature(nll)] #[macro_use] extern crate bitflags; @@ -44,6 +45,7 @@ mod types; pub mod parser; mod ascii_helpers; mod builtins; +mod iter; mod shell; pub use shell::{ diff --git a/src/lib/shell/binary/mod.rs b/src/lib/shell/binary/mod.rs index c1c37875cd909fe8b6bfa5211c54a8893ac2afe5..35cecdcce22ae3f67796a165c76961552fb6d420 100644 --- a/src/lib/shell/binary/mod.rs +++ b/src/lib/shell/binary/mod.rs @@ -11,6 +11,7 @@ use self::{ }; use super::{flow_control::Statement, status::*, FlowLogic, Shell, ShellHistory}; use liner::{Buffer, Context}; +use iter::Batching; use std::{env, fs::File, io::ErrorKind, iter, path::Path, process}; pub const MAN_ION: &'static str = r#"NAME @@ -186,41 +187,46 @@ impl Binary for Shell { fn prompt(&mut self) -> String { prompt(self) } } -// TODO: Convert this into an iterator to eliminate heap allocations. -fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> { - let mut res = Vec::new(); +fn word_divide<'a>(buf: &'a Buffer) -> Vec<(usize, usize)> { // -> impl Iterator<Item = (usize, usize)> + 'a + let mut word_start = None; - macro_rules! check_boundary { - ($c: expr, $index: expr, $escaped: expr) => {{ - if let Some(start) = word_start { - if $c == ' ' && !$escaped { - res.push((start, $index)); - word_start = None; - } + #[inline] + fn check_boundary(word_start: &mut Option<usize>, c: char, index: usize, escaped: bool) -> Option<Result<(usize, usize), ()>> { + if let &mut Some(start) = word_start { + if c == ' ' && !escaped { + *word_start = None; + Some(Ok((start, index))) } else { - if $c != ' ' { - word_start = Some($index); - } + Some(Err(())) + } + } else { + if c != ' ' { + *word_start = Some(index); } - }}; + Some(Err(())) + } } - let mut iter = buf.chars().enumerate(); - while let Some((i, &c)) = iter.next() { - match c { - '\\' => { + buf.chars().enumerate().batching(|iter| { + match iter.next() { + Some((i, '\\')) => { if let Some((_, &cnext)) = iter.next() { // We use `i` in order to include the backslash as part of the word - check_boundary!(cnext, i, true); + check_boundary(&mut word_start, cnext, i, true) + } else { + Some(Err(())) } } - c => check_boundary!(c, i, false), + Some((i, c)) => check_boundary(&mut word_start, *c, i, false), + None => { + // When start has been set, that means we have encountered a full word. + word_start.map(|start| Ok((start, buf.num_chars()))) + } } - } - if let Some(start) = word_start { - // When start has been set, that means we have encountered a full word. - res.push((start, buf.num_chars())); - } - res + }) + .filter_map(|item| { + item.ok() + }) + .collect() }