<commit_before>#![crate_name = "fold"]
#![feature(collections, rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, Write};
use std::path::Path;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "fold";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let (args, obs_width) = handle_obsolete(&args[..]);
    let program = args[0].clone();

    let opts = [
        getopts::optflag("b", "bytes", "count using bytes rather than columns (meaning control characters such as newline are not treated specially)"),
        getopts::optflag("s", "spaces", "break lines at word boundaries rather than a hard cut-off"),
        getopts::optopt("w", "width", "set WIDTH as the maximum line width rather than 80", "WIDTH"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];

    let matches = match getopts::getopts(&args[1..], &opts) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("h") {
        println!("{} v{}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {} [OPTION]... [FILE]...", program);
        println!("");
        print!("{}", getopts::usage("Writes each file (or standard input if no files are given) to standard output whilst breaking long lines", &opts));
    } else if matches.opt_present("V") {
        println!("{} v{}", NAME, VERSION);
    } else {
        let bytes = matches.opt_present("b");
        let spaces = matches.opt_present("s");
        let poss_width =
            if matches.opt_present("w") {
                matches.opt_str("w")
            } else {
                match obs_width {
                    Some(v) => Some(v.to_string()),
                    None => None,
                }
            };
        let width = match poss_width {
            Some(inp_width) => match inp_width.parse::<usize>() {
                Ok(width) => width,
                Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e)
            },
            None => 80
        };
        let files = if matches.free.is_empty() {
            vec!("-".to_string())
        } else {
            matches.free
        };
        fold(files, bytes, spaces, width);
    }

    0
}

fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
    for (i, arg) in args.iter().enumerate() {
        let slice = &arg;
        if slice.chars().next().unwrap() == '-' && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
            return (args[..i].to_vec() + &args[i + 1..],
                    Some(slice[1..].to_string()));
        }
    }
    (args.to_vec(), None)
}

#[inline]
fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: usize) {
    for filename in filenames.iter() {
        let filename: &str = &filename;
        let mut stdin_buf;
        let mut file_buf;
        let buffer = BufReader::new(
            if filename == "-" {
                stdin_buf = stdin();
                &mut stdin_buf as &mut Read
            } else {
                file_buf = safe_unwrap!(File::open(Path::new(filename)));
                &mut file_buf as &mut Read
            }
        );
        fold_file(buffer, bytes, spaces, width);
    }
}

#[inline]
fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width: usize) {
    let mut line = String::new();
    while safe_unwrap!(file.read_line(&mut line)) > 0 {
        if bytes {
            let len = line.len();
            let mut i = 0;
            while i < len {
                let width = if len - i >= width { width } else { len - i };
                let slice = {
                    let slice = &line[i..i + width];
                    if spaces && i + width < len {
                        match slice.rfind(|ch: char| ch.is_whitespace()) {
                            Some(m) => &slice[..m + 1],
                            None => slice
                        }
                    } else {
                        slice
                    }
                };
                print!("{}", slice);
                i += slice.len();
            }
        } else {
            let mut len = line.chars().count();
            let newline = line.ends_with("\n");
            if newline {
                if len == 1 {
                    println!("");
                    continue;
                }
                len -= 1;
                line.truncate(len);
            }
            let mut output = String::new();
            let mut count = 0;
            for (i, ch) in line.chars().enumerate() {
                if count >= width {
                    let (val, ncount) = {
                        let slice = &output[..];
                        let (out, val, ncount) =
                            if spaces && i + 1 < len {
                                match rfind_whitespace(slice) {
                                    Some(m) => {
                                        let routput = slice.slice_chars(m + 1, slice.chars().count());
                                        let ncount = routput.chars().fold(0usize, |out, ch: char| {
                                            out + match ch {
                                                '\t' => 8,
                                                '\x08' => if out > 0 { -1 } else { 0 },
                                                '\r' => return 0,
                                                _ => 1
                                            }
                                        });
                                        (slice.slice_chars(0, m + 1), routput, ncount)
                                    },
                                    None => (slice, "", 0)
                                }
                            } else {
                                (slice, "", 0)
                            };
                        println!("{}", out);
                        (val.to_string(), ncount)
                    };
                    output = val;
                    count = ncount;
                }
                match ch {
                    '\t' => {
                        count += 8;
                        if count > width {
                            println!("{}", output);
                            output.truncate(0);
                            count = 8;
                        }
                    }
                    '\x08' => {
                        if count > 0 {
                            count -= 1;
                            let len = output.len() - 1;
                            output.truncate(len);
                        }
                        continue;
                    }
                    '\r' => {
                        output.truncate(0);
                        count = 0;
                        continue;
                    }
                    _ => count += 1
                };
                output.push(ch);
            }
            if count > 0 {
                if newline {
                    println!("{}", output);
                } else {
                    print!("{}", output);
                }
            }
        }
    }
}

#[inline]
fn rfind_whitespace(slice: &str) -> Option<usize> {
    for (i, ch) in slice.chars().rev().enumerate() {
        if ch.is_whitespace() {
            return Some(slice.chars().count() - (i + 1));
        }
    }
    None
}
<commit_msg>Update fold to replace vector concatenation.<commit_after>#![crate_name = "fold"]
#![feature(collections, rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, Write};
use std::path::Path;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "fold";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> i32 {
    let (args, obs_width) = handle_obsolete(&args[..]);
    let program = args[0].clone();

    let opts = [
        getopts::optflag("b", "bytes", "count using bytes rather than columns (meaning control characters such as newline are not treated specially)"),
        getopts::optflag("s", "spaces", "break lines at word boundaries rather than a hard cut-off"),
        getopts::optopt("w", "width", "set WIDTH as the maximum line width rather than 80", "WIDTH"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];

    let matches = match getopts::getopts(&args[1..], &opts) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f)
    };

    if matches.opt_present("h") {
        println!("{} v{}", NAME, VERSION);
        println!("");
        println!("Usage:");
        println!("  {} [OPTION]... [FILE]...", program);
        println!("");
        print!("{}", getopts::usage("Writes each file (or standard input if no files are given) to standard output whilst breaking long lines", &opts));
    } else if matches.opt_present("V") {
        println!("{} v{}", NAME, VERSION);
    } else {
        let bytes = matches.opt_present("b");
        let spaces = matches.opt_present("s");
        let poss_width =
            if matches.opt_present("w") {
                matches.opt_str("w")
            } else {
                match obs_width {
                    Some(v) => Some(v.to_string()),
                    None => None,
                }
            };
        let width = match poss_width {
            Some(inp_width) => match inp_width.parse::<usize>() {
                Ok(width) => width,
                Err(e) => crash!(1, "illegal width value (\"{}\"): {}", inp_width, e)
            },
            None => 80
        };
        let files = if matches.free.is_empty() {
            vec!("-".to_string())
        } else {
            matches.free
        };
        fold(files, bytes, spaces, width);
    }

    0
}

fn handle_obsolete(args: &[String]) -> (Vec<String>, Option<String>) {
    for (i, arg) in args.iter().enumerate() {
        let slice = &arg;
        if slice.chars().next().unwrap() == '-' && slice.len() > 1 && slice.chars().nth(1).unwrap().is_digit(10) {
            let mut v = args.to_vec();
            v.remove(i);
            return (v, Some(slice[1..].to_string()));
        }
    }
    (args.to_vec(), None)
}

#[inline]
fn fold(filenames: Vec<String>, bytes: bool, spaces: bool, width: usize) {
    for filename in filenames.iter() {
        let filename: &str = &filename;
        let mut stdin_buf;
        let mut file_buf;
        let buffer = BufReader::new(
            if filename == "-" {
                stdin_buf = stdin();
                &mut stdin_buf as &mut Read
            } else {
                file_buf = safe_unwrap!(File::open(Path::new(filename)));
                &mut file_buf as &mut Read
            }
        );
        fold_file(buffer, bytes, spaces, width);
    }
}

#[inline]
fn fold_file<T: Read>(mut file: BufReader<T>, bytes: bool, spaces: bool, width: usize) {
    let mut line = String::new();
    while safe_unwrap!(file.read_line(&mut line)) > 0 {
        if bytes {
            let len = line.len();
            let mut i = 0;
            while i < len {
                let width = if len - i >= width { width } else { len - i };
                let slice = {
                    let slice = &line[i..i + width];
                    if spaces && i + width < len {
                        match slice.rfind(|ch: char| ch.is_whitespace()) {
                            Some(m) => &slice[..m + 1],
                            None => slice
                        }
                    } else {
                        slice
                    }
                };
                print!("{}", slice);
                i += slice.len();
            }
        } else {
            let mut len = line.chars().count();
            let newline = line.ends_with("\n");
            if newline {
                if len == 1 {
                    println!("");
                    continue;
                }
                len -= 1;
                line.truncate(len);
            }
            let mut output = String::new();
            let mut count = 0;
            for (i, ch) in line.chars().enumerate() {
                if count >= width {
                    let (val, ncount) = {
                        let slice = &output[..];
                        let (out, val, ncount) =
                            if spaces && i + 1 < len {
                                match rfind_whitespace(slice) {
                                    Some(m) => {
                                        let routput = slice.slice_chars(m + 1, slice.chars().count());
                                        let ncount = routput.chars().fold(0usize, |out, ch: char| {
                                            out + match ch {
                                                '\t' => 8,
                                                '\x08' => if out > 0 { -1 } else { 0 },
                                                '\r' => return 0,
                                                _ => 1
                                            }
                                        });
                                        (slice.slice_chars(0, m + 1), routput, ncount)
                                    },
                                    None => (slice, "", 0)
                                }
                            } else {
                                (slice, "", 0)
                            };
                        println!("{}", out);
                        (val.to_string(), ncount)
                    };
                    output = val;
                    count = ncount;
                }
                match ch {
                    '\t' => {
                        count += 8;
                        if count > width {
                            println!("{}", output);
                            output.truncate(0);
                            count = 8;
                        }
                    }
                    '\x08' => {
                        if count > 0 {
                            count -= 1;
                            let len = output.len() - 1;
                            output.truncate(len);
                        }
                        continue;
                    }
                    '\r' => {
                        output.truncate(0);
                        count = 0;
                        continue;
                    }
                    _ => count += 1
                };
                output.push(ch);
            }
            if count > 0 {
                if newline {
                    println!("{}", output);
                } else {
                    print!("{}", output);
                }
            }
        }
    }
}

