<commit_before>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{BasicCompleter, Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:             &'b Shell<'a>,
    history_completer: Option<BasicCompleter>,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self { IonCompleter { shell, history_completer: None } }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);

        if let Some(ref mut history) = &mut self.history_completer {
            let vars = self.shell.variables();

            completions.extend(history.completions(start));
            // Initialize a new completer from the definitions collected.
            // Creates a list of definitions from the shell environment that
            // will be used
            // in the creation of a custom completer.
            completions.extend(
                self.shell
                    .builtins()
                    .keys()
                    // Add built-in commands to the completer's definitions.
                    .map(ToString::to_string)
                    // Add the aliases to the completer's definitions.
                    .chain(vars.aliases().map(|(key, _)| key.to_string()))
                    // Add the list of available functions to the completer's
                    // definitions.
                    .chain(vars.functions().map(|(key, _)| key.to_string()))
                    // Add the list of available variables to the completer's
                    // definitions. TODO: We should make
                    // it free to do String->SmallString
                    //       and mostly free to go back (free if allocated)
                    .chain(vars.string_vars().map(|(s, _)| ["$", &s].concat()))
                    .filter(|s| s.starts_with(start)),
            );
            // Creates completers containing definitions from all directories
            // listed
            // in the environment's **$PATH** variable.
            let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                env::split_paths(&paths)
                    .map(|s| IonFileCompleter::new(Some(s), &self.shell))
                    .collect()
            } else {
                vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
            };
            // Merge the collected definitions with the file path definitions.
            completions.extend(MultiCompleter::new(file_completers).completions(start));
        }
        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            let filename = match pos {
                CursorPosition::InWord(index) => index > 0,
                CursorPosition::InSpace(Some(_), _) => true,
                CursorPosition::InSpace(None, _) => false,
                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                CursorPosition::OnWordRightEdge(index) => words
                    .into_iter()
                    .nth(index)
                    .map(|(start, end)| event.editor.current_buffer().range(start, end))
                    .and_then(|filename| {
                        Some(complete_as_file(&env::current_dir().ok()?, &filename, index))
                    })
                    .filter(|&x| x)
                    .is_some(),
            };

            // Add the history list to the completer's definitions.
            // Map each underlying `liner::Buffer` into a `String`.
            self.history_completer = if filename {
                Some(BasicCompleter::new(
                    event
                        .editor
                        .context()
                        .history
                        .buffers
                        .iter()
                        .map(ToString::to_string)
                        .collect(),
                ))
            } else {
                None
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return completions.collect();
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }

    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?.trim_start_matches(&path.to_string_lossy().as_ref());
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(joined)
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn filename_completion() {
        let shell = Shell::library();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: &PathBuf, filename: &str, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as
    // a file
    filename.starts_with('.') ||
    // If the file starts with a dollar sign, it's a variable, not a file
    (!filename.starts_with('$') &&
    // Once we are beyond the first string, assume its a file
    (index > 0 ||
    // If we are referencing a file that exists then just complete to that file
    file.exists() ||
    // If we have a partial file inside an existing directory, e.g. /foo/b when
    // /foo/bar exists, then treat it as file as long as `foo` isn't the
    // current directory, otherwise this would apply to any string `foo`
    file.parent().filter(|parent| parent.exists() && parent != current_dir).is_some()))
    // By default assume its not a file
}
<commit_msg>Bring back escaping of files with spaces in it<commit_after>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{BasicCompleter, Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:             &'b Shell<'a>,
    history_completer: Option<BasicCompleter>,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self { IonCompleter { shell, history_completer: None } }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);

        if let Some(ref mut history) = &mut self.history_completer {
            let vars = self.shell.variables();

            completions.extend(history.completions(start));
            // Initialize a new completer from the definitions collected.
            // Creates a list of definitions from the shell environment that
            // will be used
            // in the creation of a custom completer.
            completions.extend(
                self.shell
                    .builtins()
                    .keys()
                    // Add built-in commands to the completer's definitions.
                    .map(ToString::to_string)
                    // Add the aliases to the completer's definitions.
                    .chain(vars.aliases().map(|(key, _)| key.to_string()))
                    // Add the list of available functions to the completer's
                    // definitions.
                    .chain(vars.functions().map(|(key, _)| key.to_string()))
                    // Add the list of available variables to the completer's
                    // definitions. TODO: We should make
                    // it free to do String->SmallString
                    //       and mostly free to go back (free if allocated)
                    .chain(vars.string_vars().map(|(s, _)| ["$", &s].concat()))
                    .filter(|s| s.starts_with(start)),
            );
            // Creates completers containing definitions from all directories
            // listed
            // in the environment's **$PATH** variable.
            let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                env::split_paths(&paths)
                    .map(|s| IonFileCompleter::new(Some(s), &self.shell))
                    .collect()
            } else {
                vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
            };
            // Merge the collected definitions with the file path definitions.
            completions.extend(MultiCompleter::new(file_completers).completions(start));
        }
        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            let filename = match pos {
                CursorPosition::InWord(index) => index > 0,
                CursorPosition::InSpace(Some(_), _) => true,
                CursorPosition::InSpace(None, _) => false,
                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                CursorPosition::OnWordRightEdge(index) => words
                    .into_iter()
                    .nth(index)
                    .map(|(start, end)| event.editor.current_buffer().range(start, end))
                    .and_then(|filename| {
                        Some(complete_as_file(&env::current_dir().ok()?, &filename, index))
                    })
                    .filter(|&x| x)
                    .is_some(),
            };

            // Add the history list to the completer's definitions.
            // Map each underlying `liner::Buffer` into a `String`.
            self.history_completer = if filename {
                Some(BasicCompleter::new(
                    event
                        .editor
                        .context()
                        .history
                        .buffers
                        .iter()
                        .map(ToString::to_string)
                        .collect(),
                ))
            } else {
                None
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return completions.collect();
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }

    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?.trim_start_matches(&path.to_string_lossy().as_ref());
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn filename_completion() {
        let shell = Shell::library();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: &PathBuf, filename: &str, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as
    // a file
    filename.starts_with('.') ||
    // If the file starts with a dollar sign, it's a variable, not a file
    (!filename.starts_with('$') &&
    // Once we are beyond the first string, assume its a file
    (index > 0 ||
    // If we are referencing a file that exists then just complete to that file
    file.exists() ||
    // If we have a partial file inside an existing directory, e.g. /foo/b when
    // /foo/bar exists, then treat it as file as long as `foo` isn't the
    // current directory, otherwise this would apply to any string `foo`
    file.parent().filter(|parent| parent.exists() && parent != current_dir).is_some()))
    // By default assume its not a file
}
<|endoftext|>


<commit_before>use {Future, Poll};

use std::prelude::v1::*;
use std::any::Any;
use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe};

/// Future for the `catch_unwind` combinator.
///
/// This is created by this `Future::catch_uwnind` method.
pub struct CatchUnwind<F> where F: Future {
    future: Option<F>,
}

pub fn new<F>(future: F) -> CatchUnwind<F>
    where F: Future + UnwindSafe,
{
    CatchUnwind {
        future: Some(future),
    }
}

impl<F> Future for CatchUnwind<F>
    where F: Future + UnwindSafe,
{
    type Item = Result<F::Item, F::Error>;
    type Error = Box<Any + Send>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut future = self.future.take().expect("cannot poll twice");
        match catch_unwind(|| (future.poll(), future)) {
            Ok((Poll::NotReady, f)) => {
                self.future = Some(f);
                Poll::NotReady
            }
            Ok((Poll::Ok(v), _)) => Poll::Ok(Ok(v)),
            Ok((Poll::Err(e), _)) => Poll::Ok(Err(e)),
            Err(e) => Poll::Err(e),
        }
    }
}

impl<F: Future> Future for AssertUnwindSafe<F> {
    type Item = F::Item;
    type Error = F::Error;

