<commit_before>use toml:: { Parser, Decoder, Value };
use rustc_serialize:: { Decodable };
use std::default:: { Default };
use std::path:: { Path };
use std::fs::File;
use std::io:: { Read, Result };
use std::convert:: { From };

#[derive(RustcDecodable)]
pub struct Configuration {
    server: Server
}

#[derive(RustcDecodable)]
pub struct Server {
    host: String,
    port: i64
}

impl<'a> From<&'a Path> for Configuration {
    fn from(path: &'a Path) -> Self {
        from_toml_file(path).unwrap()
    }
}

impl Default for Server {
    fn default() -> Self {
        Server {
            host: "127.0.0.1".to_string(),
            port: 6767
        }
    }
}

impl Default for Configuration {
    fn default() -> Self {
        Configuration {
            server: Server::default()
        }
    }
}

pub fn from_toml_file(path: &Path) -> Result<Configuration> {
    let content = read_file(path).unwrap();
    let toml = &content[..];
    let value = Parser::new(toml).parse().unwrap();
    let mut decoder = Decoder::new(Value::Table(value));
    let configuration = Configuration::decode(&mut decoder).unwrap();
    Ok(configuration)
}

pub fn read_file(path: &Path) -> Result<String> {
    let mut file = try!(File::open(path));
    let mut buffer = String::new();
    let _ = try!(file.read_to_string(&mut buffer));
    Ok(buffer.clone())
}

#[cfg(test)]
mod tests {
    use std::path:: { Path };
    use std::env:: { current_dir };
    use configuration:: { read_file, Server, Configuration };

    #[test]
    fn test_read_file() {
        let cwd = current_dir().unwrap();
        let path = Path::new(&cwd).join("tests/fixtures/server.toml");
        let content = read_file(path.as_path());
        let expected = r#"[server]
host = "127.0.0.1"
port = 3000
"#;

        assert_eq!(content.unwrap(), expected.to_string());
    }

    #[test]
    fn test_server_default() {
        let server = Server::default();

        assert_eq!(server.host, "127.0.0.1".to_string());
        assert_eq!(server.port, 6767);
    }

    #[test]
    fn test_config_default() {
        let config = Configuration::default();

        assert_eq!(config.server.host, "127.0.0.1".to_string());
        assert_eq!(config.server.port, 6767);
    }

}
<commit_msg>i64 to u16<commit_after>use toml:: { Parser, Decoder, Value };
use rustc_serialize:: { Decodable };
use std::default:: { Default };
use std::path:: { Path };
use std::fs::File;
use std::io:: { Read, Result };
use std::convert:: { From };

#[derive(RustcDecodable)]
pub struct Configuration {
    server: Server
}

#[derive(RustcDecodable)]
pub struct Server {
    host: String,
    port: u16
}

impl<'a> From<&'a Path> for Configuration {
    fn from(path: &'a Path) -> Self {
        from_toml_file(path).unwrap()
    }
}

impl Default for Server {
    fn default() -> Self {
        Server {
            host: "127.0.0.1".to_string(),
            port: 6767
        }
    }
}

impl Default for Configuration {
    fn default() -> Self {
        Configuration {
            server: Server::default()
        }
    }
}

pub fn from_toml_file(path: &Path) -> Result<Configuration> {
    let content = read_file(path).unwrap();
    let toml = &content[..];
    let value = Parser::new(toml).parse().unwrap();
    let mut decoder = Decoder::new(Value::Table(value));
    let configuration = Configuration::decode(&mut decoder).unwrap();
    Ok(configuration)
}

pub fn read_file(path: &Path) -> Result<String> {
    let mut file = try!(File::open(path));
    let mut buffer = String::new();
    let _ = try!(file.read_to_string(&mut buffer));
    Ok(buffer.clone())
}

#[cfg(test)]
mod tests {
    use std::path:: { Path };
    use std::env:: { current_dir };
    use configuration:: { read_file, Server, Configuration };

    #[test]
    fn test_read_file() {
        let cwd = current_dir().unwrap();
        let path = Path::new(&cwd).join("tests/fixtures/server.toml");
        let content = read_file(path.as_path());
        let expected = r#"[server]
host = "127.0.0.1"
port = 3000
"#;

        assert_eq!(content.unwrap(), expected.to_string());
    }

    #[test]
    fn test_server_default() {
        let server = Server::default();

        assert_eq!(server.host, "127.0.0.1".to_string());
        assert_eq!(server.port, 6767);
    }

    #[test]
    fn test_config_default() {
        let config = Configuration::default();

        assert_eq!(config.server.host, "127.0.0.1".to_string());
        assert_eq!(config.server.port, 6767);
    }

}
<|endoftext|>


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

//! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131)
//!
//! Rust targets a wide variety of usecases, and in the interest of flexibility,
//! allows new target triples to be defined in configuration files. Most users
//! will not need to care about these, but this is invaluable when porting Rust
//! to a new platform, and allows for an unprecedented level of control over how
//! the compiler works.
//!
//! # Using custom targets
//!
//! A target triple, as passed via `rustc --target=TRIPLE`, will first be
//! compared against the list of built-in targets. This is to ease distributing
//! rustc (no need for configuration files) and also to hold these built-in
//! targets as immutable and sacred. If `TRIPLE` is not one of the built-in
//! targets, rustc will check if a file named `TRIPLE` exists. If it does, it
//! will be loaded as the target configuration. If the file does not exist,
//! rustc will search each directory in the environment variable
//! `RUST_TARGET_PATH` for a file named `TRIPLE.json`. The first one found will
//! be loaded. If no file is found in any of those directories, a fatal error
//! will be given.  `RUST_TARGET_PATH` includes `/etc/rustc` as its last entry,
//! to be searched by default.
//!
//! Projects defining their own targets should use
//! `--target=path/to/my-awesome-platform.json` instead of adding to
//! `RUST_TARGET_PATH`.
//!
//! # Defining a new target
//!
//! Targets are defined using [JSON](http://json.org/). The `Target` struct in
//! this module defines the format the JSON file should take, though each
//! underscore in the field names should be replaced with a hyphen (`-`) in the
//! JSON file. Some fields are required in every target specification, such as
//! `data-layout`, `llvm-target`, `target-endian`, `target-word-size`, and
//! `arch`. In general, options passed to rustc with `-C` override the target's
//! settings, though `target-feature` and `link-args` will *add* to the list
//! specified by the target, rather than replace.

use serialize::json::Json;
use syntax::{diagnostic, abi};
use std::default::Default;
use std::io::fs::PathExtensions;

mod windows_base;
mod linux_base;
mod apple_base;
mod freebsd_base;
mod dragonfly_base;

mod arm_apple_ios;
mod arm_linux_androideabi;
mod arm_unknown_linux_gnueabi;
mod arm_unknown_linux_gnueabihf;
mod i686_apple_darwin;
mod i386_apple_ios;
mod i686_pc_windows_gnu;
mod i686_unknown_linux_gnu;
mod mips_unknown_linux_gnu;
mod mipsel_unknown_linux_gnu;
mod x86_64_apple_darwin;
mod x86_64_pc_windows_gnu;
mod x86_64_unknown_freebsd;
mod x86_64_unknown_dragonfly;
mod x86_64_unknown_linux_gnu;

/// Everything `rustc` knows about how to compile for a specific target.
///
/// Every field here must be specified, and has no default value.
#[deriving(Clone, Show)]
pub struct Target {
    /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM.
    pub data_layout: String,
    /// Target triple to pass to LLVM.
    pub llvm_target: String,
    /// String to use as the `target_endian` `cfg` variable.
    pub target_endian: String,
    /// String to use as the `target_word_size` `cfg` variable.
    pub target_word_size: String,
    /// OS name to use for conditional compilation.
    pub target_os: String,
    /// Architecture to use for ABI considerations. Valid options: "x86", "x86_64", "arm", and
    /// "mips". "mips" includes "mipsel".
    pub arch: String,
    /// Optional settings with defaults.
    pub options: TargetOptions,
}

/// Optional aspects of a target specification.
///
/// This has an implementation of `Default`, see each field for what the default is. In general,
/// these try to take "minimal defaults" that don't assume anything about the runtime they run in.
#[deriving(Clone, Show)]
pub struct TargetOptions {
    /// Linker to invoke. Defaults to "cc".
    pub linker: String,
    /// Linker arguments that are unconditionally passed *before* any user-defined libraries.
    pub pre_link_args: Vec<String>,
    /// Linker arguments that are unconditionally passed *after* any user-defined libraries.
    pub post_link_args: Vec<String>,
    /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults to "default".
    pub cpu: String,
    /// Default target features to pass to LLVM. These features will *always* be passed, and cannot
    /// be disabled even via `-C`. Corresponds to `llc -mattr=$features`.
    pub features: String,
    /// Whether dynamic linking is available on this target. Defaults to false.
    pub dynamic_linking: bool,
    /// Whether executables are available on this target. iOS, for example, only allows static
    /// libraries. Defaults to false.
    pub executables: bool,
    /// Whether LLVM's segmented stack prelude is supported by whatever runtime is available.
    /// Will emit stack checks and calls to __morestack. Defaults to false.
    pub morestack: bool,
    /// Relocation model to use in object file. Corresponds to `llc
    /// -relocation-model=$relocation_model`. Defaults to "pic".
    pub relocation_model: String,
    /// Code model to use. Corresponds to `llc -code-model=$code_model`. Defaults to "default".
    pub code_model: String,
    /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false.
    pub disable_redzone: bool,
    /// Eliminate frame pointers from stack frames if possible. Defaults to true.
    pub eliminate_frame_pointer: bool,
    /// Emit each function in its own section. Defaults to true.
    pub function_sections: bool,
    /// String to prepend to the name of every dynamic library. Defaults to "lib".
    pub dll_prefix: String,
    /// String to append to the name of every dynamic library. Defaults to ".so".
    pub dll_suffix: String,
    /// String to append to the name of every executable.
    pub exe_suffix: String,
    /// String to prepend to the name of every static library. Defaults to "lib".
    pub staticlib_prefix: String,
    /// String to append to the name of every static library. Defaults to ".a".
    pub staticlib_suffix: String,
    /// Whether the target toolchain is like OSX's. Only useful for compiling against iOS/OS X, in
    /// particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false.
    pub is_like_osx: bool,
    /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows,
    /// only realy used for figuring out how to find libraries, since Windows uses its own
    /// library naming convention. Defaults to false.
    pub is_like_windows: bool,
    /// Whether the linker support GNU-like arguments such as -O. Defaults to false.
    pub linker_is_gnu: bool,
    /// Whether the linker support rpaths or not. Defaults to false.
    pub has_rpath: bool,
    /// Whether to disable linking to compiler-rt. Defaults to false, as LLVM will emit references
    /// to the functions that compiler-rt provides.
    pub no_compiler_rt: bool,
    /// Dynamically linked executables can be compiled as position independent if the default
    /// relocation model of position independent code is not changed. This is a requirement to take
    /// advantage of ASLR, as otherwise the functions in the executable are not randomized and can
    /// be used during an exploit of a vulnerability in any code.
    pub position_independant_executables: bool,
}