#[inline]
fn rfind_whitespace(slice: &str) -> Option<usize> {
    for (i, ch) in slice.chars().rev().enumerate() {
        if ch.is_whitespace() {
            return Some(slice.chars().count() - (i + 1));
        }
    }
    None
}
<|endoftext|>


<commit_before>//! Filtering and sorting the list of files before displaying them.

use std::cmp::Ordering;
use std::iter::FromIterator;
use std::os::unix::fs::MetadataExt;

use crate::fs::DotFilter;
use crate::fs::File;

/// The **file filter** processes a list of files before displaying them to
/// the user, by removing files they don’t want to see, and putting the list
/// in the desired order.
///
/// Usually a user does not want to see *every* file in the list. The most
/// common case is to remove files starting with `.`, which are designated
/// as ‘hidden’ files.
///
/// The special files `.` and `..` files are not actually filtered out, but
/// need to be inserted into the list, in a special case.
///
/// The filter also governs sorting the list. After being filtered, pairs of
/// files are compared and sorted based on the result, with the sort field
/// performing the comparison.
#[derive(PartialEq, Debug, Clone)]
pub struct FileFilter {

    /// Whether directories should be listed first, and other types of file
    /// second. Some users prefer it like this.
    pub list_dirs_first: bool,

    /// The metadata field to sort by.
    pub sort_field: SortField,

    /// Whether to reverse the sorting order. This would sort the largest
    /// files first, or files starting with Z, or the most-recently-changed
    /// ones, depending on the sort field.
    pub reverse: bool,

    /// Whether to only show directories.
    pub only_dirs: bool,

    /// Which invisible “dot” files to include when listing a directory.
    ///
    /// Files starting with a single “.” are used to determine “system” or
    /// “configuration” files that should not be displayed in a regular
    /// directory listing, and the directory entries “.” and “..” are
    /// considered extra-special.
    ///
    /// This came about more or less by a complete historical accident,
    /// when the original `ls` tried to hide `.` and `..`:
    ///
    /// [Linux History: How Dot Files Became Hidden Files](https://linux-audit.com/linux-history-how-dot-files-became-hidden-files/)
    pub dot_filter: DotFilter,

    /// Glob patterns to ignore. Any file name that matches *any* of these
    /// patterns won’t be displayed in the list.
    pub ignore_patterns: IgnorePatterns,

    /// Whether to ignore Git-ignored patterns.
    pub git_ignore: GitIgnore,
}

impl FileFilter {
    /// Remove every file in the given vector that does *not* pass the
    /// filter predicate for files found inside a directory.
    pub fn filter_child_files(&self, files: &mut Vec<File<'_>>) {
        files.retain(|f| ! self.ignore_patterns.is_ignored(&f.name));

        if self.only_dirs {
            files.retain(File::is_directory);
        }
    }

    /// Remove every file in the given vector that does *not* pass the
    /// filter predicate for file names specified on the command-line.
    ///
    /// The rules are different for these types of files than the other
    /// type because the ignore rules can be used with globbing. For
    /// example, running `exa -I='*.tmp' .vimrc` shouldn’t filter out the
    /// dotfile, because it’s been directly specified. But running
    /// `exa -I='*.ogg' music/*` should filter out the ogg files obtained
    /// from the glob, even though the globbing is done by the shell!
    pub fn filter_argument_files(&self, files: &mut Vec<File<'_>>) {
        files.retain(|f| {
            ! self.ignore_patterns.is_ignored(&f.name)
        });
    }

    /// Sort the files in the given vector based on the sort field option.
    pub fn sort_files<'a, F>(&self, files: &mut Vec<F>)
    where F: AsRef<File<'a>>
    {
        files.sort_by(|a, b| {
            self.sort_field.compare_files(a.as_ref(), b.as_ref())
        });

        if self.reverse {
            files.reverse();
        }

        if self.list_dirs_first {
            // This relies on the fact that `sort_by` is *stable*: it will keep
            // adjacent elements next to each other.
            files.sort_by(|a, b| {
                b.as_ref().points_to_directory()
                    .cmp(&a.as_ref().points_to_directory())
            });
        }
    }
}

/// User-supplied field to sort by.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SortField {

    /// Don’t apply any sorting. This is usually used as an optimisation in
    /// scripts, where the order doesn’t matter.
    Unsorted,

    /// The file name. This is the default sorting.
    Name(SortCase),

    /// The file’s extension, with extensionless files being listed first.
    Extension(SortCase),

    /// The file’s size, in bytes.
    Size,

    /// The file’s inode, which usually corresponds to the order in which
    /// files were created on the filesystem, more or less.
    FileInode,

    /// The time the file was modified (the “mtime”).
    ///
    /// As this is stored as a Unix timestamp, rather than a local time
    /// instance, the time zone does not matter and will only be used to
    /// display the timestamps, not compare them.
    ModifiedDate,

    /// The time the file was accessed (the “atime”).
    ///
    /// Oddly enough, this field rarely holds the *actual* accessed time.
    /// Recording a read time means writing to the file each time it’s read
    /// slows the whole operation down, so many systems will only update the
    /// timestamp in certain circumstances. This has become common enough that
    /// it’s now expected behaviour!
    /// <http://unix.stackexchange.com/a/8842>
    AccessedDate,

    /// The time the file was changed (the “ctime”).
    ///
    /// This field is used to mark the time when a file’s metadata
    /// changed — its permissions, owners, or link count.
    ///
    /// In original Unix, this was, however, meant as creation time.
    /// <https://www.bell-labs.com/usr/dmr/www/cacm.html>
    ChangedDate,

    /// The time the file was created (the “btime” or “birthtime”).
    CreatedDate,

    /// The type of the file: directories, links, pipes, regular, files, etc.
    ///
    /// Files are ordered according to the `PartialOrd` implementation of
    /// `fs::fields::Type`, so changing that will change this.
    FileType,

    /// The “age” of the file, which is the time it was modified sorted
    /// backwards. The reverse of the `ModifiedDate` ordering!
    ///
    /// It turns out that listing the most-recently-modified files first is a
    /// common-enough use case that it deserves its own variant. This would be
    /// implemented by just using the modified date and setting the reverse
    /// flag, but this would make reversing *that* output not work, which is
    /// bad, even though that’s kind of nonsensical. So it’s its own variant
    /// that can be reversed like usual.
    ModifiedAge,

    /// The file's name, however if the name of the file begins with `.`
    /// ignore the leading `.` and then sort as Name
    NameMixHidden(SortCase),
}

/// Whether a field should be sorted case-sensitively or case-insensitively.
/// This determines which of the `natord` functions to use.
///
/// I kept on forgetting which one was sensitive and which one was
/// insensitive. Would a case-sensitive sort put capital letters first because
/// it takes the case of the letters into account, or intermingle them with
/// lowercase letters because it takes the difference between the two cases
/// into account? I gave up and just named these two variants after the
/// effects they have.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SortCase {

    /// Sort files case-sensitively with uppercase first, with ‘A’ coming
    /// before ‘a’.
    ABCabc,

    /// Sort files case-insensitively, with ‘A’ being equal to ‘a’.
    AaBbCc,
}

impl SortField {

    /// Compares two files to determine the order they should be listed in,
    /// depending on the search field.
    ///
    /// The `natord` crate is used here to provide a more *natural* sorting
    /// order than just sorting character-by-character. This splits filenames
    /// into groups between letters and numbers, and then sorts those blocks
    /// together, so `file10` will sort after `file9`, instead of before it
    /// because of the `1`.
    pub fn compare_files(self, a: &File<'_>, b: &File<'_>) -> Ordering {
        use self::SortCase::{ABCabc, AaBbCc};

        match self {
            Self::Unsorted  => Ordering::Equal,

            Self::Name(ABCabc)  => natord::compare(&a.name, &b.name),
            Self::Name(AaBbCc)  => natord::compare_ignore_case(&a.name, &b.name),

            Self::Size          => a.metadata.len().cmp(&b.metadata.len()),
            Self::FileInode     => a.metadata.ino().cmp(&b.metadata.ino()),
            Self::ModifiedDate  => a.modified_time().cmp(&b.modified_time()),
            Self::AccessedDate  => a.accessed_time().cmp(&b.accessed_time()),
            Self::ChangedDate   => a.changed_time().cmp(&b.changed_time()),
            Self::CreatedDate   => a.created_time().cmp(&b.created_time()),
            Self::ModifiedAge   => b.modified_time().cmp(&a.modified_time()),  // flip b and a

            Self::FileType => match a.type_char().cmp(&b.type_char()) { // todo: this recomputes
                Ordering::Equal  => natord::compare(&*a.name, &*b.name),
                order            => order,
            },

            Self::Extension(ABCabc) => match a.ext.cmp(&b.ext) {
                Ordering::Equal  => natord::compare(&*a.name, &*b.name),
                order            => order,
            },

            Self::Extension(AaBbCc) => match a.ext.cmp(&b.ext) {
                Ordering::Equal  => natord::compare_ignore_case(&*a.name, &*b.name),
                order            => order,
            },

            Self::NameMixHidden(ABCabc) => natord::compare(
                Self::strip_dot(&a.name),
                Self::strip_dot(&b.name)
            ),
            Self::NameMixHidden(AaBbCc) => natord::compare_ignore_case(
                Self::strip_dot(&a.name),
                Self::strip_dot(&b.name)
            )
        }
    }