    fn poll(&mut self) -> Poll<F::Item, F::Error> {
        self.0.poll()
    }
}
<commit_msg>Fix a typo<commit_after>use {Future, Poll};

use std::prelude::v1::*;
use std::any::Any;
use std::panic::{catch_unwind, UnwindSafe, AssertUnwindSafe};

/// Future for the `catch_unwind` combinator.
///
/// This is created by this `Future::catch_unwind` method.
pub struct CatchUnwind<F> where F: Future {
    future: Option<F>,
}

pub fn new<F>(future: F) -> CatchUnwind<F>
    where F: Future + UnwindSafe,
{
    CatchUnwind {
        future: Some(future),
    }
}

impl<F> Future for CatchUnwind<F>
    where F: Future + UnwindSafe,
{
    type Item = Result<F::Item, F::Error>;
    type Error = Box<Any + Send>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let mut future = self.future.take().expect("cannot poll twice");
        match catch_unwind(|| (future.poll(), future)) {
            Ok((Poll::NotReady, f)) => {
                self.future = Some(f);
                Poll::NotReady
            }
            Ok((Poll::Ok(v), _)) => Poll::Ok(Ok(v)),
            Ok((Poll::Err(e), _)) => Poll::Ok(Err(e)),
            Err(e) => Poll::Err(e),
        }
    }
}

impl<F: Future> Future for AssertUnwindSafe<F> {
    type Item = F::Item;
    type Error = F::Error;

    fn poll(&mut self) -> Poll<F::Item, F::Error> {
        self.0.poll()
    }
}
<|endoftext|>


<commit_before>use nom_lua53;
use nom_lua53::Statement;
use nom_lua53::stat_expr_types::Block;
use types::LuaState;
use expression;
use std;
use super::{Result, var_to_string};
use super::types::LuaValue;

pub enum FlowControl {
    None,
    Return(Vec<LuaValue>),
    Break,
}

pub fn exec_statement(stmt: &Statement, ctx: &mut LuaState) -> Result<FlowControl> {
    match stmt {
        &Statement::LVarAssign(ref ass) => {
            let values = ass.vals
                .as_ref()
                .expect("There should be some values. Why isn't there any value?!");
            for (var, val) in ass.vars.iter().zip(values.iter()) {
                let computed_value = expression::eval_expr(val, &ctx);
                let local_scope = ctx.get_mutable_local_scope().unwrap();
                local_scope.set_string(var_to_string(var), &computed_value.unwrap());
            };
            Ok(FlowControl::None)
        }
        &Statement::Assignment(ref ass) => {
            let mut values = Vec::with_capacity(ass.vals.len());
            for exp in ass.vals.iter() {
                values.push(expression::eval_expr(&exp, &ctx)?);
            }

            for (prefexp, val) in ass.vars.iter().zip(values.drain(..)) {
                let assignment = expression::prefixexp::resolve_prefix_expr(
                    &prefexp.prefix,
                    &prefexp.suffix_chain,
                    ctx,
                )?;
                assignment.environment.set(&assignment.index, &val)?;
            }
            println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
            Ok(FlowControl::None)
        }
        &Statement::Semicolon => {
            Ok(FlowControl::None)
        }
        &Statement::Ite(ref ite) => {
            exec_if_then_else(ite, ctx)
        }
        _ => {Ok(FlowControl::None)}
    }
}

pub fn exec_block(block: &Block, ctx: &mut LuaState) -> Result<FlowControl> {
    for stmt in block.stmts.iter() {
        match exec_statement(&stmt, ctx)? {
            FlowControl::Break => return Ok(FlowControl::Break),
            FlowControl::Return(val) => return Ok(FlowControl::Return(val)),
            FlowControl::None => {}
        }
    };
    let ret = if let Some(ref expressions) = block.ret_stmt {
        let mut results = std::vec::Vec::with_capacity(expressions.len());
        for exp in expressions {
            results.push(expression::eval_expr(exp, ctx)?)
        }
        Ok(FlowControl::Return(results))
    } else {
        Ok(FlowControl::None)
    };

    return ret
        }

pub fn exec_if_then_else(ite: &nom_lua53::IfThenElse, ctx: &mut LuaState) -> Result<FlowControl> {
    if expression::boolean_coercion(&expression::eval_expr(&ite.cond, ctx)?) {
        exec_block(&ite.then_blk, ctx)
    } else {
        match ite.elseifs.iter().fold(None,
         |current, &(ref exp, ref block)|
          if current.is_none() {
              match expression::eval_expr(&exp, ctx) {
                  Ok(_) => Some(exec_block(&block, ctx)),
                  Err(err) => Some(Err(err))
              }
          } else { current }) {
              Some(val) => val,
              None => match &ite.else_blk {
                  &Some(ref blk) => exec_block(blk, ctx),
                  &None => Ok(FlowControl::None)
              }
          }
    }
}<commit_msg>Add break statement support<commit_after>use nom_lua53;
use nom_lua53::Statement;
use nom_lua53::stat_expr_types::Block;
use types::LuaState;
use expression;
use std;
use super::{Result, var_to_string};
use super::types::LuaValue;

pub enum FlowControl {
    None,
    Return(Vec<LuaValue>),
    Break,
}

pub fn exec_statement(stmt: &Statement, ctx: &mut LuaState) -> Result<FlowControl> {
    match stmt {
        &Statement::LVarAssign(ref ass) => {
            let values = ass.vals
                .as_ref()
                .expect("There should be some values. Why isn't there any value?!");
            for (var, val) in ass.vars.iter().zip(values.iter()) {
                let computed_value = expression::eval_expr(val, &ctx);
                let local_scope = ctx.get_mutable_local_scope().unwrap();
                local_scope.set_string(var_to_string(var), &computed_value.unwrap());
            };
            Ok(FlowControl::None)
        }
        &Statement::Assignment(ref ass) => {
            let mut values = Vec::with_capacity(ass.vals.len());
            for exp in ass.vals.iter() {
                values.push(expression::eval_expr(&exp, &ctx)?);
            }

            for (prefexp, val) in ass.vars.iter().zip(values.drain(..)) {
                let assignment = expression::prefixexp::resolve_prefix_expr(
                    &prefexp.prefix,
                    &prefexp.suffix_chain,
                    ctx,
                )?;
                assignment.environment.set(&assignment.index, &val)?;
            }
            println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
            Ok(FlowControl::None)
        }
        &Statement::Semicolon => {
            Ok(FlowControl::None)
        }
        &Statement::Ite(ref ite) => {
            exec_if_then_else(ite, ctx)
        }
        &Statement::Break => Ok(FlowControl::Break),
        _ => {Ok(FlowControl::None)}
    }
}

pub fn exec_block(block: &Block, ctx: &mut LuaState) -> Result<FlowControl> {
    for stmt in block.stmts.iter() {
        match exec_statement(&stmt, ctx)? {
            FlowControl::Break => return Ok(FlowControl::Break),
            FlowControl::Return(val) => return Ok(FlowControl::Return(val)),
            FlowControl::None => {}
        }
    };
    let ret = if let Some(ref expressions) = block.ret_stmt {
        let mut results = std::vec::Vec::with_capacity(expressions.len());
        for exp in expressions {
            results.push(expression::eval_expr(exp, ctx)?)
        }
        Ok(FlowControl::Return(results))
    } else {
        Ok(FlowControl::None)
    };

    return ret
        }

pub fn exec_if_then_else(ite: &nom_lua53::IfThenElse, ctx: &mut LuaState) -> Result<FlowControl> {
    if expression::boolean_coercion(&expression::eval_expr(&ite.cond, ctx)?) {
        exec_block(&ite.then_blk, ctx)
    } else {
        match ite.elseifs.iter().fold(None,
         |current, &(ref exp, ref block)|
          if current.is_none() {
              match expression::eval_expr(&exp, ctx) {
                  Ok(_) => Some(exec_block(&block, ctx)),
                  Err(err) => Some(Err(err))
              }
          } else { current }) {
              Some(val) => val,
              None => match &ite.else_blk {
                  &Some(ref blk) => exec_block(blk, ctx),
                  &None => Ok(FlowControl::None)
              }
          }
    }
}<|endoftext|>