impl Default for TargetOptions {
    /// Create a set of "sane defaults" for any target. This is still incomplete, and if used for
    /// compilation, will certainly not work.
    fn default() -> TargetOptions {
        TargetOptions {
            linker: "cc".to_string(),
            pre_link_args: Vec::new(),
            post_link_args: Vec::new(),
            cpu: "generic".to_string(),
            features: "".to_string(),
            dynamic_linking: false,
            executables: false,
            morestack: false,
            relocation_model: "pic".to_string(),
            code_model: "default".to_string(),
            disable_redzone: false,
            eliminate_frame_pointer: true,
            function_sections: true,
            dll_prefix: "lib".to_string(),
            dll_suffix: ".so".to_string(),
            exe_suffix: "".to_string(),
            staticlib_prefix: "lib".to_string(),
            staticlib_suffix: ".a".to_string(),
            is_like_osx: false,
            is_like_windows: false,
            linker_is_gnu: false,
            has_rpath: false,
            no_compiler_rt: false,
            position_independant_executables: false,
        }
    }
}

impl Target {
    /// Given a function ABI, turn "System" into the correct ABI for this target.
    pub fn adjust_abi(&self, abi: abi::Abi) -> abi::Abi {
        match abi {
            abi::System => {
                if self.options.is_like_windows && self.arch.as_slice() == "x86" {
                    abi::Stdcall
                } else {
                    abi::C
                }
            },
            abi => abi
        }
    }

    /// Load a target descriptor from a JSON object.
    pub fn from_json(obj: Json) -> Target {
        // this is 1. ugly, 2. error prone.

        let handler = diagnostic::default_handler(diagnostic::Auto, None);

        let get_req_field = |name: &str| {
            match obj.find(name)
                     .map(|s| s.as_string())
                     .and_then(|os| os.map(|s| s.to_string())) {
                Some(val) => val,
                None =>
                    handler.fatal((format!("Field {} in target specification is required", name))
                                  .as_slice())
            }
        };

        let mut base = Target {
            data_layout: get_req_field("data-layout"),
            llvm_target: get_req_field("llvm-target"),
            target_endian: get_req_field("target-endian"),
            target_word_size: get_req_field("target-word-size"),
            arch: get_req_field("arch"),
            target_os: get_req_field("os"),
            options: Default::default(),
        };

        macro_rules! key (
            ($key_name:ident) => ( {
                let name = (stringify!($key_name)).replace("_", "-");
                obj.find(name[]).map(|o| o.as_string()
                                    .map(|s| base.options.$key_name = s.to_string()));
            } );
            ($key_name:ident, bool) => ( {
                let name = (stringify!($key_name)).replace("_", "-");
                obj.find(name[]).map(|o| o.as_boolean().map(|s| base.options.$key_name = s));
            } );
            ($key_name:ident, list) => ( {
                let name = (stringify!($key_name)).replace("_", "-");
                obj.find(name[]).map(|o| o.as_list()
                    .map(|v| base.options.$key_name = v.iter()
                        .map(|a| a.as_string().unwrap().to_string()).collect()
                        )
                    );
            } );
        )

        key!(cpu);
        key!(linker);
        key!(relocation_model);
        key!(code_model);
        key!(dll_prefix);
        key!(dll_suffix);
        key!(exe_suffix);
        key!(staticlib_prefix);
        key!(staticlib_suffix);
        key!(features);
        key!(dynamic_linking, bool);
        key!(executables, bool);
        key!(morestack, bool);
        key!(disable_redzone, bool);
        key!(eliminate_frame_pointer, bool);
        key!(function_sections, bool);
        key!(is_like_osx, bool);
        key!(is_like_windows, bool);
        key!(linker_is_gnu, bool);
        key!(has_rpath, bool);
        key!(no_compiler_rt, bool);
        key!(pre_link_args, list);
        key!(post_link_args, list);

        base
    }

    /// Search RUST_TARGET_PATH for a JSON file specifying the given target triple. Note that it
    /// could also just be a bare filename already, so also check for that. If one of the hardcoded
    /// targets we know about, just return it directly.
    ///
    /// The error string could come from any of the APIs called, including filesystem access and
    /// JSON decoding.
    pub fn search(target: &str) -> Result<Target, String> {
        use std::os;
        use std::io::File;
        use std::path::Path;
        use serialize::json;

        fn load_file(path: &Path) -> Result<Target, String> {
            let mut f = try!(File::open(path).map_err(|e| e.to_string()));
            let obj = try!(json::from_reader(&mut f).map_err(|e| e.to_string()));
            Ok(Target::from_json(obj))
        }

        // this would use a match if stringify! were allowed in pattern position
        macro_rules! load_specific (
            ( $($name:ident),+ ) => (
                {
                    let target = target.replace("-", "_");
                    let target = target.as_slice();
                    if false { }
                    $(
                        else if target == stringify!($name) {
                            let t = $name::target();
                            debug!("Got builtin target: {}", t);
                            return Ok(t);
                        }
                    )*
                    else if target == "x86_64-w64-mingw32" {
                        let t = x86_64_pc_windows_gnu::target();
                        return Ok(t);
                    } else if target == "i686-w64-mingw32" {
                        let t = i686_pc_windows_gnu::target();
                        return Ok(t);
                    }
                }
            )
        )

        load_specific!(
            x86_64_unknown_linux_gnu,
            i686_unknown_linux_gnu,
            mips_unknown_linux_gnu,
            mipsel_unknown_linux_gnu,
            arm_linux_androideabi,
            arm_unknown_linux_gnueabi,
            arm_unknown_linux_gnueabihf,

            x86_64_unknown_freebsd,

            x86_64_unknown_dragonfly,

            x86_64_apple_darwin,
            i686_apple_darwin,
            i386_apple_ios,
            arm_apple_ios,

            x86_64_pc_windows_gnu,
            i686_pc_windows_gnu
        )

        let path = Path::new(target);

        if path.is_file() {
            return load_file(&path);
        }

        let path = {
            let mut target = target.to_string();
            target.push_str(".json");
            Path::new(target)
        };

        let target_path = os::getenv("RUST_TARGET_PATH").unwrap_or(String::new());

        let paths = os::split_paths(target_path.as_slice());
        // FIXME 16351: add a sane default search path?

        for dir in paths.iter() {
            let p =  dir.join(path.clone());
            if p.is_file() {
                return load_file(&p);
            }
        }

        Err(format!("Could not find specification for target {}", target))
    }
}
<commit_msg>Add missing dragonfly module.<commit_after>// Copyright 2014 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.

//! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131)
//!
//! Rust targets a wide variety of usecases, and in the interest of flexibility,
//! allows new target triples to be defined in configuration files. Most users
//! will not need to care about these, but this is invaluable when porting Rust
//! to a new platform, and allows for an unprecedented level of control over how
//! the compiler works.
//!
//! # Using custom targets
//!
//! A target triple, as passed via `rustc --target=TRIPLE`, will first be
//! compared against the list of built-in targets. This is to ease distributing
//! rustc (no need for configuration files) and also to hold these built-in
//! targets as immutable and sacred. If `TRIPLE` is not one of the built-in
//! targets, rustc will check if a file named `TRIPLE` exists. If it does, it
//! will be loaded as the target configuration. If the file does not exist,
//! rustc will search each directory in the environment variable
//! `RUST_TARGET_PATH` for a file named `TRIPLE.json`. The first one found will
//! be loaded. If no file is found in any of those directories, a fatal error
//! will be given.  `RUST_TARGET_PATH` includes `/etc/rustc` as its last entry,
//! to be searched by default.
//!
//! Projects defining their own targets should use
//! `--target=path/to/my-awesome-platform.json` instead of adding to
//! `RUST_TARGET_PATH`.
//!
//! # Defining a new target
//!
//! Targets are defined using [JSON](http://json.org/). The `Target` struct in
//! this module defines the format the JSON file should take, though each
//! underscore in the field names should be replaced with a hyphen (`-`) in the
//! JSON file. Some fields are required in every target specification, such as
//! `data-layout`, `llvm-target`, `target-endian`, `target-word-size`, and
//! `arch`. In general, options passed to rustc with `-C` override the target's
//! settings, though `target-feature` and `link-args` will *add* to the list
//! specified by the target, rather than replace.

use serialize::json::Json;
use syntax::{diagnostic, abi};
use std::default::Default;
use std::io::fs::PathExtensions;

mod windows_base;
mod linux_base;
mod apple_base;
mod freebsd_base;
mod dragonfly_base;