    fn strip_dot(n: &str) -> &str {
        match n.strip_prefix('.') {
            Some(s) => s,
            None    => n,
        }
    }
}

/// The **ignore patterns** are a list of globs that are tested against
/// each filename, and if any of them match, that file isn’t displayed.
/// This lets a user hide, say, text files by ignoring `*.txt`.
#[derive(PartialEq, Default, Debug, Clone)]
pub struct IgnorePatterns {
    patterns: Vec<glob::Pattern>,
}

impl FromIterator<glob::Pattern> for IgnorePatterns {

    fn from_iter<I>(iter: I) -> Self
    where I: IntoIterator<Item = glob::Pattern>
    {
        let patterns = iter.into_iter().collect();
        Self { patterns }
    }
}

impl IgnorePatterns {

    /// Create a new list from the input glob strings, turning the inputs that
    /// are valid glob patterns into an `IgnorePatterns`. The inputs that
    /// don’t parse correctly are returned separately.
    pub fn parse_from_iter<'a, I: IntoIterator<Item = &'a str>>(iter: I) -> (Self, Vec<glob::PatternError>) {
        let iter = iter.into_iter();

        // Almost all glob patterns are valid, so it’s worth pre-allocating
        // the vector with enough space for all of them.
        let mut patterns = match iter.size_hint() {
            (_, Some(count))  => Vec::with_capacity(count),
             _                => Vec::new(),
        };

        // Similarly, assume there won’t be any errors.
        let mut errors = Vec::new();

        for input in iter {
            match glob::Pattern::new(input) {
                Ok(pat) => patterns.push(pat),
                Err(e)  => errors.push(e),
            }
        }

        (Self { patterns }, errors)
    }

    /// Create a new empty set of patterns that matches nothing.
    pub fn empty() -> Self {
        Self { patterns: Vec::new() }
    }

    /// Test whether the given file should be hidden from the results.
    fn is_ignored(&self, file: &str) -> bool {
        self.patterns.iter().any(|p| p.matches(file))
    }
}

/// Whether to ignore or display files that Git would ignore.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum GitIgnore {

    /// Ignore files that Git would ignore.
    CheckAndIgnore,

    /// Display files, even if Git would ignore them.
    Off,
}

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

    #[test]
    fn empty_matches_nothing() {
        let pats = IgnorePatterns::empty();
        assert_eq!(false, pats.is_ignored("nothing"));
        assert_eq!(false, pats.is_ignored("test.mp3"));
    }

    #[test]
    fn ignores_a_glob() {
        let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "*.mp3" ]);
        assert!(fails.is_empty());
        assert_eq!(false, pats.is_ignored("nothing"));
        assert_eq!(true,  pats.is_ignored("test.mp3"));
    }

    #[test]
    fn ignores_an_exact_filename() {
        let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing" ]);
        assert!(fails.is_empty());
        assert_eq!(true,  pats.is_ignored("nothing"));
        assert_eq!(false, pats.is_ignored("test.mp3"));
    }

    #[test]
    fn ignores_both() {
        let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing", "*.mp3" ]);
        assert!(fails.is_empty());
        assert_eq!(true, pats.is_ignored("nothing"));
        assert_eq!(true, pats.is_ignored("test.mp3"));
    }
}
<commit_msg>updated unix.stackexchange link from http to https<commit_after>//! Filtering and sorting the list of files before displaying them.

use std::cmp::Ordering;
use std::iter::FromIterator;
use std::os::unix::fs::MetadataExt;

use crate::fs::DotFilter;
use crate::fs::File;

/// The **file filter** processes a list of files before displaying them to
/// the user, by removing files they don’t want to see, and putting the list
/// in the desired order.
///
/// Usually a user does not want to see *every* file in the list. The most
/// common case is to remove files starting with `.`, which are designated
/// as ‘hidden’ files.
///
/// The special files `.` and `..` files are not actually filtered out, but
/// need to be inserted into the list, in a special case.
///
/// The filter also governs sorting the list. After being filtered, pairs of
/// files are compared and sorted based on the result, with the sort field
/// performing the comparison.
#[derive(PartialEq, Debug, Clone)]
pub struct FileFilter {

    /// Whether directories should be listed first, and other types of file
    /// second. Some users prefer it like this.
    pub list_dirs_first: bool,

    /// The metadata field to sort by.
    pub sort_field: SortField,

    /// Whether to reverse the sorting order. This would sort the largest
    /// files first, or files starting with Z, or the most-recently-changed
    /// ones, depending on the sort field.
    pub reverse: bool,

    /// Whether to only show directories.
    pub only_dirs: bool,

    /// Which invisible “dot” files to include when listing a directory.
    ///
    /// Files starting with a single “.” are used to determine “system” or
    /// “configuration” files that should not be displayed in a regular
    /// directory listing, and the directory entries “.” and “..” are
    /// considered extra-special.
    ///
    /// This came about more or less by a complete historical accident,
    /// when the original `ls` tried to hide `.` and `..`:
    ///
    /// [Linux History: How Dot Files Became Hidden Files](https://linux-audit.com/linux-history-how-dot-files-became-hidden-files/)
    pub dot_filter: DotFilter,

    /// Glob patterns to ignore. Any file name that matches *any* of these
    /// patterns won’t be displayed in the list.
    pub ignore_patterns: IgnorePatterns,

    /// Whether to ignore Git-ignored patterns.
    pub git_ignore: GitIgnore,
}

impl FileFilter {
    /// Remove every file in the given vector that does *not* pass the
    /// filter predicate for files found inside a directory.
    pub fn filter_child_files(&self, files: &mut Vec<File<'_>>) {
        files.retain(|f| ! self.ignore_patterns.is_ignored(&f.name));

        if self.only_dirs {
            files.retain(File::is_directory);
        }
    }

    /// Remove every file in the given vector that does *not* pass the
    /// filter predicate for file names specified on the command-line.
    ///
    /// The rules are different for these types of files than the other
    /// type because the ignore rules can be used with globbing. For
    /// example, running `exa -I='*.tmp' .vimrc` shouldn’t filter out the
    /// dotfile, because it’s been directly specified. But running
    /// `exa -I='*.ogg' music/*` should filter out the ogg files obtained
    /// from the glob, even though the globbing is done by the shell!
    pub fn filter_argument_files(&self, files: &mut Vec<File<'_>>) {
        files.retain(|f| {
            ! self.ignore_patterns.is_ignored(&f.name)
        });
    }

    /// Sort the files in the given vector based on the sort field option.
    pub fn sort_files<'a, F>(&self, files: &mut Vec<F>)
    where F: AsRef<File<'a>>
    {
        files.sort_by(|a, b| {
            self.sort_field.compare_files(a.as_ref(), b.as_ref())
        });

        if self.reverse {
            files.reverse();
        }

        if self.list_dirs_first {
            // This relies on the fact that `sort_by` is *stable*: it will keep
            // adjacent elements next to each other.
            files.sort_by(|a, b| {
                b.as_ref().points_to_directory()
                    .cmp(&a.as_ref().points_to_directory())
            });
        }
    }
}

/// User-supplied field to sort by.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SortField {

    /// Don’t apply any sorting. This is usually used as an optimisation in
    /// scripts, where the order doesn’t matter.
    Unsorted,

    /// The file name. This is the default sorting.
    Name(SortCase),

    /// The file’s extension, with extensionless files being listed first.
    Extension(SortCase),

    /// The file’s size, in bytes.
    Size,

    /// The file’s inode, which usually corresponds to the order in which
    /// files were created on the filesystem, more or less.
    FileInode,

    /// The time the file was modified (the “mtime”).
    ///
    /// As this is stored as a Unix timestamp, rather than a local time
    /// instance, the time zone does not matter and will only be used to
    /// display the timestamps, not compare them.
    ModifiedDate,

    /// The time the file was accessed (the “atime”).
    ///
    /// Oddly enough, this field rarely holds the *actual* accessed time.
    /// Recording a read time means writing to the file each time it’s read
    /// slows the whole operation down, so many systems will only update the
    /// timestamp in certain circumstances. This has become common enough that
    /// it’s now expected behaviour!
    /// <https://unix.stackexchange.com/a/8842>
    AccessedDate,

    /// The time the file was changed (the “ctime”).
    ///
    /// This field is used to mark the time when a file’s metadata
    /// changed — its permissions, owners, or link count.
    ///
    /// In original Unix, this was, however, meant as creation time.
    /// <https://www.bell-labs.com/usr/dmr/www/cacm.html>
    ChangedDate,

    /// The time the file was created (the “btime” or “birthtime”).
    CreatedDate,

    /// The type of the file: directories, links, pipes, regular, files, etc.
    ///
    /// Files are ordered according to the `PartialOrd` implementation of
    /// `fs::fields::Type`, so changing that will change this.
    FileType,

    /// The “age” of the file, which is the time it was modified sorted
    /// backwards. The reverse of the `ModifiedDate` ordering!
    ///
    /// It turns out that listing the most-recently-modified files first is a
    /// common-enough use case that it deserves its own variant. This would be
    /// implemented by just using the modified date and setting the reverse
    /// flag, but this would make reversing *that* output not work, which is
    /// bad, even though that’s kind of nonsensical. So it’s its own variant
    /// that can be reversed like usual.
    ModifiedAge,

    /// The file's name, however if the name of the file begins with `.`
    /// ignore the leading `.` and then sort as Name
    NameMixHidden(SortCase),
}