<commit_before>use nom_lua53;
use nom_lua53::Statement;
use nom_lua53::stat_expr_types::Block;
use types::LuaState;
use expression;
use std;
use super::{Result, var_to_string};
use super::types::LuaValue;

pub enum FlowControl {
    None,
    Return(Vec<LuaValue>),
    Break,
}

pub fn exec_statement(stmt: &Statement, ctx: &mut LuaState) -> Result<FlowControl> {
    match stmt {
        &Statement::LVarAssign(ref ass) => {
            let values = ass.vals
                .as_ref()
                .expect("There should be some values. Why isn't there any value?!");
            for (var, val) in ass.vars.iter().zip(values.iter()) {
                let computed_value = expression::eval_expr(val, &ctx);
                let local_scope = ctx.get_mutable_local_scope().unwrap();
                local_scope.set_string(var_to_string(var), &computed_value.unwrap());
            };
            Ok(FlowControl::None)
        }
        &Statement::Assignment(ref ass) => {
            let mut values = Vec::with_capacity(ass.vals.len());
            for exp in ass.vals.iter() {
                values.push(expression::eval_expr(&exp, &ctx)?);
            }

            for (prefexp, val) in ass.vars.iter().zip(values.drain(..)) {
                let assignment = expression::prefixexp::resolve_prefix_expr(
                    &prefexp.prefix,
                    &prefexp.suffix_chain,
                    ctx,
                )?;
                assignment.environment.set(&assignment.index, &val)?;
            }
            println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
            Ok(FlowControl::None)
        }
        &Statement::Semicolon => {
            Ok(FlowControl::None)
        }
        &Statement::Ite(ref ite) => {
            exec_if_then_else(ite, ctx)
        }
        &Statement::Break => Ok(FlowControl::Break),
        _ => {Ok(FlowControl::None)}
    }
}

pub fn exec_block(block: &Block, ctx: &mut LuaState) -> Result<FlowControl> {
    ctx.push_scope();
    for stmt in block.stmts.iter() {
        match exec_statement(&stmt, ctx)? {
            FlowControl::Break => return Ok(FlowControl::Break),
            FlowControl::Return(val) => return Ok(FlowControl::Return(val)),
            FlowControl::None => {}
        }
    };
    let ret = if let Some(ref expressions) = block.ret_stmt {
        let mut results = std::vec::Vec::with_capacity(expressions.len());
        for exp in expressions {
            results.push(expression::eval_expr(exp, ctx)?)
        }
        Ok(FlowControl::Return(results))
    } else {
        Ok(FlowControl::None)
    };

    ctx.pop_scope();
    return ret
}

pub fn exec_if_then_else(ite: &nom_lua53::IfThenElse, ctx: &mut LuaState) -> Result<FlowControl> {
    if expression::boolean_coercion(&expression::eval_expr(&ite.cond, ctx)?) {
        exec_block(&ite.then_blk, ctx)
    } else {
        match ite.elseifs.iter().fold(None,
         |current, &(ref exp, ref block)|
          if current.is_none() {
              match expression::eval_expr(&exp, ctx) {
                  Ok(_) => Some(exec_block(&block, ctx)),
                  Err(err) => Some(Err(err))
              }
          } else { current }) {
              Some(val) => val,
              None => match &ite.else_blk {
                  &Some(ref blk) => exec_block(blk, ctx),
                  &None => Ok(FlowControl::None)
              }
          }
    }
}<commit_msg>Implement the while loop<commit_after>use nom_lua53;
use nom_lua53::Statement;
use nom_lua53::stat_expr_types::Block;
use types::LuaState;
use expression;
use std;
use super::{Result, var_to_string};
use super::types::LuaValue;

pub enum FlowControl {
    None,
    Return(Vec<LuaValue>),
    Break,
}

pub fn exec_statement(stmt: &Statement, ctx: &mut LuaState) -> Result<FlowControl> {
    match stmt {
        &Statement::LVarAssign(ref ass) => {
            let values = ass.vals
                .as_ref()
                .expect("There should be some values. Why isn't there any value?!");
            for (var, val) in ass.vars.iter().zip(values.iter()) {
                let computed_value = expression::eval_expr(val, &ctx);
                let local_scope = ctx.get_mutable_local_scope().unwrap();
                local_scope.set_string(var_to_string(var), &computed_value.unwrap());
            };
            Ok(FlowControl::None)
        }
        &Statement::Assignment(ref ass) => {
            let mut values = Vec::with_capacity(ass.vals.len());
            for exp in ass.vals.iter() {
                values.push(expression::eval_expr(&exp, &ctx)?);
            }

            for (prefexp, val) in ass.vars.iter().zip(values.drain(..)) {
                let assignment = expression::prefixexp::resolve_prefix_expr(
                    &prefexp.prefix,
                    &prefexp.suffix_chain,
                    ctx,
                )?;
                assignment.environment.set(&assignment.index, &val)?;
            }
            println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
            Ok(FlowControl::None)
        }
        &Statement::Semicolon => {
            Ok(FlowControl::None)
        }
        &Statement::Ite(ref ite) => {
            exec_if_then_else(ite, ctx)
        }
        &Statement::While(ref blk) => {
            exec_while(blk, ctx)
        }
        &Statement::Break => Ok(FlowControl::Break),
        _ => {Ok(FlowControl::None)}
    }
}

pub fn exec_block(block: &Block, ctx: &mut LuaState) -> Result<FlowControl> {
    ctx.push_scope();
    for stmt in block.stmts.iter() {
        match exec_statement(&stmt, ctx)? {
            FlowControl::Break => return Ok(FlowControl::Break),
            FlowControl::Return(val) => return Ok(FlowControl::Return(val)),
            FlowControl::None => {}
        }
    };
    let ret = if let Some(ref expressions) = block.ret_stmt {
        let mut results = std::vec::Vec::with_capacity(expressions.len());
        for exp in expressions {
            results.push(expression::eval_expr(exp, ctx)?)
        }
        Ok(FlowControl::Return(results))
    } else {
        Ok(FlowControl::None)
    };

    ctx.pop_scope();
    return ret
}

pub fn exec_if_then_else(ite: &nom_lua53::IfThenElse, ctx: &mut LuaState) -> Result<FlowControl> {
    if expression::boolean_coercion(&expression::eval_expr(&ite.cond, ctx)?) {
        exec_block(&ite.then_blk, ctx)
    } else {
        match ite.elseifs.iter().fold(None,
         |current, &(ref exp, ref block)|
          if current.is_none() {
              match expression::eval_expr(&exp, ctx) {
                  Ok(_) => Some(exec_block(&block, ctx)),
                  Err(err) => Some(Err(err))
              }
          } else { current }) {
              Some(val) => val,
              None => match &ite.else_blk {
                  &Some(ref blk) => exec_block(blk, ctx),
                  &None => Ok(FlowControl::None)
              }
          }
    }
}

pub fn exec_while(blk: &nom_lua53::WhileBlock, ctx: &mut LuaState) -> Result<FlowControl> {
    while expression::boolean_coercion(&expression::eval_expr(&blk.cond, ctx)?) {
        let disrupt = exec_block(&blk.block, ctx)?;
        match disrupt {
            FlowControl::Return(val) => {
                return Ok(FlowControl::Return(val))
            }
            FlowControl::Break => {
                return Ok(FlowControl::None)
            }
            FlowControl::None => {}
        }
    }
    return Ok(FlowControl::None)
}<|endoftext|>


<commit_before>use std::ops::{Index, IndexMut};
use super::tensors::Matrix;
use typenum::Pow;
use typenum::uint::Unsigned;
use typenum::consts::U2;
use generic_array::{GenericArray, ArrayLength};

