<commit_before>// Copyright 2015 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.

#![cfg(not(test))]

extern crate log;
extern crate rustfmt_nightly as rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{run, Input, Summary};
use rustfmt::file_lines::FileLines;
use rustfmt::config::{Config, WriteMode, get_toml_path};

use std::{env, error};
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use getopts::{Matches, Options};

type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        config_path: Option<PathBuf>,
        minimal_config_path: Option<String>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Output default config to a file
    ConfigOutputDefault { path: String },
    /// No file specified, read from stdin
    Stdin {
        input: String,
        config_path: Option<PathBuf>,
    },
}

/// Parsed command line options.
#[derive(Clone, Debug, Default)]
struct CliOptions {
    skip_children: bool,
    verbose: bool,
    write_mode: Option<WriteMode>,
    file_lines: FileLines, // Default is all lines in all files.
}

impl CliOptions {
    fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
        let mut options = CliOptions::default();
        options.skip_children = matches.opt_present("skip-children");
        options.verbose = matches.opt_present("verbose");

        if let Some(ref write_mode) = matches.opt_str("write-mode") {
            if let Ok(write_mode) = WriteMode::from_str(write_mode) {
                options.write_mode = Some(write_mode);
            } else {
                return Err(FmtError::from(
                    format!("Invalid write-mode: {}", write_mode),
                ));
            }
        } else {
            println!("Warning: the default write-mode for Rustfmt will soon change to overwrite - this will not leave backups of changed files.");
        }

        if let Some(ref file_lines) = matches.opt_str("file-lines") {
            options.file_lines = file_lines.parse()?;
        }

        Ok(options)
    }

    fn apply_to(self, config: &mut Config) {
        config.set().skip_children(self.skip_children);
        config.set().verbose(self.verbose);
        config.set().file_lines(self.file_lines);
        if let Some(write_mode) = self.write_mode {
            config.set().write_mode(write_mode);
        }
    }
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(
    config_path: Option<PathBuf>,
    input_file: &Path,
) -> FmtResult<(Config, Option<PathBuf>)> {

    if let Some(config_file) = config_path {
        let toml = Config::from_toml_path(config_file.as_ref())?;
        return Ok((toml, Some(config_file)));
    }
    Config::from_resolved_toml_path(input_file).map_err(|e| FmtError::from(e))
}

fn make_opts() -> Options {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "print verbose output");
    opts.optopt(
        "",
        "write-mode",
        "mode to write in (not usable when piping from stdin)",
        "[replace|overwrite|display|diff|coverage|checkstyle]",
    );
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag(
        "",
        "config-help",
        "show details of rustfmt configuration options",
    );
    opts.optopt(
        "",
        "dump-default-config",
        "Dumps the default configuration to a file and exits.",
        "PATH",
    );
    opts.optopt(
        "",
        "dump-minimal-config",
        "Dumps configuration options that were checked during formatting to a file.",
        "PATH",
    );
    opts.optopt(
        "",
        "config-path",
        "Recursively searches the given path for the rustfmt.toml config file. If not \
         found reverts to the input file path",
        "[Path for the configuration file]",
    );
    opts.optopt(
        "",
        "file-lines",
        "Format specified line ranges. See README for more detail on the JSON format.",
        "JSON",
    );

    opts
}