mod arm_apple_ios;
mod arm_linux_androideabi;
mod arm_unknown_linux_gnueabi;
mod arm_unknown_linux_gnueabihf;
mod i686_apple_darwin;
mod i386_apple_ios;
mod i686_pc_windows_gnu;
mod i686_unknown_dragonfly;
mod i686_unknown_linux_gnu;
mod mips_unknown_linux_gnu;
mod mipsel_unknown_linux_gnu;
mod x86_64_apple_darwin;
mod x86_64_pc_windows_gnu;
mod x86_64_unknown_freebsd;
mod x86_64_unknown_dragonfly;
mod x86_64_unknown_linux_gnu;

/// Everything `rustc` knows about how to compile for a specific target.
///
/// Every field here must be specified, and has no default value.
#[deriving(Clone, Show)]
pub struct Target {
    /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM.
    pub data_layout: String,
    /// Target triple to pass to LLVM.
    pub llvm_target: String,
    /// String to use as the `target_endian` `cfg` variable.
    pub target_endian: String,
    /// String to use as the `target_word_size` `cfg` variable.
    pub target_word_size: String,
    /// OS name to use for conditional compilation.
    pub target_os: String,
    /// Architecture to use for ABI considerations. Valid options: "x86", "x86_64", "arm", and
    /// "mips". "mips" includes "mipsel".
    pub arch: String,
    /// Optional settings with defaults.
    pub options: TargetOptions,
}

/// Optional aspects of a target specification.
///
/// This has an implementation of `Default`, see each field for what the default is. In general,
/// these try to take "minimal defaults" that don't assume anything about the runtime they run in.
#[deriving(Clone, Show)]
pub struct TargetOptions {
    /// Linker to invoke. Defaults to "cc".
    pub linker: String,
    /// Linker arguments that are unconditionally passed *before* any user-defined libraries.
    pub pre_link_args: Vec<String>,
    /// Linker arguments that are unconditionally passed *after* any user-defined libraries.
    pub post_link_args: Vec<String>,
    /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults to "default".
    pub cpu: String,
    /// Default target features to pass to LLVM. These features will *always* be passed, and cannot
    /// be disabled even via `-C`. Corresponds to `llc -mattr=$features`.
    pub features: String,
    /// Whether dynamic linking is available on this target. Defaults to false.
    pub dynamic_linking: bool,
    /// Whether executables are available on this target. iOS, for example, only allows static
    /// libraries. Defaults to false.
    pub executables: bool,
    /// Whether LLVM's segmented stack prelude is supported by whatever runtime is available.
    /// Will emit stack checks and calls to __morestack. Defaults to false.
    pub morestack: bool,
    /// Relocation model to use in object file. Corresponds to `llc
    /// -relocation-model=$relocation_model`. Defaults to "pic".
    pub relocation_model: String,
    /// Code model to use. Corresponds to `llc -code-model=$code_model`. Defaults to "default".
    pub code_model: String,
    /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false.
    pub disable_redzone: bool,
    /// Eliminate frame pointers from stack frames if possible. Defaults to true.
    pub eliminate_frame_pointer: bool,
    /// Emit each function in its own section. Defaults to true.
    pub function_sections: bool,
    /// String to prepend to the name of every dynamic library. Defaults to "lib".
    pub dll_prefix: String,
    /// String to append to the name of every dynamic library. Defaults to ".so".
    pub dll_suffix: String,
    /// String to append to the name of every executable.
    pub exe_suffix: String,
    /// String to prepend to the name of every static library. Defaults to "lib".
    pub staticlib_prefix: String,
    /// String to append to the name of every static library. Defaults to ".a".
    pub staticlib_suffix: String,
    /// Whether the target toolchain is like OSX's. Only useful for compiling against iOS/OS X, in
    /// particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false.
    pub is_like_osx: bool,
    /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows,
    /// only realy used for figuring out how to find libraries, since Windows uses its own
    /// library naming convention. Defaults to false.
    pub is_like_windows: bool,
    /// Whether the linker support GNU-like arguments such as -O. Defaults to false.
    pub linker_is_gnu: bool,
    /// Whether the linker support rpaths or not. Defaults to false.
    pub has_rpath: bool,
    /// Whether to disable linking to compiler-rt. Defaults to false, as LLVM will emit references
    /// to the functions that compiler-rt provides.
    pub no_compiler_rt: bool,
    /// Dynamically linked executables can be compiled as position independent if the default
    /// relocation model of position independent code is not changed. This is a requirement to take
    /// advantage of ASLR, as otherwise the functions in the executable are not randomized and can
    /// be used during an exploit of a vulnerability in any code.
    pub position_independant_executables: bool,
}

impl Default for TargetOptions {
    /// Create a set of "sane defaults" for any target. This is still incomplete, and if used for
    /// compilation, will certainly not work.
    fn default() -> TargetOptions {
        TargetOptions {
            linker: "cc".to_string(),
            pre_link_args: Vec::new(),
            post_link_args: Vec::new(),
            cpu: "generic".to_string(),
            features: "".to_string(),
            dynamic_linking: false,
            executables: false,
            morestack: false,
            relocation_model: "pic".to_string(),
            code_model: "default".to_string(),
            disable_redzone: false,
            eliminate_frame_pointer: true,
            function_sections: true,
            dll_prefix: "lib".to_string(),
            dll_suffix: ".so".to_string(),
            exe_suffix: "".to_string(),
            staticlib_prefix: "lib".to_string(),
            staticlib_suffix: ".a".to_string(),
            is_like_osx: false,
            is_like_windows: false,
            linker_is_gnu: false,
            has_rpath: false,
            no_compiler_rt: false,
            position_independant_executables: false,
        }
    }
}

impl Target {
    /// Given a function ABI, turn "System" into the correct ABI for this target.
    pub fn adjust_abi(&self, abi: abi::Abi) -> abi::Abi {
        match abi {
            abi::System => {
                if self.options.is_like_windows && self.arch.as_slice() == "x86" {
                    abi::Stdcall
                } else {
                    abi::C
                }
            },
            abi => abi
        }
    }

    /// Load a target descriptor from a JSON object.
    pub fn from_json(obj: Json) -> Target {
        // this is 1. ugly, 2. error prone.

        let handler = diagnostic::default_handler(diagnostic::Auto, None);

        let get_req_field = |name: &str| {
            match obj.find(name)
                     .map(|s| s.as_string())
                     .and_then(|os| os.map(|s| s.to_string())) {
                Some(val) => val,
                None =>
                    handler.fatal((format!("Field {} in target specification is required", name))
                                  .as_slice())
            }
        };

        let mut base = Target {
            data_layout: get_req_field("data-layout"),
            llvm_target: get_req_field("llvm-target"),
            target_endian: get_req_field("target-endian"),
            target_word_size: get_req_field("target-word-size"),
            arch: get_req_field("arch"),
            target_os: get_req_field("os"),
            options: Default::default(),
        };

        macro_rules! key (
            ($key_name:ident) => ( {
                let name = (stringify!($key_name)).replace("_", "-");
                obj.find(name[]).map(|o| o.as_string()
                                    .map(|s| base.options.$key_name = s.to_string()));
            } );
            ($key_name:ident, bool) => ( {
                let name = (stringify!($key_name)).replace("_", "-");
                obj.find(name[]).map(|o| o.as_boolean().map(|s| base.options.$key_name = s));
            } );
            ($key_name:ident, list) => ( {
                let name = (stringify!($key_name)).replace("_", "-");
                obj.find(name[]).map(|o| o.as_list()
                    .map(|v| base.options.$key_name = v.iter()
                        .map(|a| a.as_string().unwrap().to_string()).collect()
                        )
                    );
            } );
        )

        key!(cpu);
        key!(linker);
        key!(relocation_model);
        key!(code_model);
        key!(dll_prefix);
        key!(dll_suffix);
        key!(exe_suffix);
        key!(staticlib_prefix);
        key!(staticlib_suffix);
        key!(features);
        key!(dynamic_linking, bool);
        key!(executables, bool);
        key!(morestack, bool);
        key!(disable_redzone, bool);
        key!(eliminate_frame_pointer, bool);
        key!(function_sections, bool);
        key!(is_like_osx, bool);
        key!(is_like_windows, bool);
        key!(linker_is_gnu, bool);
        key!(has_rpath, bool);
        key!(no_compiler_rt, bool);
        key!(pre_link_args, list);
        key!(post_link_args, list);

        base
    }

    /// Search RUST_TARGET_PATH for a JSON file specifying the given target triple. Note that it
    /// could also just be a bare filename already, so also check for that. If one of the hardcoded
    /// targets we know about, just return it directly.
    ///
    /// The error string could come from any of the APIs called, including filesystem access and
    /// JSON decoding.
    pub fn search(target: &str) -> Result<Target, String> {
        use std::os;
        use std::io::File;
        use std::path::Path;
        use serialize::json;

        fn load_file(path: &Path) -> Result<Target, String> {
            let mut f = try!(File::open(path).map_err(|e| e.to_string()));
            let obj = try!(json::from_reader(&mut f).map_err(|e| e.to_string()));
            Ok(Target::from_json(obj))
        }

        // this would use a match if stringify! were allowed in pattern position
        macro_rules! load_specific (
            ( $($name:ident),+ ) => (
                {
                    let target = target.replace("-", "_");
                    let target = target.as_slice();
                    if false { }
                    $(
                        else if target == stringify!($name) {
                            let t = $name::target();
                            debug!("Got builtin target: {}", t);
                            return Ok(t);
                        }
                    )*
                    else if target == "x86_64-w64-mingw32" {
                        let t = x86_64_pc_windows_gnu::target();
                        return Ok(t);
                    } else if target == "i686-w64-mingw32" {
                        let t = i686_pc_windows_gnu::target();
                        return Ok(t);
                    }
                }
            )
        )

        load_specific!(
            x86_64_unknown_linux_gnu,
            i686_unknown_linux_gnu,
            mips_unknown_linux_gnu,
            mipsel_unknown_linux_gnu,
            arm_linux_androideabi,
            arm_unknown_linux_gnueabi,
            arm_unknown_linux_gnueabihf,

            x86_64_unknown_freebsd,

            i686_unknown_dragonfly,
            x86_64_unknown_dragonfly,

            x86_64_apple_darwin,
            i686_apple_darwin,
            i386_apple_ios,
            arm_apple_ios,

            x86_64_pc_windows_gnu,
            i686_pc_windows_gnu
        )

        let path = Path::new(target);

        if path.is_file() {
            return load_file(&path);
        }

        let path = {
            let mut target = target.to_string();
            target.push_str(".json");
            Path::new(target)
        };

        let target_path = os::getenv("RUST_TARGET_PATH").unwrap_or(String::new());

        let paths = os::split_paths(target_path.as_slice());
        // FIXME 16351: add a sane default search path?

        for dir in paths.iter() {
            let p =  dir.join(path.clone());
            if p.is_file() {
                return load_file(&p);
            }
        }

        Err(format!("Could not find specification for target {}", target))
    }
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use std::option::Option;

#[derive(Clone, Default)]
pub struct DecodeRegister {
    pub addr_init: Option<u16>,
    pub addr_intermediate: Option<u16>,
    pub addr_final: Option<u16>,