/// CoordinateSystem trait marks a struct (usually a unit struct) as representing a coordinate system.
pub trait CoordinateSystem : Sized {
    /// An associated type representing the dimension of the coordinate system
	type Dimension: Unsigned + ArrayLength<f64>;

    /// Function returning a small value for purposes of numerical differentiation.
    /// What is considered a small value may depend on the point, hence the parameter.
    /// Returns just 0.01 by default.
    #[allow(unused_variables)]
    fn small(x: &Point<Self>) -> f64 {
        0.01
    }

    /// Function returning the dimension
    fn dimension() -> usize {
        Self::Dimension::to_usize()
    }
}

/// Struct representing a point on the manifold. The information about the coordinate system is saved in the type parameter,
/// so that only operations on objects in one coordinate system will be allowed.
pub struct Point<T: CoordinateSystem> {
    /// The coordinates of the point.
    x: GenericArray<f64, T::Dimension>,
}

impl<T> Point<T> where T: CoordinateSystem
{
    /// Creates a new point with coordinates described by the slice.
    pub fn new(coords: GenericArray<f64, T::Dimension>) -> Point<T> {
        Point { x: coords }
    }
}

impl<T> Clone for Point<T> where T: CoordinateSystem
{
    fn clone(&self) -> Point<T> {
        Point::new(self.x.clone())
    }
}

impl<T> Copy for Point<T> where T: CoordinateSystem, <T::Dimension as ArrayLength<f64>>::ArrayType: Copy {}

impl<T> Index<usize> for Point<T> where T: CoordinateSystem
{
    type Output = f64;

    fn index(&self, idx: usize) -> &f64 {
        &self.x[idx]
    }
}

impl<T> IndexMut<usize> for Point<T> where T: CoordinateSystem
{
    fn index_mut(&mut self, idx: usize) -> &mut f64 {
        &mut self.x[idx]
    }
}

impl<T> PartialEq<Point<T>> for Point<T>
    where T: CoordinateSystem
{
    fn eq(&self, rhs: &Point<T>) -> bool {
        (0..T::dimension()).all(|i| self[i] == rhs[i])
    }
}

impl<T> Eq for Point<T> where T: CoordinateSystem {}

/// Trait used for conversions between different coordinate systems. Implementing ConversionTo<T> for a CoordinateSystem
/// will allow objects in that system to be converted to the system T (note that T also has to be a CoordinateSystem).
pub trait ConversionTo<T: CoordinateSystem + 'static> : CoordinateSystem
    where T::Dimension: Pow<U2>,
          <T::Dimension as Pow<U2>>::Output: ArrayLength<f64>
{
    /// Function converting the coordinates of a point.
    fn convert_point(p: &Point<Self>) -> Point<T>;

    /// Function calculating a Jacobian at a point - that is, the matrix of derivatives of the coordinate conversions.
    fn jacobian(p: &Point<Self>) -> Matrix<T> {
        let d = Self::dimension();
        let mut result = Matrix::new(Self::convert_point(p));
        let h = Self::small(p);

        for j in 0..d {
            let mut x = p.clone();
            x[j] = x[j] - h;
            let y1 = Self::convert_point(&x);

            x[j] = x[j] + h * 2.0;
            let y2 = Self::convert_point(&x);

            for i in 0..d {
                // calculate dyi/dxj
                let index = [i, j];
                result[&index[..]] = (y2[i] - y1[i]) / (2.0 * h);
            }
        }

        result
    }

    /// The inverse matrix of the Jacobian at a point.
    fn inv_jacobian(p: &Point<Self>) -> Matrix<T>;

}
<commit_msg>Made #[allow(unused_variables)] unnecessary<commit_after>use std::ops::{Index, IndexMut};
use super::tensors::Matrix;
use typenum::Pow;
use typenum::uint::Unsigned;
use typenum::consts::U2;
use generic_array::{GenericArray, ArrayLength};

/// CoordinateSystem trait marks a struct (usually a unit struct) as representing a coordinate system.
pub trait CoordinateSystem : Sized {
    /// An associated type representing the dimension of the coordinate system
	type Dimension: Unsigned + ArrayLength<f64>;

    /// Function returning a small value for purposes of numerical differentiation.
    /// What is considered a small value may depend on the point, hence the parameter.
    /// Returns just 0.01 by default.
    fn small(_: &Point<Self>) -> f64 {
        0.01
    }

    /// Function returning the dimension
    fn dimension() -> usize {
        Self::Dimension::to_usize()
    }
}

/// Struct representing a point on the manifold. The information about the coordinate system is saved in the type parameter,
/// so that only operations on objects in one coordinate system will be allowed.
pub struct Point<T: CoordinateSystem> {
    /// The coordinates of the point.
    x: GenericArray<f64, T::Dimension>,
}

impl<T> Point<T> where T: CoordinateSystem
{
    /// Creates a new point with coordinates described by the slice.
    pub fn new(coords: GenericArray<f64, T::Dimension>) -> Point<T> {
        Point { x: coords }
    }
}

impl<T> Clone for Point<T> where T: CoordinateSystem
{
    fn clone(&self) -> Point<T> {
        Point::new(self.x.clone())
    }
}

impl<T> Copy for Point<T> where T: CoordinateSystem, <T::Dimension as ArrayLength<f64>>::ArrayType: Copy {}

impl<T> Index<usize> for Point<T> where T: CoordinateSystem
{
    type Output = f64;

    fn index(&self, idx: usize) -> &f64 {
        &self.x[idx]
    }
}

impl<T> IndexMut<usize> for Point<T> where T: CoordinateSystem
{
    fn index_mut(&mut self, idx: usize) -> &mut f64 {
        &mut self.x[idx]
    }
}

impl<T> PartialEq<Point<T>> for Point<T>
    where T: CoordinateSystem
{
    fn eq(&self, rhs: &Point<T>) -> bool {
        (0..T::dimension()).all(|i| self[i] == rhs[i])
    }
}

impl<T> Eq for Point<T> where T: CoordinateSystem {}

/// Trait used for conversions between different coordinate systems. Implementing ConversionTo<T> for a CoordinateSystem
/// will allow objects in that system to be converted to the system T (note that T also has to be a CoordinateSystem).
pub trait ConversionTo<T: CoordinateSystem + 'static> : CoordinateSystem
    where T::Dimension: Pow<U2>,
          <T::Dimension as Pow<U2>>::Output: ArrayLength<f64>
{
    /// Function converting the coordinates of a point.
    fn convert_point(p: &Point<Self>) -> Point<T>;

    /// Function calculating a Jacobian at a point - that is, the matrix of derivatives of the coordinate conversions.
    fn jacobian(p: &Point<Self>) -> Matrix<T> {
        let d = Self::dimension();
        let mut result = Matrix::new(Self::convert_point(p));
        let h = Self::small(p);

        for j in 0..d {
            let mut x = p.clone();
            x[j] = x[j] - h;
            let y1 = Self::convert_point(&x);

            x[j] = x[j] + h * 2.0;
            let y2 = Self::convert_point(&x);

            for i in 0..d {
                // calculate dyi/dxj
                let index = [i, j];
                result[&index[..]] = (y2[i] - y1[i]) / (2.0 * h);
            }
        }

        result
    }

    /// The inverse matrix of the Jacobian at a point.
    fn inv_jacobian(p: &Point<Self>) -> Matrix<T>;

}
<|endoftext|>


<commit_before>//
//use std::fs::File;
//use std::io::prelude::*;

use core::bufferlog::BufferLog;

use core::mapped_file::MappedFile;
//use core::mapped_file::MappedFileIterator;
use core::mapped_file::FileHandle;

//
pub type Id = u64;
pub type Offset = u64;
pub type PageSize = usize;