/// Whether a field should be sorted case-sensitively or case-insensitively.
/// This determines which of the `natord` functions to use.
///
/// I kept on forgetting which one was sensitive and which one was
/// insensitive. Would a case-sensitive sort put capital letters first because
/// it takes the case of the letters into account, or intermingle them with
/// lowercase letters because it takes the difference between the two cases
/// into account? I gave up and just named these two variants after the
/// effects they have.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SortCase {

    /// Sort files case-sensitively with uppercase first, with ‘A’ coming
    /// before ‘a’.
    ABCabc,

    /// Sort files case-insensitively, with ‘A’ being equal to ‘a’.
    AaBbCc,
}

impl SortField {

    /// Compares two files to determine the order they should be listed in,
    /// depending on the search field.
    ///
    /// The `natord` crate is used here to provide a more *natural* sorting
    /// order than just sorting character-by-character. This splits filenames
    /// into groups between letters and numbers, and then sorts those blocks
    /// together, so `file10` will sort after `file9`, instead of before it
    /// because of the `1`.
    pub fn compare_files(self, a: &File<'_>, b: &File<'_>) -> Ordering {
        use self::SortCase::{ABCabc, AaBbCc};

        match self {
            Self::Unsorted  => Ordering::Equal,

            Self::Name(ABCabc)  => natord::compare(&a.name, &b.name),
            Self::Name(AaBbCc)  => natord::compare_ignore_case(&a.name, &b.name),

            Self::Size          => a.metadata.len().cmp(&b.metadata.len()),
            Self::FileInode     => a.metadata.ino().cmp(&b.metadata.ino()),
            Self::ModifiedDate  => a.modified_time().cmp(&b.modified_time()),
            Self::AccessedDate  => a.accessed_time().cmp(&b.accessed_time()),
            Self::ChangedDate   => a.changed_time().cmp(&b.changed_time()),
            Self::CreatedDate   => a.created_time().cmp(&b.created_time()),
            Self::ModifiedAge   => b.modified_time().cmp(&a.modified_time()),  // flip b and a

            Self::FileType => match a.type_char().cmp(&b.type_char()) { // todo: this recomputes
                Ordering::Equal  => natord::compare(&*a.name, &*b.name),
                order            => order,
            },

            Self::Extension(ABCabc) => match a.ext.cmp(&b.ext) {
                Ordering::Equal  => natord::compare(&*a.name, &*b.name),
                order            => order,
            },

            Self::Extension(AaBbCc) => match a.ext.cmp(&b.ext) {
                Ordering::Equal  => natord::compare_ignore_case(&*a.name, &*b.name),
                order            => order,
            },

            Self::NameMixHidden(ABCabc) => natord::compare(
                Self::strip_dot(&a.name),
                Self::strip_dot(&b.name)
            ),
            Self::NameMixHidden(AaBbCc) => natord::compare_ignore_case(
                Self::strip_dot(&a.name),
                Self::strip_dot(&b.name)
            )
        }
    }

    fn strip_dot(n: &str) -> &str {
        match n.strip_prefix('.') {
            Some(s) => s,
            None    => n,
        }
    }
}

/// The **ignore patterns** are a list of globs that are tested against
/// each filename, and if any of them match, that file isn’t displayed.
/// This lets a user hide, say, text files by ignoring `*.txt`.
#[derive(PartialEq, Default, Debug, Clone)]
pub struct IgnorePatterns {
    patterns: Vec<glob::Pattern>,
}

impl FromIterator<glob::Pattern> for IgnorePatterns {

    fn from_iter<I>(iter: I) -> Self
    where I: IntoIterator<Item = glob::Pattern>
    {
        let patterns = iter.into_iter().collect();
        Self { patterns }
    }
}

impl IgnorePatterns {

    /// Create a new list from the input glob strings, turning the inputs that
    /// are valid glob patterns into an `IgnorePatterns`. The inputs that
    /// don’t parse correctly are returned separately.
    pub fn parse_from_iter<'a, I: IntoIterator<Item = &'a str>>(iter: I) -> (Self, Vec<glob::PatternError>) {
        let iter = iter.into_iter();

        // Almost all glob patterns are valid, so it’s worth pre-allocating
        // the vector with enough space for all of them.
        let mut patterns = match iter.size_hint() {
            (_, Some(count))  => Vec::with_capacity(count),
             _                => Vec::new(),
        };

        // Similarly, assume there won’t be any errors.
        let mut errors = Vec::new();

        for input in iter {
            match glob::Pattern::new(input) {
                Ok(pat) => patterns.push(pat),
                Err(e)  => errors.push(e),
            }
        }

        (Self { patterns }, errors)
    }

    /// Create a new empty set of patterns that matches nothing.
    pub fn empty() -> Self {
        Self { patterns: Vec::new() }
    }

    /// Test whether the given file should be hidden from the results.
    fn is_ignored(&self, file: &str) -> bool {
        self.patterns.iter().any(|p| p.matches(file))
    }
}

/// Whether to ignore or display files that Git would ignore.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum GitIgnore {

    /// Ignore files that Git would ignore.
    CheckAndIgnore,

    /// Display files, even if Git would ignore them.
    Off,
}

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

    #[test]
    fn empty_matches_nothing() {
        let pats = IgnorePatterns::empty();
        assert_eq!(false, pats.is_ignored("nothing"));
        assert_eq!(false, pats.is_ignored("test.mp3"));
    }

    #[test]
    fn ignores_a_glob() {
        let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "*.mp3" ]);
        assert!(fails.is_empty());
        assert_eq!(false, pats.is_ignored("nothing"));
        assert_eq!(true,  pats.is_ignored("test.mp3"));
    }

    #[test]
    fn ignores_an_exact_filename() {
        let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing" ]);
        assert!(fails.is_empty());
        assert_eq!(true,  pats.is_ignored("nothing"));
        assert_eq!(false, pats.is_ignored("test.mp3"));
    }

    #[test]
    fn ignores_both() {
        let (pats, fails) = IgnorePatterns::parse_from_iter(vec![ "nothing", "*.mp3" ]);
        assert!(fails.is_empty());
        assert_eq!(true, pats.is_ignored("nothing"));
        assert_eq!(true, pats.is_ignored("test.mp3"));
    }
}
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::codemap::DUMMY_SP;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub trait Generate {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item>;
}

impl Generate for Block {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        match self {
            Block::Describe(describe) => describe.generate(cx, up),
            Block::It(it) => it.generate(cx, up),
            Block::Bench(bench) => bench.generate(cx, up)
        }
    }
}

impl Generate for Describe {
    fn generate(mut self,
                cx: &mut ExtCtxt,
                up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        if let Some(ref up) = up {
            self.before = up.before.iter()
                .chain(self.before.iter())
                .cloned()
                .collect();
            self.after = self.after.iter()
                .chain(up.after.iter())
                .cloned()
                .collect();
        }

        let mut items = self.blocks.iter().map(|block| {
            block.clone().generate(cx, Some(&self))
        }).collect::<Vec<_>>();

        items.push(quote_item!(cx, pub use super::*;).unwrap());

        cx.item_mod(DUMMY_SP,
                    DUMMY_SP,
                    name,
                    vec![],
                    items)
    }
}

impl Generate for It {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();
        let block = block.fold(head, merge_blocks);

        quote_item!(cx, #[test] fn $name() { $block }).unwrap()
    }
}

impl Generate for Bench {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();
        let block = block.fold(head, merge_blocks);

        let ident = self.ident;
        quote_item!(cx, #[bench] fn $name($ident: &mut ::test::Bencher) {
            $block
        }).unwrap()
    }
}

fn merge_blocks(left: P<ast::Block>, right: P<ast::Block>) -> P<ast::Block> {
    let mut stmts = left.stmts.clone();
    stmts.extend(right.stmts.clone());

    P(ast::Block {
        stmts: stmts,
        ..(*left).clone()
    })
}
<commit_msg>Use `quote_item!` in `impl Generate for Describe`.<commit_after>use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub trait Generate {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item>;
}

impl Generate for Block {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        match self {
            Block::Describe(describe) => describe.generate(cx, up),
            Block::It(it) => it.generate(cx, up),
            Block::Bench(bench) => bench.generate(cx, up)
        }
    }
}

impl Generate for Describe {
    fn generate(mut self,
                cx: &mut ExtCtxt,
                up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        if let Some(ref up) = up {
            self.before = up.before.iter()
                .chain(self.before.iter())
                .cloned()
                .collect();
            self.after = self.after.iter()
                .chain(up.after.iter())
                .cloned()
                .collect();
        }

        let mut items = self.blocks.iter().map(|block| {
            block.clone().generate(cx, Some(&self))
        }).collect::<Vec<_>>();

        items.push(quote_item!(cx, pub use super::*;).unwrap());

        quote_item!(cx, mod $name { $items }).unwrap()
    }
}

impl Generate for It {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();
        let block = block.fold(head, merge_blocks);

        quote_item!(cx, #[test] fn $name() { $block }).unwrap()
    }
}