    pub value_init: Option<u8>,
    pub value_intermediate: Option<u8>,
    pub value_final: Option<u8>,

    pub info: OpcodeExecInfo,
}

#[allow(non_snake_case)]
#[derive(Clone, Default)]
pub struct CpuState {
    pub pc: u16,
    pub sp: u8,
    pub a:  u8,
    pub x:  u8,
    pub y:  u8,

    // flags
    pub C: bool, // carry
    pub Z: bool, // zero
    pub I: bool, // interrupt
    pub D: bool, // decimal
    pub B: bool, // break
             // bit 5 is not used by the nes and is always 1
    pub V: bool, // overflow
    pub S: bool, // sign/negative

    // these are not strictly 6502 registers, but are useful for modeling the cpu
    pub instruction_register: u8,
    pub decode_register:DecodeRegister,
}

impl CpuState {
    pub fn unpack_flags(self: &CpuState) -> u8 {
        ( (self.C as u8) << 0)
      | ( (self.Z as u8) << 1)
      | ( (self.I as u8) << 2)
      | ( (self.D as u8) << 3)
      | ( (self.B as u8) << 4)
      | ( (1           ) << 5) // flag 5 is unused
      | ( (self.V as u8) << 6)
      | ( (self.S as u8) << 7)
    }
    pub fn pack_flags(self: &mut CpuState,flags:u8) {
        self.C        = ( (flags >> 0) & 1) == 1;
        self.Z        = ( (flags >> 1) & 1) == 1;
        self.I        = ( (flags >> 2) & 1) == 1;
        self.D        = ( (flags >> 3) & 1) == 1;
        self.B        = ( (flags >> 4) & 1) == 1;
        // flags 5 is unused
        self.V        = ( (flags >> 6) & 1) == 1;
        self.S        = ( (flags >> 7) & 1) == 1;
    }
}

<commit_msg>added comment for cpu flags<commit_after>use cpu::common_defs::OpcodeExecInfo;
use std::option::Option;

#[derive(Clone, Default)]
pub struct DecodeRegister {
    pub addr_init: Option<u16>,
    pub addr_intermediate: Option<u16>,
    pub addr_final: Option<u16>,

    pub value_init: Option<u8>,
    pub value_intermediate: Option<u8>,
    pub value_final: Option<u8>,

    pub info: OpcodeExecInfo,
}

#[allow(non_snake_case)]
#[derive(Clone, Default)]
pub struct CpuState {
    pub pc: u16,
    pub sp: u8,
    pub a:  u8,
    pub x:  u8,
    pub y:  u8,

    // flags
    pub C: bool, // carry
    pub Z: bool, // zero
    pub I: bool, // interrupt
    pub D: bool, // decimal
    pub B: bool, // break
             // bit 5 is not used by the nes and is always 1
    pub V: bool, // overflow
    pub S: bool, // sign/negative