//
#[derive(Debug)]
pub enum OpenMode {
    ReadOnly = 0,
    ReadWrite = 1,
}

/// The **Buffer** represent a linear array of bytes.

/// it can be in memory only or backed by an on disk file.

/// The editor **Modes** use this api to read/modify the content
/// of the file at the byte level
#[derive(Debug)]
pub struct Buffer<'a> {
    pub id: Id,
    pub file_name: String,
    pub size: usize,
    pub nr_changes: u64, // number of changes since last save
    mode: OpenMode,
    pub data: FileHandle<'a>,
    pub buffer_log: BufferLog,
    // phantom: PhantomData<&'a u8>,
}

impl<'a> Buffer<'a> {
    /// Creates a new `Buffer`.
    ///
    /// file_name param[in] path to the file we want to load in the buffer,
    /// use "/dev/null" to create empty buffer
    /// this function allocate a buffer
    /// if file_name is null the content will be stored in heap
    /// if file_name is non null the the content will be read from the file
    /// if document_name is null , file_name will be used to give a name to the buffer
    /// mode = 0 : read only , mode 1 : read_write
    /// the allocated_bid pointer will be filled on successfull open operation
    pub fn new(file_name: String, mode: OpenMode) -> Option<Buffer<'a>> {
        // TODO: check permission
        // TODO: check file's type => ignore directory (for now)
        println!("-- mapping file {}", file_name);

        let page_size = 4096 * 256 * 2;
        let file = match MappedFile::new(file_name.clone(), page_size) {
            Some(file) => file,
            None => {
                eprintln!("cannot map file '{}'", file_name);
                return None;
            }
        };

        let size = file.as_ref().borrow().size() as usize;

        println!("'{}' opened size '{}'", file_name, size);

        Some(Buffer {
            id: 0,
            file_name: file_name.clone(),
            mode,
            size,
            nr_changes: 0,
            data: file,
            buffer_log: BufferLog::new(),
        })
    }

    /// close a previously opened buffer see buffer_open
    pub fn close(&mut self) -> bool {
        false
    }

    /// returns the name of the file associated to the buffer
    pub fn get_file_name(&self) -> String {
        self.file_name.clone()
    }

    /// change the on disk target file
    pub fn set_file_name(&mut self, name: String) -> bool {
        self.file_name = name;
        true
    }

    /// returns the number of bytes a given buffer contains
    pub fn size(&self) -> usize {
        self.size
    }

    /// returns the number of changes sine the last save

    ///     0  => the no change since last save

    ///     >0 => the number of changes since last save

    pub fn nr_changes(&self) -> u64 {
        self.nr_changes
    }

    /// copy the content of the buffer up to 'nr_bytes' into the data Vec
    /// the read bytes are appended to the data Vec
    /// return XXX on error (use ioresult)
    pub fn read(&self, offset: u64, nr_bytes: usize, mut data: &mut Vec<u8>) -> usize {
        let mut it = MappedFile::iter_from(&self.data, offset);
        MappedFile::read(&mut it, nr_bytes, &mut data)
    }

    /// insert the 'data' Vec content in the buffer up to 'nr_bytes'
    /// return the number of written bytes (TODO: use io::Result)
    pub fn insert(&mut self, offset: u64, nr_bytes: usize, data: &[u8]) -> usize {
        let mut it = MappedFile::iter_from(&self.data, offset);
        MappedFile::insert(&mut it, &data);

        self.size += nr_bytes;
        self.nr_changes += 1;

        nr_bytes
    }

    /// remove up to 'nr_bytes' from the buffer starting at offset
    /// if removed_data is provided will call self.read(offset, nr_bytes, data)
    /// before remove the bytes
    pub fn remove(
        &mut self,
        offset: u64,
        nr_bytes: usize,
        removed_data: Option<&mut Vec<u8>>,
    ) -> usize {
        let start_offset = ::std::cmp::min(offset as usize, self.size);
        let end_offset = ::std::cmp::min(start_offset + nr_bytes, self.size);
        let nr_bytes_removed = (end_offset - start_offset) as usize;

        // copy removed data
        if let Some(v) = removed_data {
            self.read(offset, nr_bytes_removed, v);
        }

        let mut it = MappedFile::iter_from(&self.data, start_offset as u64);
        MappedFile::remove(&mut it, nr_bytes_removed);

        self.size -= nr_bytes_removed;
        self.nr_changes += 1;

        nr_bytes_removed
    }

    /// can be used to know the number of blocks that compose the buffer,
    /// api to be used by indexer etc...
    pub fn nr_pages(&self) -> u64 {
        1
    }

    /*
    /// returns the position and size of a given page
    pub fn get_page_info(&self, page_index: u64) -> (Offset, PageSize) {
        // if page_index > 0 {
        //    (0, self.size)
        // } else {
        //    (0, self.size)
        // }
        (0, self.size)
    }
*/

    pub fn sync_to_disk(&self, tmp_file_name: &str) -> ::std::io::Result<()> {
        use std::fs;
        use std::fs::File;
        // use std::io::prelude::*;
        use std::io::Write;

        let mut f = File::create(&tmp_file_name)?;

        let mut offset: u64 = 0;
        while offset != self.size as u64 {
            let nr_bytes = 4096 * 32;
            let mut data = Vec::with_capacity(nr_bytes);
            let read_size = self.read(offset, nr_bytes, &mut data);
            f.write(&data)?;
            offset += read_size as u64;
        }
        f.sync_all()?;
        fs::rename(&tmp_file_name, &self.file_name)?;
        Ok(())
    }
}

#[test]
fn test_buffer() {
    let mut bb = Buffer::new("/dev/null".to_owned(), OpenMode::ReadWrite).unwrap();

    let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

    bb.insert(0, 10, &data);
    let mut rdata = Vec::new();
    let nread = bb.read(0, bb.size(), &mut rdata);
    assert_eq!(rdata, data);
    assert_eq!(nread, bb.size());

    let data = vec![0, 1, 2, 6, 7, 8, 9];
    let rm_expect = vec![3, 4, 5];

    let mut rm = vec![];
    let n = bb.remove(3, 3, Some(&mut rm));
    assert_eq!(n, 3);
    assert_eq!(rm, rm_expect);

    rdata.clear();
    let nread = bb.read(0, bb.size(), &mut rdata);
    assert_eq!(rdata, data);

    println!("rm {:?}", rm);
    println!("rm_expect {:?}", rm_expect);
}
<commit_msg>core::buffer : TODO: fn sync_to_disk is currently broken, because fd swapping is not implemented in mapped_file<commit_after>//
//use std::fs::File;
//use std::io::prelude::*;

use core::bufferlog::BufferLog;

use core::mapped_file::MappedFile;
//use core::mapped_file::MappedFileIterator;
use core::mapped_file::FileHandle;

//
pub type Id = u64;
pub type Offset = u64;
pub type PageSize = usize;

//
#[derive(Debug)]
pub enum OpenMode {
    ReadOnly = 0,
    ReadWrite = 1,
}

/// The **Buffer** represent a linear array of bytes.

/// it can be in memory only or backed by an on disk file.

/// The editor **Modes** use this api to read/modify the content
/// of the file at the byte level
#[derive(Debug)]
pub struct Buffer<'a> {
    pub id: Id,
    pub file_name: String,
    pub size: usize,
    pub nr_changes: u64, // number of changes since last save
    mode: OpenMode,
    pub data: FileHandle<'a>,
    pub buffer_log: BufferLog,
    // phantom: PhantomData<&'a u8>,
}

impl<'a> Buffer<'a> {
    /// Creates a new `Buffer`.
    ///
    /// file_name param[in] path to the file we want to load in the buffer,
    /// use "/dev/null" to create empty buffer
    /// this function allocate a buffer
    /// if file_name is null the content will be stored in heap
    /// if file_name is non null the the content will be read from the file
    /// if document_name is null , file_name will be used to give a name to the buffer
    /// mode = 0 : read only , mode 1 : read_write
    /// the allocated_bid pointer will be filled on successfull open operation
    pub fn new(file_name: String, mode: OpenMode) -> Option<Buffer<'a>> {
        // TODO: check permission
        // TODO: check file's type => ignore directory (for now)
        println!("-- mapping file {}", file_name);