fn execute(opts: &Options) -> FmtResult<Summary> {
    let matches = opts.parse(env::args().skip(1))?;

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage(opts, "");
            Summary::print_exit_codes();
            Ok(Summary::new())
        }
        Operation::Version => {
            print_version();
            Ok(Summary::new())
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            Ok(Summary::new())
        }
        Operation::ConfigOutputDefault { path } => {
            let mut file = File::create(path)?;
            let toml = Config::default().all_options().to_toml()?;
            file.write_all(toml.as_bytes())?;
            Ok(Summary::new())
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) =
                match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;

            // write_mode is always Plain for Stdin.
            config.set().write_mode(WriteMode::Plain);

            // parse file_lines
            if let Some(ref file_lines) = matches.opt_str("file-lines") {
                config.set().file_lines(file_lines.parse()?);
                for f in config.file_lines().files() {
                    if f != "stdin" {
                        println!("Warning: Extra file listed in file_lines option '{}'", f);
                    }
                }
            }

            Ok(run(Input::Text(input), &config))
        }
        Operation::Format {
            files,
            config_path,
            minimal_config_path,
        } => {
            let options = CliOptions::from_matches(&matches)?;

            for f in options.file_lines.files() {
                if !files.contains(&PathBuf::from(f)) {
                    println!("Warning: Extra file listed in file_lines option '{}'", f);
                }
            }

            let mut config = Config::default();
            // Load the config path file if provided
            if let Some(config_file) = config_path.as_ref() {
                config = Config::from_toml_path(config_file.as_ref())?;
            };

            if options.verbose {
                if let Some(path) = config_path.as_ref() {
                    println!("Using rustfmt config file {}", path.display());
                }
            }

            let mut error_summary = Summary::new();
            for file in files {
                if !file.exists() {
                    println!("Error: file `{}` does not exist", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else if file.is_dir() {
                    println!("Error: `{}` is a directory", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else {
                    // Check the file directory if the config-path could not be read or not provided
                    if config_path.is_none() {
                        let (config_tmp, path_tmp) =
                            Config::from_resolved_toml_path(file.parent().unwrap())?;
                        if options.verbose {
                            if let Some(path) = path_tmp.as_ref() {
                                println!(
                                    "Using rustfmt config file {} for {}",
                                    path.display(),
                                    file.display()
                                );
                            }
                        }
                        config = config_tmp;
                    }

                    options.clone().apply_to(&mut config);
                    error_summary.add(run(Input::File(file), &config));
                }
            }

            // If we were given a path via dump-minimal-config, output any options
            // that were used during formatting as TOML.
            if let Some(path) = minimal_config_path {
                let mut file = File::create(path)?;
                let toml = config.used_options().to_toml()?;
                file.write_all(toml.as_bytes())?;
            }

            Ok(error_summary)
        }
    }
}

fn main() {
    let _ = env_logger::init();

    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok(summary) => {
            if summary.has_operational_errors() {
                1
            } else if summary.has_parsing_errors() {
                2
            } else if summary.has_formatting_errors() {
                3
            } else if summary.has_diff {
                // should only happen in diff mode
                4
            } else {
                assert!(summary.has_no_errors());
                0
            }
        }
        Err(e) => {
            print_usage(&opts, &e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!(
        "{}\n\nusage: {} [options] <file>...",
        reason,
        env::args_os().next().unwrap().to_string_lossy()
    );
    println!("{}", opts.usage(&reason));
}

fn print_version() {
    println!(
        "{}-nightly{}",
        env!("CARGO_PKG_VERSION"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    )
}

fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
    if matches.opt_present("h") {
        return Ok(Operation::Help);
    }

    if matches.opt_present("config-help") {
        return Ok(Operation::ConfigHelp);
    }

    if let Some(path) = matches.opt_str("dump-default-config") {
        return Ok(Operation::ConfigOutputDefault { path });
    }

    if matches.opt_present("version") {
        return Ok(Operation::Version);
    }

    let config_path_not_found = |path: &str| -> FmtResult<Operation> {
        Err(FmtError::from(format!(
            "Error: unable to find a config file for the given path: `{}`",
            path
        )))
    };

    // Read the config_path and convert to parent dir if a file is provided.
    // If a config file cannot be found from the given path, return error.
    let config_path: Option<PathBuf> = match matches.opt_str("config-path").map(PathBuf::from) {
        Some(ref path) if !path.exists() => return config_path_not_found(path.to_str().unwrap()),
        Some(ref path) if path.is_dir() => {
            let config_file_path = get_toml_path(path)?;
            if config_file_path.is_some() {
                config_file_path
            } else {
                return config_path_not_found(path.to_str().unwrap());
            }
        }
        path @ _ => path,
    };

    // If no path is given, we won't output a minimal config.
    let minimal_config_path = matches.opt_str("dump-minimal-config");

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {
        let mut buffer = String::new();
        io::stdin().read_to_string(&mut buffer)?;

        return Ok(Operation::Stdin {
            input: buffer,
            config_path: config_path,
        });
    }

    let files: Vec<_> = matches
        .free
        .iter()
        .map(|s| {
            let p = PathBuf::from(s);
            // we will do comparison later, so here tries to canonicalize first
            // to get the expected behavior.
            p.canonicalize().unwrap_or(p)
        })
        .collect();

    Ok(Operation::Format {
        files: files,
        config_path: config_path,
        minimal_config_path: minimal_config_path,
    })
}
<commit_msg>Fix a typo<commit_after>// Copyright 2015 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.

#![cfg(not(test))]

extern crate log;
extern crate rustfmt_nightly as rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{run, Input, Summary};
use rustfmt::file_lines::FileLines;
use rustfmt::config::{Config, WriteMode, get_toml_path};

use std::{env, error};
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;

use getopts::{Matches, Options};

type FmtError = Box<error::Error + Send + Sync>;
type FmtResult<T> = std::result::Result<T, FmtError>;

/// Rustfmt operations.
enum Operation {
    /// Format files and their child modules.
    Format {
        files: Vec<PathBuf>,
        config_path: Option<PathBuf>,
        minimal_config_path: Option<String>,
    },
    /// Print the help message.
    Help,
    // Print version information
    Version,
    /// Print detailed configuration help.
    ConfigHelp,
    /// Output default config to a file
    ConfigOutputDefault { path: String },
    /// No file specified, read from stdin
    Stdin {
        input: String,
        config_path: Option<PathBuf>,
    },
}

/// Parsed command line options.
#[derive(Clone, Debug, Default)]
struct CliOptions {
    skip_children: bool,
    verbose: bool,
    write_mode: Option<WriteMode>,
    file_lines: FileLines, // Default is all lines in all files.
}

impl CliOptions {
    fn from_matches(matches: &Matches) -> FmtResult<CliOptions> {
        let mut options = CliOptions::default();
        options.skip_children = matches.opt_present("skip-children");
        options.verbose = matches.opt_present("verbose");

        if let Some(ref write_mode) = matches.opt_str("write-mode") {
            if let Ok(write_mode) = WriteMode::from_str(write_mode) {
                options.write_mode = Some(write_mode);
            } else {
                return Err(FmtError::from(
                    format!("Invalid write-mode: {}", write_mode),
                ));
            }
        } else {
            println!(
                "Warning: the default write-mode for Rustfmt will soon change to overwrite \
                 - this will not leave backups of changed files."
            );
        }

        if let Some(ref file_lines) = matches.opt_str("file-lines") {
            options.file_lines = file_lines.parse()?;
        }

        Ok(options)
    }

    fn apply_to(self, config: &mut Config) {
        config.set().skip_children(self.skip_children);
        config.set().verbose(self.verbose);
        config.set().file_lines(self.file_lines);
        if let Some(write_mode) = self.write_mode {
            config.set().write_mode(write_mode);
        }
    }
}

/// read the given config file path recursively if present else read the project file path
fn match_cli_path_or_file(
    config_path: Option<PathBuf>,
    input_file: &Path,
) -> FmtResult<(Config, Option<PathBuf>)> {

    if let Some(config_file) = config_path {
        let toml = Config::from_toml_path(config_file.as_ref())?;
        return Ok((toml, Some(config_file)));
    }
    Config::from_resolved_toml_path(input_file).map_err(|e| FmtError::from(e))
}

fn make_opts() -> Options {
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("V", "version", "show version information");
    opts.optflag("v", "verbose", "print verbose output");
    opts.optopt(
        "",
        "write-mode",
        "mode to write in (not usable when piping from stdin)",
        "[replace|overwrite|display|diff|coverage|checkstyle]",
    );
    opts.optflag("", "skip-children", "don't reformat child modules");

    opts.optflag(
        "",
        "config-help",
        "show details of rustfmt configuration options",
    );
    opts.optopt(
        "",
        "dump-default-config",
        "Dumps the default configuration to a file and exits.",
        "PATH",
    );
    opts.optopt(
        "",
        "dump-minimal-config",
        "Dumps configuration options that were checked during formatting to a file.",
        "PATH",
    );
    opts.optopt(
        "",
        "config-path",
        "Recursively searches the given path for the rustfmt.toml config file. If not \
         found reverts to the input file path",
        "[Path for the configuration file]",
    );
    opts.optopt(
        "",
        "file-lines",
        "Format specified line ranges. See README for more detail on the JSON format.",
        "JSON",
    );

    opts
}

fn execute(opts: &Options) -> FmtResult<Summary> {
    let matches = opts.parse(env::args().skip(1))?;

    match determine_operation(&matches)? {
        Operation::Help => {
            print_usage(opts, "");
            Summary::print_exit_codes();
            Ok(Summary::new())
        }
        Operation::Version => {
            print_version();
            Ok(Summary::new())
        }
        Operation::ConfigHelp => {
            Config::print_docs();
            Ok(Summary::new())
        }
        Operation::ConfigOutputDefault { path } => {
            let mut file = File::create(path)?;
            let toml = Config::default().all_options().to_toml()?;
            file.write_all(toml.as_bytes())?;
            Ok(Summary::new())
        }
        Operation::Stdin { input, config_path } => {
            // try to read config from local directory
            let (mut config, _) =
                match_cli_path_or_file(config_path, &env::current_dir().unwrap())?;

            // write_mode is always Plain for Stdin.
            config.set().write_mode(WriteMode::Plain);

            // parse file_lines
            if let Some(ref file_lines) = matches.opt_str("file-lines") {
                config.set().file_lines(file_lines.parse()?);
                for f in config.file_lines().files() {
                    if f != "stdin" {
                        println!("Warning: Extra file listed in file_lines option '{}'", f);
                    }
                }
            }

            Ok(run(Input::Text(input), &config))
        }
        Operation::Format {
            files,
            config_path,
            minimal_config_path,
        } => {
            let options = CliOptions::from_matches(&matches)?;

            for f in options.file_lines.files() {
                if !files.contains(&PathBuf::from(f)) {
                    println!("Warning: Extra file listed in file_lines option '{}'", f);
                }
            }

            let mut config = Config::default();
            // Load the config path file if provided
            if let Some(config_file) = config_path.as_ref() {
                config = Config::from_toml_path(config_file.as_ref())?;
            };

            if options.verbose {
                if let Some(path) = config_path.as_ref() {
                    println!("Using rustfmt config file {}", path.display());
                }
            }

            let mut error_summary = Summary::new();
            for file in files {
                if !file.exists() {
                    println!("Error: file `{}` does not exist", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else if file.is_dir() {
                    println!("Error: `{}` is a directory", file.to_str().unwrap());
                    error_summary.add_operational_error();
                } else {
                    // Check the file directory if the config-path could not be read or not provided
                    if config_path.is_none() {
                        let (config_tmp, path_tmp) =
                            Config::from_resolved_toml_path(file.parent().unwrap())?;
                        if options.verbose {
                            if let Some(path) = path_tmp.as_ref() {
                                println!(
                                    "Using rustfmt config file {} for {}",
                                    path.display(),
                                    file.display()
                                );
                            }
                        }
                        config = config_tmp;
                    }

                    options.clone().apply_to(&mut config);
                    error_summary.add(run(Input::File(file), &config));
                }
            }

            // If we were given a path via dump-minimal-config, output any options
            // that were used during formatting as TOML.
            if let Some(path) = minimal_config_path {
                let mut file = File::create(path)?;
                let toml = config.used_options().to_toml()?;
                file.write_all(toml.as_bytes())?;
            }

            Ok(error_summary)
        }
    }
}

fn main() {
    let _ = env_logger::init();

    let opts = make_opts();

    let exit_code = match execute(&opts) {
        Ok(summary) => {
            if summary.has_operational_errors() {
                1
            } else if summary.has_parsing_errors() {
                2
            } else if summary.has_formatting_errors() {
                3
            } else if summary.has_diff {
                // should only happen in diff mode
                4
            } else {
                assert!(summary.has_no_errors());
                0
            }
        }
        Err(e) => {
            print_usage(&opts, &e.to_string());
            1
        }
    };
    // Make sure standard output is flushed before we exit.
    std::io::stdout().flush().unwrap();

    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!(
        "{}\n\nusage: {} [options] <file>...",
        reason,
        env::args_os().next().unwrap().to_string_lossy()
    );
    println!("{}", opts.usage(&reason));
}

fn print_version() {
    println!(
        "{}-nightly{}",
        env!("CARGO_PKG_VERSION"),
        include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
    )
}

fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
    if matches.opt_present("h") {
        return Ok(Operation::Help);
    }

    if matches.opt_present("config-help") {
        return Ok(Operation::ConfigHelp);
    }

    if let Some(path) = matches.opt_str("dump-default-config") {
        return Ok(Operation::ConfigOutputDefault { path });
    }

    if matches.opt_present("version") {
        return Ok(Operation::Version);
    }

    let config_path_not_found = |path: &str| -> FmtResult<Operation> {
        Err(FmtError::from(format!(
            "Error: unable to find a config file for the given path: `{}`",
            path
        )))
    };

    // Read the config_path and convert to parent dir if a file is provided.
    // If a config file cannot be found from the given path, return error.
    let config_path: Option<PathBuf> = match matches.opt_str("config-path").map(PathBuf::from) {
        Some(ref path) if !path.exists() => return config_path_not_found(path.to_str().unwrap()),
        Some(ref path) if path.is_dir() => {
            let config_file_path = get_toml_path(path)?;
            if config_file_path.is_some() {
                config_file_path
            } else {
                return config_path_not_found(path.to_str().unwrap());
            }
        }
        path @ _ => path,
    };

    // If no path is given, we won't output a minimal config.
    let minimal_config_path = matches.opt_str("dump-minimal-config");

    // if no file argument is supplied, read from stdin
    if matches.free.is_empty() {
        let mut buffer = String::new();
        io::stdin().read_to_string(&mut buffer)?;

        return Ok(Operation::Stdin {
            input: buffer,
            config_path: config_path,
        });
    }

    let files: Vec<_> = matches
        .free
        .iter()
        .map(|s| {
            let p = PathBuf::from(s);
            // we will do comparison later, so here tries to canonicalize first
            // to get the expected behavior.
            p.canonicalize().unwrap_or(p)
        })
        .collect();

    Ok(Operation::Format {
        files: files,
        config_path: config_path,
        minimal_config_path: minimal_config_path,
    })
}
<|endoftext|>


<commit_before>use cssparser::Parser;

use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, ViewParams};
use crate::error::*;
use crate::filter::Filter;
use crate::filters::{
    gaussian_blur::GaussianBlur, FilterResolveError, FilterSpec, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

/// CSS Filter functions from the Filter Effects Module Level 1
///
/// https://www.w3.org/TR/filter-effects/#filter-functions
#[derive(Debug, Clone, PartialEq)]
pub enum FilterFunction {
    Blur(Blur),
}

/// Parameters for the `blur()` filter function
///
/// https://www.w3.org/TR/filter-effects/#funcdef-filter-blur
#[derive(Debug, Clone, PartialEq)]
pub struct Blur {
    std_deviation: Option<Length<Both>>,
}

fn parse_function<'i, F>(
    parser: &mut Parser<'i, '_>,
    name: &str,
    f: F,
) -> Result<FilterFunction, ParseError<'i>>
where
    F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<FilterFunction, ParseError<'i>>,
{
    parser.expect_function_matching(name)?;
    parser.parse_nested_block(f)
}

// This function doesn't fail, but returns a Result like the other parsers, so tell Clippy
// about that.
#[allow(clippy::unnecessary_wraps)]
fn parse_blur<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
    let length = if let Ok(length) = parser.try_parse(|p| Length::parse(p)) {
        Some(length)
    } else {
        None
    };

    Ok(FilterFunction::Blur(Blur {
        std_deviation: length,
    }))
}

impl Blur {
    fn to_filter_spec(&self, values: &ComputedValues, params: &ViewParams) -> FilterSpec {
        // The 0.0 default is from the spec
        let std_dev = self
            .std_deviation
            .map(|l| l.normalize(values, params))
            .unwrap_or(0.0);

        let params = NormalizeParams::new(values, params);

        let user_space_filter = Filter::default().to_user_space(&params);

        let gaussian_blur = ResolvedPrimitive {
            primitive: Primitive::default(),
            params: PrimitiveParams::GaussianBlur(GaussianBlur {
                std_deviation: (std_dev, std_dev),
                ..GaussianBlur::default()
            }),
        }
        .into_user_space(&params);

        FilterSpec {
            user_space_filter,
            primitives: vec![gaussian_blur],
        }
    }
}

impl Parse for FilterFunction {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let loc = parser.current_source_location();

        if let Ok(func) = parser.try_parse(|p| parse_function(p, "blur", parse_blur)) {
            return Ok(func);
        }

        return Err(loc.new_custom_error(ValueErrorKind::parse_error("expected filter function")));
    }
}

impl FilterFunction {
    // If this function starts actually returning an Err, remove this Clippy exception:
    #[allow(clippy::unnecessary_wraps)]
    pub fn to_filter_spec(
        &self,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> Result<FilterSpec, FilterResolveError> {
        // This is the default for primitive_units
        let params = draw_ctx.push_coord_units(CoordUnits::UserSpaceOnUse);

        match self {
            FilterFunction::Blur(v) => Ok(v.to_filter_spec(values, &params)),
        }
    }
}

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

    #[test]
    fn parses_blur() {
        assert_eq!(
            FilterFunction::parse_str("blur()").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: None
            })
        );

        assert_eq!(
            FilterFunction::parse_str("blur(5px)").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: Some(Length::new(5.0, LengthUnit::Px))
            })
        );
    }

    #[test]
    fn invalid_blur_yields_error() {
        assert!(FilterFunction::parse_str("blur(foo)").is_err());
        assert!(FilterFunction::parse_str("blur(42 43)").is_err());
    }
}
<commit_msg>Blur.to_filter_spec - take a NormalizeParams<commit_after>use cssparser::Parser;