impl Generate for Bench {
    fn generate(self, cx: &mut ExtCtxt, up: Option<&Describe>) -> P<ast::Item> {
        let name = cx.ident_of(&self.name);

        let block = if let Some(ref up) = up {
            up.before.iter()
                .chain(Some(self.block).iter())
                .chain(up.after.iter())
                .cloned().collect()
        } else {
            vec![self.block]
        };

        let mut block = block.into_iter();
        let head = block.next().unwrap();
        let block = block.fold(head, merge_blocks);

        let ident = self.ident;
        quote_item!(cx, #[bench] fn $name($ident: &mut ::test::Bencher) {
            $block
        }).unwrap()
    }
}

fn merge_blocks(left: P<ast::Block>, right: P<ast::Block>) -> P<ast::Block> {
    let mut stmts = left.stmts.clone();
    stmts.extend(right.stmts.clone());

    P(ast::Block {
        stmts: stmts,
        ..(*left).clone()
    })
}
<|endoftext|>


<commit_before>use super::point::Point;
use super::number::NumberOps;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point) -> EdgeIntersection {
   if p2.y > y {
      if p1.y < y {
         EdgeIntersection::Single(
            Point::new(h_intersect(y, p1, p2), y)
         )
      } else {
         EdgeIntersection::None
      }
   } else {
      if p1.y > y && p2.y < y {
         EdgeIntersection::Double(
            Point::new(h_intersect(y, p1, p2), y),
            Point::new(p2.x, p2.y)
         )
      } else {
         EdgeIntersection::Single(
            Point::new(p2.x, p2.y)
         )
      }
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point) -> EdgeIntersection {
   if p2.y < y {
      if p1.y > y {
         EdgeIntersection::Single(
            Point::new(h_intersect(y, p1, p2), y)
         )
      } else {
         EdgeIntersection::None
      }
   } else {
      if p1.y < y && p2.y > y {
         EdgeIntersection::Double(
            Point::new(h_intersect(y, p1, p2), y),
            Point::new(p2.x, p2.y)
         )
      } else {
         EdgeIntersection::Single(
            Point::new(p2.x, p2.y)
         )
      }
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point) -> EdgeIntersection {
   if p2.x > x {
      if p1.x < x {
         EdgeIntersection::Single(
            Point::new(x, v_intersect(x, p1, p2))
         )
      } else {
         EdgeIntersection::None
      }
   } else {
      if p1.x > x && p2.x < x {
         EdgeIntersection::Double(
            Point::new(x, v_intersect(x, p1, p2)),
            Point::new(p2.x, p2.y)
         )
      } else {
         EdgeIntersection::Single(
            Point::new(p2.x, p2.y)
         )
      }
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point) -> EdgeIntersection {
   if p2.x < x {
      if p1.x > x {
         EdgeIntersection::Single(
            Point::new(x, v_intersect(x, p1, p2))
         )
      } else {
         EdgeIntersection::None
      }
   } else {
      if p1.x < x && p2.x > x {
         EdgeIntersection::Double(
            Point::new(x, v_intersect(x, p1, p2)),
            Point::new(p2.x, p2.y)
         )
      } else {
         EdgeIntersection::Single(
            Point::new(p2.x, p2.y)
         )
      }
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

#[derive(Debug)]
pub enum EdgeIntersection {
   Double(Point, Point),
   Single(Point),
   None,
}

pub fn hv_clip<F>(intersect: F, y: i64, points: &mut Vec<Point>, start: usize) where
   F: Fn(i64, Point, Point) -> EdgeIntersection,
{
   let end = points.len();

   if end - start < 2 {
      return;
   }

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];
      match intersect(y, p1, p2) {
         EdgeIntersection::Double(r1, r2) => {
            points.push(r1);
            points.push(r2);
         },
         EdgeIntersection::Single(r) => {
            points.push(r);
         },
         EdgeIntersection::None => {}
      }

      p1 = p2;
   }
}

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

   use super::super::point::Point;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            let mut points = vec![
               Point::new(50, 50),
               Point::new(100, 200),
               Point::new(150, 100),
            ];
            points.reserve(30);

            let start = 0;
            let end = points.len();
            hv_clip(h_down_intersect, 150, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(h_up_intersect, 80, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(v_right_intersect, 70, &mut points, start);

            let start = end;
            hv_clip(v_left_intersect, 140, &mut points, start);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = vec![
         Point::new(50, 50),
         Point::new(100, 200),
         Point::new(150, 100),
      ];

      let start = 0;
      let end = points.len();
      hv_clip(h_down_intersect, 150, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(h_up_intersect, 80, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_right_intersect, 70, &mut points, start);

      let start = end;
      hv_clip(v_left_intersect, 140, &mut points, start);

      assert_eq!(points.len(), 25);
   }
}
<commit_msg>Clip optimize<commit_after>use super::point::Point;
use super::number::NumberOps;

#[inline]
pub fn h_down_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y > y {
      if p1.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y > y && p2.y < y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_up_intersect(y: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.y < y {
      if p1.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
   } else {
      if p1.y < y && p2.y > y {
         points.push(Point::new(h_intersect(y, p1, p2), y));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_left_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x > x {
      if p1.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x > x && p2.x < x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn v_right_intersect(x: i64, p1: Point, p2: Point, points: &mut Vec<Point>) {
   if p2.x < x {
      if p1.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
   } else {
      if p1.x < x && p2.x > x {
         points.push(Point::new(x, v_intersect(x, p1, p2)));
      }
      points.push(Point::new(p2.x, p2.y));
   }
}

#[inline]
pub fn h_intersect(y: i64, p1: Point, p2: Point) -> i64 {
   p1.x + ((p2.x - p1.x) * (y - p1.y)).rounding_div(p2.y - p1.y)
}

#[inline]
pub fn v_intersect(x: i64, p1: Point, p2: Point) -> i64 {
   p1.y + ((p2.y - p1.y) * (x - p1.x)).rounding_div(p2.x - p1.x)
}

pub fn hv_clip<F>(intersect: F, y: i64, points: &mut Vec<Point>, start: usize) where
   F: Fn(i64, Point, Point, &mut Vec<Point>),
{
   let end = points.len();

   if end - start < 2 {
      return;
   }

   let mut p1 = points[end-1];

   for i in start..end {
      let p2 = points[i];
      intersect(y, p1, p2, points);

      p1 = p2;
   }
}

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

   use super::super::point::Point;

   use super::*;

   #[bench]
   fn bench_clip(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            let mut points = vec![
               Point::new(50, 50),
               Point::new(100, 200),
               Point::new(150, 100),
            ];
            points.reserve(30);

            let start = 0;
            let end = points.len();
            hv_clip(h_down_intersect, 150, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(h_up_intersect, 80, &mut points, start);

            let start = end;
            let end = points.len();
            hv_clip(v_right_intersect, 70, &mut points, start);

            let start = end;
            hv_clip(v_left_intersect, 140, &mut points, start);
         }
      });
   }

   #[test]
   fn test_clip() {
      let mut points = vec![
         Point::new(50, 50),
         Point::new(100, 200),
         Point::new(150, 100),
      ];

      let start = 0;
      let end = points.len();
      hv_clip(h_down_intersect, 150, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(h_up_intersect, 80, &mut points, start);

      let start = end;
      let end = points.len();
      hv_clip(v_right_intersect, 70, &mut points, start);

      let start = end;
      hv_clip(v_left_intersect, 140, &mut points, start);

      assert_eq!(points.len(), 25);
   }
}
<|endoftext|>


<commit_before>use super::utils::types::*;
use super::utils::bls::AggregateSignature;

pub struct AggregateVote {
    pub shard_id: u16,
    pub shard_block_hash: Sha256Digest,
    pub notary_bitfield: Bitfield,
    pub aggregate_sig: AggregateSignature,
}

impl AggregateVote {
    pub fn new_for_shard(shard_id: u16,
                         shard_block_hash: Sha256Digest)
        -> AggregateVote {
        AggregateVote {
            shard_id: shard_id,
            shard_block_hash: shard_block_hash,
            notary_bitfield: Vec::new(),
            aggregate_sig: AggregateSignature::new()
        }
    }
}

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

    #[test]
    fn test_new_for_shard() {
        let id = 1;
        let hash = Sha256Digest::random();
        let v = AggregateVote::new_for_shard(id, hash);
        assert_eq!(v.shard_id, id);
        assert_eq!(v.shard_block_hash, hash);
    }
}
<commit_msg>Add RLP encoding for AggregateVote<commit_after>use super::utils::types::*;
use super::utils::bls::AggregateSignature;
use super::rlp::{ RlpStream, Encodable };

pub struct AggregateVote {
    pub shard_id: u16,
    pub shard_block_hash: Sha256Digest,
    pub notary_bitfield: Bitfield,
    pub aggregate_sig: AggregateSignature,
}

impl AggregateVote {
    pub fn new_for_shard(shard_id: u16,
                         shard_block_hash: Sha256Digest)
        -> AggregateVote {
        AggregateVote {
            shard_id: shard_id,
            shard_block_hash: shard_block_hash,
            notary_bitfield: Vec::new(),
            aggregate_sig: AggregateSignature::new()
        }
    }
}

/*
 * RLP Encoding
 */
impl Encodable for AggregateVote {
    fn rlp_append(&self, s: &mut RlpStream) {
        s.append(&self.shard_id);
        s.append(&self.shard_block_hash);
        s.append(&self.notary_bitfield);
        // s.append(&self.aggregate_sig);   // TODO: represent this in RLP
    }
}

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

    #[test]
    fn test_new_for_shard() {
        let id = 1;
        let hash = Sha256Digest::random();
        let v = AggregateVote::new_for_shard(id, hash);
        assert_eq!(v.shard_id, id);
        assert_eq!(v.shard_block_hash, hash);
    }

    #[test]
    fn test_serialization() {
        let a = AggregateVote {
            shard_id: 100,
            shard_block_hash: Sha256Digest::zero(),
            notary_bitfield: Vec::new(),
            aggregate_sig: AggregateSignature::new()
        };
        let e = rlp::encode(&a);
        assert_eq!(e.len(), 35);
        assert_eq!(e[0], 100);
        assert_eq!(e[1], 160);
        assert_eq!(e[2..34], [0; 32]);
        assert_eq!(e[34], 128);
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use rustc::mir;
use rustc::ty::layout::LayoutOf;
use rustc::mir::interpret::{EvalResult, Scalar, PointerArithmetic};

use super::{EvalContext, Machine};

/// Classify whether an operator is "left-homogeneous", i.e. the LHS has the
/// same type as the result.
#[inline]
fn binop_left_homogeneous(op: mir::BinOp) -> bool {
    use rustc::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
        Offset | Shl | Shr =>
            true,
        Eq | Ne | Lt | Le | Gt | Ge =>
            false,
    }
}
/// Classify whether an operator is "right-homogeneous", i.e. the RHS has the
/// same type as the LHS.
#[inline]
fn binop_right_homogeneous(op: mir::BinOp) -> bool {
    use rustc::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
        Eq | Ne | Lt | Le | Gt | Ge =>
            true,
        Offset | Shl | Shr =>
            false,
    }
}

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
    pub fn run(&mut self) -> EvalResult<'tcx> {
        while self.step()? {}
        Ok(())
    }

    /// Returns true as long as there are more things to do.
    fn step(&mut self) -> EvalResult<'tcx, bool> {
        if self.stack.is_empty() {
            return Ok(false);
        }

        let block = self.frame().block;
        let stmt_id = self.frame().stmt;
        let mir = self.mir();
        let basic_block = &mir.basic_blocks()[block];

        let old_frames = self.cur_frame();

        if let Some(stmt) = basic_block.statements.get(stmt_id) {
            assert_eq!(old_frames, self.cur_frame());
            self.statement(stmt)?;
            return Ok(true);
        }

        M::before_terminator(self)?;

        let terminator = basic_block.terminator();
        assert_eq!(old_frames, self.cur_frame());
        self.terminator(terminator)?;
        Ok(true)
    }

    fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
        debug!("{:?}", stmt);

        use rustc::mir::StatementKind::*;

        // Some statements (e.g. box) push new stack frames.
        // We have to record the stack frame number *before* executing the statement.
        let frame_idx = self.cur_frame();
        self.tcx.span = stmt.source_info.span;
        self.memory.tcx.span = stmt.source_info.span;

        match stmt.kind {
            Assign(ref place, ref rvalue) => self.eval_rvalue_into_place(rvalue, place)?,

            SetDiscriminant {
                ref place,
                variant_index,
            } => {
                let dest = self.eval_place(place)?;
                self.write_discriminant_index(variant_index, dest)?;
            }

            // Mark locals as alive
            StorageLive(local) => {
                let old_val = self.storage_live(local)?;
                self.deallocate_local(old_val)?;
            }

            // Mark locals as dead
            StorageDead(local) => {
                let old_val = self.storage_dead(local);
                self.deallocate_local(old_val)?;
            }

            // No dynamic semantics attached to `FakeRead`; MIR
            // interpreter is solely intended for borrowck'ed code.
            FakeRead(..) => {}

            // Validity checks.
            Validate(op, ref places) => {
                for operand in places {
                    M::validation_op(self, op, operand)?;
                }
            }

            EndRegion(..) => {}
            AscribeUserType(..) => {}

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}

            InlineAsm { .. } => return err!(InlineAsm),
        }

        self.stack[frame_idx].stmt += 1;
        Ok(())
    }

    /// Evaluate an assignment statement.
    ///
    /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
    /// type writes its results directly into the memory specified by the place.
    fn eval_rvalue_into_place(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        place: &mir::Place<'tcx>,
    ) -> EvalResult<'tcx> {
        let dest = self.eval_place(place)?;

        use rustc::mir::Rvalue::*;
        match *rvalue {
            Use(ref operand) => {
                // Avoid recomputing the layout
                let op = self.eval_operand(operand, Some(dest.layout))?;
                self.copy_op(op, dest)?;
            }

            BinaryOp(bin_op, ref left, ref right) => {
                let layout = if binop_left_homogeneous(bin_op) { Some(dest.layout) } else { None };
                let left = self.read_value(self.eval_operand(left, layout)?)?;
                let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
                let right = self.read_value(self.eval_operand(right, layout)?)?;
                self.binop_ignore_overflow(
                    bin_op,
                    left,
                    right,
                    dest,
                )?;
            }

            CheckedBinaryOp(bin_op, ref left, ref right) => {
                // Due to the extra boolean in the result, we can never reuse the `dest.layout`.
                let left = self.read_value(self.eval_operand(left, None)?)?;
                let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
                let right = self.read_value(self.eval_operand(right, layout)?)?;
                self.binop_with_overflow(
                    bin_op,
                    left,
                    right,
                    dest,
                )?;
            }

            UnaryOp(un_op, ref operand) => {
                // The operand always has the same type as the result.
                let val = self.read_value(self.eval_operand(operand, Some(dest.layout))?)?;
                let val = self.unary_op(un_op, val.to_scalar()?, dest.layout)?;
                self.write_scalar(val, dest)?;
            }

            Aggregate(ref kind, ref operands) => {
                let (dest, active_field_index) = match **kind {
                    mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                        self.write_discriminant_index(variant_index, dest)?;
                        if adt_def.is_enum() {
                            (self.place_downcast(dest, variant_index)?, active_field_index)
                        } else {
                            (dest, active_field_index)
                        }
                    }
                    _ => (dest, None)
                };

                for (i, operand) in operands.iter().enumerate() {
                    let op = self.eval_operand(operand, None)?;
                    // Ignore zero-sized fields.
                    if !op.layout.is_zst() {
                        let field_index = active_field_index.unwrap_or(i);
                        let field_dest = self.place_field(dest, field_index as u64)?;
                        self.copy_op(op, field_dest)?;
                    }
                }
            }

            Repeat(ref operand, _) => {
                let op = self.eval_operand(operand, None)?;
                let dest = self.force_allocation(dest)?;
                let length = dest.len(&self)?;

                if length > 0 {
                    // write the first
                    let first = self.mplace_field(dest, 0)?;
                    self.copy_op(op, first.into())?;

                    if length > 1 {
                        // copy the rest
                        let (dest, dest_align) = first.to_scalar_ptr_align();
                        let rest = dest.ptr_offset(first.layout.size, &self)?;
                        self.memory.copy_repeatedly(
                            dest, dest_align, rest, dest_align, first.layout.size, length - 1, true
                        )?;
                    }
                }
            }

            Len(ref place) => {
                // FIXME(CTFE): don't allow computing the length of arrays in const eval
                let src = self.eval_place(place)?;
                let mplace = self.force_allocation(src)?;
                let len = mplace.len(&self)?;
                let size = self.pointer_size();
                self.write_scalar(
                    Scalar::from_uint(len, size),
                    dest,
                )?;
            }

            Ref(_, _, ref place) => {
                let src = self.eval_place(place)?;
                let val = self.force_allocation(src)?.to_ref();
                self.write_value(val, dest)?;
            }

            NullaryOp(mir::NullOp::Box, _) => {
                M::box_alloc(self, dest)?;
            }

            NullaryOp(mir::NullOp::SizeOf, ty) => {
                let ty = self.monomorphize(ty, self.substs());
                let layout = self.layout_of(ty)?;
                assert!(!layout.is_unsized(),
                        "SizeOf nullary MIR operator called for unsized type");
                let size = self.pointer_size();
                self.write_scalar(
                    Scalar::from_uint(layout.size.bytes(), size),
                    dest,
                )?;
            }

            Cast(kind, ref operand, cast_ty) => {
                debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest.layout.ty);
                let src = self.eval_operand(operand, None)?;
                self.cast(src, kind, dest)?;
            }

            Discriminant(ref place) => {
                let place = self.eval_place(place)?;
                let discr_val = self.read_discriminant(self.place_to_op(place)?)?.0;
                let size = dest.layout.size;
                self.write_scalar(Scalar::from_uint(discr_val, size), dest)?;
            }
        }

        self.dump_place(*dest);

        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
        debug!("{:?}", terminator.kind);
        self.tcx.span = terminator.source_info.span;
        self.memory.tcx.span = terminator.source_info.span;

        let old_stack = self.cur_frame();
        let old_bb = self.frame().block;
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            // This should change *something*
            debug_assert!(self.cur_frame() != old_stack || self.frame().block != old_bb);
            debug!("// {:?}", self.frame().block);
        }
        Ok(())
    }
}
<commit_msg>Make EvalContext::step public again<commit_after>// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains the `EvalContext` methods for executing a single step of the interpreter.
//!
//! The main entry point is the `step` method.