    // these are not strictly 6502 registers, but are useful for modeling the cpu
    pub instruction_register: u8,
    pub decode_register:DecodeRegister,
}

// svubdizc
// 00100110 26
// 00100100 24
impl CpuState {
    pub fn unpack_flags(self: &CpuState) -> u8 {
        ( (self.C as u8) << 0)
      | ( (self.Z as u8) << 1)
      | ( (self.I as u8) << 2)
      | ( (self.D as u8) << 3)
      | ( (self.B as u8) << 4)
      | ( (1           ) << 5) // flag 5 is unused
      | ( (self.V as u8) << 6)
      | ( (self.S as u8) << 7)
    }
    pub fn pack_flags(self: &mut CpuState,flags:u8) {
        self.C        = ( (flags >> 0) & 1) == 1;
        self.Z        = ( (flags >> 1) & 1) == 1;
        self.I        = ( (flags >> 2) & 1) == 1;
        self.D        = ( (flags >> 3) & 1) == 1;
        self.B        = ( (flags >> 4) & 1) == 1;
        // flags 5 is unused
        self.V        = ( (flags >> 6) & 1) == 1;
        self.S        = ( (flags >> 7) & 1) == 1;
    }
}

<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;
use std::time::Duration;

use hab_core::util::sys;
use hyper::client::{Client as HyperClient, IntoUrl, RequestBuilder};
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use openssl::ssl::{SslConnectorBuilder, SslConnector, SslMethod, SslOption, SSL_OP_NO_SSLV2,
                   SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::{Error, Result};
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT: u64 = 60;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(
        endpoint: T,
        product: &str,
        version: &str,
        fs_root_path: Option<&Path>,
    ) -> Result<Self>
    where
        T: IntoUrl,
    {
        let endpoint = endpoint.into_url().map_err(Error::UrlParseError)?;
        Ok(ApiClient {
            inner: new_hyper_client(&endpoint, fs_root_path)?,
            proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
            target_scheme: endpoint.scheme().to_string(),
            endpoint: endpoint,
            user_agent_header: user_agent(product, version)?,
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();

        if path.is_empty() {
            return url;
        }

        if url.path().ends_with("/") || path.starts_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }

        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);
    let timeout = Some(Duration::from_secs(CLIENT_SOCKET_RW_TIMEOUT));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!(
        "{}/{} ({}-{}; {})",
        product.trim(),
        version.trim(),
        uname.machine.trim().to_lowercase(),
        uname.sys_name.trim().to_lowercase(),
        uname.release.trim().to_lowercase()
    );
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(conn.builder_mut(), fs_root_path)?;
    conn.builder_mut().set_options(options);
    conn.builder_mut().set_cipher_list(
        "ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH",
    )?;
    Ok(conn.build())
}
<commit_msg>[http-client] Address compiler warnings in api-client module.<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::Path;
use std::time::Duration;

use hab_core::util::sys;
use hyper::client::{Client as HyperClient, IntoUrl, RequestBuilder};
use hyper::client::pool::{Config, Pool};
use hyper::header::UserAgent;
use hyper::http::h1::Http11Protocol;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;
use openssl::ssl::{SslConnectorBuilder, SslConnector, SslMethod, SslOption, SSL_OP_NO_SSLV2,
                   SSL_OP_NO_SSLV3, SSL_OP_NO_COMPRESSION};
use url::Url;

use error::{Error, Result};
use net::ProxyHttpsConnector;
use proxy::{ProxyInfo, proxy_unless_domain_exempted};
use ssl;

// Read and write TCP socket timeout for Hyper/HTTP client calls.
const CLIENT_SOCKET_RW_TIMEOUT: u64 = 60;

header! { (ProxyAuthorization, "Proxy-Authorization") => [String] }

/// A generic wrapper around a Hyper HTTP client intended for API-like usage.
///
/// When an `ApiClient` is created, it has a constant URL base which is assumed to be some API
/// endpoint. This allows the underlying Hyper client to load and use any relevant HTTP proxy
/// support and to provide reasonable User-Agent HTTP headers, etc.
#[derive(Debug)]
pub struct ApiClient {
    /// The base URL for the client.
    endpoint: Url,
    /// An instance of a `hyper::Client` which is configured with an SSL context and optionally
    /// using an HTTP proxy.
    inner: HyperClient,
    /// Proxy information, if a proxy is being used.
    proxy: Option<ProxyInfo>,
    /// The URL scheme of the endpoint.
    target_scheme: String,
    /// The `User-Agent` header string to use for HTTP calls.
    user_agent_header: UserAgent,
}

impl ApiClient {
    /// Creates and returns a new `ApiClient` instance.
    ///
    /// # Errors
    ///
    /// * If the underlying Hyper client cannot be created
    /// * If a suitable SSL context cannot be established
    /// * If an HTTP proxy cannot be correctly setup
    /// * If a `User-Agent` HTTP header string cannot be constructed
    pub fn new<T>(
        endpoint: T,
        product: &str,
        version: &str,
        fs_root_path: Option<&Path>,
    ) -> Result<Self>
    where
        T: IntoUrl,
    {
        let endpoint = endpoint.into_url().map_err(Error::UrlParseError)?;
        Ok(ApiClient {
            inner: new_hyper_client(&endpoint, fs_root_path)?,
            proxy: proxy_unless_domain_exempted(Some(&endpoint))?,
            target_scheme: endpoint.scheme().to_string(),
            endpoint: endpoint,
            user_agent_header: user_agent(product, version)?,
        })
    }

    /// Builds an HTTP GET request for a given path.
    pub fn get(&self, path: &str) -> RequestBuilder {
        self.get_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP GET request for a given path with the ability to customize the target URL.
    pub fn get_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("GET {} with {:?}", &url, &self);
        self.add_headers(self.inner.get(url))
    }

    /// Builds an HTTP HEAD request for a given path.
    pub fn head(&self, path: &str) -> RequestBuilder {
        self.head_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP HEAD request for a given path with the ability to customize the target URL.
    pub fn head_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("HEAD {} with {:?}", &url, &self);
        self.add_headers(self.inner.head(url))
    }

    /// Builds an HTTP PATCH request for a given path.
    pub fn patch(&self, path: &str) -> RequestBuilder {
        self.patch_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PATCH request for a given path with the ability to customize the target URL.
    pub fn patch_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PATH {} with {:?}", &url, &self);
        self.add_headers(self.inner.patch(url))
    }

    /// Builds an HTTP POST request for a given path.
    pub fn post(&self, path: &str) -> RequestBuilder {
        self.post_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP POST request for a given path with the ability to customize the target URL.
    pub fn post_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("POST {} with {:?}", &url, &self);
        self.add_headers(self.inner.post(url))
    }

    /// Builds an HTTP PUT request for a given path.
    pub fn put(&self, path: &str) -> RequestBuilder {
        self.put_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP PUT request for a given path with the ability to customize the target URL.
    pub fn put_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("PUT {} with {:?}", &url, &self);
        self.add_headers(self.inner.put(url))
    }

    /// Builds an HTTP DELETE request for a given path.
    pub fn delete(&self, path: &str) -> RequestBuilder {
        self.delete_with_custom_url(path, |_| {})
    }

    /// Builds an HTTP DELETE request for a given path with the ability to customize the target URL.
    pub fn delete_with_custom_url<F>(&self, path: &str, mut customize_url: F) -> RequestBuilder
    where
        F: FnMut(&mut Url),
    {
        let mut url = self.url_for(path);
        customize_url(&mut url);
        debug!("DELETE {} with {:?}", &url, &self);
        self.add_headers(self.inner.delete(url))
    }

    fn add_headers<'a>(&'a self, rb: RequestBuilder<'a>) -> RequestBuilder {
        let mut rb = rb.header(self.user_agent_header.clone());
        // If the target URL is an `"http"` scheme and we're using a proxy server, then add the
        // proxy authorization header if appropriate. Note that for `"https"` targets, the proxy
        // server will be operating in TCP tunneling mode and will be authenticated on connection to
        // the proxy server which is why we should not add an additional header in this latter
        // case.
        if self.target_scheme == "http" {
            if let Some(ref info) = self.proxy {
                if let Some(header_value) = info.authorization_header_value() {
                    rb = rb.header(ProxyAuthorization(header_value));
                }
            }
        }
        rb
    }

    fn url_for(&self, path: &str) -> Url {
        let mut url = self.endpoint.clone();

        if path.is_empty() {
            return url;
        }

        if url.path().ends_with("/") || path.starts_with("/") {
            url.set_path(&format!("{}{}", self.endpoint.path(), path));
        } else {
            url.set_path(&format!("{}/{}", self.endpoint.path(), path));
        }

        url
    }
}

/// Builds a new hyper HTTP client with appropriate SSL configuration and HTTP/HTTPS proxy support.
///
/// ## Linux Platforms
///
/// We need a set of root certificates when connected to SSL/TLS web endpoints and this usually
/// boiled down to using an all-in-one certificate file (such as a `cacert.pem` file) or a directory
/// of files which are certificates. The strategy to location or use a reasonable set of
/// certificates is as follows:
///
/// 1. If the `SSL_CERT_FILE` environment variable is set, then use its value for the certificates.
///    Internally this is triggering default OpenSSL behavior for this environment variable.
/// 2. If the `SSL_CERT_DIR` environment variable is set, then use its value for the directory
///    containing certificates. Like the `SSL_CERT_FILE` case above, this triggers default OpenSSL
///    behavior for this environment variable.
/// 3. If the `core/cacerts` Habitat package is installed locally, then use the latest release's
///    `cacert.pem` file.
/// 4. If none of the conditions above are met, then a `cacert.pem` will be written in an SSL cache
///    directory (by default `/hab/cache/ssl` for a root user and `$HOME/.hab/cache/ssl` for a
///    non-root user) and this will be used. The contents of this file will be inlined in this
///    crate at build time as a fallback insurance policy, meaning that if the a program using this
///    code is operating in a minimal environment which may not contain system certificates, it can
///    still operate. Once a `core/cacerts` Habitat package is present, the logic would fall back
///    to preferring the package version to the cached/inline file version.
///
/// ## Mac Platforms
///
/// The Mac platform uses a Security Framework to store and find root certificates and the hyper
/// library will default to using this on the Mac. Therefore the behavior on the Mac remains
/// unchanged and will use the system's certificates.
///
fn new_hyper_client(url: &Url, fs_root_path: Option<&Path>) -> Result<HyperClient> {
    let connector = ssl_connector(fs_root_path)?;
    let ssl_client = OpensslClient::from(connector);
    let timeout = Some(Duration::from_secs(CLIENT_SOCKET_RW_TIMEOUT));

    match proxy_unless_domain_exempted(Some(url))? {
        Some(proxy) => {
            debug!("Using proxy {}:{}...", proxy.host(), proxy.port());
            let connector = ProxyHttpsConnector::new(proxy, ssl_client)?;
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
        None => {
            let connector = HttpsConnector::new(ssl_client);
            let pool = Pool::with_connector(Config::default(), connector);
            let mut client = HyperClient::with_protocol(Http11Protocol::with_connector(pool));
            client.set_read_timeout(timeout);
            client.set_write_timeout(timeout);
            Ok(client)
        }
    }
}

/// Returns an HTTP User-Agent string type for use by Hyper when making HTTP requests.
///
/// The general form for Habitat-related clients are of the following form:
///
/// ```text
/// <PRODUCT>/<VERSION> (<TARGET>; <KERNEL_RELEASE>)
/// ```
///
/// where:
///
/// * `<PRODUCT>`: is the provided product name
/// * `<VERSION>`: is the provided version string which may also include a release number
/// * `<TARGET>`: is the machine architecture and the kernel separated by a dash in lower case
/// * `<KERNEL_RELEASE>`: is the kernel release string from `uname`
///
/// For example:
///
/// ```text
/// hab/0.6.0/20160606153031 (x86_64-darwin; 14.5.0)
/// ```
///
/// # Errors
///
/// * If system information cannot be obtained via `uname`
fn user_agent(product: &str, version: &str) -> Result<UserAgent> {
    let uname = sys::uname()?;
    let ua = format!(
        "{}/{} ({}-{}; {})",
        product.trim(),
        version.trim(),
        uname.machine.trim().to_lowercase(),
        uname.sys_name.trim().to_lowercase(),
        uname.release.trim().to_lowercase()
    );
    debug!("User-Agent: {}", &ua);
    Ok(UserAgent(ua))
}

fn ssl_connector(fs_root_path: Option<&Path>) -> Result<SslConnector> {
    let mut conn = SslConnectorBuilder::new(SslMethod::tls())?;
    let mut options = SslOption::empty();
    options.toggle(SSL_OP_NO_SSLV2);
    options.toggle(SSL_OP_NO_SSLV3);
    options.toggle(SSL_OP_NO_COMPRESSION);
    ssl::set_ca(&mut conn, fs_root_path)?;
    conn.set_options(options);
    conn.set_cipher_list(
        "ALL!EXPORT!EXPORT40!EXPORT56!aNULL!LOW!RC4@STRENGTH",
    )?;
    Ok(conn.build())
}
<|endoftext|>


<commit_before>use clap::{App, Arg, ArgMatches};
use rav1e::*;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::slice;
use std::sync::Arc;
use std::time::Instant;
use y4m;

pub struct EncoderIO {
  pub input: Box<dyn Read>,
  pub output: Box<dyn Write>,
  pub rec: Option<Box<dyn Write>>
}

pub struct CliOptions {
  pub io: EncoderIO,
  pub enc: EncoderConfig,
  pub limit: usize,
  pub verbose: bool,
}

pub fn parse_cli() -> CliOptions {
  let matches = App::new("rav1e")
    .version("0.1.0")
    .about("AV1 video encoder")
    .arg(
      Arg::with_name("INPUT")
        .help("Uncompressed YUV4MPEG2 video input")
        .required(true)
        .index(1)
    ).arg(
      Arg::with_name("OUTPUT")
        .help("Compressed AV1 in IVF video output")
        .short("o")
        .long("output")
        .required(true)
        .takes_value(true)
    ).arg(Arg::with_name("RECONSTRUCTION").short("r").takes_value(true))
    .arg(
      Arg::with_name("LIMIT")
        .help("Maximum number of frames to encode")
        .short("l")
        .long("limit")
        .takes_value(true)
        .default_value("0")
    ).arg(
      Arg::with_name("QP")
        .help("Quantizer (0-255)")
        .long("quantizer")
        .takes_value(true)
        .default_value("100")
    ).arg(
      Arg::with_name("SPEED")
        .help("Speed level (0(slow)-10(fast))")
        .short("s")
        .long("speed")
        .takes_value(true)
        .default_value("3")
    ).arg(
      Arg::with_name("KEYFRAME_INTERVAL")
        .help("Keyframe interval")
        .short("I")
        .long("keyint")
        .takes_value(true)
        .default_value("30")
    ).arg(
      Arg::with_name("LOW_LATENCY")
        .help("low latency mode. true or false")
        .long("low_latency")
        .takes_value(true)
        .default_value("true")
    ).arg(
      Arg::with_name("TUNE")
        .help("Quality tuning (Will enforce partition sizes >= 8x8)")
        .long("tune")
        .possible_values(&Tune::variants())
        .default_value("psnr")
        .case_insensitive(true)
    ).arg(
      Arg::with_name("VERBOSE")
        .help("verbose logging, output info for every frame")
        .long("verbose")
        .short("v")
    ).get_matches();

  let io = EncoderIO {
    input: match matches.value_of("INPUT").unwrap() {
      "-" => Box::new(io::stdin()) as Box<dyn Read>,
      f => Box::new(File::open(&f).unwrap()) as Box<dyn Read>
    },
    output: match matches.value_of("OUTPUT").unwrap() {
      "-" => Box::new(io::stdout()) as Box<dyn Write>,
      f => Box::new(File::create(&f).unwrap()) as Box<dyn Write>
    },
    rec: matches
      .value_of("RECONSTRUCTION")
      .map(|f| Box::new(File::create(&f).unwrap()) as Box<dyn Write>)
  };

  CliOptions {
    io,
    enc: parse_config(&matches),
    limit: matches.value_of("LIMIT").unwrap().parse().unwrap(),
    verbose: matches.is_present("VERBOSE"),
  }
}

fn parse_config(matches: &ArgMatches) -> EncoderConfig {
  let speed = matches.value_of("SPEED").unwrap().parse().unwrap();
  let quantizer = matches.value_of("QP").unwrap().parse().unwrap();

  // Validate arguments
  if quantizer == 0 {
    unimplemented!("Lossless encoding not yet implemented");
  } else if quantizer > 255 || speed > 10 {
    panic!("argument out of range");
  }

  let mut cfg = EncoderConfig::with_speed_preset(speed);
  cfg.key_frame_interval = matches.value_of("KEYFRAME_INTERVAL").unwrap().parse().unwrap();
  cfg.low_latency = matches.value_of("LOW_LATENCY").unwrap().parse().unwrap();
  cfg.tune = matches.value_of("TUNE").unwrap().parse().unwrap();
  cfg.quantizer = quantizer;
  cfg
}

#[derive(Debug, Clone, Copy)]
pub struct FrameSummary {
  /// Frame size in bytes
  pub size: usize,
  pub number: u64,
  pub frame_type: FrameType
}

impl From<Packet> for FrameSummary {
  fn from(packet: Packet) -> Self {
    Self {
      size: packet.data.len(),
      number: packet.number,
      frame_type: packet.frame_type,
    }
  }
}

impl fmt::Display for FrameSummary {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(
      f,
      "Frame {} - {} - {} bytes",
      self.number,
      self.frame_type,
      self.size
    )
  }
}

/// Encode and write a frame.
/// Returns frame information in a `Result`.
pub fn process_frame(
  ctx: &mut Context, output_file: &mut dyn Write,
  y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>,
  mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>
) -> Result<Vec<FrameSummary>, ()> {
  let width = y4m_dec.get_width();
  let height = y4m_dec.get_height();
  let y4m_bits = y4m_dec.get_bit_depth();
  let y4m_bytes = y4m_dec.get_bytes_per_sample();
  let bit_depth = y4m_dec.get_colorspace().get_bit_depth();

  let read_frame =
  if ctx.needs_more_frames(ctx.get_frame_count()) {
    match y4m_dec.read_frame() {
      Ok(y4m_frame) => {
        let y4m_y = y4m_frame.get_y_plane();
        let y4m_u = y4m_frame.get_u_plane();
        let y4m_v = y4m_frame.get_v_plane();
        let mut input = ctx.new_frame();
        {
          let input = Arc::get_mut(&mut input).unwrap();
          input.planes[0].copy_from_raw_u8(&y4m_y, width * y4m_bytes, y4m_bytes);
          input.planes[1].copy_from_raw_u8(
            &y4m_u,
            width * y4m_bytes / 2,
            y4m_bytes
          );
          input.planes[2].copy_from_raw_u8(
            &y4m_v,
            width * y4m_bytes / 2,
            y4m_bytes
          );
        }

        match y4m_bits {
          8 | 10 | 12 => {}
          _ => panic!("unknown input bit depth!")
        }

        let _ = ctx.send_frame(input);
        true
      }
      _ => {
        let frames_to_be_coded = ctx.get_frame_count();
        ctx.set_frames_to_be_coded(frames_to_be_coded);
        ctx.flush();
        false
      }
    }
  } else {
    ctx.flush();
    false
  };

  let mut has_data = true;
  let mut frame_summaries = Vec::new();
  while has_data {
    let pkt_wrapped = ctx.receive_packet();
    match pkt_wrapped {
      Ok(pkt) => {
        write_ivf_frame(output_file, pkt.number as u64, pkt.data.as_ref());
        if let Some(y4m_enc_uw) = y4m_enc.as_mut() {
          if let Some(ref rec) = pkt.rec {
            let pitch_y = if bit_depth > 8 { width * 2 } else { width };
            let pitch_uv = pitch_y / 2;

            let (mut rec_y, mut rec_u, mut rec_v) = (
              vec![128u8; pitch_y * height],
              vec![128u8; pitch_uv * (height / 2)],
              vec![128u8; pitch_uv * (height / 2)]
            );

            let (stride_y, stride_u, stride_v) = (
              rec.planes[0].cfg.stride,
              rec.planes[1].cfg.stride,
              rec.planes[2].cfg.stride
            );

            for (line, line_out) in rec.planes[0]
              .data_origin()
              .chunks(stride_y)
              .zip(rec_y.chunks_mut(pitch_y))
            {
              if bit_depth > 8 {
                unsafe {
                  line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                    line.as_ptr() as (*const u8),
                    pitch_y
                  ));
                }
              } else {
                line_out.copy_from_slice(
                  &line.iter().map(|&v| v as u8).collect::<Vec<u8>>()[..pitch_y]
                );
              }
            }
            for (line, line_out) in rec.planes[1]
              .data_origin()
              .chunks(stride_u)
              .zip(rec_u.chunks_mut(pitch_uv))
            {
              if bit_depth > 8 {
                unsafe {
                  line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                    line.as_ptr() as (*const u8),
                    pitch_uv
                  ));
                }
              } else {
                line_out.copy_from_slice(
                  &line.iter().map(|&v| v as u8).collect::<Vec<u8>>()[..pitch_uv]
                );
              }
            }
            for (line, line_out) in rec.planes[2]
              .data_origin()
              .chunks(stride_v)
              .zip(rec_v.chunks_mut(pitch_uv))
            {
              if bit_depth > 8 {
                unsafe {
                  line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                    line.as_ptr() as (*const u8),
                    pitch_uv
                  ));
                }
              } else {
                line_out.copy_from_slice(
                  &line.iter().map(|&v| v as u8).collect::<Vec<u8>>()[..pitch_uv]
                );
              }
            }