use crate::coord_units::CoordUnits;
use crate::drawing_ctx::DrawingCtx;
use crate::error::*;
use crate::filter::Filter;
use crate::filters::{
    gaussian_blur::GaussianBlur, FilterResolveError, FilterSpec, Primitive, PrimitiveParams,
    ResolvedPrimitive,
};
use crate::length::*;
use crate::parsers::Parse;
use crate::properties::ComputedValues;

/// CSS Filter functions from the Filter Effects Module Level 1
///
/// https://www.w3.org/TR/filter-effects/#filter-functions
#[derive(Debug, Clone, PartialEq)]
pub enum FilterFunction {
    Blur(Blur),
}

/// Parameters for the `blur()` filter function
///
/// https://www.w3.org/TR/filter-effects/#funcdef-filter-blur
#[derive(Debug, Clone, PartialEq)]
pub struct Blur {
    std_deviation: Option<Length<Both>>,
}

fn parse_function<'i, F>(
    parser: &mut Parser<'i, '_>,
    name: &str,
    f: F,
) -> Result<FilterFunction, ParseError<'i>>
where
    F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<FilterFunction, ParseError<'i>>,
{
    parser.expect_function_matching(name)?;
    parser.parse_nested_block(f)
}

// This function doesn't fail, but returns a Result like the other parsers, so tell Clippy
// about that.
#[allow(clippy::unnecessary_wraps)]
fn parse_blur<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseError<'i>> {
    let length = if let Ok(length) = parser.try_parse(|p| Length::parse(p)) {
        Some(length)
    } else {
        None
    };

    Ok(FilterFunction::Blur(Blur {
        std_deviation: length,
    }))
}

impl Blur {
    fn to_filter_spec(&self, params: &NormalizeParams) -> FilterSpec {
        // The 0.0 default is from the spec
        let std_dev = self.std_deviation.map(|l| l.to_user(params)).unwrap_or(0.0);

        let user_space_filter = Filter::default().to_user_space(params);

        let gaussian_blur = ResolvedPrimitive {
            primitive: Primitive::default(),
            params: PrimitiveParams::GaussianBlur(GaussianBlur {
                std_deviation: (std_dev, std_dev),
                ..GaussianBlur::default()
            }),
        }
        .into_user_space(params);

        FilterSpec {
            user_space_filter,
            primitives: vec![gaussian_blur],
        }
    }
}

impl Parse for FilterFunction {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let loc = parser.current_source_location();

        if let Ok(func) = parser.try_parse(|p| parse_function(p, "blur", parse_blur)) {
            return Ok(func);
        }

        return Err(loc.new_custom_error(ValueErrorKind::parse_error("expected filter function")));
    }
}

impl FilterFunction {
    // If this function starts actually returning an Err, remove this Clippy exception:
    #[allow(clippy::unnecessary_wraps)]
    pub fn to_filter_spec(
        &self,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> Result<FilterSpec, FilterResolveError> {
        // userSpaceonUse is the default for primitive_units
        let view_params = draw_ctx.push_coord_units(CoordUnits::UserSpaceOnUse);
        let params = NormalizeParams::new(values, &view_params);

        match self {
            FilterFunction::Blur(v) => Ok(v.to_filter_spec(&params)),
        }
    }
}

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