        let page_size = 4096 * 256 * 2;
        let file = match MappedFile::new(file_name.clone(), page_size) {
            Some(file) => file,
            None => {
                eprintln!("cannot map file '{}'", file_name);
                return None;
            }
        };

        let size = file.as_ref().borrow().size() as usize;

        println!("'{}' opened size '{}'", file_name, size);

        Some(Buffer {
            id: 0,
            file_name: file_name.clone(),
            mode,
            size,
            nr_changes: 0,
            data: file,
            buffer_log: BufferLog::new(),
        })
    }

    /// close a previously opened buffer see buffer_open
    pub fn close(&mut self) -> bool {
        false
    }

    /// returns the name of the file associated to the buffer
    pub fn get_file_name(&self) -> String {
        self.file_name.clone()
    }

    /// change the on disk target file
    pub fn set_file_name(&mut self, name: String) -> bool {
        self.file_name = name;
        true
    }

    /// returns the number of bytes a given buffer contains
    pub fn size(&self) -> usize {
        self.size
    }

    /// returns the number of changes sine the last save

    ///     0  => the no change since last save

    ///     >0 => the number of changes since last save

    pub fn nr_changes(&self) -> u64 {
        self.nr_changes
    }

    /// copy the content of the buffer up to 'nr_bytes' into the data Vec
    /// the read bytes are appended to the data Vec
    /// return XXX on error (use ioresult)
    pub fn read(&self, offset: u64, nr_bytes: usize, mut data: &mut Vec<u8>) -> usize {
        let mut it = MappedFile::iter_from(&self.data, offset);
        MappedFile::read(&mut it, nr_bytes, &mut data)
    }

    /// insert the 'data' Vec content in the buffer up to 'nr_bytes'
    /// return the number of written bytes (TODO: use io::Result)
    pub fn insert(&mut self, offset: u64, nr_bytes: usize, data: &[u8]) -> usize {
        let mut it = MappedFile::iter_from(&self.data, offset);
        MappedFile::insert(&mut it, &data);

        self.size += nr_bytes;
        self.nr_changes += 1;

        nr_bytes
    }

    /// remove up to 'nr_bytes' from the buffer starting at offset
    /// if removed_data is provided will call self.read(offset, nr_bytes, data)
    /// before remove the bytes
    pub fn remove(
        &mut self,
        offset: u64,
        nr_bytes: usize,
        removed_data: Option<&mut Vec<u8>>,
    ) -> usize {
        let start_offset = ::std::cmp::min(offset as usize, self.size);
        let end_offset = ::std::cmp::min(start_offset + nr_bytes, self.size);
        let nr_bytes_removed = (end_offset - start_offset) as usize;

        // copy removed data
        if let Some(v) = removed_data {
            self.read(offset, nr_bytes_removed, v);
        }

        let mut it = MappedFile::iter_from(&self.data, start_offset as u64);
        MappedFile::remove(&mut it, nr_bytes_removed);

        self.size -= nr_bytes_removed;
        self.nr_changes += 1;

        nr_bytes_removed
    }

    /// can be used to know the number of blocks that compose the buffer,
    /// api to be used by indexer etc...
    pub fn nr_pages(&self) -> u64 {
        1
    }

    /*
    /// returns the position and size of a given page
    pub fn get_page_info(&self, page_index: u64) -> (Offset, PageSize) {
        // if page_index > 0 {
        //    (0, self.size)
        // } else {
        //    (0, self.size)
        // }
        (0, self.size)
    }
*/

    //
    // TODO: This is currently broken, because fd swapping is not implemented yet in mapped_file
    //
    //
    pub fn sync_to_disk(&self, tmp_file_name: &str) -> ::std::io::Result<()> {
        use std::fs;
        use std::fs::File;
        // use std::io::prelude::*;
        use std::io::Write;

        let mut f = File::create(&tmp_file_name)?;

        let mut offset: u64 = 0;
        while offset != self.size as u64 {
            let nr_bytes = 4096 * 32;
            let mut data = Vec::with_capacity(nr_bytes);
            let read_size = self.read(offset, nr_bytes, &mut data);
            f.write(&data)?;
            offset += read_size as u64;
        }
        f.sync_all()?;
        fs::rename(&tmp_file_name, &self.file_name)?;
        Ok(())
    }
}

#[test]
fn test_buffer() {
    let mut bb = Buffer::new("/dev/null".to_owned(), OpenMode::ReadWrite).unwrap();

    let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

    bb.insert(0, 10, &data);
    let mut rdata = Vec::new();
    let nread = bb.read(0, bb.size(), &mut rdata);
    assert_eq!(rdata, data);
    assert_eq!(nread, bb.size());

    let data = vec![0, 1, 2, 6, 7, 8, 9];
    let rm_expect = vec![3, 4, 5];

    let mut rm = vec![];
    let n = bb.remove(3, 3, Some(&mut rm));
    assert_eq!(n, 3);
    assert_eq!(rm, rm_expect);

    rdata.clear();
    let nread = bb.read(0, bb.size(), &mut rdata);
    assert_eq!(rdata, data);

    println!("rm {:?}", rm);
    println!("rm_expect {:?}", rm_expect);
}
<|endoftext|>


<commit_before>use std::ops::Div;

use byteorder::{ByteOrder, LittleEndian};
use typenum::consts::{U8, U72, U104, U136, U144, U224, U256, U384, U512};
use typenum::uint::Unsigned;

use digest;
use utils::buffer::{FixedBuf, FixedBuffer, StandardPadding};

#[derive(Copy)]
struct State {
    hash: [u64; 25],
    rest: usize,
    block_size: usize,
}

impl Clone for State {
    fn clone(&self) -> Self {
        State {
            hash: self.hash,
            rest: self.rest,
            block_size: self.block_size,
        }
    }
}

const ROUND_CONSTS: [u64; 24] = [0x0000000000000001,
                                 0x0000000000008082,
                                 0x800000000000808a,
                                 0x8000000080008000,
                                 0x000000000000808b,
                                 0x0000000080000001,
                                 0x8000000080008081,
                                 0x8000000000008009,
                                 0x000000000000008a,
                                 0x0000000000000088,
                                 0x0000000080008009,
                                 0x000000008000000a,
                                 0x000000008000808b,
                                 0x800000000000008b,
                                 0x8000000000008089,
                                 0x8000000000008003,
                                 0x8000000000008002,
                                 0x8000000000000080,
                                 0x000000000000800a,
                                 0x800000008000000a,
                                 0x8000000080008081,
                                 0x8000000000008080,
                                 0x0000000080000001,
                                 0x8000000080008008];

const SHIFTS: [u32; 25] = [0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39, 41, 45, 15, 21,
                           8, 18, 2, 61, 56, 14];

impl State {
    fn init(bits: usize) -> Self {
        let rate = 1600 - bits * 2;
        assert!(rate <= 1600 && (rate % 64) == 0);
        State {
            hash: [0; 25],
            rest: 0,
            block_size: rate / 8,
        }
    }

    #[allow(needless_range_loop)]
    fn theta(&mut self) {
        let mut a = [0u64; 5];
        let mut b = [0u64; 5];

        for i in 0..5 {
            a[i] = self.hash[i] ^ self.hash[i + 5] ^ self.hash[i + 10] ^ self.hash[i + 15] ^
                   self.hash[i + 20];
        }

        for i in 0..5 {
            b[i] = a[(i + 1) % 5].rotate_left(1) ^ a[(i + 4) % 5];
        }

        for i in 0..5 {
            self.hash[i] ^= b[i];
            self.hash[i + 5] ^= b[i];
            self.hash[i + 10] ^= b[i];
            self.hash[i + 15] ^= b[i];
            self.hash[i + 20] ^= b[i];
        }
    }