use rustc::mir;
use rustc::ty::layout::LayoutOf;
use rustc::mir::interpret::{EvalResult, Scalar, PointerArithmetic};

use super::{EvalContext, Machine};

/// Classify whether an operator is "left-homogeneous", i.e. the LHS has the
/// same type as the result.
#[inline]
fn binop_left_homogeneous(op: mir::BinOp) -> bool {
    use rustc::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
        Offset | Shl | Shr =>
            true,
        Eq | Ne | Lt | Le | Gt | Ge =>
            false,
    }
}
/// Classify whether an operator is "right-homogeneous", i.e. the RHS has the
/// same type as the LHS.
#[inline]
fn binop_right_homogeneous(op: mir::BinOp) -> bool {
    use rustc::mir::BinOp::*;
    match op {
        Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr |
        Eq | Ne | Lt | Le | Gt | Ge =>
            true,
        Offset | Shl | Shr =>
            false,
    }
}

impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
    pub fn run(&mut self) -> EvalResult<'tcx> {
        while self.step()? {}
        Ok(())
    }

    /// Returns true as long as there are more things to do.
    pub fn step(&mut self) -> EvalResult<'tcx, bool> {
        if self.stack.is_empty() {
            return Ok(false);
        }

        let block = self.frame().block;
        let stmt_id = self.frame().stmt;
        let mir = self.mir();
        let basic_block = &mir.basic_blocks()[block];

        let old_frames = self.cur_frame();

        if let Some(stmt) = basic_block.statements.get(stmt_id) {
            assert_eq!(old_frames, self.cur_frame());
            self.statement(stmt)?;
            return Ok(true);
        }

        M::before_terminator(self)?;

        let terminator = basic_block.terminator();
        assert_eq!(old_frames, self.cur_frame());
        self.terminator(terminator)?;
        Ok(true)
    }

    fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> {
        debug!("{:?}", stmt);

        use rustc::mir::StatementKind::*;

        // Some statements (e.g. box) push new stack frames.
        // We have to record the stack frame number *before* executing the statement.
        let frame_idx = self.cur_frame();
        self.tcx.span = stmt.source_info.span;
        self.memory.tcx.span = stmt.source_info.span;

        match stmt.kind {
            Assign(ref place, ref rvalue) => self.eval_rvalue_into_place(rvalue, place)?,

            SetDiscriminant {
                ref place,
                variant_index,
            } => {
                let dest = self.eval_place(place)?;
                self.write_discriminant_index(variant_index, dest)?;
            }

            // Mark locals as alive
            StorageLive(local) => {
                let old_val = self.storage_live(local)?;
                self.deallocate_local(old_val)?;
            }

            // Mark locals as dead
            StorageDead(local) => {
                let old_val = self.storage_dead(local);
                self.deallocate_local(old_val)?;
            }

            // No dynamic semantics attached to `FakeRead`; MIR
            // interpreter is solely intended for borrowck'ed code.
            FakeRead(..) => {}

            // Validity checks.
            Validate(op, ref places) => {
                for operand in places {
                    M::validation_op(self, op, operand)?;
                }
            }

            EndRegion(..) => {}
            AscribeUserType(..) => {}

            // Defined to do nothing. These are added by optimization passes, to avoid changing the
            // size of MIR constantly.
            Nop => {}

            InlineAsm { .. } => return err!(InlineAsm),
        }

        self.stack[frame_idx].stmt += 1;
        Ok(())
    }

    /// Evaluate an assignment statement.
    ///
    /// There is no separate `eval_rvalue` function. Instead, the code for handling each rvalue
    /// type writes its results directly into the memory specified by the place.
    fn eval_rvalue_into_place(
        &mut self,
        rvalue: &mir::Rvalue<'tcx>,
        place: &mir::Place<'tcx>,
    ) -> EvalResult<'tcx> {
        let dest = self.eval_place(place)?;

        use rustc::mir::Rvalue::*;
        match *rvalue {
            Use(ref operand) => {
                // Avoid recomputing the layout
                let op = self.eval_operand(operand, Some(dest.layout))?;
                self.copy_op(op, dest)?;
            }

            BinaryOp(bin_op, ref left, ref right) => {
                let layout = if binop_left_homogeneous(bin_op) { Some(dest.layout) } else { None };
                let left = self.read_value(self.eval_operand(left, layout)?)?;
                let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
                let right = self.read_value(self.eval_operand(right, layout)?)?;
                self.binop_ignore_overflow(
                    bin_op,
                    left,
                    right,
                    dest,
                )?;
            }

            CheckedBinaryOp(bin_op, ref left, ref right) => {
                // Due to the extra boolean in the result, we can never reuse the `dest.layout`.
                let left = self.read_value(self.eval_operand(left, None)?)?;
                let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None };
                let right = self.read_value(self.eval_operand(right, layout)?)?;
                self.binop_with_overflow(
                    bin_op,
                    left,
                    right,
                    dest,
                )?;
            }

            UnaryOp(un_op, ref operand) => {
                // The operand always has the same type as the result.
                let val = self.read_value(self.eval_operand(operand, Some(dest.layout))?)?;
                let val = self.unary_op(un_op, val.to_scalar()?, dest.layout)?;
                self.write_scalar(val, dest)?;
            }

            Aggregate(ref kind, ref operands) => {
                let (dest, active_field_index) = match **kind {
                    mir::AggregateKind::Adt(adt_def, variant_index, _, _, active_field_index) => {
                        self.write_discriminant_index(variant_index, dest)?;
                        if adt_def.is_enum() {
                            (self.place_downcast(dest, variant_index)?, active_field_index)
                        } else {
                            (dest, active_field_index)
                        }
                    }
                    _ => (dest, None)
                };

                for (i, operand) in operands.iter().enumerate() {
                    let op = self.eval_operand(operand, None)?;
                    // Ignore zero-sized fields.
                    if !op.layout.is_zst() {
                        let field_index = active_field_index.unwrap_or(i);
                        let field_dest = self.place_field(dest, field_index as u64)?;
                        self.copy_op(op, field_dest)?;
                    }
                }
            }

            Repeat(ref operand, _) => {
                let op = self.eval_operand(operand, None)?;
                let dest = self.force_allocation(dest)?;
                let length = dest.len(&self)?;

                if length > 0 {
                    // write the first
                    let first = self.mplace_field(dest, 0)?;
                    self.copy_op(op, first.into())?;

                    if length > 1 {
                        // copy the rest
                        let (dest, dest_align) = first.to_scalar_ptr_align();
                        let rest = dest.ptr_offset(first.layout.size, &self)?;
                        self.memory.copy_repeatedly(
                            dest, dest_align, rest, dest_align, first.layout.size, length - 1, true
                        )?;
                    }
                }
            }

            Len(ref place) => {
                // FIXME(CTFE): don't allow computing the length of arrays in const eval
                let src = self.eval_place(place)?;
                let mplace = self.force_allocation(src)?;
                let len = mplace.len(&self)?;
                let size = self.pointer_size();
                self.write_scalar(
                    Scalar::from_uint(len, size),
                    dest,
                )?;
            }

            Ref(_, _, ref place) => {
                let src = self.eval_place(place)?;
                let val = self.force_allocation(src)?.to_ref();
                self.write_value(val, dest)?;
            }

            NullaryOp(mir::NullOp::Box, _) => {
                M::box_alloc(self, dest)?;
            }

            NullaryOp(mir::NullOp::SizeOf, ty) => {
                let ty = self.monomorphize(ty, self.substs());
                let layout = self.layout_of(ty)?;
                assert!(!layout.is_unsized(),
                        "SizeOf nullary MIR operator called for unsized type");
                let size = self.pointer_size();
                self.write_scalar(
                    Scalar::from_uint(layout.size.bytes(), size),
                    dest,
                )?;
            }

            Cast(kind, ref operand, cast_ty) => {
                debug_assert_eq!(self.monomorphize(cast_ty, self.substs()), dest.layout.ty);
                let src = self.eval_operand(operand, None)?;
                self.cast(src, kind, dest)?;
            }

            Discriminant(ref place) => {
                let place = self.eval_place(place)?;
                let discr_val = self.read_discriminant(self.place_to_op(place)?)?.0;
                let size = dest.layout.size;
                self.write_scalar(Scalar::from_uint(discr_val, size), dest)?;
            }
        }

        self.dump_place(*dest);

        Ok(())
    }

    fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> EvalResult<'tcx> {
        debug!("{:?}", terminator.kind);
        self.tcx.span = terminator.source_info.span;
        self.memory.tcx.span = terminator.source_info.span;

        let old_stack = self.cur_frame();
        let old_bb = self.frame().block;
        self.eval_terminator(terminator)?;
        if !self.stack.is_empty() {
            // This should change *something*
            debug_assert!(self.cur_frame() != old_stack || self.frame().block != old_bb);
            debug!("// {:?}", self.frame().block);
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! lint when there is a large size difference between variants on an enum

use rustc::lint::*;
use rustc::hir::*;
use utils::{span_lint_and_then, snippet_opt};
use rustc::ty::layout::TargetDataLayout;
use rustc::ty::TypeFoldable;
use rustc::traits::Reveal;

/// **What it does:** Checks for large size differences between variants on `enum`s.
///
/// **Why is this bad?** Enum size is bounded by the largest variant. Having a large variant
/// can penalize the memory layout of that enum.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Test {
///    A(i32),
///    B([i32; 8000]),
/// }
/// ```
declare_lint! {
    pub LARGE_ENUM_VARIANT,
    Warn,
    "large size difference between variants on an enum"
}

#[derive(Copy,Clone)]
pub struct LargeEnumVariant {
    maximum_size_difference_allowed: u64,
}

impl LargeEnumVariant {
    pub fn new(maximum_size_difference_allowed: u64) -> Self {
        LargeEnumVariant { maximum_size_difference_allowed: maximum_size_difference_allowed }
    }
}

impl LintPass for LargeEnumVariant {
    fn get_lints(&self) -> LintArray {
        lint_array!(LARGE_ENUM_VARIANT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        let did = cx.tcx.hir.local_def_id(item.id);
        if let ItemEnum(ref def, _) = item.node {
            let ty = cx.tcx.item_type(did);
            let adt = ty.ty_adt_def().expect("already checked whether this is an enum");

            let mut smallest_variant: Option<(_, _)> = None;
            let mut largest_variant: Option<(_, _)> = None;

            for (i, variant) in adt.variants.iter().enumerate() {
                let data_layout = TargetDataLayout::parse(cx.sess());
                cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
                    let size: u64 = variant.fields
                        .iter()
                        .map(|f| {
                            let ty = cx.tcx.item_type(f.did);
                            if ty.needs_subst() {
                                0 // we can't reason about generics, so we treat them as zero sized
                            } else {
                                ty.layout(&infcx)
                                    .expect("layout should be computable for concrete type")
                                    .size(&data_layout)
                                    .bytes()
                            }
                        })
                        .sum();

                    let grouped = (size, (i, variant));

                    update_if(&mut smallest_variant, grouped, |a, b| b.0 <= a.0);
                    update_if(&mut largest_variant, grouped, |a, b| b.0 >= a.0);
                });
            }

            if let (Some(smallest), Some(largest)) = (smallest_variant, largest_variant) {
                let difference = largest.0 - smallest.0;

                if difference > self.maximum_size_difference_allowed {
                    let (i, variant) = largest.1;

                    span_lint_and_then(cx,
                                       LARGE_ENUM_VARIANT,
                                       def.variants[i].span,
                                       "large size difference between variants",
                                       |db| {
                        if variant.fields.len() == 1 {
                            let span = match def.variants[i].node.data {
                                VariantData::Struct(ref fields, _) |
                                VariantData::Tuple(ref fields, _) => fields[0].ty.span,
                                VariantData::Unit(_) => unreachable!(),
                            };
                            if let Some(snip) = snippet_opt(cx, span) {
                                db.span_suggestion(span,
                                                   "consider boxing the large fields to reduce the total size of the \
                                                    enum",
                                                   format!("Box<{}>", snip));
                                return;
                            }
                        }
                        db.span_help(def.variants[i].span,
                                     "consider boxing the large fields to reduce the total size of the enum");
                    });
                }
            }

        }
    }
}