            let rec_frame = y4m::Frame::new([&rec_y, &rec_u, &rec_v], None);
            y4m_enc_uw.write_frame(&rec_frame).unwrap();
          }
        }
        frame_summaries.push(pkt.into());
      },
      _ => { has_data = false; }
    }
  }
  Ok(frame_summaries)
}

#[derive(Debug, Clone)]
pub struct ProgressInfo {
  /// Frame rate of the video
  frame_rate: y4m::Ratio,
  /// The length of the whole video, in frames, if known
  total_frames: Option<usize>,
  /// The time the encode was started
  time_started: Instant,
  /// List of frames encoded so far
  frame_info: Vec<FrameSummary>,
  /// Video size so far in bytes.
  ///
  /// This value will be updated in the CLI very frequently, so we cache the previous value
  /// to reduce the overall complexity.
  encoded_size: usize,
}

impl ProgressInfo {
  pub fn new(frame_rate: y4m::Ratio, total_frames: Option<usize>) -> Self {
    Self {
      frame_rate,
      total_frames,
      time_started: Instant::now(),
      frame_info: Vec::with_capacity(total_frames.unwrap_or_default()),
      encoded_size: 0,
    }
  }

  pub fn add_frame(&mut self, frame: FrameSummary) {
    self.encoded_size += frame.size;
    self.frame_info.push(frame);
  }

  pub fn frames_encoded(&self) -> usize {
    self.frame_info.len()
  }

  pub fn encoding_fps(&self) -> f64 {
    let seconds = Instant::now().duration_since(self.time_started).as_secs();
    self.frame_info.len() as f64 / seconds as f64
  }

  pub fn video_fps(&self) -> f64 {
    self.frame_rate.num as f64 / self.frame_rate.den as f64
  }

  /// Returns the bitrate of the frames so far, in bits/second
  pub fn bitrate(&self) -> usize {
    let bits = self.encoded_size * 8;
    let seconds = self.frame_info.len() as f64 / self.video_fps();
    (bits as f64 / seconds) as usize
  }

  /// Estimates the final filesize in bytes, if the number of frames is known
  pub fn estimated_size(&self) -> usize {
    self.total_frames
      .map(|frames| self.encoded_size * frames / self.frames_encoded())
      .unwrap_or_default()
  }

  /// Number of frames of given type which appear in the video
  pub fn get_frame_type_count(&self, frame_type: FrameType) -> usize {
    self.frame_info.iter()
      .filter(|frame| frame.frame_type == frame_type)
      .count()
  }

  /// Size in bytes of all frames of given frame type
  pub fn get_frame_type_size(&self, frame_type: FrameType) -> usize {
    self.frame_info.iter()
      .filter(|frame| frame.frame_type == frame_type)
      .map(|frame| frame.size)
      .sum()
  }

  pub fn print_stats(&self) -> String {
    let (key, key_size) = (self.get_frame_type_count(FrameType::KEY), self.get_frame_type_size(FrameType::KEY));
    let (inter, inter_size) = (self.get_frame_type_count(FrameType::INTER), self.get_frame_type_size(FrameType::INTER));
    let (ionly, ionly_size) = (self.get_frame_type_count(FrameType::INTRA_ONLY), self.get_frame_type_size(FrameType::INTRA_ONLY));
    let (switch, switch_size) = (self.get_frame_type_count(FrameType::SWITCH), self.get_frame_type_size(FrameType::SWITCH));
    format!("\
    Key Frames: {:>6}    avg size: {:>7} B\n\
    Inter:      {:>6}    avg size: {:>7} B\n\
    Intra Only: {:>6}    avg size: {:>7} B\n\
    Switch:     {:>6}    avg size: {:>7} B",
      key, key_size / key,
      inter, inter_size / inter,
      ionly, ionly_size / key,
      switch, switch_size / key
    )
  }
}