    #[test]
    fn parses_blur() {
        assert_eq!(
            FilterFunction::parse_str("blur()").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: None
            })
        );

        assert_eq!(
            FilterFunction::parse_str("blur(5px)").unwrap(),
            FilterFunction::Blur(Blur {
                std_deviation: Some(Length::new(5.0, LengthUnit::Px))
            })
        );
    }

    #[test]
    fn invalid_blur_yields_error() {
        assert!(FilterFunction::parse_str("blur(foo)").is_err());
        assert!(FilterFunction::parse_str("blur(42 43)").is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 The 6502-rs Developers
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the names of the copyright holders nor the names of any
//    contributors may be used to endorse or promote products derived from this
//    software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// Abbreviations
//
// General
//
//        M | `Memory location`
//
// Registers
//
//        A | accumulator
//        X | general purpose register
//        Y | general purpose register
//        F | processor status flags, collectively
// NV-BDIZC | processor status flags, individually
//        S | stack pointer
//       PC | program counter
//

#[deriving(Show, PartialEq, Eq)]
pub enum Instruction
      //                                  i/o vars should be listed as follows:
      //                                  NV BDIZC A X Y S PC M
      //
      //                                | outputs               | inputs
{
  ADC(i8) // ADd with Carry................ | NV ...ZC A            = A + M + C

, AND // logical AND (bitwise)......... | N. ...Z. A            = A && M
, ASL // Arithmetic Shift Left......... | N. ...ZC A            = M << 1
, BCC // Branch if Carry Clear......... | .. .....         PC   = !C
, BCS // Branch if Carry Set........... | .. .....         PC   = C
, BEQ // Branch if Equal (to zero?).... | .. .....         PC   = Z
, BIT // BIT test...................... | NV ...Z.              = A & M
, BMI // Branch if Minus............... | .. .....         PC   = N
, BNE // Branch if Not Equal........... | .. .....         PC   = !Z
, BPL // Branch if Positive............ | .. .....         PC   = Z
, BRK // BReaK......................... | .. B....       S PC   =
, BVC // Branch if oVerflow Clear...... | .. .....         PC   = !V
, BVS // Branch if oVerflow Set........ | .. .....         PC   = V
, CLC // CLear Carry flag.............. | .. ....C              = 0
, CLD // Clear Decimal Mode............ | .. .D...              = 0
, CLI // Clear Interrupt Disable....... | .. ..I..              = 0
, CLV // Clear oVerflow flag........... | .V .....              = 0
, CMP // Compare....................... | N. ...ZC              = A - M
, CPX // Compare X register............ | N. ...ZC              = X - M
, CPY // Compare Y register............ | N. ...ZC              = Y - M
, DEC // DECrement memory.............. | N. ...Z.            M = M - 1
, DEX // DEcrement X register.......... | N. ...Z.   X          = X - 1
, DEY // DEcrement Y register.......... | N. ...Z.     Y        = Y - 1
, EOR // Exclusive OR (bitwise)........ | N. ...Z. A            = A ^ M
, INC // INCrement memory.............. | N. ...Z.            M = M + 1
, INX // INcrement X register.......... | N. ...Z.   X          = X + 1
, INY // INcrement Y register.......... | N. ...Z.     Y        = Y + 1
, JMP // JuMP.......................... | .. .....       S PC   =
, JSR // Jump to SubRoutine............ | .. .....       S PC   =
, LDA // LoaD Accumulator.............. | N. ...Z. A            = M
, LDX // LoaD X register............... | N. ...Z.   X          = M
, LDY // LoaD Y register............... | N. ...Z.     Y        = M
, LSR // Logical Shift Right........... | N. ...ZC A            = A/2
      //                               or N. ...ZC            M = M/2
, NOP // No OPeration.................. | .. .....              =
, ORA // inclusive OR (bitwise)........ | N. ...Z. A            = A | M
, PHA // PusH Accumulator.............. | .. .....       S    M = A
, PHP // PusH Processor status......... | .. .....       S    M = F
, PLA // PuLl Accumulator.............. | N. ...Z. A     S      = M (stack)
, PLP // PuLl Processor status......... | NV BDIZC       S      = M (stack)
, ROL // ROtate Left................... | N. ...ZC A            = C A rotated
      //                               or N. ...ZC            M = C M rotated
, ROR // ROtate Right.................. | N. ...ZC A            = C A rotated
      //                               or N. ...ZC            M = C M rotated
, RTI // ReTurn from Interrupt......... | NV BDIZC         PC   = M (stack)
, RTS // ReTurn from Subroutine........ | .. .....         PC   = M (stack)
, SBC // SuBtract with Carry........... | NV ...ZC A            = A-M-(1-C)
, SEC // SEt Carry flag................ | .. ....C              = 1
, SED // SEt Decimal flag.............. | .. .D...              = 1
, SEI // SEt Interrupt disable......... | .. ..I..              = 1
, STA // STore Accumulator............. | .. .....            M = A
, STX // STore X register.............. | .. .....            M = X
, STY // STore Y register.............. | .. .....            M = Y
, TAX // Transfer Accumulator to X..... | N. ...Z.   X          = A
, TAY // Transfer Accumulator to Y..... | N. ...Z.     Y        = A
, TSX // Transfer Stack pointer to X... | N. ...Z.   X          = S
, TXA // Transfer X to Accumulator..... | N. ...Z. A            = X
, TXS // Transfer X to Stack pointer... | .. .....       S      = X
, TYA // Transfer Y to Accumulator..... | N. ...Z. A            = Y
}

<commit_msg>Formatting<commit_after>// Copyright (C) 2014 The 6502-rs Developers
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the names of the copyright holders nor the names of any
//    contributors may be used to endorse or promote products derived from this
//    software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// Abbreviations
//
// General
//
//        M | `Memory location`
//
// Registers
//
//        A | accumulator
//        X | general purpose register
//        Y | general purpose register
//        F | processor status flags, collectively
// NV-BDIZC | processor status flags, individually
//        S | stack pointer
//       PC | program counter
//

#[deriving(Show, PartialEq, Eq)]
pub enum Instruction
      //                                  i/o vars should be listed as follows:
      //                                  NV BDIZC A X Y S PC M
      //
      //                                | outputs               | inputs
{
  ADC(i8) // ADd with Carry............ | NV ...ZC A            = A + M + C

, AND // logical AND (bitwise)......... | N. ...Z. A            = A && M
, ASL // Arithmetic Shift Left......... | N. ...ZC A            = M << 1
, BCC // Branch if Carry Clear......... | .. .....         PC   = !C
, BCS // Branch if Carry Set........... | .. .....         PC   = C
, BEQ // Branch if Equal (to zero?).... | .. .....         PC   = Z
, BIT // BIT test...................... | NV ...Z.              = A & M
, BMI // Branch if Minus............... | .. .....         PC   = N
, BNE // Branch if Not Equal........... | .. .....         PC   = !Z
, BPL // Branch if Positive............ | .. .....         PC   = Z
, BRK // BReaK......................... | .. B....       S PC   =
, BVC // Branch if oVerflow Clear...... | .. .....         PC   = !V
, BVS // Branch if oVerflow Set........ | .. .....         PC   = V
, CLC // CLear Carry flag.............. | .. ....C              = 0
, CLD // Clear Decimal Mode............ | .. .D...              = 0
, CLI // Clear Interrupt Disable....... | .. ..I..              = 0
, CLV // Clear oVerflow flag........... | .V .....              = 0
, CMP // Compare....................... | N. ...ZC              = A - M
, CPX // Compare X register............ | N. ...ZC              = X - M
, CPY // Compare Y register............ | N. ...ZC              = Y - M
, DEC // DECrement memory.............. | N. ...Z.            M = M - 1
, DEX // DEcrement X register.......... | N. ...Z.   X          = X - 1
, DEY // DEcrement Y register.......... | N. ...Z.     Y        = Y - 1
, EOR // Exclusive OR (bitwise)........ | N. ...Z. A            = A ^ M
, INC // INCrement memory.............. | N. ...Z.            M = M + 1
, INX // INcrement X register.......... | N. ...Z.   X          = X + 1
, INY // INcrement Y register.......... | N. ...Z.     Y        = Y + 1
, JMP // JuMP.......................... | .. .....       S PC   =
, JSR // Jump to SubRoutine............ | .. .....       S PC   =
, LDA // LoaD Accumulator.............. | N. ...Z. A            = M
, LDX // LoaD X register............... | N. ...Z.   X          = M
, LDY // LoaD Y register............... | N. ...Z.     Y        = M
, LSR // Logical Shift Right........... | N. ...ZC A            = A/2
      //                               or N. ...ZC            M = M/2
, NOP // No OPeration.................. | .. .....              =
, ORA // inclusive OR (bitwise)........ | N. ...Z. A            = A | M
, PHA // PusH Accumulator.............. | .. .....       S    M = A
, PHP // PusH Processor status......... | .. .....       S    M = F
, PLA // PuLl Accumulator.............. | N. ...Z. A     S      = M (stack)
, PLP // PuLl Processor status......... | NV BDIZC       S      = M (stack)
, ROL // ROtate Left................... | N. ...ZC A            = C A rotated
      //                               or N. ...ZC            M = C M rotated
, ROR // ROtate Right.................. | N. ...ZC A            = C A rotated
      //                               or N. ...ZC            M = C M rotated
, RTI // ReTurn from Interrupt......... | NV BDIZC         PC   = M (stack)
, RTS // ReTurn from Subroutine........ | .. .....         PC   = M (stack)
, SBC // SuBtract with Carry........... | NV ...ZC A            = A-M-(1-C)
, SEC // SEt Carry flag................ | .. ....C              = 1
, SED // SEt Decimal flag.............. | .. .D...              = 1
, SEI // SEt Interrupt disable......... | .. ..I..              = 1
, STA // STore Accumulator............. | .. .....            M = A
, STX // STore X register.............. | .. .....            M = X
, STY // STore Y register.............. | .. .....            M = Y
, TAX // Transfer Accumulator to X..... | N. ...Z.   X          = A
, TAY // Transfer Accumulator to Y..... | N. ...Z.     Y        = A
, TSX // Transfer Stack pointer to X... | N. ...Z.   X          = S
, TXA // Transfer X to Accumulator..... | N. ...Z. A            = X
, TXS // Transfer X to Stack pointer... | .. .....       S      = X
, TYA // Transfer Y to Accumulator..... | N. ...Z. A            = Y
}

<|endoftext|>


<commit_before>//! A helper class for dealing with static archives

use std::ffi::{CString, CStr};
use std::io;
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
use std::str;

use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind};
use rustc_codegen_ssa::{METADATA_FILENAME, RLIB_BYTECODE_EXTENSION};
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, find_library};
use rustc::session::Session;

struct ArchiveConfig<'a> {
    pub sess: &'a Session,
    pub dst: PathBuf,
    pub src: Option<PathBuf>,
    pub lib_search_paths: Vec<PathBuf>,
}

/// Helper for adding many files to an archive.
#[must_use = "must call build() to finish building the archive"]
pub struct LlvmArchiveBuilder<'a> {
    config: ArchiveConfig<'a>,
    removals: Vec<String>,
    additions: Vec<Addition>,
    should_update_symbols: bool,
    src_archive: Option<Option<ArchiveRO>>,
}

enum Addition {
    File {
        path: PathBuf,
        name_in_archive: String,
    },
    Archive {
        archive: ArchiveRO,
        skip: Box<dyn FnMut(&str) -> bool>,
    },
}

fn is_relevant_child(c: &Child<'_>) -> bool {
    match c.name() {
        Some(name) => !name.contains("SYMDEF"),
        None => false,
    }
}

fn archive_config<'a>(sess: &'a Session,
                      output: &Path,
                      input: Option<&Path>) -> ArchiveConfig<'a> {
    use rustc_codegen_ssa::back::link::archive_search_paths;
    ArchiveConfig {
        sess,
        dst: output.to_path_buf(),
        src: input.map(|p| p.to_path_buf()),
        lib_search_paths: archive_search_paths(sess),
    }
}

impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
    /// Creates a new static archive, ready for modifying the archive specified
    /// by `config`.
    fn new(sess: &'a Session,
            output: &Path,
            input: Option<&Path>) -> LlvmArchiveBuilder<'a> {
        let config = archive_config(sess, output, input);
        LlvmArchiveBuilder {
            config,
            removals: Vec::new(),
            additions: Vec::new(),
            should_update_symbols: false,
            src_archive: None,
        }
    }

    /// Removes a file from this archive
    fn remove_file(&mut self, file: &str) {
        self.removals.push(file.to_string());
    }

    /// Lists all files in an archive
    fn src_files(&mut self) -> Vec<String> {
        if self.src_archive().is_none() {
            return Vec::new()
        }

        let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap();

        archive.iter()
               .filter_map(|child| child.ok())
               .filter(is_relevant_child)
               .filter_map(|child| child.name())
               .filter(|name| !self.removals.iter().any(|x| x == name))
               .map(|name| name.to_owned())
               .collect()
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    fn add_native_library(&mut self, name: &str) {
        let location = find_library(name, &self.config.lib_search_paths,
                                    self.config.sess);
        self.add_archive(&location, |_| false).unwrap_or_else(|e| {
            self.config.sess.fatal(&format!("failed to add native library {}: {}",
                                            location.to_string_lossy(), e));
        });
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    fn add_rlib(&mut self,
                    rlib: &Path,
                    name: &str,
                    lto: bool,
                    skip_objects: bool) -> io::Result<()> {
        // Ignoring obj file starting with the crate name
        // as simple comparison is not enough - there
        // might be also an extra name suffix
        let obj_start = name.to_owned();

        self.add_archive(rlib, move |fname: &str| {
            // Ignore bytecode/metadata files, no matter the name.
            if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
                return true
            }

            // Don't include Rust objects if LTO is enabled
            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
                return true
            }

            // Otherwise if this is *not* a rust object and we're skipping
            // objects then skip this file
            if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
                return true
            }

            // ok, don't skip this
            return false
        })
    }

    /// Adds an arbitrary file to this archive
    fn add_file(&mut self, file: &Path) {
        let name = file.file_name().unwrap().to_str().unwrap();
        self.additions.push(Addition::File {
            path: file.to_path_buf(),
            name_in_archive: name.to_owned(),
        });
    }

    /// Indicate that the next call to `build` should update all symbols in
    /// the archive (equivalent to running 'ar s' over it).
    fn update_symbols(&mut self) {
        self.should_update_symbols = true;
    }

    /// Combine the provided files, rlibs, and native libraries into a single
    /// `Archive`.
    fn build(mut self) {
        let kind = self.llvm_archive_kind().unwrap_or_else(|kind|
            self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", kind)));

        if let Err(e) = self.build_with_llvm(kind) {
            self.config.sess.fatal(&format!("failed to build archive: {}", e));
        }

    }
}

impl<'a> LlvmArchiveBuilder<'a> {
    fn src_archive(&mut self) -> Option<&ArchiveRO> {
        if let Some(ref a) = self.src_archive {
            return a.as_ref()
        }
        let src = self.config.src.as_ref()?;
        self.src_archive = Some(ArchiveRO::open(src).ok());
        self.src_archive.as_ref().unwrap().as_ref()
    }

    fn add_archive<F>(&mut self, archive: &Path, skip: F)
                      -> io::Result<()>
        where F: FnMut(&str) -> bool + 'static
    {
        let archive = match ArchiveRO::open(archive) {
            Ok(ar) => ar,
            Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
        };
        self.additions.push(Addition::Archive {
            archive,
            skip: Box::new(skip),
        });
        Ok(())
    }

    fn llvm_archive_kind(&self) -> Result<ArchiveKind, &str> {
        let kind = &*self.config.sess.target.target.options.archive_format;
        kind.parse().map_err(|_| kind)
    }

    fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> {
        let removals = mem::replace(&mut self.removals, Vec::new());
        let mut additions = mem::replace(&mut self.additions, Vec::new());
        let mut strings = Vec::new();
        let mut members = Vec::new();

        let dst = CString::new(self.config.dst.to_str().unwrap())?;
        let should_update_symbols = self.should_update_symbols;

        unsafe {
            if let Some(archive) = self.src_archive() {
                for child in archive.iter() {
                    let child = child.map_err(string_to_io_error)?;
                    let child_name = match child.name() {
                        Some(s) => s,
                        None => continue,
                    };
                    if removals.iter().any(|r| r == child_name) {
                        continue
                    }

                    let name = CString::new(child_name)?;
                    members.push(llvm::LLVMRustArchiveMemberNew(ptr::null(),
                                                                name.as_ptr(),
                                                                Some(child.raw)));
                    strings.push(name);
                }
            }
            for addition in &mut additions {
                match addition {
                    Addition::File { path, name_in_archive } => {
                        let path = CString::new(path.to_str().unwrap())?;
                        let name = CString::new(name_in_archive.clone())?;
                        members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(),
                                                                    name.as_ptr(),
                                                                    None));
                        strings.push(path);
                        strings.push(name);
                    }
                    Addition::Archive { archive, skip } => {
                        for child in archive.iter() {
                            let child = child.map_err(string_to_io_error)?;
                            if !is_relevant_child(&child) {
                                continue
                            }
                            let child_name = child.name().unwrap();
                            if skip(child_name) {
                                continue
                            }

                            // It appears that LLVM's archive writer is a little
                            // buggy if the name we pass down isn't just the
                            // filename component, so chop that off here and
                            // pass it in.
                            //
                            // See LLVM bug 25877 for more info.
                            let child_name = Path::new(child_name)
                                                  .file_name().unwrap()
                                                  .to_str().unwrap();
                            let name = CString::new(child_name)?;
                            let m = llvm::LLVMRustArchiveMemberNew(ptr::null(),
                                                                   name.as_ptr(),
                                                                   Some(child.raw));
                            members.push(m);
                            strings.push(name);
                        }
                    }
                }
            }

            let r = llvm::LLVMRustWriteArchive(dst.as_ptr(),
                                               members.len() as libc::size_t,
                                               members.as_ptr() as *const &_,
                                               should_update_symbols,
                                               kind);
            let ret = if r.into_result().is_err() {
                let err = llvm::LLVMRustGetLastError();
                let msg = if err.is_null() {
                    "failed to write archive".into()
                } else {
                    String::from_utf8_lossy(CStr::from_ptr(err).to_bytes())
                };
                Err(io::Error::new(io::ErrorKind::Other, msg))
            } else {
                Ok(())
            };
            for member in members {
                llvm::LLVMRustArchiveMemberFree(member);
            }
            ret
        }
    }
}

fn string_to_io_error(s: String) -> io::Error {
    io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s))
}
<commit_msg>Check if the archive has already been added to avoid duplicates<commit_after>//! A helper class for dealing with static archives

use std::ffi::{CString, CStr};
use std::io;
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
use std::str;

use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind};
use rustc_codegen_ssa::{METADATA_FILENAME, RLIB_BYTECODE_EXTENSION};
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, find_library};
use rustc::session::Session;

struct ArchiveConfig<'a> {
    pub sess: &'a Session,
    pub dst: PathBuf,
    pub src: Option<PathBuf>,
    pub lib_search_paths: Vec<PathBuf>,
}

/// Helper for adding many files to an archive.
#[must_use = "must call build() to finish building the archive"]
pub struct LlvmArchiveBuilder<'a> {
    config: ArchiveConfig<'a>,
    removals: Vec<String>,
    additions: Vec<Addition>,
    should_update_symbols: bool,
    src_archive: Option<Option<ArchiveRO>>,
}

enum Addition {
    File {
        path: PathBuf,
        name_in_archive: String,
    },
    Archive {
        path: PathBuf,
        archive: ArchiveRO,
        skip: Box<dyn FnMut(&str) -> bool>,
    },
}

impl Addition {
    fn path(&self) -> &Path {
        match self {
            Addition::File { path, .. } | Addition::Archive { path, .. } => path,
        }
    }
}

fn is_relevant_child(c: &Child<'_>) -> bool {
    match c.name() {
        Some(name) => !name.contains("SYMDEF"),
        None => false,
    }
}

fn archive_config<'a>(sess: &'a Session,
                      output: &Path,
                      input: Option<&Path>) -> ArchiveConfig<'a> {
    use rustc_codegen_ssa::back::link::archive_search_paths;
    ArchiveConfig {
        sess,
        dst: output.to_path_buf(),
        src: input.map(|p| p.to_path_buf()),
        lib_search_paths: archive_search_paths(sess),
    }
}

impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
    /// Creates a new static archive, ready for modifying the archive specified
    /// by `config`.
    fn new(sess: &'a Session,
            output: &Path,
            input: Option<&Path>) -> LlvmArchiveBuilder<'a> {
        let config = archive_config(sess, output, input);
        LlvmArchiveBuilder {
            config,
            removals: Vec::new(),
            additions: Vec::new(),
            should_update_symbols: false,
            src_archive: None,
        }
    }

    /// Removes a file from this archive
    fn remove_file(&mut self, file: &str) {
        self.removals.push(file.to_string());
    }

    /// Lists all files in an archive
    fn src_files(&mut self) -> Vec<String> {
        if self.src_archive().is_none() {
            return Vec::new()
        }

        let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap();

        archive.iter()
               .filter_map(|child| child.ok())
               .filter(is_relevant_child)
               .filter_map(|child| child.name())
               .filter(|name| !self.removals.iter().any(|x| x == name))
               .map(|name| name.to_owned())
               .collect()
    }

    /// Adds all of the contents of a native library to this archive. This will
    /// search in the relevant locations for a library named `name`.
    fn add_native_library(&mut self, name: &str) {
        let location = find_library(name, &self.config.lib_search_paths,
                                    self.config.sess);
        self.add_archive(&location, |_| false).unwrap_or_else(|e| {
            self.config.sess.fatal(&format!("failed to add native library {}: {}",
                                            location.to_string_lossy(), e));
        });
    }

    /// Adds all of the contents of the rlib at the specified path to this
    /// archive.
    ///
    /// This ignores adding the bytecode from the rlib, and if LTO is enabled
    /// then the object file also isn't added.
    fn add_rlib(&mut self,
                    rlib: &Path,
                    name: &str,
                    lto: bool,
                    skip_objects: bool) -> io::Result<()> {
        // Ignoring obj file starting with the crate name
        // as simple comparison is not enough - there
        // might be also an extra name suffix
        let obj_start = name.to_owned();

        self.add_archive(rlib, move |fname: &str| {
            // Ignore bytecode/metadata files, no matter the name.
            if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
                return true
            }

            // Don't include Rust objects if LTO is enabled
            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
                return true
            }

            // Otherwise if this is *not* a rust object and we're skipping
            // objects then skip this file
            if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
                return true
            }

            // ok, don't skip this
            return false
        })
    }

    /// Adds an arbitrary file to this archive
    fn add_file(&mut self, file: &Path) {
        let name = file.file_name().unwrap().to_str().unwrap();
        self.additions.push(Addition::File {
            path: file.to_path_buf(),
            name_in_archive: name.to_owned(),
        });
    }

    /// Indicate that the next call to `build` should update all symbols in
    /// the archive (equivalent to running 'ar s' over it).
    fn update_symbols(&mut self) {
        self.should_update_symbols = true;
    }

    /// Combine the provided files, rlibs, and native libraries into a single
    /// `Archive`.
    fn build(mut self) {
        let kind = self.llvm_archive_kind().unwrap_or_else(|kind|
            self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", kind)));

        if let Err(e) = self.build_with_llvm(kind) {
            self.config.sess.fatal(&format!("failed to build archive: {}", e));
        }

    }
}

impl<'a> LlvmArchiveBuilder<'a> {
    fn src_archive(&mut self) -> Option<&ArchiveRO> {
        if let Some(ref a) = self.src_archive {
            return a.as_ref()
        }
        let src = self.config.src.as_ref()?;
        self.src_archive = Some(ArchiveRO::open(src).ok());
        self.src_archive.as_ref().unwrap().as_ref()
    }

    fn add_archive<F>(&mut self, archive: &Path, skip: F)
                      -> io::Result<()>
        where F: FnMut(&str) -> bool + 'static
    {
        let archive_ro = match ArchiveRO::open(archive) {
            Ok(ar) => ar,
            Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
        };
        if self.additions.iter().any(|ar| ar.path() == archive) {
            return Ok(())
        }
        self.additions.push(Addition::Archive {
            path: archive.to_path_buf(),
            archive: archive_ro,
            skip: Box::new(skip),
        });
        Ok(())
    }

    fn llvm_archive_kind(&self) -> Result<ArchiveKind, &str> {
        let kind = &*self.config.sess.target.target.options.archive_format;
        kind.parse().map_err(|_| kind)
    }

    fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> {
        let removals = mem::replace(&mut self.removals, Vec::new());
        let mut additions = mem::replace(&mut self.additions, Vec::new());
        let mut strings = Vec::new();
        let mut members = Vec::new();

        let dst = CString::new(self.config.dst.to_str().unwrap())?;
        let should_update_symbols = self.should_update_symbols;

        unsafe {
            if let Some(archive) = self.src_archive() {
                for child in archive.iter() {
                    let child = child.map_err(string_to_io_error)?;
                    let child_name = match child.name() {
                        Some(s) => s,
                        None => continue,
                    };
                    if removals.iter().any(|r| r == child_name) {
                        continue
                    }

                    let name = CString::new(child_name)?;
                    members.push(llvm::LLVMRustArchiveMemberNew(ptr::null(),
                                                                name.as_ptr(),
                                                                Some(child.raw)));
                    strings.push(name);
                }
            }
            for addition in &mut additions {
                match addition {
                    Addition::File { path, name_in_archive } => {
                        let path = CString::new(path.to_str().unwrap())?;
                        let name = CString::new(name_in_archive.clone())?;
                        members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(),
                                                                    name.as_ptr(),
                                                                    None));
                        strings.push(path);
                        strings.push(name);
                    }
                    Addition::Archive { archive, skip, .. } => {
                        for child in archive.iter() {
                            let child = child.map_err(string_to_io_error)?;
                            if !is_relevant_child(&child) {
                                continue
                            }
                            let child_name = child.name().unwrap();
                            if skip(child_name) {
                                continue
                            }

                            // It appears that LLVM's archive writer is a little
                            // buggy if the name we pass down isn't just the
                            // filename component, so chop that off here and
                            // pass it in.
                            //
                            // See LLVM bug 25877 for more info.
                            let child_name = Path::new(child_name)
                                                  .file_name().unwrap()
                                                  .to_str().unwrap();
                            let name = CString::new(child_name)?;
                            let m = llvm::LLVMRustArchiveMemberNew(ptr::null(),
                                                                   name.as_ptr(),
                                                                   Some(child.raw));
                            members.push(m);
                            strings.push(name);
                        }
                    }
                }
            }

            let r = llvm::LLVMRustWriteArchive(dst.as_ptr(),
                                               members.len() as libc::size_t,
                                               members.as_ptr() as *const &_,
                                               should_update_symbols,
                                               kind);
            let ret = if r.into_result().is_err() {
                let err = llvm::LLVMRustGetLastError();
                let msg = if err.is_null() {
                    "failed to write archive".into()
                } else {
                    String::from_utf8_lossy(CStr::from_ptr(err).to_bytes())
                };
                Err(io::Error::new(io::ErrorKind::Other, msg))
            } else {
                Ok(())
            };
            for member in members {
                llvm::LLVMRustArchiveMemberFree(member);
            }
            ret
        }
    }
}

fn string_to_io_error(s: String) -> io::Error {
    io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s))
}
<|endoftext|>


<commit_before>// Copyright 2016 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.

//! A vector type intended to be used for collecting from iterators onto the stack.
//!
//! Space for up to N elements is provided on the stack.  If more elements are collected, Vec is
//! used to store the values on the heap. SmallVec is similar to AccumulateVec, but adds
//! the ability to push elements.
//!
//! The N above is determined by Array's implementor, by way of an associated constant.

use std::ops::{Deref, DerefMut};
use std::iter::{IntoIterator, FromIterator};
use std::fmt::{self, Debug};
use std::mem;
use std::ptr;

use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};

use accumulate_vec::{IntoIter, AccumulateVec};
use array_vec::Array;

pub struct SmallVec(AccumulateVec);

impl Clone for SmallVec
    where A: Array,
          A::Element: Clone {
    fn clone(&self) -> Self {
        SmallVec(self.0.clone())
    }
}

impl Debug for SmallVec
    where A: Array + Debug,
          A::Element: Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple("SmallVec").field(&self.0).finish()
    }
}

impl SmallVec {
    pub fn new() -> Self {
        SmallVec(AccumulateVec::new())
    }

    pub fn is_array(&self) -> bool {
        self.0.is_array()
    }

    pub fn with_capacity(cap: usize) -> Self {
        let mut vec = SmallVec::new();
        vec.reserve(cap);
        vec
    }

    pub fn one(el: A::Element) -> Self {
        SmallVec(AccumulateVec::one(el))
    }

    pub fn many<I: IntoIterator<Item=A::Element>>(els: I) -> Self {
        SmallVec(AccumulateVec::many(els))
    }

    pub fn expect_one(self, err: &'static str) -> A::Element {
        assert!(self.len() == 1, err);
        match self.0 {
            AccumulateVec::Array(arr) => arr.into_iter().next().unwrap(),
            AccumulateVec::Heap(vec) => vec.into_iter().next().unwrap(),
        }
    }

    /// Will reallocate onto the heap if needed.
    pub fn push(&mut self, el: A::Element) {
        self.reserve(1);
        match self.0 {
            AccumulateVec::Array(ref mut array) => array.push(el),
            AccumulateVec::Heap(ref mut vec) => vec.push(el),
        }
    }

    pub fn reserve(&mut self, n: usize) {
        match self.0 {
            AccumulateVec::Array(_) => {
                if self.len() + n > A::LEN {
                    let len = self.len();
                    let array = mem::replace(&mut self.0,
                            AccumulateVec::Heap(Vec::with_capacity(len + n)));
                    if let AccumulateVec::Array(array) = array {
                        match self.0 {
                            AccumulateVec::Heap(ref mut vec) => vec.extend(array),
                            _ => unreachable!()
                        }
                    }
                }
            }
            AccumulateVec::Heap(ref mut vec) => vec.reserve(n)
        }
    }

    pub unsafe fn set_len(&mut self, len: usize) {
        match self.0 {
            AccumulateVec::Array(ref mut arr) => arr.set_len(len),
            AccumulateVec::Heap(ref mut vec) => vec.set_len(len),
        }
    }

    pub fn insert(&mut self, index: usize, element: A::Element) {
        let len = self.len();

        // Reserve space for shifting elements to the right
        self.reserve(1);

        assert!(index <= len);

        unsafe {
            // infallible
            // The spot to put the new value
            {
                let p = self.as_mut_ptr().offset(index as isize);
                // Shift everything over to make space. (Duplicating the
                // `index`th element into two consecutive places.)
                ptr::copy(p, p.offset(1), len - index);
                // Write it in, overwriting the first copy of the `index`th
                // element.
                ptr::write(p, element);
            }
            self.set_len(len + 1);
        }
    }

    pub fn truncate(&mut self, len: usize) {
        unsafe {
            while len < self.len() {
                // Decrement len before the drop_in_place(), so a panic on Drop
                // doesn't re-drop the just-failed value.
                let newlen = self.len() - 1;
                self.set_len(newlen);
                ::std::ptr::drop_in_place(self.get_unchecked_mut(newlen));
            }
        }
    }
}