fn update_if<T, F>(old: &mut Option<T>, new: T, f: F) where F: Fn(&T, &T) -> bool {
    if let Some(ref mut val) = *old {
        if f(val, &new) {
            *val = new;
        }
    } else {
        *old = Some(new);
    }
}
<commit_msg>rustfmt<commit_after>//! lint when there is a large size difference between variants on an enum

use rustc::lint::*;
use rustc::hir::*;
use utils::{span_lint_and_then, snippet_opt};
use rustc::ty::layout::TargetDataLayout;
use rustc::ty::TypeFoldable;
use rustc::traits::Reveal;

/// **What it does:** Checks for large size differences between variants on `enum`s.
///
/// **Why is this bad?** Enum size is bounded by the largest variant. Having a large variant
/// can penalize the memory layout of that enum.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// enum Test {
///    A(i32),
///    B([i32; 8000]),
/// }
/// ```
declare_lint! {
    pub LARGE_ENUM_VARIANT,
    Warn,
    "large size difference between variants on an enum"
}

#[derive(Copy,Clone)]
pub struct LargeEnumVariant {
    maximum_size_difference_allowed: u64,
}

impl LargeEnumVariant {
    pub fn new(maximum_size_difference_allowed: u64) -> Self {
        LargeEnumVariant { maximum_size_difference_allowed: maximum_size_difference_allowed }
    }
}