impl fmt::Display for ProgressInfo {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    if let Some(total_frames) = self.total_frames {
      write!(
        f,
        "encoded {}/{} frames, {:.2} fps, {:.2} Kb/s, est. size: {:.2} MB",
        self.frames_encoded(),
        total_frames,
        self.encoding_fps(),
        self.bitrate() as f64 / 1024f64,
        self.estimated_size() as f64 / (1024 * 1024) as f64
      )
    } else {
      write!(
        f,
        "encoded {} frames, {:.2} fps, {:.2} Kb/s",
        self.frames_encoded(),
        self.encoding_fps(),
        self.bitrate() as f64 / 1024f64
      )
    }
  }
}
<commit_msg>Fix compile warning caused by 0b21d2f6 (#727)<commit_after>use clap::{App, Arg, ArgMatches};
use rav1e::*;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::slice;
use std::sync::Arc;
use std::time::Instant;
use y4m;

pub struct EncoderIO {
  pub input: Box<dyn Read>,
  pub output: Box<dyn Write>,
  pub rec: Option<Box<dyn Write>>
}

pub struct CliOptions {
  pub io: EncoderIO,
  pub enc: EncoderConfig,
  pub limit: usize,
  pub verbose: bool,
}

pub fn parse_cli() -> CliOptions {
  let matches = App::new("rav1e")
    .version("0.1.0")
    .about("AV1 video encoder")
    .arg(
      Arg::with_name("INPUT")
        .help("Uncompressed YUV4MPEG2 video input")
        .required(true)
        .index(1)
    ).arg(
      Arg::with_name("OUTPUT")
        .help("Compressed AV1 in IVF video output")
        .short("o")
        .long("output")
        .required(true)
        .takes_value(true)
    ).arg(Arg::with_name("RECONSTRUCTION").short("r").takes_value(true))
    .arg(
      Arg::with_name("LIMIT")
        .help("Maximum number of frames to encode")
        .short("l")
        .long("limit")
        .takes_value(true)
        .default_value("0")
    ).arg(
      Arg::with_name("QP")
        .help("Quantizer (0-255)")
        .long("quantizer")
        .takes_value(true)
        .default_value("100")
    ).arg(
      Arg::with_name("SPEED")
        .help("Speed level (0(slow)-10(fast))")
        .short("s")
        .long("speed")
        .takes_value(true)
        .default_value("3")
    ).arg(
      Arg::with_name("KEYFRAME_INTERVAL")
        .help("Keyframe interval")
        .short("I")
        .long("keyint")
        .takes_value(true)
        .default_value("30")
    ).arg(
      Arg::with_name("LOW_LATENCY")
        .help("low latency mode. true or false")
        .long("low_latency")
        .takes_value(true)
        .default_value("true")
    ).arg(
      Arg::with_name("TUNE")
        .help("Quality tuning (Will enforce partition sizes >= 8x8)")
        .long("tune")
        .possible_values(&Tune::variants())
        .default_value("psnr")
        .case_insensitive(true)
    ).arg(
      Arg::with_name("VERBOSE")
        .help("verbose logging, output info for every frame")
        .long("verbose")
        .short("v")
    ).get_matches();

  let io = EncoderIO {
    input: match matches.value_of("INPUT").unwrap() {
      "-" => Box::new(io::stdin()) as Box<dyn Read>,
      f => Box::new(File::open(&f).unwrap()) as Box<dyn Read>
    },
    output: match matches.value_of("OUTPUT").unwrap() {
      "-" => Box::new(io::stdout()) as Box<dyn Write>,
      f => Box::new(File::create(&f).unwrap()) as Box<dyn Write>
    },
    rec: matches
      .value_of("RECONSTRUCTION")
      .map(|f| Box::new(File::create(&f).unwrap()) as Box<dyn Write>)
  };

  CliOptions {
    io,
    enc: parse_config(&matches),
    limit: matches.value_of("LIMIT").unwrap().parse().unwrap(),
    verbose: matches.is_present("VERBOSE"),
  }
}

fn parse_config(matches: &ArgMatches) -> EncoderConfig {
  let speed = matches.value_of("SPEED").unwrap().parse().unwrap();
  let quantizer = matches.value_of("QP").unwrap().parse().unwrap();

  // Validate arguments
  if quantizer == 0 {
    unimplemented!("Lossless encoding not yet implemented");
  } else if quantizer > 255 || speed > 10 {
    panic!("argument out of range");
  }

  let mut cfg = EncoderConfig::with_speed_preset(speed);
  cfg.key_frame_interval = matches.value_of("KEYFRAME_INTERVAL").unwrap().parse().unwrap();
  cfg.low_latency = matches.value_of("LOW_LATENCY").unwrap().parse().unwrap();
  cfg.tune = matches.value_of("TUNE").unwrap().parse().unwrap();
  cfg.quantizer = quantizer;
  cfg
}

#[derive(Debug, Clone, Copy)]
pub struct FrameSummary {
  /// Frame size in bytes
  pub size: usize,
  pub number: u64,
  pub frame_type: FrameType
}

impl From<Packet> for FrameSummary {
  fn from(packet: Packet) -> Self {
    Self {
      size: packet.data.len(),
      number: packet.number,
      frame_type: packet.frame_type,
    }
  }
}

impl fmt::Display for FrameSummary {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(
      f,
      "Frame {} - {} - {} bytes",
      self.number,
      self.frame_type,
      self.size
    )
  }
}

/// Encode and write a frame.
/// Returns frame information in a `Result`.
pub fn process_frame(
  ctx: &mut Context, output_file: &mut dyn Write,
  y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>,
  mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>
) -> Result<Vec<FrameSummary>, ()> {
  let width = y4m_dec.get_width();
  let height = y4m_dec.get_height();
  let y4m_bits = y4m_dec.get_bit_depth();
  let y4m_bytes = y4m_dec.get_bytes_per_sample();
  let bit_depth = y4m_dec.get_colorspace().get_bit_depth();

  if ctx.needs_more_frames(ctx.get_frame_count()) {
    match y4m_dec.read_frame() {
      Ok(y4m_frame) => {
        let y4m_y = y4m_frame.get_y_plane();
        let y4m_u = y4m_frame.get_u_plane();
        let y4m_v = y4m_frame.get_v_plane();
        let mut input = ctx.new_frame();
        {
          let input = Arc::get_mut(&mut input).unwrap();
          input.planes[0].copy_from_raw_u8(&y4m_y, width * y4m_bytes, y4m_bytes);
          input.planes[1].copy_from_raw_u8(
            &y4m_u,
            width * y4m_bytes / 2,
            y4m_bytes
          );
          input.planes[2].copy_from_raw_u8(
            &y4m_v,
            width * y4m_bytes / 2,
            y4m_bytes
          );
        }

        match y4m_bits {
          8 | 10 | 12 => {}
          _ => panic!("unknown input bit depth!")
        }

        let _ = ctx.send_frame(input);
        true
      }
      _ => {
        let frames_to_be_coded = ctx.get_frame_count();
        ctx.set_frames_to_be_coded(frames_to_be_coded);
        ctx.flush();
        false
      }
    }
  } else {
    ctx.flush();
    false
  };

  let mut has_data = true;
  let mut frame_summaries = Vec::new();
  while has_data {
    let pkt_wrapped = ctx.receive_packet();
    match pkt_wrapped {
      Ok(pkt) => {
        write_ivf_frame(output_file, pkt.number as u64, pkt.data.as_ref());
        if let Some(y4m_enc_uw) = y4m_enc.as_mut() {
          if let Some(ref rec) = pkt.rec {
            let pitch_y = if bit_depth > 8 { width * 2 } else { width };
            let pitch_uv = pitch_y / 2;

            let (mut rec_y, mut rec_u, mut rec_v) = (
              vec![128u8; pitch_y * height],
              vec![128u8; pitch_uv * (height / 2)],
              vec![128u8; pitch_uv * (height / 2)]
            );

            let (stride_y, stride_u, stride_v) = (
              rec.planes[0].cfg.stride,
              rec.planes[1].cfg.stride,
              rec.planes[2].cfg.stride
            );

            for (line, line_out) in rec.planes[0]
              .data_origin()
              .chunks(stride_y)
              .zip(rec_y.chunks_mut(pitch_y))
            {
              if bit_depth > 8 {
                unsafe {
                  line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                    line.as_ptr() as (*const u8),
                    pitch_y
                  ));
                }
              } else {
                line_out.copy_from_slice(
                  &line.iter().map(|&v| v as u8).collect::<Vec<u8>>()[..pitch_y]
                );
              }
            }
            for (line, line_out) in rec.planes[1]
              .data_origin()
              .chunks(stride_u)
              .zip(rec_u.chunks_mut(pitch_uv))
            {
              if bit_depth > 8 {
                unsafe {
                  line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                    line.as_ptr() as (*const u8),
                    pitch_uv
                  ));
                }
              } else {
                line_out.copy_from_slice(
                  &line.iter().map(|&v| v as u8).collect::<Vec<u8>>()[..pitch_uv]
                );
              }
            }
            for (line, line_out) in rec.planes[2]
              .data_origin()
              .chunks(stride_v)
              .zip(rec_v.chunks_mut(pitch_uv))
            {
              if bit_depth > 8 {
                unsafe {
                  line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                    line.as_ptr() as (*const u8),
                    pitch_uv
                  ));
                }
              } else {
                line_out.copy_from_slice(
                  &line.iter().map(|&v| v as u8).collect::<Vec<u8>>()[..pitch_uv]
                );
              }
            }

            let rec_frame = y4m::Frame::new([&rec_y, &rec_u, &rec_v], None);
            y4m_enc_uw.write_frame(&rec_frame).unwrap();
          }
        }
        frame_summaries.push(pkt.into());
      },
      _ => { has_data = false; }
    }
  }
  Ok(frame_summaries)
}

#[derive(Debug, Clone)]
pub struct ProgressInfo {
  /// Frame rate of the video
  frame_rate: y4m::Ratio,
  /// The length of the whole video, in frames, if known
  total_frames: Option<usize>,
  /// The time the encode was started
  time_started: Instant,
  /// List of frames encoded so far
  frame_info: Vec<FrameSummary>,
  /// Video size so far in bytes.
  ///
  /// This value will be updated in the CLI very frequently, so we cache the previous value
  /// to reduce the overall complexity.
  encoded_size: usize,
}