impl Deref for SmallVec {
    type Target = AccumulateVec;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for SmallVec {
    fn deref_mut(&mut self) -> &mut AccumulateVec {
        &mut self.0
    }
}

impl FromIterator for SmallVec {
    fn from_iter(iter: I) -> Self where I: IntoIterator<Item=A::Element> {
        SmallVec(iter.into_iter().collect())
    }
}

impl Extend for SmallVec {
    fn extend<I: IntoIterator<Item=A::Element>>(&mut self, iter: I) {
        if self.is_array() {
            let iter = iter.into_iter();
            self.reserve(iter.size_hint().0);

            for el in iter {
                self.push(el);
            }
        } else {
            match self.0 {
                AccumulateVec::Heap(ref mut vec) => vec.extend(iter),
                _ => unreachable!()
            }
        }
    }
}

impl IntoIterator for SmallVec {
    type Item = A::Element;
    type IntoIter = IntoIter;
    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl Default for SmallVec {
    fn default() -> SmallVec {
        SmallVec::new()
    }
}

impl Encodable for SmallVec
    where A: Array,
          A::Element: Encodable {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_seq(self.len(), |s| {
            for (i, e) in self.iter().enumerate() {
                s.emit_seq_elt(i, |s| e.encode(s))?;
            }
            Ok(())
        })
    }
}

impl Decodable for SmallVec
    where A: Array,
          A::Element: Decodable {
    fn decode<D: Decoder>(d: &mut D) -> Result<SmallVec, D::Error> {
        d.read_seq(|d, len| {
            (0..len).map(|i| d.read_seq_elt(i, |d| Decodable::decode(d))).collect()
        })
    }
}

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

    use super::*;

    #[bench]
    fn fill_small_vec_1_10_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::with_capacity(10);

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_1_10_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::new();

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_8_10_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::with_capacity(10);

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_8_10_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::new();

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_32_10_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::with_capacity(10);

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_32_10_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::new();

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_1_50_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::with_capacity(50);

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_1_50_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::new();

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_8_50_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::with_capacity(50);

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_8_50_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::new();

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_32_50_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::with_capacity(50);

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_32_50_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::new();

            sv.extend(0..50);
        })
    }
}
<commit_msg>Rollup merge of #52942 - llogiq:smallvec-opt, r=Mark-Simulacrum<commit_after>// Copyright 2016 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.

//! A vector type intended to be used for collecting from iterators onto the stack.
//!
//! Space for up to N elements is provided on the stack.  If more elements are collected, Vec is
//! used to store the values on the heap. SmallVec is similar to AccumulateVec, but adds
//! the ability to push elements.
//!
//! The N above is determined by Array's implementor, by way of an associated constant.

use std::ops::{Deref, DerefMut};
use std::iter::{IntoIterator, FromIterator};
use std::fmt::{self, Debug};
use std::mem;
use std::ptr;

use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};

use accumulate_vec::{IntoIter, AccumulateVec};
use array_vec::Array;

pub struct SmallVec(AccumulateVec);

impl Clone for SmallVec
    where A: Array,
          A::Element: Clone {
    fn clone(&self) -> Self {
        SmallVec(self.0.clone())
    }
}

impl Debug for SmallVec
    where A: Array + Debug,
          A::Element: Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple("SmallVec").field(&self.0).finish()
    }
}

impl SmallVec {
    pub fn new() -> Self {
        SmallVec(AccumulateVec::new())
    }

    pub fn is_array(&self) -> bool {
        self.0.is_array()
    }

    pub fn with_capacity(cap: usize) -> Self {
        let mut vec = SmallVec::new();
        vec.reserve(cap);
        vec
    }

    pub fn one(el: A::Element) -> Self {
        SmallVec(AccumulateVec::one(el))
    }

    pub fn many<I: IntoIterator<Item=A::Element>>(els: I) -> Self {
        SmallVec(AccumulateVec::many(els))
    }

    pub fn expect_one(self, err: &'static str) -> A::Element {
        assert!(self.len() == 1, err);
        match self.0 {
            AccumulateVec::Array(arr) => arr.into_iter().next().unwrap(),
            AccumulateVec::Heap(vec) => vec.into_iter().next().unwrap(),
        }
    }

    /// Will reallocate onto the heap if needed.
    pub fn push(&mut self, el: A::Element) {
        self.reserve(1);
        match self.0 {
            AccumulateVec::Array(ref mut array) => array.push(el),
            AccumulateVec::Heap(ref mut vec) => vec.push(el),
        }
    }

    pub fn reserve(&mut self, n: usize) {
        match self.0 {
            AccumulateVec::Array(_) => {
                if self.len() + n > A::LEN {
                    let len = self.len();
                    let array = mem::replace(&mut self.0,
                            AccumulateVec::Heap(Vec::with_capacity(len + n)));
                    if let AccumulateVec::Array(array) = array {
                        match self.0 {
                            AccumulateVec::Heap(ref mut vec) => vec.extend(array),
                            _ => unreachable!()
                        }
                    }
                }
            }
            AccumulateVec::Heap(ref mut vec) => vec.reserve(n)
        }
    }

    pub unsafe fn set_len(&mut self, len: usize) {
        match self.0 {
            AccumulateVec::Array(ref mut arr) => arr.set_len(len),
            AccumulateVec::Heap(ref mut vec) => vec.set_len(len),
        }
    }

    pub fn insert(&mut self, index: usize, element: A::Element) {
        let len = self.len();

        // Reserve space for shifting elements to the right
        self.reserve(1);

        assert!(index <= len);

        unsafe {
            // infallible
            // The spot to put the new value
            {
                let p = self.as_mut_ptr().offset(index as isize);
                // Shift everything over to make space. (Duplicating the
                // `index`th element into two consecutive places.)
                ptr::copy(p, p.offset(1), len - index);
                // Write it in, overwriting the first copy of the `index`th
                // element.
                ptr::write(p, element);
            }
            self.set_len(len + 1);
        }
    }

    pub fn truncate(&mut self, len: usize) {
        unsafe {
            while len < self.len() {
                // Decrement len before the drop_in_place(), so a panic on Drop
                // doesn't re-drop the just-failed value.
                let newlen = self.len() - 1;
                self.set_len(newlen);
                ::std::ptr::drop_in_place(self.get_unchecked_mut(newlen));
            }
        }
    }
}

impl Deref for SmallVec {
    type Target = AccumulateVec;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for SmallVec {
    fn deref_mut(&mut self) -> &mut AccumulateVec {
        &mut self.0
    }
}

impl FromIterator for SmallVec {
    fn from_iter(iter: I) -> Self where I: IntoIterator<Item=A::Element> {
        SmallVec(iter.into_iter().collect())
    }
}

impl Extend for SmallVec {
    fn extend<I: IntoIterator<Item=A::Element>>(&mut self, iter: I) {
        let iter = iter.into_iter();
        self.reserve(iter.size_hint().0);
        match self.0 {
            AccumulateVec::Heap(ref mut vec) => vec.extend(iter),
            _ => iter.for_each(|el| self.push(el))
        }
    }
}

impl IntoIterator for SmallVec {
    type Item = A::Element;
    type IntoIter = IntoIter;
    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl Default for SmallVec {
    fn default() -> SmallVec {
        SmallVec::new()
    }
}

impl Encodable for SmallVec
    where A: Array,
          A::Element: Encodable {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_seq(self.len(), |s| {
            for (i, e) in self.iter().enumerate() {
                s.emit_seq_elt(i, |s| e.encode(s))?;
            }
            Ok(())
        })
    }
}

impl Decodable for SmallVec
    where A: Array,
          A::Element: Decodable {
    fn decode<D: Decoder>(d: &mut D) -> Result<SmallVec, D::Error> {
        d.read_seq(|d, len| {
            (0..len).map(|i| d.read_seq_elt(i, |d| Decodable::decode(d))).collect()
        })
    }
}

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

    use super::*;

    #[bench]
    fn fill_small_vec_1_10_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::with_capacity(10);

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_1_10_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::new();

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_8_10_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::with_capacity(10);

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_8_10_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::new();

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_32_10_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::with_capacity(10);

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_32_10_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::new();

            sv.extend(0..10);
        })
    }

    #[bench]
    fn fill_small_vec_1_50_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::with_capacity(50);

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_1_50_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 1]> = SmallVec::new();

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_8_50_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::with_capacity(50);

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_8_50_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 8]> = SmallVec::new();

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_32_50_with_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::with_capacity(50);

            sv.extend(0..50);
        })
    }

    #[bench]
    fn fill_small_vec_32_50_wo_cap(b: &mut Bencher) {
        b.iter(|| {
            let mut sv: SmallVec<[usize; 32]> = SmallVec::new();

            sv.extend(0..50);
        })
    }
}
<|endoftext|>


<commit_before>use std::cmp;