impl LintPass for LargeEnumVariant {
    fn get_lints(&self) -> LintArray {
        lint_array!(LARGE_ENUM_VARIANT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        let did = cx.tcx.hir.local_def_id(item.id);
        if let ItemEnum(ref def, _) = item.node {
            let ty = cx.tcx.item_type(did);
            let adt = ty.ty_adt_def().expect("already checked whether this is an enum");

            let mut smallest_variant: Option<(_, _)> = None;
            let mut largest_variant: Option<(_, _)> = None;

            for (i, variant) in adt.variants.iter().enumerate() {
                let data_layout = TargetDataLayout::parse(cx.sess());
                cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
                    let size: u64 = variant.fields
                        .iter()
                        .map(|f| {
                            let ty = cx.tcx.item_type(f.did);
                            if ty.needs_subst() {
                                0 // we can't reason about generics, so we treat them as zero sized
                            } else {
                                ty.layout(&infcx)
                                    .expect("layout should be computable for concrete type")
                                    .size(&data_layout)
                                    .bytes()
                            }
                        })
                        .sum();

                    let grouped = (size, (i, variant));

                    update_if(&mut smallest_variant, grouped, |a, b| b.0 <= a.0);
                    update_if(&mut largest_variant, grouped, |a, b| b.0 >= a.0);
                });
            }

            if let (Some(smallest), Some(largest)) = (smallest_variant, largest_variant) {
                let difference = largest.0 - smallest.0;

                if difference > self.maximum_size_difference_allowed {
                    let (i, variant) = largest.1;

                    span_lint_and_then(cx,
                                       LARGE_ENUM_VARIANT,
                                       def.variants[i].span,
                                       "large size difference between variants",
                                       |db| {
                        if variant.fields.len() == 1 {
                            let span = match def.variants[i].node.data {
                                VariantData::Struct(ref fields, _) |
                                VariantData::Tuple(ref fields, _) => fields[0].ty.span,
                                VariantData::Unit(_) => unreachable!(),
                            };
                            if let Some(snip) = snippet_opt(cx, span) {
                                db.span_suggestion(span,
                                                   "consider boxing the large fields to reduce the total size of the \
                                                    enum",
                                                   format!("Box<{}>", snip));
                                return;
                            }
                        }
                        db.span_help(def.variants[i].span,
                                     "consider boxing the large fields to reduce the total size of the enum");
                    });
                }
            }

        }
    }
}

fn update_if<T, F>(old: &mut Option<T>, new: T, f: F)
    where F: Fn(&T, &T) -> bool
{
    if let Some(ref mut val) = *old {
        if f(val, &new) {
            *val = new;
        }
    } else {
        *old = Some(new);
    }
}
<|endoftext|>


<commit_before>use crate::utils::{eager_or_lazy, usage};
use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;

use super::UNNECESSARY_LAZY_EVALUATIONS;

/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
/// replaced with `<fn>(return value of simple closure)`
pub(super) fn lint<'tcx>(
    cx: &LateContext<'tcx>,
    expr: &'tcx hir::Expr<'_>,
    args: &'tcx [hir::Expr<'_>],
    simplify_using: &str,
) {
    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));

    if is_option || is_result {
        if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
            let body = cx.tcx.hir().body(eid);
            let body_expr = &body.value;

            if usage::BindingUsageFinder::are_params_used(cx, body) {
                return;
            }

            if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
                let msg = if is_option {
                    "unnecessary closure used to substitute value for `Option::None`"
                } else {
                    "unnecessary closure used to substitute value for `Result::Err`"
                };
                let applicability = if body
                    .params
                    .iter()
                    .all(|param| matches!(param.pat.kind, hir::PatKind::Wild))
                {
                    Applicability::MachineApplicable
                } else {
                    // replacing the lambda may break type inference
                    Applicability::MaybeIncorrect
                };

                span_lint_and_sugg(
                    cx,
                    UNNECESSARY_LAZY_EVALUATIONS,
                    expr.span,
                    msg,
                    &format!("Use `{}` instead", simplify_using),
                    format!(
                        "{0}.{1}({2})",
                        snippet(cx, args[0].span, ".."),
                        simplify_using,
                        snippet(cx, body_expr.span, ".."),
                    ),
                    applicability,
                );
            }
        }
    }
}
<commit_msg>Include bindings as machine applicable<commit_after>use crate::utils::{eager_or_lazy, usage};
use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;

use super::UNNECESSARY_LAZY_EVALUATIONS;

/// lint use of `<fn>_else(simple closure)` for `Option`s and `Result`s that can be
/// replaced with `<fn>(return value of simple closure)`
pub(super) fn lint<'tcx>(
    cx: &LateContext<'tcx>,
    expr: &'tcx hir::Expr<'_>,
    args: &'tcx [hir::Expr<'_>],
    simplify_using: &str,
) {
    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(option_type));
    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym!(result_type));

    if is_option || is_result {
        if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
            let body = cx.tcx.hir().body(eid);
            let body_expr = &body.value;

            if usage::BindingUsageFinder::are_params_used(cx, body) {
                return;
            }

            if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
                let msg = if is_option {
                    "unnecessary closure used to substitute value for `Option::None`"
                } else {
                    "unnecessary closure used to substitute value for `Result::Err`"
                };
                let applicability = if body
                    .params
                    .iter()
                    // bindings are checked to be unused above
                    .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild))
                {
                    Applicability::MachineApplicable
                } else {
                    // replacing the lambda may break type inference
                    Applicability::MaybeIncorrect
                };

                span_lint_and_sugg(
                    cx,
                    UNNECESSARY_LAZY_EVALUATIONS,
                    expr.span,
                    msg,
                    &format!("Use `{}` instead", simplify_using),
                    format!(
                        "{0}.{1}({2})",
                        snippet(cx, args[0].span, ".."),
                        simplify_using,
                        snippet(cx, body_expr.span, ".."),
                    ),
                    applicability,
                );
            }
        }
    }
}
<|endoftext|>