impl ProgressInfo {
  pub fn new(frame_rate: y4m::Ratio, total_frames: Option<usize>) -> Self {
    Self {
      frame_rate,
      total_frames,
      time_started: Instant::now(),
      frame_info: Vec::with_capacity(total_frames.unwrap_or_default()),
      encoded_size: 0,
    }
  }

  pub fn add_frame(&mut self, frame: FrameSummary) {
    self.encoded_size += frame.size;
    self.frame_info.push(frame);
  }

  pub fn frames_encoded(&self) -> usize {
    self.frame_info.len()
  }

  pub fn encoding_fps(&self) -> f64 {
    let seconds = Instant::now().duration_since(self.time_started).as_secs();
    self.frame_info.len() as f64 / seconds as f64
  }

  pub fn video_fps(&self) -> f64 {
    self.frame_rate.num as f64 / self.frame_rate.den as f64
  }

  /// Returns the bitrate of the frames so far, in bits/second
  pub fn bitrate(&self) -> usize {
    let bits = self.encoded_size * 8;
    let seconds = self.frame_info.len() as f64 / self.video_fps();
    (bits as f64 / seconds) as usize
  }

  /// Estimates the final filesize in bytes, if the number of frames is known
  pub fn estimated_size(&self) -> usize {
    self.total_frames
      .map(|frames| self.encoded_size * frames / self.frames_encoded())
      .unwrap_or_default()
  }

  /// Number of frames of given type which appear in the video
  pub fn get_frame_type_count(&self, frame_type: FrameType) -> usize {
    self.frame_info.iter()
      .filter(|frame| frame.frame_type == frame_type)
      .count()
  }

  /// Size in bytes of all frames of given frame type
  pub fn get_frame_type_size(&self, frame_type: FrameType) -> usize {
    self.frame_info.iter()
      .filter(|frame| frame.frame_type == frame_type)
      .map(|frame| frame.size)
      .sum()
  }

  pub fn print_stats(&self) -> String {
    let (key, key_size) = (self.get_frame_type_count(FrameType::KEY), self.get_frame_type_size(FrameType::KEY));
    let (inter, inter_size) = (self.get_frame_type_count(FrameType::INTER), self.get_frame_type_size(FrameType::INTER));
    let (ionly, ionly_size) = (self.get_frame_type_count(FrameType::INTRA_ONLY), self.get_frame_type_size(FrameType::INTRA_ONLY));
    let (switch, switch_size) = (self.get_frame_type_count(FrameType::SWITCH), self.get_frame_type_size(FrameType::SWITCH));
    format!("\
    Key Frames: {:>6}    avg size: {:>7} B\n\
    Inter:      {:>6}    avg size: {:>7} B\n\
    Intra Only: {:>6}    avg size: {:>7} B\n\
    Switch:     {:>6}    avg size: {:>7} B",
      key, key_size / key,
      inter, inter_size / inter,
      ionly, ionly_size / key,
      switch, switch_size / key
    )
  }
}

impl fmt::Display for ProgressInfo {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    if let Some(total_frames) = self.total_frames {
      write!(
        f,
        "encoded {}/{} frames, {:.2} fps, {:.2} Kb/s, est. size: {:.2} MB",
        self.frames_encoded(),
        total_frames,
        self.encoding_fps(),
        self.bitrate() as f64 / 1024f64,
        self.estimated_size() as f64 / (1024 * 1024) as f64
      )
    } else {
      write!(
        f,
        "encoded {} frames, {:.2} fps, {:.2} Kb/s",
        self.frames_encoded(),
        self.encoding_fps(),
        self.bitrate() as f64 / 1024f64
      )
    }
  }
}
<|endoftext|>


<commit_before>extern crate mdbook;
#[macro_use]
extern crate clap;

use std::env;
use std::error::Error;
use std::io::{self, Write};
use std::path::{Path, PathBuf};

use clap::{App, ArgMatches, SubCommand};

use mdbook::MDBook;

const NAME: &'static str = "mdbook";

fn main() {
    // Create a list of valid arguments and sub-commands
    let matches = App::new(NAME)
                    .about("Create a book in form of a static website from markdown files")
                    .author("Mathieu David <mathieudavid@mathieudavid.org>")
                    // Get the version from our Cargo.toml using clap's crate_version!() macro
                    .version(&*format!("v{}", crate_version!()))
                    .subcommand_required(true)
                    .after_help("For more information about a specific command, try `mdbook <command> --help`")
                    .subcommand(SubCommand::with_name("init")
                        .about("Create boilerplate structure and files in the directory")
                        // the {n} denotes a newline which will properly aligned in all help messages
                        .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")
                        .arg_from_usage("--theme 'Copies the default theme into your source folder'"))
                    .subcommand(SubCommand::with_name("build")
                        .about("Build the book from the markdown files")
                        .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'"))
                    .subcommand(SubCommand::with_name("watch")
                        .about("Watch the files for changes"))
                    .get_matches();

    // Check which subcomamnd the user ran...
    let res = match matches.subcommand() {
        ("init", Some(sub_matches))  => init(sub_matches),
        ("build", Some(sub_matches)) => build(sub_matches),
        ("watch", _)                 => unimplemented!(),
        (_, _)                       => unreachable!()
    };

    if let Err(e) = res {
        writeln!(&mut io::stderr(), "An error occured:\n{}", e).ok();
    }
}

fn init(args: &ArgMatches) -> Result<(), Box<Error>> {

    let book_dir = get_book_dir(args);
    let book = MDBook::new(&book_dir);

    // Call the function that does the initialization
    try!(book.init());

    // If flag `--theme` is present, copy theme to src
    if args.is_present("theme") {

        // Print warning
        print!("\nCopying the default theme to {:?}", book.get_src());
        println!("could potentially overwrite files already present in that directory.");
        print!("\nAre you sure you want to continue? (y/n) ");

        // Read answer from user

        // Joke while I don't read user response, has to be deleted when merged into master !!!
        println!("\n\nI am doing it anyways... (at the moment)");

        // Call the function that copies the theme
        try!(book.copy_theme());

        println!("");
    }

    Ok(())
}

fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::new(&book_dir).read_config();

    try!(book.build());

    Ok(())
}

fn get_book_dir(args: &ArgMatches) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() {
           env::current_dir().unwrap().join(dir)
        } else {
           p.to_path_buf()
        }
    } else {
        env::current_dir().unwrap()
    }
}
<commit_msg>Added user prompting and a --force argument. Closes #35<commit_after>extern crate mdbook;
#[macro_use]
extern crate clap;

use std::env;
use std::error::Error;
use std::io::{self, Write};
use std::path::{Path, PathBuf};

use clap::{App, ArgMatches, SubCommand};

use mdbook::MDBook;

const NAME: &'static str = "mdbook";

fn main() {
    // Create a list of valid arguments and sub-commands
    let matches = App::new(NAME)
                    .about("Create a book in form of a static website from markdown files")
                    .author("Mathieu David <mathieudavid@mathieudavid.org>")
                    // Get the version from our Cargo.toml using clap's crate_version!() macro
                    .version(&*format!("v{}", crate_version!()))
                    .subcommand_required(true)
                    .after_help("For more information about a specific command, try `mdbook <command> --help`")
                    .subcommand(SubCommand::with_name("init")
                        .about("Create boilerplate structure and files in the directory")
                        // the {n} denotes a newline which will properly aligned in all help messages
                        .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'")
                        .arg_from_usage("--theme 'Copies the default theme into your source folder'")
                        .arg_from_usage("--force 'skip confirmation prompts'"))
                    .subcommand(SubCommand::with_name("build")
                        .about("Build the book from the markdown files")
                        .arg_from_usage("[dir] 'A directory for your book{n}(Defaults to Current Directory when ommitted)'"))
                    .subcommand(SubCommand::with_name("watch")
                        .about("Watch the files for changes"))
                    .get_matches();

    // Check which subcomamnd the user ran...
    let res = match matches.subcommand() {
        ("init", Some(sub_matches))  => init(sub_matches),
        ("build", Some(sub_matches)) => build(sub_matches),
        ("watch", _)                 => unimplemented!(),
        (_, _)                       => unreachable!()
    };

    if let Err(e) = res {
        writeln!(&mut io::stderr(), "An error occured:\n{}", e).ok();
    }
}

fn confirm() -> bool {
    io::stdout().flush().unwrap();
    let mut s = String::new();
    io::stdin().read_line(&mut s).ok();
    match &*s.trim() {
        "Y" | "y" | "yes" | "Yes" => true,
        _ => false
    }
}

fn init(args: &ArgMatches) -> Result<(), Box<Error>> {

    let book_dir = get_book_dir(args);
    let book = MDBook::new(&book_dir);

    // Call the function that does the initialization
    try!(book.init());

    // If flag `--theme` is present, copy theme to src
    if args.is_present("theme") {

        // Skip this id `--force` is present
        if !args.is_present("force") {
            // Print warning
            print!("\nCopying the default theme to {:?}", book.get_src());
            println!("could potentially overwrite files already present in that directory.");
            print!("\nAre you sure you want to continue? (y/n) ");

            // Read answer from user and exit if it's not 'yes'
            if !confirm() {
                println!("\nexiting...\n");
                ::std::process::exit(0);
            }
        }

        // Call the function that copies the theme
        try!(book.copy_theme());
        println!("\nTheme copied.");

        println!("");
    }

    Ok(())
}

fn build(args: &ArgMatches) -> Result<(), Box<Error>> {
    let book_dir = get_book_dir(args);
    let mut book = MDBook::new(&book_dir).read_config();

    try!(book.build());

    Ok(())
}

fn get_book_dir(args: &ArgMatches) -> PathBuf {
    if let Some(dir) = args.value_of("dir") {
        // Check if path is relative from current dir, or absolute...
        let p = Path::new(dir);
        if p.is_relative() {
           env::current_dir().unwrap().join(dir)
        } else {
           p.to_path_buf()
        }
    } else {
        env::current_dir().unwrap()
    }
}
<|endoftext|>