pub fn levenshtein(a: &str, b: &str) -> i32 {
    // get length of unicode chars
    let len_a = a.chars().count();
    let len_b = b.chars().count();

    // initialize matrix
    let mut mat: Vec<Vec<i32>> = vec![vec![0; len_b + 1]; len_a + 1];
    for i in 1..(len_a + 1) {
        mat[i][0] = i as i32;
    }
    for i in 1..(len_b + 1) {
        mat[0][i] = i as i32;
    }

    // apply edit operations
    for (i, a_char) in a.chars().enumerate() {
        for (j, b_char) in b.chars().enumerate() {
            let substitution = if a_char == b_char {0} else {1};
            mat[i+1][j+1] = cmp::min(
                cmp::min(
                    mat[i][j+1] + 1,        // deletion
                    mat[i+1][j] + 1         // insertion
                ),
                mat[i][j] + substitution    // substitution
            );
        }
    }

    return mat[len_a][len_b];
}

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

    #[test]
    fn basic() {
        assert_eq!(levenshtein("kitten", "sitting"), 3);
        assert_eq!(levenshtein("book", "back"), 2);
        assert_eq!(levenshtein("table", "dinner"), 5);
        assert_eq!(levenshtein("person", "pardon"), 2);
        assert_eq!(levenshtein("person", "persons"), 1);
    }

    #[test]
    fn equal() {
        assert_eq!(levenshtein("kitten", "kitten"), 0);
        assert_eq!(levenshtein("a", "a"), 0);
    }

    #[test]
    fn cases() {
        assert_eq!(levenshtein("Hello", "hello"), 1);
        assert_eq!(levenshtein("World", "world"), 1);
    }

    #[test]
    fn empty() {
        assert_eq!(levenshtein("book", ""), 4);
        assert_eq!(levenshtein("", "book"), 4);
        assert_eq!(levenshtein("", ""), 0);
    }

    #[test]
    fn unicode() {
        assert_eq!(levenshtein("Späße", "Spaß"), 2);
        assert_eq!(levenshtein("さようなら", "こんにちは"), 5);
        assert_eq!(levenshtein("さようなら", "さようなう"), 1);
        assert_eq!(levenshtein("こんにちは", "こんにちは abc"), 4);
        assert_eq!(levenshtein("༆༃ʘ", "༆˥ʘ"), 1);
    }

}<commit_msg>More documentation<commit_after>use std::cmp;

/// # Levenshtein distance
///
/// The levenshtein distance is the number of per-character changes (insertion, deletion & substitution)
/// one string differs from annother.
/// See Wikipedia for more details: [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance)
///
/// ## Examples
/// ```
/// let distance = levenshtein("kitten", "sitting");
/// assert_eq!(distance, 3);
/// ```
pub fn levenshtein(a: &str, b: &str) -> i32 {
    // get length of unicode chars
    let len_a = a.chars().count();
    let len_b = b.chars().count();

    // initialize matrix
    let mut mat: Vec<Vec<i32>> = vec![vec![0; len_b + 1]; len_a + 1];
    for i in 1..(len_a + 1) {
        mat[i][0] = i as i32;
    }
    for i in 1..(len_b + 1) {
        mat[0][i] = i as i32;
    }

    // apply edit operations
    for (i, a_char) in a.chars().enumerate() {
        for (j, b_char) in b.chars().enumerate() {
            let substitution = if a_char == b_char {0} else {1};
            mat[i+1][j+1] = cmp::min(
                cmp::min(
                    mat[i][j+1] + 1,        // deletion
                    mat[i+1][j] + 1         // insertion
                ),
                mat[i][j] + substitution    // substitution
            );
        }
    }

    return mat[len_a][len_b];
}

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

    #[test]
    fn basic() {
        assert_eq!(levenshtein("kitten", "sitting"), 3);
        assert_eq!(levenshtein("book", "back"), 2);
        assert_eq!(levenshtein("table", "dinner"), 5);
        assert_eq!(levenshtein("person", "pardon"), 2);
        assert_eq!(levenshtein("person", "persons"), 1);
    }

    #[test]
    fn equal() {
        assert_eq!(levenshtein("kitten", "kitten"), 0);
        assert_eq!(levenshtein("a", "a"), 0);
    }

    #[test]
    fn cases() {
        assert_eq!(levenshtein("Hello", "hello"), 1);
        assert_eq!(levenshtein("World", "world"), 1);
    }

    #[test]
    fn empty() {
        assert_eq!(levenshtein("book", ""), 4);
        assert_eq!(levenshtein("", "book"), 4);
        assert_eq!(levenshtein("", ""), 0);
    }

    #[test]
    fn unicode() {
        assert_eq!(levenshtein("Späße", "Spaß"), 2);
        assert_eq!(levenshtein("さようなら", "こんにちは"), 5);
        assert_eq!(levenshtein("さようなら", "さようなう"), 1);
        assert_eq!(levenshtein("こんにちは", "こんにちは abc"), 4);
        assert_eq!(levenshtein("༆༃ʘ", "༆˥ʘ"), 1);
    }

}<|endoftext|>


<commit_before>//! Command prompt for xi-term. currently this is
//! heavily inspired by vim and is just designed to
//! get a simple base to work off of.

use std::io::Error;
use std::io::Write;
use termion::event::{Event, Key};

use core::{Command, ParseCommandError};
use termion::clear::CurrentLine as ClearLine;
use termion::cursor::Goto;

use std::str::FromStr;

#[derive(Debug, Default)]
pub struct CommandPrompt {
    dex: usize,
    chars: String,
}

impl CommandPrompt {
    /// Process a terminal event for the command prompt.
    pub fn handle_input(&mut self, input: &Event) -> Result<Option<Command>, ParseCommandError> {
        match input {
            Event::Key(Key::Char('\n')) => self.finalize(),
            Event::Key(Key::Backspace) => Ok(self.back()),
            Event::Key(Key::Delete) => Ok(self.delete()),
            Event::Key(Key::Left) => Ok(self.left()),
            Event::Key(Key::Right) => Ok(self.right()),
            Event::Key(Key::Char(chr)) => Ok(self.new_key(*chr)),
            _ => Ok(None),
        }
    }

    fn left(&mut self) -> Option<Command> {
        if self.dex > 0 {
            self.dex -= 1;
        }
        None
    }

    fn right(&mut self) -> Option<Command> {
        if self.dex < self.chars.len() {
            self.dex += 1;
        }
        None
    }

    fn delete(&mut self) -> Option<Command> {
        if self.dex < self.chars.len() {
            self.chars.remove(self.dex);
        }
        None
    }

    fn back(&mut self) -> Option<Command> {
        if !self.chars.is_empty() {
            self.dex -= 1;
            self.chars.remove(self.dex);
            None
        } else {
            Some(Command::Cancel)
        }
    }

    /// Gets called when any character is pressed.
    fn new_key(&mut self, chr: char) -> Option<Command> {
        self.chars.insert(self.dex, chr);
        self.dex += 1;
        None
    }

    /// Gets called when return is pressed,
    fn finalize(&mut self) -> Result<Option<Command>, ParseCommandError> {
        Ok(Some(FromStr::from_str(&self.chars)?))
    }

    pub fn render<W: Write>(&mut self, w: &mut W, row: u16) -> Result<(), Error> {
        if let Err(err) = write!(w, "{}{}:{}{}", Goto(1, row), ClearLine, self.chars, Goto(self.dex as u16+2, row)) {
            error!("faile to render status bar: {:?}", err);
        }
        Ok(())
    }
}
<commit_msg>Map ctrl-H to backspace for command prompt (#83)<commit_after>//! Command prompt for xi-term. currently this is
//! heavily inspired by vim and is just designed to
//! get a simple base to work off of.

use std::io::Error;
use std::io::Write;
use termion::event::{Event, Key};

use core::{Command, ParseCommandError};
use termion::clear::CurrentLine as ClearLine;
use termion::cursor::Goto;

use std::str::FromStr;

#[derive(Debug, Default)]
pub struct CommandPrompt {
    dex: usize,
    chars: String,
}

impl CommandPrompt {
    /// Process a terminal event for the command prompt.
    pub fn handle_input(&mut self, input: &Event) -> Result<Option<Command>, ParseCommandError> {
        match input {
            Event::Key(Key::Char('\n')) => self.finalize(),
            Event::Key(Key::Backspace) | Event::Key(Key::Ctrl('h')) => Ok(self.back()),
            Event::Key(Key::Delete) => Ok(self.delete()),
            Event::Key(Key::Left) => Ok(self.left()),
            Event::Key(Key::Right) => Ok(self.right()),
            Event::Key(Key::Char(chr)) => Ok(self.new_key(*chr)),
            _ => Ok(None),
        }
    }

    fn left(&mut self) -> Option<Command> {
        if self.dex > 0 {
            self.dex -= 1;
        }
        None
    }

    fn right(&mut self) -> Option<Command> {
        if self.dex < self.chars.len() {
            self.dex += 1;
        }
        None
    }

    fn delete(&mut self) -> Option<Command> {
        if self.dex < self.chars.len() {
            self.chars.remove(self.dex);
        }
        None
    }

    fn back(&mut self) -> Option<Command> {
        if !self.chars.is_empty() {
            self.dex -= 1;
            self.chars.remove(self.dex);
            None
        } else {
            Some(Command::Cancel)
        }
    }

    /// Gets called when any character is pressed.
    fn new_key(&mut self, chr: char) -> Option<Command> {
        self.chars.insert(self.dex, chr);
        self.dex += 1;
        None
    }

    /// Gets called when return is pressed,
    fn finalize(&mut self) -> Result<Option<Command>, ParseCommandError> {
        Ok(Some(FromStr::from_str(&self.chars)?))
    }

    pub fn render<W: Write>(&mut self, w: &mut W, row: u16) -> Result<(), Error> {
        if let Err(err) = write!(w, "{}{}:{}{}", Goto(1, row), ClearLine, self.chars, Goto(self.dex as u16+2, row)) {
            error!("faile to render status bar: {:?}", err);
        }
        Ok(())
    }
}
<|endoftext|>