    fn rho(&mut self) {
        for (i, shift) in SHIFTS.iter().enumerate() {
            self.hash[i] = self.hash[i].rotate_left(*shift);
        }
    }

    fn pi(&mut self) {
        let tmp = self.hash[1];
        self.hash[1] = self.hash[6];
        self.hash[6] = self.hash[9];
        self.hash[9] = self.hash[22];
        self.hash[22] = self.hash[14];
        self.hash[14] = self.hash[20];
        self.hash[20] = self.hash[2];
        self.hash[2] = self.hash[12];
        self.hash[12] = self.hash[13];
        self.hash[13] = self.hash[19];
        self.hash[19] = self.hash[23];
        self.hash[23] = self.hash[15];
        self.hash[15] = self.hash[4];
        self.hash[4] = self.hash[24];
        self.hash[24] = self.hash[21];
        self.hash[21] = self.hash[8];
        self.hash[8] = self.hash[16];
        self.hash[16] = self.hash[5];
        self.hash[5] = self.hash[3];
        self.hash[3] = self.hash[18];
        self.hash[18] = self.hash[17];
        self.hash[17] = self.hash[11];
        self.hash[11] = self.hash[7];
        self.hash[7] = self.hash[10];
        self.hash[10] = tmp;
        // NOTE: self.hash[0] is left untouched
    }

    fn chi(&mut self) {
        for i in 0..5 {
            let i = i * 5;
            let tmp_0 = self.hash[i];
            let tmp_1 = self.hash[i + 1];

            self.hash[i] ^= !tmp_1 & self.hash[i + 2];
            self.hash[i + 1] ^= !self.hash[i + 2] & self.hash[i + 3];
            self.hash[i + 2] ^= !self.hash[i + 3] & self.hash[i + 4];
            self.hash[i + 3] ^= !self.hash[i + 4] & tmp_0;
            self.hash[i + 4] ^= !tmp_0 & tmp_1;
        }
    }

    fn permutation(&mut self) {
        for round in &ROUND_CONSTS {
            self.theta();
            self.rho();
            self.pi();
            self.chi();

            // iota
            self.hash[0] ^= *round;
        }
    }

    fn process(&mut self, data: &[u8]) {
        let max = self.block_size / 8;
        for (h, c) in self.hash[0..max].iter_mut().zip(data.chunks(8)) {
            *h ^= LittleEndian::read_u64(c)
        }

        self.permutation();
    }
}

macro_rules! sha3_impl {
    ($name:ident -> $size:ty, $bsize:ty) => {
        #[derive(Clone)]
        pub struct $name {
            state: State,
            buffer: FixedBuffer<$bsize>,
        }

        impl Default for $name {
            fn default() -> Self {
                $name {
                    state: State::init(<$size as Unsigned>::to_usize()),
                    buffer: FixedBuffer::new(),
                }
            }
        }

        impl digest::Digest for $name {
            type OutputBits = $size;
            type OutputBytes = <$size as Div<U8>>::Output;

            type BlockSize = $bsize;

            fn update<T>(&mut self, data: T) where T: AsRef<[u8]> {
                let state = &mut self.state;
                self.buffer.input(data.as_ref(), |d| state.process(d));
            }

            fn result<T>(mut self, mut out: T) where T: AsMut<[u8]> {
                let mut ret = out.as_mut();
                assert!(ret.len() >= Self::output_bytes());
                let state = &mut self.state;

                self.buffer.pad(0b00000110, 0, |d| state.process(d));
                let buf = self.buffer.full_buffer();
                let last = buf.len() - 1;
                buf[last] |= 0b10000000;
                state.process(buf);

                unsafe {
                    use std::ptr;
                    ptr::copy_nonoverlapping(
                        state.hash.as_ptr() as *const u8,
                        ret.as_mut_ptr(),
                        Self::output_bytes())
                };
            }
        }
    }
}

sha3_impl!(Sha224 -> U224, U144);
sha3_impl!(Sha256 -> U256, U136);
sha3_impl!(Sha384 -> U384, U104);
sha3_impl!(Sha512 -> U512, U72);
<commit_msg>refactor(digest:sha3): optimizing sha3 implementation<commit_after>use std::ops::Div;

use byteorder::{ByteOrder, LittleEndian};
use typenum::consts::{U8, U72, U104, U136, U144, U224, U256, U384, U512};
use typenum::uint::Unsigned;

use digest;
use utils::buffer::{FixedBuf, FixedBuffer, StandardPadding};

#[derive(Copy)]
struct State {
    hash: [u64; 25],
    rest: usize,
    block_size: usize,
}

impl Clone for State {
    fn clone(&self) -> Self {
        State {
            hash: self.hash,
            rest: self.rest,
            block_size: self.block_size,
        }
    }
}

const ROUND_CONSTS: [u64; 24] = [0x0000000000000001,
                                 0x0000000000008082,
                                 0x800000000000808a,
                                 0x8000000080008000,
                                 0x000000000000808b,
                                 0x0000000080000001,
                                 0x8000000080008081,
                                 0x8000000000008009,
                                 0x000000000000008a,
                                 0x0000000000000088,
                                 0x0000000080008009,
                                 0x000000008000000a,
                                 0x000000008000808b,
                                 0x800000000000008b,
                                 0x8000000000008089,
                                 0x8000000000008003,
                                 0x8000000000008002,
                                 0x8000000000000080,
                                 0x000000000000800a,
                                 0x800000008000000a,
                                 0x8000000080008081,
                                 0x8000000000008080,
                                 0x0000000080000001,
                                 0x8000000080008008];

impl State {
    fn init(bits: usize) -> Self {
        let rate = 1600 - bits * 2;
        assert!(rate <= 1600 && (rate % 64) == 0);
        State {
            hash: [0; 25],
            rest: 0,
            block_size: rate / 8,
        }
    }

    fn permutation(&mut self) {
        let mut a: [u64; 25] = self.hash;
        let mut c: [u64; 5] = [
            a[0] ^ a[5] ^ a[10] ^ a[15] ^ a[20],
            a[1] ^ a[6] ^ a[11] ^ a[16] ^ a[21],
            a[2] ^ a[7] ^ a[12] ^ a[17] ^ a[22],
            a[3] ^ a[8] ^ a[13] ^ a[18] ^ a[23],
            a[4] ^ a[9] ^ a[14] ^ a[19] ^ a[24]
        ];
        for i in 0..12 {
            self.round(i * 2, &mut a, &mut c);
            self.round(i * 2 + 1, &mut a, &mut c);
        }
        self.hash = a;
    }

    #[inline(always)]
    fn round(&self, i: usize, a: &mut [u64;25], c: &mut [u64;5])
    {
        let d0 = c[4] ^ c[1].rotate_left(1);
        let d1 = c[0] ^ c[2].rotate_left(1);
        let d2 = c[1] ^ c[3].rotate_left(1);
        let d3 = c[2] ^ c[4].rotate_left(1);
        let d4 = c[3] ^ c[0].rotate_left(1);

        let b0  =  a[ 0] ^ d0;
        let b10 = (a[ 1] ^ d1).rotate_left(1);
        let b20 = (a[ 2] ^ d2).rotate_left(62);
        let b5  = (a[ 3] ^ d3).rotate_left(28);
        let b15 = (a[ 4] ^ d4).rotate_left(27);
        let b16 = (a[ 5] ^ d0).rotate_left(36);
        let b1  = (a[ 6] ^ d1).rotate_left(44);
        let b11 = (a[ 7] ^ d2).rotate_left(6);
        let b21 = (a[ 8] ^ d3).rotate_left(55);
        let b6  = (a[ 9] ^ d4).rotate_left(20);
        let b7  = (a[10] ^ d0).rotate_left(3);
        let b17 = (a[11] ^ d1).rotate_left(10);
        let b2  = (a[12] ^ d2).rotate_left(43);
        let b12 = (a[13] ^ d3).rotate_left(25);
        let b22 = (a[14] ^ d4).rotate_left(39);
        let b23 = (a[15] ^ d0).rotate_left(41);
        let b8  = (a[16] ^ d1).rotate_left(45);
        let b18 = (a[17] ^ d2).rotate_left(15);
        let b3  = (a[18] ^ d3).rotate_left(21);
        let b13 = (a[19] ^ d4).rotate_left(8);
        let b14 = (a[20] ^ d0).rotate_left(18);
        let b24 = (a[21] ^ d1).rotate_left(2);
        let b9  = (a[22] ^ d2).rotate_left(61);
        let b19 = (a[23] ^ d3).rotate_left(56);
        let b4  = (a[24] ^ d4).rotate_left(14);

        a[0] = (b0 ^ ((!b1) & b2)) ^ ROUND_CONSTS[i];
        c[0] = a[0];
        a[1] = b1 ^ ((!b2) & b3);
        c[1] = a[1];
        a[2] = b2 ^ ((!b3) & b4);
        c[2] = a[2];
        a[3] = b3 ^ ((!b4) & b0);
        c[3] = a[3];
        a[4] = b4 ^ ((!b0) & b1);
        c[4] = a[4];

        a[5] = b5 ^ ((!b6) & b7);
        c[0] ^= a[5];
        a[6] = b6 ^ ((!b7) & b8);
        c[1] ^= a[6];
        a[7] = b7 ^ ((!b8) & b9);
        c[2] ^= a[7];
        a[8] = b8 ^ ((!b9) & b5);
        c[3] ^= a[8];
        a[9] = b9 ^ ((!b5) & b6);
        c[4] ^= a[9];

        a[10] = b10 ^ ((!b11) & b12);
        c[0] ^= a[10];
        a[11] = b11 ^ ((!b12) & b13);
        c[1] ^= a[11];
        a[12] = b12 ^ ((!b13) & b14);
        c[2] ^= a[12];
        a[13] = b13 ^ ((!b14) & b10);
        c[3] ^= a[13];
        a[14] = b14 ^ ((!b10) & b11);
        c[4] ^= a[14];

        a[15] = b15 ^ ((!b16) & b17);
        c[0] ^= a[15];
        a[16] = b16 ^ ((!b17) & b18);
        c[1] ^= a[16];
        a[17] = b17 ^ ((!b18) & b19);
        c[2] ^= a[17];
        a[18] = b18 ^ ((!b19) & b15);
        c[3] ^= a[18];
        a[19] = b19 ^ ((!b15) & b16);
        c[4] ^= a[19];

        a[20] = b20 ^ ((!b21) & b22);
        c[0] ^= a[20];
        a[21] = b21 ^ ((!b22) & b23);
        c[1] ^= a[21];
        a[22] = b22 ^ ((!b23) & b24);
        c[2] ^= a[22];
        a[23] = b23 ^ ((!b24) & b20);
        c[3] ^= a[23];
        a[24] = b24 ^ ((!b20) & b21);
        c[4] ^= a[24];
    }

    fn process(&mut self, data: &[u8]) {
        let max = self.block_size / 8;
        for (h, c) in self.hash[0..max].iter_mut().zip(data.chunks(8)) {
            *h ^= LittleEndian::read_u64(c)
        }

        self.permutation();
    }
}

macro_rules! sha3_impl {
    ($name:ident -> $size:ty, $bsize:ty) => {
        #[derive(Clone)]
        pub struct $name {
            state: State,
            buffer: FixedBuffer<$bsize>,
        }

        impl Default for $name {
            fn default() -> Self {
                $name {
                    state: State::init(<$size as Unsigned>::to_usize()),
                    buffer: FixedBuffer::new(),
                }
            }
        }

        impl digest::Digest for $name {
            type OutputBits = $size;
            type OutputBytes = <$size as Div<U8>>::Output;

            type BlockSize = $bsize;

            fn update<T>(&mut self, data: T) where T: AsRef<[u8]> {
                let state = &mut self.state;
                self.buffer.input(data.as_ref(), |d| state.process(d));
            }

            fn result<T>(mut self, mut out: T) where T: AsMut<[u8]> {
                let mut ret = out.as_mut();
                assert!(ret.len() >= Self::output_bytes());
                let state = &mut self.state;

                self.buffer.pad(0b00000110, 0, |d| state.process(d));
                let buf = self.buffer.full_buffer();
                let last = buf.len() - 1;
                buf[last] |= 0b10000000;
                state.process(buf);

                unsafe {
                    use std::ptr;
                    ptr::copy_nonoverlapping(
                        state.hash.as_ptr() as *const u8,
                        ret.as_mut_ptr(),
                        Self::output_bytes())
                };
            }
        }
    }
}

sha3_impl!(Sha224 -> U224, U144);
sha3_impl!(Sha256 -> U256, U136);
sha3_impl!(Sha384 -> U384, U104);
sha3_impl!(Sha512 -> U512, U72);
<|endoftext|>


<commit_before>use std::cmp::Ordering;

/// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.
pub fn min_set_impl<I, K, F, Compare>(mut it: I,
                                mut key_for: F,
                                mut compare: Compare) -> Vec<I::Item>
    where I: Iterator,
          F: FnMut(&I::Item) -> K,
          Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
{
    let (mut result, mut current_key) = match it.next() {
        None => return Vec::new(),
        Some(element) => {
            let key = key_for(&element);
            (vec![element], key)
        }
    };

    it.for_each(|element| {
        let key = key_for(&element);
        match compare(&element, &result[0], &key, &current_key) {
            Ordering::Less => {
                result.clear();
                result.push(element);
                current_key = key;
            },
            Ordering::Equal => {
                result.push(element);
            },
            Ordering::Greater => {
            },
        }
    });

    result
}

/// Implementation guts for `ax_set`, `max_set_by`, and `max_set_by_key`.
pub fn max_set_impl<I, K, F, Compare>(it: I,
                                key_for: F,
                                mut compare: Compare) -> Vec<I::Item>
    where I: Iterator,
          F: FnMut(&I::Item) -> K,
          Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
{
    min_set_impl(it, key_for, |it1, it2, key1, key2| compare(it2, it1, key2, key1))
}

<commit_msg>Add {min,max}_set(_by{_key)?)? functions (6) inline bulk into match arm<commit_after>use std::cmp::Ordering;

/// Implementation guts for `min_set`, `min_set_by`, and `min_set_by_key`.
pub fn min_set_impl<I, K, F, Compare>(mut it: I,
                                mut key_for: F,
                                mut compare: Compare) -> Vec<I::Item>
    where I: Iterator,
          F: FnMut(&I::Item) -> K,
          Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
{
    match it.next() {
        None => Vec::new(),
        Some(element) => {
            let mut current_key = key_for(&element);
            let mut result = vec![element];
            it.for_each(|element| {
                let key = key_for(&element);
                match compare(&element, &result[0], &key, &current_key) {
                    Ordering::Less => {
                        result.clear();
                        result.push(element);
                        current_key = key;
                    },
                    Ordering::Equal => {
                        result.push(element);
                    },
                    Ordering::Greater => {
                    },
                }
            });
            result
        }
    }

}

/// Implementation guts for `ax_set`, `max_set_by`, and `max_set_by_key`.
pub fn max_set_impl<I, K, F, Compare>(it: I,
                                key_for: F,
                                mut compare: Compare) -> Vec<I::Item>
    where I: Iterator,
          F: FnMut(&I::Item) -> K,
          Compare: FnMut(&I::Item, &I::Item, &K, &K) -> Ordering,
{
    min_set_impl(it, key_for, |it1, it2, key1, key2| compare(it2, it1, key2, key1))
}

<|endoftext|>