<commit_before>#![feature(path)]
extern crate tcl;
use std::path::Path;

#[test]
fn create_interp() {
    let env = tcl::init();
    env.interpreter();
}
#[test]
fn interp_safety() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    assert_eq!(interp.is_safe(), false);
    assert_eq!(interp.make_safe().is_ok(), true);
    assert_eq!(interp.is_safe(), true);
}
#[test]
fn empty_string_result() {
    let env = tcl::init();
    let interp = env.interpreter();
    assert_eq!("".to_string(), interp.string_result());
}
#[test]
fn eval_simple_file_fail() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.eval_file(&Path::new("HOLOLO")) {
        tcl::TclResult::Error(message) => {
            assert_eq!("couldn\'t read file \"HOLOLO\": no such file or directory".to_string(), message)
        },
        otherwise => panic!("Should have errored, instead got {:?}", otherwise)
    }
}

#[test]
fn eval_simple_file() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.eval_file(&Path::new("tests/simple-test.tcl")) {
        tcl::TclResult::Ok => {
            assert_eq!("6".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn eval_simple() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.eval("return Hello", tcl::EvalScope::Local) {
        tcl::TclResult::Ok => {
            assert_eq!("Hello".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn eval_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let command = env.string("expr {1 + 2}");
    match interp.eval_object(&command, tcl::EvalScope::Local, tcl::ByteCompile::Compile) {
        tcl::TclResult::Ok => {
            assert_eq!("3".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn object_result() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    interp.eval("expr { 1 + 2 }", tcl::EvalScope::Local);
    let obj = interp.object_result();

    let result = interp.get_integer_from_object(&obj);
    match result {
        Ok(x) => assert_eq!(3, x),
        Err(s) => panic!("{}", s)
    }
}

#[test]
fn list_append() {
    let env = tcl::init();

    let mut interp = env.interpreter();
    let mut command_list = env.object();
    interp.list_append(&mut command_list, &env.string("expr"));
    interp.list_append(&mut command_list, &env.string("1+2"));

    match interp.eval_object(&command_list, tcl::EvalScope::Local, tcl::ByteCompile::Compile) {
        tcl::TclResult::Ok => {
            assert_eq!("3".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_boolean() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_boolean("1 == 1") {
        Ok(result) => assert_eq!(true, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_boolean_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1 == 1");
    match interp.expression_boolean_from_object(&expr) {
        Ok(result) => assert_eq!(true, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_double() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_double("1 / 2") {
        Ok(result) => assert_eq!(0.5, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_double_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1 / 2");
    match interp.expression_double_from_object(&expr) {
        Ok(result) => assert_eq!(0.5, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_long() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_long("1 + 1") {
        Ok(result) => assert_eq!(2, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_long_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1 + 1");
    match interp.expression_long_from_object(&expr) {
        Ok(result) => assert_eq!(2, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_object_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1 + 1");
    match interp.expression_object_from_object(&expr) {
        Ok(result) => {
            assert_eq!("2".to_string(), result.get_string())
        },
        Err(otherwise) => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_string() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_string("1 + 1") {
        tcl::TclResult::Ok => {
            assert_eq!("2".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}
<commit_msg>Corrected tests<commit_after>#![feature(path)]
extern crate tcl;
use std::path::Path;

#[test]
fn create_interp() {
    let env = tcl::init();
    env.interpreter();
}
#[test]
fn interp_safety() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    assert_eq!(interp.is_safe(), false);
    assert_eq!(interp.make_safe().is_ok(), true);
    assert_eq!(interp.is_safe(), true);
}
#[test]
fn empty_string_result() {
    let env = tcl::init();
    let interp = env.interpreter();
    assert_eq!("".to_string(), interp.string_result());
}
#[test]
fn eval_simple_file_fail() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.eval_file(&Path::new("HOLOLO")) {
        tcl::TclResult::Error(message) => {
            assert_eq!("couldn\'t read file \"HOLOLO\": no such file or directory".to_string(), message)
        },
        otherwise => panic!("Should have errored, instead got {:?}", otherwise)
    }
}

#[test]
fn eval_simple_file() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.eval_file(&Path::new("tests/simple-test.tcl")) {
        tcl::TclResult::Ok => {
            assert_eq!("6".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn eval_simple() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.eval("return Hello", tcl::EvalScope::Local) {
        tcl::TclResult::Ok => {
            assert_eq!("Hello".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn eval_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let command = env.string("expr {1 + 2}");
    match interp.eval_object(&command, tcl::EvalScope::Local, tcl::ByteCompile::Compile) {
        tcl::TclResult::Ok => {
            assert_eq!("3".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn object_result() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    interp.eval("expr { 1 + 2 }", tcl::EvalScope::Local);
    let obj = interp.object_result();

    let result = interp.get_integer_from_object(&obj);
    match result {
        Ok(x) => assert_eq!(3, x),
        Err(s) => panic!("{}", s)
    }
}

#[test]
fn list_append() {
    let env = tcl::init();

    let mut interp = env.interpreter();
    let mut command_list = env.object();
    interp.list_append(&mut command_list, &env.string("expr"));
    interp.list_append(&mut command_list, &env.string("1+2"));

    match interp.eval_object(&command_list, tcl::EvalScope::Local, tcl::ByteCompile::Compile) {
        tcl::TclResult::Ok => {
            assert_eq!("3".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_boolean() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_boolean("1 == 1") {
        Ok(result) => assert_eq!(true, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_boolean_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1 == 1");
    match interp.expression_boolean_from_object(&expr) {
        Ok(result) => assert_eq!(true, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_double() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_double("1.0 / 2.0") {
        Ok(result) => assert_eq!(0.5, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_double_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1.0 / 2.0");
    match interp.expression_double_from_object(&expr) {
        Ok(result) => assert_eq!(0.5, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_long() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_long("1 + 1") {
        Ok(result) => assert_eq!(2, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_long_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1 + 1");
    match interp.expression_long_from_object(&expr) {
        Ok(result) => assert_eq!(2, result),
        otherwise => panic!("{:?}", otherwise)
    }
}

#[test]
fn expression_object_from_object() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    let expr = env.string("1 + 1");
    match interp.expression_object_from_object(&expr) {
        Ok(result) => {
            assert_eq!("2".to_string(), result.get_string())
        },
        Err(otherwise) => panic!("{:?}", otherwise)
    };
}

#[test]
fn expression_string() {
    let env = tcl::init();
    let mut interp = env.interpreter();
    match interp.expression_string("1 + 1") {
        tcl::TclResult::Ok => {
            assert_eq!("2".to_string(), interp.string_result())
        },
        otherwise => panic!("{:?}", otherwise)
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate itertools;

extern crate libimagnotes;
extern crate libimagrt;
extern crate libimagentryedit;
extern crate libimagerror;
extern crate libimagutil;

use std::process::exit;

use itertools::Itertools;

use libimagentryedit::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagnotes::note::Note;
use libimagerror::trace::{MapErrTrace, trace_error};
use libimagutil::info_result::*;
use libimagutil::warn_result::WarnResult;

mod ui;
use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-notes",
                                    &version!()[..],
                                    "Note taking helper",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "create" => create(&rt),
                "delete" => delete(&rt),
                "edit"   => edit(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn name_from_cli(rt: &Runtime, subcmd: &str) -> String {
    rt.cli().subcommand_matches(subcmd).unwrap().value_of("name").map(String::from).unwrap()
}

fn create(rt: &Runtime) {
    let name = name_from_cli(rt, "create");
    Note::new(rt.store(), name.clone(), String::new()).map_err_trace().ok();

    if rt.cli().subcommand_matches("create").unwrap().is_present("edit") &&
            !edit_entry(rt, name) {
        exit(1);
    }
}

fn delete(rt: &Runtime) {
    Note::delete(rt.store(), String::from(name_from_cli(rt, "delete")))
        .map_err_trace()
        .map_info_str("Ok")
        .ok();
}

fn edit(rt: &Runtime) {
    edit_entry(rt, name_from_cli(rt, "edit"));
}

fn edit_entry(rt: &Runtime, name: String) -> bool {
    let mut note = match Note::get(rt.store(), name) {
        Ok(Some(note)) => note,
        Ok(None) => {
            warn!("Cannot edit nonexistent Note");
            return false
        },
        Err(e) => {
            trace_error(&e);
            warn!("Cannot edit nonexistent Note");
            return false
        },
    };

    note.edit_content(rt).map_err_trace().map_warn_err_str("Editing failed").is_ok()
}

fn list(rt: &Runtime) {
    use std::cmp::Ordering;

    Note::all_notes(rt.store())
        .map_err_trace_exit(1)
        .map(|iter| {
            let notes = iter.filter_map(|note| note.map_err_trace().ok())
                .sorted_by(|note_a, note_b| {
                    if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
                        return a.cmp(&b)
                    } else {
                        return Ordering::Greater;
                    }
                });

            for note in notes.iter() {
                note.get_name()
                    .map(|name| println!("{}", name))
                    .map_err_trace()
                    .ok();
            }
        })
        .ok();
}

<commit_msg>Refactor and simplify imag-notes binary<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate itertools;

extern crate libimagnotes;
extern crate libimagrt;
extern crate libimagentryedit;
extern crate libimagerror;
extern crate libimagutil;

use itertools::Itertools;

use libimagentryedit::edit::Edit;
use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagnotes::note::Note;
use libimagnotes::notestore::*;
use libimagerror::trace::MapErrTrace;
use libimagutil::info_result::*;
use libimagutil::warn_result::WarnResult;

mod ui;
use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-notes",
                                    &version!()[..],
                                    "Note taking helper",
                                    build_ui);

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "create" => create(&rt),
                "delete" => delete(&rt),
                "edit"   => edit(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn name_from_cli(rt: &Runtime, subcmd: &str) -> String {
    rt.cli().subcommand_matches(subcmd).unwrap().value_of("name").map(String::from).unwrap()
}

fn create(rt: &Runtime) {
    let name = name_from_cli(rt, "create");
    let mut note = rt
        .store()
        .new_note(name.clone(), String::new())
        .map_err_trace_exit(1)
        .unwrap();

    if rt.cli().subcommand_matches("create").unwrap().is_present("edit") {
        let _ = note
            .edit_content(rt)
            .map_warn_err_str("Editing failed")
            .map_err_trace_exit(1);
    }
}

fn delete(rt: &Runtime) {
    let _ = rt.store()
        .delete_note(name_from_cli(rt, "delete"))
        .map_info_str("Ok")
        .map_err_trace_exit(1);
}

fn edit(rt: &Runtime) {
    let name = name_from_cli(rt, "edit");
    let _ = rt
        .store()
        .get_note(name.clone())
        .map_err_trace_exit(1)
        .unwrap()
        .map(|mut note| {
            let _ = note
                .edit_content(rt)
                .map_warn_err_str("Editing failed")
                .map_err_trace_exit(1);
        })
        .unwrap_or_else(|| {
            error!("Cannot find note with name '{}'", name);
        });
}

fn list(rt: &Runtime) {
    use std::cmp::Ordering;

    rt.store()
        .all_notes()
        .map_err_trace_exit(1)
        .map(|iter| {
            let notes = iter
                .filter_map(|noteid| rt.store().get(noteid).map_err_trace_exit(1).unwrap())
                .sorted_by(|note_a, note_b| {
                    if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
                        return a.cmp(&b)
                    } else {
                        return Ordering::Greater;
                    }
                });

            for note in notes.iter() {
                note.get_name()
                    .map(|name| println!("{}", name))
                    .map_err_trace()
                    .ok();
            }
        })
        .ok();
}

<|endoftext|>


<commit_before>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashMap, HashSet};
use std::cmp;

use tempdir::TempDir;

use collector::{Benchmark as CollectedBenchmark, BenchmarkState, Patch, Run, Stat};

use errors::{Result, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

use Mode;

fn run(mut cmd: Command) -> Result<process::Output> {
    trace!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<()> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name '*.rs' | xargs touch"]);
    run(cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base(&self) -> Command {
        let mut cargo = self.sysroot.command("cargo");
        cargo
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env(
                "CARGO_INCREMENTAL",
                &format!("{}", self.incremental.0 as usize),
            )
            .env("USE_PERF", &format!("{}", self.perf as usize));
        cargo
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn run(self, cwd: &Path, mut cargo: Cargo) -> Result<process::Output> {
        let mut cmd = self.base();
        cmd.current_dir(cwd);
        if self.options.check && cargo == Cargo::Build {
            cargo = Cargo::Check;
        }
        debug!(
            "running cargo {:?}{}",
            cargo,
            if self.options.release {
                " --release"
            } else {
                ""
            }
        );
        cmd.arg(match cargo {
            Cargo::Check | Cargo::Build => "rustc",
            Cargo::Clean => "clean",
        });
        {
            let mut cargo = self.base();
            cargo
                .current_dir(cwd)
                .arg("pkgid")
                .arg("--manifest-path")
                .arg(&self.manifest_path);
            let out = run(cargo)
                .unwrap_or_else(|e| {
                    panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
                })
                .stdout;
            let package_id = str::from_utf8(&out).unwrap();
            cmd.arg("-p").arg(package_id.trim());
        }
        if cargo == Cargo::Check {
            cmd.arg("--profile").arg("check");
        }
        if self.options.release {
            cmd.arg("--release");
        }
        cmd.arg("--manifest-path").arg(&self.manifest_path);
        if cargo.takes_args() {
            cmd.args(&self.cargo_args);
            cmd.arg("--");
            cmd.arg("-Ztime-passes");
            cmd.args(&self.rustc_args);
        }
        run(cmd)
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Cargo {
    Build,
    Check,
    Clean,
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

impl Cargo {
    fn takes_args(self) -> bool {
        match self {
            Cargo::Build | Cargo::Check => true,
            Cargo::Clean => false,
        }
    }
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path).chain_err(|| format!("failed to open {:?}", config_path))?)
                .chain_err(|| format!("failed to parse {:?}", config_path))?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name,
            path,
            patches,
            config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd.arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        run(cmd).chain_err(|| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn cargo<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            manifest_path: self.config
                .cargo_toml
                .clone()
                .unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args: self.config
                .cargo_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
            rustc_args: self.config
                .cargo_rustc_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(
        &self,
        sysroot: &Sysroot,
        iterations: usize,
        mode: Mode,
    ) -> Result<CollectedBenchmark> {
        let iterations = cmp::min(iterations, self.config.runs);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        match mode {
            Mode::Normal => {
                if self.config.run_debug {
                    opts.push(Options {
                        check: true,
                        release: false,
                    });
                    opts.push(Options {
                        check: false,
                        release: false,
                    });
                }
                if self.config.run_optimized {
                    opts.push(Options {
                        check: false,
                        release: true,
                    });
                }
            }
            Mode::Test => {
                opts.push(Options {
                    check: true,
                    release: false,
                });
            }
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            info!(
                "Benchmarking {} with release: {}, check: {}, iterations: {}",
                self.name, opt.release, opt.check, iterations
            );

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.cargo(sysroot, Incremental(false), opt)
                .run(base_build.path(), Cargo::Build)?;
            clean_stats.push(process_output(clean.stdout)?);
            self.cargo(sysroot, Incremental(false), opt)
                .perf(false)
                .run(base_build.path(), Cargo::Clean)?;

            for i in 0..iterations {
                debug!("Benchmark iteration {}/{}", i + 1, iterations);
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.cargo(sysroot, Incremental(false), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr_clean = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;

                clean_stats.push(process_output(clean.stdout)?);
                incr_stats.push(process_output(incr.stdout)?);
                incr_clean_stats.push(process_output(incr_clean.stdout)?);

                for patch in &self.patches {
                    debug!("applying patch {}", patch.name);
                    patch.apply(tmp_dir.path())?;
                    touch_all(tmp_dir.path())?;
                    let out = self.cargo(sysroot, Incremental(true), opt)
                        .run(tmp_dir.path(), Cargo::Build)?;
                    let out = process_output(out.stdout)?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut().find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs
                .push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalStart,
                incr_stats,
            ));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalClean,
                incr_clean_stats,
            ));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

fn process_output(output: Vec<u8>) -> Result<Vec<Stat>> {
    let output = String::from_utf8(output)?;
    let mut stats = Vec::new();

    for line in output.lines() {
        // github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e:expr) => (match $e {
                Some(s) => s,
                None => {
                    warn!("unhandled line: {}", line);
                    continue;
                },
            });
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name, pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse()
                .chain_err(|| format!("failed to parse `{}` as an float", cnt))?,
        });
    }

    assert!(
        stats.len() >= 1,
        "at least one stat collected, stdout: {:?}",
        output
    );

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats
                .entry(stat.name.clone())
                .or_insert_with(|| Vec::new())
                .push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("options: {:?}", options);
        eprintln!("state: {:?}", state);
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats
        .into_iter()
        .map(|(stat, counts)| Stat {
            name: stat,
            cnt: counts
                .into_iter()
                .fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<commit_msg>Print both stdout and stderr when we failed to collect any stats<commit_after>//! Execute benchmarks in a sysroot.

use std::env;
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
use std::f64;
use std::fs::{self, File};
use std::collections::{HashMap, HashSet};
use std::cmp;

use tempdir::TempDir;

use collector::{Benchmark as CollectedBenchmark, BenchmarkState, Patch, Run, Stat};

use errors::{Result, ResultExt};
use rust_sysroot::sysroot::Sysroot;
use serde_json;

use Mode;

fn run(mut cmd: Command) -> Result<process::Output> {
    trace!("running: {:?}", cmd);
    let output = cmd.output()?;
    if !output.status.success() {
        bail!(
            "expected success, got {}\n\nstderr={}\n\n stdout={}",
            output.status,
            String::from_utf8_lossy(&output.stderr),
            String::from_utf8_lossy(&output.stdout)
        );
    }
    Ok(output)
}

fn touch_all(path: &Path) -> Result<()> {
    let mut cmd = Command::new("bash");
    cmd.current_dir(path)
        .args(&["-c", "find . -name '*.rs' | xargs touch"]);
    run(cmd)?;
    Ok(())
}

fn default_runs() -> usize {
    3
}

fn default_true() -> bool {
    true
}

#[derive(Debug, Clone, Deserialize)]
struct BenchmarkConfig {
    cargo_opts: Option<String>,
    cargo_rustc_opts: Option<String>,
    cargo_toml: Option<String>,
    #[serde(default)]
    disabled: bool,
    #[serde(default = "default_runs")]
    runs: usize,
    #[serde(default = "default_true")]
    run_optimized: bool,
    #[serde(default = "default_true")]
    run_debug: bool,
}

impl Default for BenchmarkConfig {
    fn default() -> BenchmarkConfig {
        BenchmarkConfig {
            cargo_opts: None,
            cargo_rustc_opts: None,
            cargo_toml: None,
            disabled: false,
            runs: default_runs(),
            run_optimized: true,
            run_debug: true,
        }
    }
}

pub struct Benchmark {
    pub name: String,
    pub path: PathBuf,
    patches: Vec<Patch>,
    config: BenchmarkConfig,
}

struct CargoProcess<'sysroot> {
    sysroot: &'sysroot Sysroot,
    incremental: Incremental,
    options: Options,
    perf: bool,
    manifest_path: String,
    cargo_args: Vec<String>,
    rustc_args: Vec<String>,
}

#[derive(Copy, Clone, Debug)]
struct Incremental(bool);

impl<'sysroot> CargoProcess<'sysroot> {
    fn base(&self) -> Command {
        let mut cargo = self.sysroot.command("cargo");
        cargo
            .env("SHELL", env::var_os("SHELL").unwrap_or_default())
            .env("RUSTC", &*FAKE_RUSTC)
            .env("RUSTC_REAL", &self.sysroot.rustc)
            .env(
                "CARGO_INCREMENTAL",
                &format!("{}", self.incremental.0 as usize),
            )
            .env("USE_PERF", &format!("{}", self.perf as usize));
        cargo
    }

    fn perf(mut self, perf: bool) -> Self {
        self.perf = perf;
        self
    }

    fn run(self, cwd: &Path, mut cargo: Cargo) -> Result<process::Output> {
        let mut cmd = self.base();
        cmd.current_dir(cwd);
        if self.options.check && cargo == Cargo::Build {
            cargo = Cargo::Check;
        }
        debug!(
            "running cargo {:?}{}",
            cargo,
            if self.options.release {
                " --release"
            } else {
                ""
            }
        );
        cmd.arg(match cargo {
            Cargo::Check | Cargo::Build => "rustc",
            Cargo::Clean => "clean",
        });
        {
            let mut cargo = self.base();
            cargo
                .current_dir(cwd)
                .arg("pkgid")
                .arg("--manifest-path")
                .arg(&self.manifest_path);
            let out = run(cargo)
                .unwrap_or_else(|e| {
                    panic!("failed to obtain pkgid in {:?}: {:?}", cwd, e);
                })
                .stdout;
            let package_id = str::from_utf8(&out).unwrap();
            cmd.arg("-p").arg(package_id.trim());
        }
        if cargo == Cargo::Check {
            cmd.arg("--profile").arg("check");
        }
        if self.options.release {
            cmd.arg("--release");
        }
        cmd.arg("--manifest-path").arg(&self.manifest_path);
        if cargo.takes_args() {
            cmd.args(&self.cargo_args);
            cmd.arg("--");
            cmd.arg("-Ztime-passes");
            cmd.args(&self.rustc_args);
        }
        run(cmd)
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Cargo {
    Build,
    Check,
    Clean,
}

#[derive(Debug, PartialEq, Clone, Copy)]
struct Options {
    release: bool,
    check: bool,
}

impl Cargo {
    fn takes_args(self) -> bool {
        match self {
            Cargo::Build | Cargo::Check => true,
            Cargo::Clean => false,
        }
    }
}

lazy_static! {
    static ref FAKE_RUSTC: PathBuf = {
        let mut fake_rustc = env::current_exe().unwrap();
        fake_rustc.pop();
        fake_rustc.push("rustc-fake");
        fake_rustc
    };
}

impl Benchmark {
    pub fn new(name: String, path: PathBuf) -> Result<Self> {
        let mut patches = vec![];
        for entry in fs::read_dir(&path)? {
            let entry = entry?;
            let path = entry.path();
            if let Some(ext) = path.extension() {
                if ext == "patch" {
                    patches.push(path.clone());
                }
            }
        }

        patches.sort();

        let patches = patches.into_iter().map(|p| Patch::new(p)).collect();

        let config_path = path.join("perf-config.json");
        let config: BenchmarkConfig = if config_path.exists() {
            serde_json::from_reader(File::open(&config_path).chain_err(|| format!("failed to open {:?}", config_path))?)
                .chain_err(|| format!("failed to parse {:?}", config_path))?
        } else {
            BenchmarkConfig::default()
        };

        Ok(Benchmark {
            name,
            path,
            patches,
            config,
        })
    }

    fn make_temp_dir(&self, base: &Path) -> Result<TempDir> {
        let tmp_dir = TempDir::new(&format!("rustc-benchmark-{}", self.name))?;
        let mut cmd = Command::new("cp");
        cmd.arg("-r")
            .arg("-T")
            .arg("--")
            .arg(base)
            .arg(tmp_dir.path());
        run(cmd).chain_err(|| format!("copying {} to tmp dir", self.name))?;
        Ok(tmp_dir)
    }

    fn cargo<'a>(
        &self,
        sysroot: &'a Sysroot,
        incremental: Incremental,
        options: Options,
    ) -> CargoProcess<'a> {
        CargoProcess {
            sysroot: sysroot,
            incremental: incremental,
            options: options,
            perf: true,
            manifest_path: self.config
                .cargo_toml
                .clone()
                .unwrap_or_else(|| String::from("Cargo.toml")),
            cargo_args: self.config
                .cargo_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
            rustc_args: self.config
                .cargo_rustc_opts
                .clone()
                .unwrap_or_default()
                .split_whitespace()
                .map(String::from)
                .collect(),
        }
    }

    /// Run a specific benchmark on a specific commit
    pub fn run(
        &self,
        sysroot: &Sysroot,
        iterations: usize,
        mode: Mode,
    ) -> Result<CollectedBenchmark> {
        let iterations = cmp::min(iterations, self.config.runs);
        if self.config.disabled {
            eprintln!("skipping {}: disabled", self.name);
            bail!("disabled benchmark");
        }

        let has_perf = Command::new("perf").output().is_ok();
        assert!(has_perf);

        let mut ret = CollectedBenchmark {
            name: self.name.clone(),
            runs: Vec::new(),
        };

        let mut opts = Vec::with_capacity(3);
        match mode {
            Mode::Normal => {
                if self.config.run_debug {
                    opts.push(Options {
                        check: true,
                        release: false,
                    });
                    opts.push(Options {
                        check: false,
                        release: false,
                    });
                }
                if self.config.run_optimized {
                    opts.push(Options {
                        check: false,
                        release: true,
                    });
                }
            }
            Mode::Test => {
                opts.push(Options {
                    check: true,
                    release: false,
                });
            }
        }

        for opt in opts {
            let mut clean_stats = Vec::new();
            let mut incr_stats = Vec::new();
            let mut incr_clean_stats = Vec::new();
            let mut incr_patched_stats: Vec<(Patch, Vec<Vec<Stat>>)> = Vec::new();

            info!(
                "Benchmarking {} with release: {}, check: {}, iterations: {}",
                self.name, opt.release, opt.check, iterations
            );

            let base_build = self.make_temp_dir(&self.path)?;
            let clean = self.cargo(sysroot, Incremental(false), opt)
                .run(base_build.path(), Cargo::Build)?;
            clean_stats.push(process_output(clean)?);
            self.cargo(sysroot, Incremental(false), opt)
                .perf(false)
                .run(base_build.path(), Cargo::Clean)?;

            for i in 0..iterations {
                debug!("Benchmark iteration {}/{}", i + 1, iterations);
                let tmp_dir = self.make_temp_dir(base_build.path())?;

                let clean = self.cargo(sysroot, Incremental(false), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;
                touch_all(tmp_dir.path())?;
                let incr_clean = self.cargo(sysroot, Incremental(true), opt)
                    .run(tmp_dir.path(), Cargo::Build)?;

                clean_stats.push(process_output(clean)?);
                incr_stats.push(process_output(incr)?);
                incr_clean_stats.push(process_output(incr_clean)?);

                for patch in &self.patches {
                    debug!("applying patch {}", patch.name);
                    patch.apply(tmp_dir.path())?;
                    touch_all(tmp_dir.path())?;
                    let out = self.cargo(sysroot, Incremental(true), opt)
                        .run(tmp_dir.path(), Cargo::Build)?;
                    let out = process_output(out)?;
                    if let Some(mut entry) = incr_patched_stats.iter_mut().find(|s| &s.0 == patch) {
                        entry.1.push(out);
                        continue;
                    }

                    incr_patched_stats.push((patch.clone(), vec![out]));
                }
            }

            ret.runs
                .push(process_stats(opt, BenchmarkState::Clean, clean_stats));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalStart,
                incr_stats,
            ));
            ret.runs.push(process_stats(
                opt,
                BenchmarkState::IncrementalClean,
                incr_clean_stats,
            ));

            for (patch, results) in incr_patched_stats {
                ret.runs.push(process_stats(
                    opt,
                    BenchmarkState::IncrementalPatched(patch),
                    results,
                ));
            }
        }

        Ok(ret)
    }
}

fn process_output(output: process::Output) -> Result<Vec<Stat>> {
    let output = String::from_utf8(output.stdout)?;
    let mut stats = Vec::new();

    for line in output.lines() {
        // github.com/torvalds/linux/blob/bc78d646e708/tools/perf/Documentation/perf-stat.txt#L281
        macro_rules! get {
            ($e:expr) => (match $e {
                Some(s) => s,
                None => {
                    warn!("unhandled line: {}", line);
                    continue;
                },
            });
        }
        let mut parts = line.split(';').map(|s| s.trim());
        let cnt = get!(parts.next());
        let _unit = get!(parts.next());
        let name = get!(parts.next());
        let _time = get!(parts.next());
        let pct = get!(parts.next());
        if cnt == "<not supported>" {
            continue;
        }
        if !pct.starts_with("100.") {
            panic!(
                "measurement of `{}` only active for {}% of the time",
                name, pct
            );
        }
        stats.push(Stat {
            name: name.to_string(),
            cnt: cnt.parse()
                .chain_err(|| format!("failed to parse `{}` as an float", cnt))?,
        });
    }

    assert!(
        stats.len() >= 1,
        "at least one stat collected, output: {:?}",
        output
    );

    Ok(stats)
}

fn process_stats(options: Options, state: BenchmarkState, runs: Vec<Vec<Stat>>) -> Run {
    let mut stats: HashMap<String, Vec<f64>> = HashMap::new();
    for run in runs.clone() {
        for stat in run {
            stats
                .entry(stat.name.clone())
                .or_insert_with(|| Vec::new())
                .push(stat.cnt);
        }
    }
    // all stats should be present in all runs
    let map = stats.values().map(|v| v.len()).collect::<HashSet<_>>();
    if map.len() != 1 {
        eprintln!("options: {:?}", options);
        eprintln!("state: {:?}", state);
        eprintln!("lengths: {:?}", map);
        eprintln!("runs: {:?}", runs);
        eprintln!("stats: {:?}", stats);
        panic!("expected all stats to be present in all runs");
    }
    let stats = stats
        .into_iter()
        .map(|(stat, counts)| Stat {
            name: stat,
            cnt: counts
                .into_iter()
                .fold(f64::INFINITY, |acc, v| f64::min(acc, v)),
        })
        .collect();

    Run {
        stats,
        check: options.check,
        release: options.release,
        state: state,
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use crate::decoders::basics::*;

const DECODE_CACHE_BITS: u32 = 11;

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // Represent the weird shifts that are needed for some NEF files
  pub shiftval: [u32;256],

  // Enable the workaround for 16 bit decodes in DNG that need to consume those
  // bits instead of the value being implied
  pub dng_bug: bool,

  // In CRW we only use the len code so the cache is not needed
  pub disable_cache: bool,

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private

  // The max number of bits in a huffman code and the table that converts those
  // bits into how many bits to consume and the decoded length and shift
  nbits: u32,
  hufftable: Vec<(u16,u16,u16)>,

  // A pregenerated table that goes straight to decoding a diff without first
  // finding a length, fetching bits, and sign extending them. The table is
  // sized by DECODE_CACHE_BITS and can have 97%+ hit rate with 11 bits
  decodecache: [Option<(u16,i32)>; 1<< DECODE_CACHE_BITS],

  initialized: bool,
}

struct MockPump {
  bits: u64,
  nbits: u32,
}

impl MockPump {
  pub fn empty() -> Self {
    MockPump {
      bits: 0,
      nbits: 0,
    }
  }

  pub fn set(&mut self, bits: u32, nbits: u32) {
    self.bits = (bits as u64) << 32;
    self.nbits = nbits + 32;
  }

  pub fn validbits(&self) -> i32 {
    self.nbits as i32 - 32
  }
}

impl BitPump for MockPump {
  fn peek_bits(&mut self, num: u32) -> u32 {
    (self.bits >> (self.nbits-num)) as u32
  }

  fn consume_bits(&mut self, num: u32) {
    self.nbits -= num;
    self.bits &= (1 << self.nbits) - 1;
  }
}

impl HuffTable {
  pub fn empty() -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      shiftval: [0;256],
      dng_bug: false,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    }
  }

  pub fn new(bits: [u32;17], huffval: [u32;256], dng_bug: bool) -> Result<HuffTable,String> {
    let mut tbl = HuffTable {
      bits: bits,
      huffval: huffval,
      shiftval: [0;256],
      dng_bug: dng_bug,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    };
    tbl.initialize()?;
    Ok(tbl)
  }

  pub fn initialize(&mut self) -> Result<(), String> {
    // Find out the max code length and allocate a table with that size
    self.nbits = 16;
    for i in 0..16 {
      if self.bits[16-i] != 0 {
        break;
      }
      self.nbits -= 1;
    }
    self.hufftable = vec![(0,0,0); 1 << self.nbits];

    // Fill in the table itself
    let mut h = 0;
    let mut pos = 0;
    for len in 0..self.nbits {
      for _ in 0..self.bits[len as usize + 1] {
        for _ in 0..(1 << (self.nbits-len-1)) {
          self.hufftable[h] = (len as u16 + 1, self.huffval[pos] as u16, self.shiftval[pos] as u16);
          h += 1;
        }
        pos += 1;
      }
    }

    // Create the decode cache by running the slow code over all the possible
    // values DECODE_CACHE_BITS wide
    if !self.disable_cache {
      let mut pump = MockPump::empty();
      let mut i = 0;
      loop {
        pump.set(i, DECODE_CACHE_BITS);
        let decode = self.huff_decode_slow(&mut pump);
        if pump.validbits() >= 0 {
          self.decodecache[i as usize] = Some(decode);
        }
        i += 1;
        if i >= 1 << DECODE_CACHE_BITS {
          break;
        }
      }
    }

    self.initialized = true;
    Ok(())
  }

  pub fn huff_decode(&self, pump: &mut dyn BitPump) -> Result<i32,String> {
    let code = pump.peek_bits(DECODE_CACHE_BITS) as usize;
    if let Some((bits,decode)) = self.decodecache[code] {
      pump.consume_bits(bits as u32);
      Ok(decode)
    } else {
      let decode = self.huff_decode_slow(pump);
      Ok(decode.1)
    }
  }

  pub fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u16,i32) {
    let len = self.huff_len(pump);
    (len.0+len.1, self.huff_diff(pump, len))
  }

  pub fn huff_len(&self, pump: &mut dyn BitPump) -> (u16,u16,u16) {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, shift) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    (bits, len, shift)
  }

  pub fn huff_diff(&self, pump: &mut dyn BitPump, input: (u16,u16,u16)) -> i32 {
    let (_, len, shift) = input;

    match len {
      0 => 0,
      16 => {
        if self.dng_bug {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // decode the difference and extend sign bit
        let fulllen: i32 = len as i32 + shift as i32;
        let shift: i32 = shift as i32;
        let bits = pump.get_bits(len as u32) as i32;
        let mut diff: i32 = ((bits << 1) + 1) << shift >> 1;
        if (diff & (1 << (fulllen - 1))) == 0 {
          diff -= (1 << fulllen) - ((shift == 0) as i32);
        }
        diff
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}
<commit_msg>Make the huffman decoding table smaller<commit_after>use std::fmt;
use crate::decoders::basics::*;

const DECODE_CACHE_BITS: u32 = 11;

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // Represent the weird shifts that are needed for some NEF files
  pub shiftval: [u32;256],

  // Enable the workaround for 16 bit decodes in DNG that need to consume those
  // bits instead of the value being implied
  pub dng_bug: bool,

  // In CRW we only use the len code so the cache is not needed
  pub disable_cache: bool,

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private

  // The max number of bits in a huffman code and the table that converts those
  // bits into how many bits to consume and the decoded length and shift
  nbits: u32,
  hufftable: Vec<(u8,u8,u8)>,

  // A pregenerated table that goes straight to decoding a diff without first
  // finding a length, fetching bits, and sign extending them. The table is
  // sized by DECODE_CACHE_BITS and can have 97%+ hit rate with 11 bits
  decodecache: [Option<(u8,i32)>; 1<< DECODE_CACHE_BITS],

  initialized: bool,
}

struct MockPump {
  bits: u64,
  nbits: u32,
}

impl MockPump {
  pub fn empty() -> Self {
    MockPump {
      bits: 0,
      nbits: 0,
    }
  }

  pub fn set(&mut self, bits: u32, nbits: u32) {
    self.bits = (bits as u64) << 32;
    self.nbits = nbits + 32;
  }

  pub fn validbits(&self) -> i32 {
    self.nbits as i32 - 32
  }
}

impl BitPump for MockPump {
  fn peek_bits(&mut self, num: u32) -> u32 {
    (self.bits >> (self.nbits-num)) as u32
  }

  fn consume_bits(&mut self, num: u32) {
    self.nbits -= num;
    self.bits &= (1 << self.nbits) - 1;
  }
}

impl HuffTable {
  pub fn empty() -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      shiftval: [0;256],
      dng_bug: false,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    }
  }

  pub fn new(bits: [u32;17], huffval: [u32;256], dng_bug: bool) -> Result<HuffTable,String> {
    let mut tbl = HuffTable {
      bits: bits,
      huffval: huffval,
      shiftval: [0;256],
      dng_bug: dng_bug,
      disable_cache: false,

      nbits: 0,
      hufftable: Vec::new(),
      decodecache: [None; 1 << DECODE_CACHE_BITS],
      initialized: false,
    };
    tbl.initialize()?;
    Ok(tbl)
  }

  pub fn initialize(&mut self) -> Result<(), String> {
    // Find out the max code length and allocate a table with that size
    self.nbits = 16;
    for i in 0..16 {
      if self.bits[16-i] != 0 {
        break;
      }
      self.nbits -= 1;
    }
    self.hufftable = vec![(0,0,0); 1 << self.nbits];

    // Fill in the table itself
    let mut h = 0;
    let mut pos = 0;
    for len in 0..self.nbits {
      for _ in 0..self.bits[len as usize + 1] {
        for _ in 0..(1 << (self.nbits-len-1)) {
          self.hufftable[h] = (len as u8 + 1, self.huffval[pos] as u8, self.shiftval[pos] as u8);
          h += 1;
        }
        pos += 1;
      }
    }

    // Create the decode cache by running the slow code over all the possible
    // values DECODE_CACHE_BITS wide
    if !self.disable_cache {
      let mut pump = MockPump::empty();
      let mut i = 0;
      loop {
        pump.set(i, DECODE_CACHE_BITS);
        let decode = self.huff_decode_slow(&mut pump);
        if pump.validbits() >= 0 {
          self.decodecache[i as usize] = Some(decode);
        }
        i += 1;
        if i >= 1 << DECODE_CACHE_BITS {
          break;
        }
      }
    }

    self.initialized = true;
    Ok(())
  }

  pub fn huff_decode(&self, pump: &mut dyn BitPump) -> Result<i32,String> {
    let code = pump.peek_bits(DECODE_CACHE_BITS) as usize;
    if let Some((bits,decode)) = self.decodecache[code] {
      pump.consume_bits(bits as u32);
      Ok(decode)
    } else {
      let decode = self.huff_decode_slow(pump);
      Ok(decode.1)
    }
  }

  pub fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u8,i32) {
    let len = self.huff_len(pump);
    (len.0+len.1, self.huff_diff(pump, len))
  }

  pub fn huff_len(&self, pump: &mut dyn BitPump) -> (u8,u8,u8) {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, shift) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    (bits, len, shift)
  }

  pub fn huff_diff(&self, pump: &mut dyn BitPump, input: (u8,u8,u8)) -> i32 {
    let (_, len, shift) = input;

    match len {
      0 => 0,
      16 => {
        if self.dng_bug {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // decode the difference and extend sign bit
        let fulllen: i32 = len as i32 + shift as i32;
        let shift: i32 = shift as i32;
        let bits = pump.get_bits(len as u32) as i32;
        let mut diff: i32 = ((bits << 1) + 1) << shift >> 1;
        if (diff & (1 << (fulllen - 1))) == 0 {
          diff -= (1 << fulllen) - ((shift == 0) as i32);
        }
        diff
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use specs;

#[derive(Clone, Debug)]
pub struct MouseInputState {
  pub mouse_left: Option<(f64, f64)>,
  pub mouse_right: Option<(f64, f64)>,
}

impl MouseInputState {
  pub fn new() -> MouseInputState {
    MouseInputState {
      mouse_left: None,
      mouse_right: None,
    }
  }
}

impl specs::Component for MouseInputState {
  type Storage = specs::HashMapStorage<MouseInputState>;
}

#[derive(Debug)]
pub enum MouseControl {
  LeftClick,
  RightClick,
}

#[derive(Debug)]
pub struct MouseControlSystem {
  queue: mpsc::Receiver<(MouseControl, Option<(f64, f64)>)>,
  left_click_pos: Option<(f64, f64)>,
  right_click_pos: Option<(f64, f64)>,
}

impl MouseControlSystem {
  pub fn new() -> (MouseControlSystem, mpsc::Sender<(MouseControl, Option<(f64, f64)>)>) {
    let (tx, rx) = mpsc::channel();
    (MouseControlSystem {
      queue: rx,
      left_click_pos: None,
      right_click_pos: None,
    }, tx)
  }
}

impl<C> specs::System<C> for MouseControlSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    let _mouse_input = arg.fetch(|w| w.write::<MouseInputState>());

    while let Ok((control_value, value)) = self.queue.try_recv() {
      match control_value {
        MouseControl::LeftClick => println!("left click {:?} {:?}", value, control_value)
        MouseControl::RightClick => println!("right click {:?} {:?}", value, control_value)
      }
    }
  }
}
<commit_msg>Add missing commas<commit_after>use std::sync::mpsc;
use specs;

#[derive(Clone, Debug)]
pub struct MouseInputState {
  pub mouse_left: Option<(f64, f64)>,
  pub mouse_right: Option<(f64, f64)>,
}

impl MouseInputState {
  pub fn new() -> MouseInputState {
    MouseInputState {
      mouse_left: None,
      mouse_right: None,
    }
  }
}

impl specs::Component for MouseInputState {
  type Storage = specs::HashMapStorage<MouseInputState>;
}

#[derive(Debug)]
pub enum MouseControl {
  LeftClick,
  RightClick,
}

#[derive(Debug)]
pub struct MouseControlSystem {
  queue: mpsc::Receiver<(MouseControl, Option<(f64, f64)>)>,
  left_click_pos: Option<(f64, f64)>,
  right_click_pos: Option<(f64, f64)>,
}

impl MouseControlSystem {
  pub fn new() -> (MouseControlSystem, mpsc::Sender<(MouseControl, Option<(f64, f64)>)>) {
    let (tx, rx) = mpsc::channel();
    (MouseControlSystem {
      queue: rx,
      left_click_pos: None,
      right_click_pos: None,
    }, tx)
  }
}

impl<C> specs::System<C> for MouseControlSystem {
  fn run(&mut self, arg: specs::RunArg, _: C) {
    let _mouse_input = arg.fetch(|w| w.write::<MouseInputState>());

    while let Ok((control_value, value)) = self.queue.try_recv() {
      match control_value {
        MouseControl::LeftClick => println!("left click {:?} {:?}", value, control_value),
        MouseControl::RightClick => println!("right click {:?} {:?}", value, control_value),
      }
    }
  }
}
<|endoftext|>


<commit_before>/* diosix capsule management
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use core::sync::atomic::{AtomicUsize, Ordering};
use spin::Mutex;
use hashbrown::hash_map::HashMap;
use hashbrown::hash_map::Entry::{Occupied, Vacant};
use hashbrown::hash_set::HashSet;
use alloc::vec::Vec;
use alloc::string::String;
use platform::cpu::Entry;
use platform::physmem::PhysMemBase;
use super::error::Cause;
use super::physmem;
use super::virtmem::Mapping;
use super::vcore::{self, Priority, VirtualCoreID};
use super::service::ServiceID;
use super::service;
use super::pcore;
use super::manifest;

pub type CapsuleID = usize;

/* arbitrarily allow up to CAPSULES_MAX capsules in the system */
const CAPSULES_MAX: usize = 1000000;

/* needed to assign system-wide unique capsule ID numbers */
lazy_static!
{
    static ref CAPSULE_ID_NEXT: AtomicUsize = AtomicUsize::new(0);
}

/* maintain a shared table of capsules */
lazy_static!
{
    /* acquire CAPSULES lock before accessing any capsules */
    static ref CAPSULES: Mutex<HashMap<CapsuleID, Capsule>> = Mutex::new(HashMap::new());
}

/* define who or what is responsible for restarting this capsule */
#[derive(Debug)]
pub enum RestartMethod
{
    FromManifest(String),   /* reload from the included manifest fs image */
    FromSpawnService        /* tell the capsule spawning service to reload us */
}

struct Capsule
{
    restart_on_crash: bool,                 /* true to auto-restart on unexpected death */
    restart_method: RestartMethod,          /* how to restart this capsule */
    vcores: HashSet<VirtualCoreID>,         /* set of virtual core IDs assigned to this capsule */
    memory: Vec<Mapping>,                   /* map capsule supervisor virtual addresses to host physical addresses */
    allowed_services: HashSet<ServiceID>,   /* set of services this capsule is allowed to provide */
    debug_buffer: String                    /* buffer to hold debug output until it's flushed */
}

impl Capsule
{
    /* create a new empty capsule using the current capsule on this physical CPU core.
    => auto_restart_flag = tue to auto-restart on crash by the hypervisor
    <= capsule object, or error code */
    pub fn new(restart_on_crash: bool, restart_method: RestartMethod) -> Result<Capsule, Cause>
    {
        Ok(Capsule
        {
            restart_on_crash,
            restart_method,
            vcores: HashSet::new(),
            memory: Vec::new(),
            allowed_services: HashSet::new(),
            debug_buffer: String::new()
        })
    }

    /* add a mapping to this capsule */
    pub fn set_memory_mapping(&mut self, to_add: Mapping)
    {
        self.memory.push(to_add);
    }

    /* get a copy of the capsule's memory mappings */
    pub fn get_memory_mappings(&self) -> Vec<Mapping> { self.memory.clone() }

    /* boot capsules are auto-restarted by the hypervisor */
    pub fn set_auto_crash_restart(&mut self, flag: bool)
    {
        self.restart_on_crash = flag;
    }

    /* returns true if this will auto-restart, or false if not */
    pub fn will_auto_crash_restart(&self) -> bool
    {
        self.restart_on_crash
    }

    /* returns restart method */
    pub fn get_restart_method(&self) -> &RestartMethod { &self.restart_method }

    /* add a virtual core ID to the capsule */
    pub fn add_vcore(&mut self, id: VirtualCoreID)
    {
        self.vcores.insert(id);
    }

    /* remove a virtual core ID from the capsule. returns true if that
       ID was present in the registered list */
    pub fn remove_vcore(&mut self, id: VirtualCoreID) -> bool
    {
        self.vcores.remove(&id)
    }

    /* return number of registered virtual cores */
    pub fn count_vcores(&self) -> usize
    {
        self.vcores.len()
    }

    /* allow capsule to register service sid */
    pub fn allow_service(&mut self, sid: ServiceID)
    {
        self.allowed_services.insert(sid);
    }

    /* check whether this capsule is allowed to register the given service
        <= true if allowed, false if not */
    pub fn check_service(&self, sid: ServiceID) -> bool
    {
        self.allowed_services.contains(&sid)
    }
}

/* handle the destruction of a capsule */
impl Drop for Capsule
{
    fn drop(&mut self)
    {
        hvdebug!("Tearing down capsule {:p}", &self);

        /* free up memory... */
        for mapping in self.memory.clone()
        {
            if let Some(r) = mapping.get_physical()
            {
                match physmem::dealloc_region(r)
                {
                    Err(e) => hvalert!("Error during capsule {:p} teardown: {:?}", &self, e),
                    Ok(_) => ()
                };
            }
        }

        /* ...and services held by this capsule */
        for sid in self.allowed_services.iter()
        {
            match service::deregister(*sid)
            {
                Err(e) => hvalert!("Failed to deregister service during teardown of capsule {:p}: {:?}", &self, e),
                Ok(()) => ()
            };
        }
    }
}

/* create a virtual core and add it to the given capsule
   => cid = capsule ID
      vid = virtual core ID
      entry = starting address for execution of this virtual core
      dtb = physical address of the device tree blob describing
            the virtual hardware environment
      prio = priority to run this virtual core
   <= return Ok for success, or error code
*/
pub fn add_vcore(cid: CapsuleID, vid: VirtualCoreID, entry: Entry, dtb: PhysMemBase, prio: Priority) -> Result<(), Cause>
{
    vcore::VirtualCore::create(cid, vid, entry, dtb, prio)?;
    match CAPSULES.lock().get_mut(&cid)
    {
        Some(c) => c.add_vcore(vid),
        None => return Err(Cause::CapsuleBadID)
    };
    Ok(())
}

/* create a new blank capsule
   Once created, it needs to be given a supervisor image, at least.
   then it is ready to be scheduled by assigning it virtual CPU cores.
   => auto_crash_restart = true to be auto-restarted by hypervisor if the capsule crashes
   <= CapsuleID for this new capsule, or an error code */
pub fn create(auto_crash_restart: bool, restart_method: RestartMethod) -> Result<CapsuleID, Cause>
{
    /* repeatedly try to generate an available ID */
    loop
    {
        /* bail out if we're at the limit */
        let mut capsules = CAPSULES.lock();
        if capsules.len() > CAPSULES_MAX
        {
            return Err(Cause::CapsuleIDExhaustion);
        }

        /* get next ID and check to see if this capsule already exists */
        let new_id = CAPSULE_ID_NEXT.fetch_add(1, Ordering::SeqCst);
        match capsules.entry(new_id)
        {
            Vacant(_) =>
            {
                /* insert our new capsule */
                capsules.insert(new_id, Capsule::new(auto_crash_restart, restart_method)?);

                /* we're all done here */
                return Ok(new_id);
            },
            _ => () /* try again */
        };
    }
}

/* mark a capsule as dying, meaning its virtual cores will be
   gradually removed and when there are none left, its RAM
   and any other resources will be deallocated.
   => cid = ID of capsule to kill
   <= Ok for success, or an error code
*/
pub fn destroy(cid: CapsuleID) -> Result<(), Cause>
{
    if let Some(victim) = CAPSULES.lock().remove(&cid)
    {
        drop(victim); // see above implementation of drop for Capsule
        Ok(())
    }
    else
    {
        Err(Cause::CapsuleBadID)
    }
}

/* mark a capsule as not only dying, but also recreate it.
   => cid = ID of capsule to restart
   <= Ok for success, or an error code
*/
pub fn restart(cid: CapsuleID) -> Result<(), Cause>
{
    let mut lock = CAPSULES.lock();
    if let Some(victim) = lock.remove(&cid)
    {
        match victim.get_restart_method()
        {
            RestartMethod::FromManifest(s) => match manifest::get_named_asset(&s.as_str())
            {
                Ok(a) =>
                {
                    drop(victim);
                    drop(lock);
                    manifest::load_asset(a)?;
                },
                Err(e) => return Err(e)
            },
            method => hvdebug!("Cannot restart capsule ID {} with method {:?}", cid, method)
        }
    }
    else
    {
        return Err(Cause::CapsuleBadID)
    }

    return Ok(());
}

/* destroy the currently running capsule */
pub fn destroy_current() -> Result<(), Cause>
{
    if let Some(c) = pcore::PhysicalCore::get_capsule_id()
    {
        if let Err(e) = destroy(c)
        {
            hvalert!("BUG: Could not kill currently running capsule ID {} ({:?})", c, e);
        }

        return Ok(())
    }

    Err(Cause::CapsuleBadID)
}

/* restart the currently running capsule */
pub fn restart_current() -> Result<(), Cause>
{
    if let Some(c) = pcore::PhysicalCore::get_capsule_id()
    {
        if let Err(e) = restart(c)
        {
            hvalert!("BUG: Could not restart currently running capsule ID {} ({:?})", c, e);
        }

        return Ok(())
    }

    Err(Cause::CapsuleBadID)
}

/* buffer a character from the guest kernel, and flush output if a newline */
pub fn debug_write(cid: CapsuleID, character: char) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut capsule) =>
        {
            let c = capsule.get_mut();

            if character != '\n'
            {
                /* buffer the character if we're not at a newline */
                c.debug_buffer.push(character);
            }
            else
            {
                /* flush the buffer and reinitialize it */
                hvdebug!("Capsule {}: {}", cid, c.debug_buffer);
                c.debug_buffer = String::new();
            }
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* allow a given capsule to offer a given service. if the capsule was already allowed the
   service then this returns without error.
    => cid = capsule ID
       sid = service ID
    <= Ok for success, or an error code */
pub fn allow_service(cid: CapsuleID, sid: ServiceID) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut c) =>
        {
            c.get_mut().allowed_services.insert(sid);
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* check whether a capsule is allowed to run the given service
    => cid = capsule ID to check
       sid = service ID to check
    <= Some true if the capsule exists and is allowed, or Some false
        if the capsule exists and is not allowed, or None if
        the capsule doesn't exist */
pub fn is_service_allowed(cid: CapsuleID, sid: ServiceID) -> Option<bool>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(c) => Some(c.get().check_service(sid)),
        Vacant(_) => None
    }
}

/* add a memory mapping to a capsule
   cid = ID of capsule to add the mapping to
   to_map = memory mapping object to add
   Ok for success, or an error code
*/
pub fn map_memory(cid: CapsuleID, to_map: Mapping) -> Result<(), Cause>
{
    if let Occupied(mut c) = CAPSULES.lock().entry(cid)
    {
        c.get_mut().set_memory_mapping(to_map);
        return Ok(());
    }
    else
    {
        return Err(Cause::CapsuleBadID);
    }
}

/* enforce hardware security restrictions for the given capsule.
   supervisor-level code will only be able to access the physical
   RAM covered by that assigned to the given capsule. call this
   when switching to a capsule, so that the previous enforcement is
   replaced by enforcement of this capsule.
   => id = capsule to enforce
   <= true for success, or fail for failure
*/
pub fn enforce(id: CapsuleID) -> bool
{
    /* this is a filthy hardcode hack that I hate but it's needed for now */
    let mut index = 0;

    match CAPSULES.lock().entry(id)
    {
        Occupied(c) =>
        {
            for mapping in c.get().get_memory_mappings()
            {
                if let Some(r) = mapping.get_physical()
                {
                    if index > 0
                    {
                        panic!("TODO / FIXME: Capsules can't have more than one physical RAM region");
                    }

                    r.grant_access();
                    index = index + 1;
                }
            }
            return true
        },
        _ => false
    }
}
<commit_msg>Assign a state to capsules Capsules are either valid, dying or restarting. When the hypervisor determines that a capsule needs to die or restart, the capsule's state is changed to dying or restarting, respectively, which will cause the virtual cores in the capsule to gradually terminate. When all the virtual cores have been removed, the capsule is torn down or restarted as necessary.<commit_after>/* diosix capsule management
 *
 * (c) Chris Williams, 2019-2021.
 *
 * See LICENSE for usage and copying.
 */

use core::sync::atomic::{AtomicUsize, Ordering};
use super::lock::Mutex;
use hashbrown::hash_map::HashMap;
use hashbrown::hash_map::Entry::{Occupied, Vacant};
use hashbrown::hash_set::HashSet;
use alloc::vec::Vec;
use alloc::string::String;
use platform::cpu::Entry;
use platform::physmem::PhysMemBase;
use super::error::Cause;
use super::physmem;
use super::virtmem::Mapping;
use super::vcore::{self, Priority, VirtualCoreID};
use super::service::ServiceID;
use super::service;
use super::pcore;

pub type CapsuleID = usize;

/* arbitrarily allow up to CAPSULES_MAX capsules in the system */
const CAPSULES_MAX: usize = 1000000;

/* needed to assign system-wide unique capsule ID numbers */
lazy_static!
{
    static ref CAPSULE_ID_NEXT: AtomicUsize = AtomicUsize::new(0);
}

/* maintain a shared table of capsules */
lazy_static!
{
    /* acquire CAPSULES lock before accessing any capsules */
    static ref CAPSULES: Mutex<HashMap<CapsuleID, Capsule>> = Mutex::new(HashMap::new());
}

/* define who or what is responsible for restarting this capsule */
#[derive(Debug)]
pub enum RestartMethod
{
    FromManifest(String),   /* reload from the included manifest fs image */
    FromSpawnService        /* tell the capsule spawning service to reload us */
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CapsuleState
{
    Valid,
    Dying,
    Restarting
}

struct Capsule
{
    state: CapsuleState,                    /* define whether this capsule is alive, dying or restarting */
    restart_on_crash: bool,                 /* true to auto-restart on unexpected death */
    restart_method: RestartMethod,          /* how to restart this capsule */
    vcores: HashSet<VirtualCoreID>,         /* set of virtual core IDs assigned to this capsule */
    memory: Vec<Mapping>,                   /* map capsule supervisor virtual addresses to host physical addresses */
    allowed_services: HashSet<ServiceID>,   /* set of services this capsule is allowed to provide */
    debug_buffer: String                    /* buffer to hold debug output until it's flushed */
}

impl Capsule
{
    /* create a new empty capsule using the current capsule on this physical CPU core.
    => auto_restart_flag = tue to auto-restart on crash by the hypervisor
    <= capsule object, or error code */
    pub fn new(restart_on_crash: bool, restart_method: RestartMethod) -> Result<Capsule, Cause>
    {
        Ok(Capsule
        {
            state: CapsuleState::Valid,
            restart_on_crash,
            restart_method,
            vcores: HashSet::new(),
            memory: Vec::new(),
            allowed_services: HashSet::new(),
            debug_buffer: String::new()
        })
    }

    /* add a mapping to this capsule */
    pub fn set_memory_mapping(&mut self, to_add: Mapping)
    {
        self.memory.push(to_add);
    }

    /* get a copy of the capsule's memory mappings */
    pub fn get_memory_mappings(&self) -> Vec<Mapping> { self.memory.clone() }

    /* boot capsules are auto-restarted by the hypervisor */
    pub fn set_auto_crash_restart(&mut self, flag: bool)
    {
        self.restart_on_crash = flag;
    }

    /* returns true if this will auto-restart, or false if not */
    pub fn will_auto_crash_restart(&self) -> bool
    {
        self.restart_on_crash
    }

    /* returns restart method */
    pub fn get_restart_method(&self) -> &RestartMethod { &self.restart_method }

    /* add a virtual core ID to the capsule */
    pub fn add_vcore(&mut self, id: VirtualCoreID)
    {
        self.vcores.insert(id);
    }

    /* remove a virtual core ID */
    pub fn remove_vcore(&mut self, id: VirtualCoreID)
    {
        self.vcores.remove(&id);
    }

    /* return number of registered virtual cores */
    pub fn count_vcores(&self) -> usize
    {
        self.vcores.len()
    }

    /* allow capsule to register service sid */
    pub fn allow_service(&mut self, sid: ServiceID)
    {
        self.allowed_services.insert(sid);
    }

    /* check whether this capsule is allowed to register the given service
        <= true if allowed, false if not */
    pub fn check_service(&self, sid: ServiceID) -> bool
    {
        self.allowed_services.contains(&sid)
    }

    /* return this capsule's state */
    pub fn get_state(&self) -> CapsuleState { self.state }

    /* mark this capsule as dying. returns true if this is possible.
    only valid or dying capsules can die */
    pub fn set_state_dying(&mut self) -> bool
    {
        match self.state
        {
            CapsuleState::Dying => (),
            CapsuleState::Valid => self.state = CapsuleState::Dying,
            _ => return false
        }

        true
    }

    /* mark this capsule as restarting. returns true if this is possible.
    only valid or restarting capsules can restart */
    pub fn set_state_restarting(&mut self) -> bool
    {
        match self.state
        {
            CapsuleState::Restarting => (),
            CapsuleState::Valid => self.state = CapsuleState::Restarting,
            _ => return false
        }

        true
    }
}

/* handle the destruction of a capsule */
impl Drop for Capsule
{
    fn drop(&mut self)
    {
        hvdebug!("Capsule {:p} dying, releasing resources", &self);

        /* free up memory... */
        for mapping in self.memory.clone()
        {
            if let Some(r) = mapping.get_physical()
            {
                match physmem::dealloc_region(r)
                {
                    Err(e) => hvalert!("Error during capsule {:p} teardown: {:?}", &self, e),
                    Ok(_) => ()
                };
            }
        }

        /* ...and services held by this capsule */
        for sid in self.allowed_services.iter()
        {
            match service::deregister(*sid)
            {
                Err(_e) => hvalert!("Failed to deregister service during teardown of capsule {:p}: {:?}", &self, _e),
                Ok(()) => ()
            };
        }
    }
}

/* create a virtual core and add it to the given capsule
   => cid = capsule ID
      vid = virtual core ID
      entry = starting address for execution of this virtual core
      dtb = physical address of the device tree blob describing
            the virtual hardware environment
      prio = priority to run this virtual core
   <= return Ok for success, or error code
*/
pub fn add_vcore(cid: CapsuleID, vid: VirtualCoreID, entry: Entry, dtb: PhysMemBase, prio: Priority) -> Result<(), Cause>
{
    vcore::VirtualCore::create(cid, vid, entry, dtb, prio)?;
    match CAPSULES.lock().get_mut(&cid)
    {
        Some(c) => c.add_vcore(vid),
        None => return Err(Cause::CapsuleBadID)
    };
    Ok(())
}

/* create a new blank capsule
   Once created, it needs to be given a supervisor image, at least.
   then it is ready to be scheduled by assigning it virtual CPU cores.
   => auto_crash_restart = true to be auto-restarted by hypervisor if the capsule crashes
   <= CapsuleID for this new capsule, or an error code */
pub fn create(auto_crash_restart: bool, restart_method: RestartMethod) -> Result<CapsuleID, Cause>
{
    /* repeatedly try to generate an available ID */
    loop
    {
        /* bail out if we're at the limit */
        let mut capsules = CAPSULES.lock();
        if capsules.len() > CAPSULES_MAX
        {
            return Err(Cause::CapsuleIDExhaustion);
        }

        /* get next ID and check to see if this capsule already exists */
        let new_id = CAPSULE_ID_NEXT.fetch_add(1, Ordering::SeqCst);
        match capsules.entry(new_id)
        {
            Vacant(_) =>
            {
                /* insert our new capsule */
                capsules.insert(new_id, Capsule::new(auto_crash_restart, restart_method)?);

                /* we're all done here */
                return Ok(new_id);
            },
            _ => () /* try again */
        };
    }
}

/* destroy the given virtualcore within the given capsule.
   when the capsule is out of vcores, destroy it.
   see destroy_current() for more details */
pub fn destroy(cid: CapsuleID, vid: VirtualCoreID) -> Result<(), Cause>
{
    /* make sure this capsule is dying */
    let mut lock = CAPSULES.lock();
    if let Some(victim) = lock.get_mut(&cid)
    {
        match victim.set_state_dying()
        {
            true =>
            {
                /* remove this current vcore ID from the capsule's
                hash table */
                victim.remove_vcore(vid);

                /* are there any vcores remaining? */
                if victim.count_vcores() == 0
                {
                    /* if not then remove this capsule
                    from the hash table, which should
                    trigger the final teardown via drop */
                    lock.remove(&cid);
                }

                return Ok(());
            },
            false => return Err(Cause::CapsuleCantDie)
        }
    }
    else
    {
        Err(Cause::CapsuleBadID)
    }
}

/* mark the currently running capsule as dying,
   or continue to kill off the capsule. each vcore
   should call this when it realizes the capsule
   is dying so that the current vcore can be removed.
   it can be called multiple times per vcore.
   when there are no vcores left, its RAM
   and any other resources will be deallocated.
   when the vcore count drops to zero, it will drop.
   it's on the caller of destroy_capsule() to reschedule
   another vcore to run.
   <= Ok for success, or an error code
*/
pub fn destroy_current() -> Result<(), Cause>
{
    let (cid, vid) = match pcore::PhysicalCore::this().get_virtualcore_id()
    {
        Some(id) => (id.capsuleid, id.vcoreid),
        None =>
        {
            hvalert!("BUG: Can't find currently running capsule to destroy");
            return Err(Cause::CapsuleBadID);
        }
    };

    destroy(cid, vid)
}

/* remove the given virtual core from the capsule and mark it as restarting.
   see restart_current() for more details */
pub fn restart(cid: CapsuleID, vid: VirtualCoreID) -> Result<(), Cause>
{
    /* make sure this capsule is dying */
    let mut lock = CAPSULES.lock();
    if let Some(victim) = lock.get_mut(&cid)
    {
        match victim.set_state_restarting()
        {
            true =>
            {
                /* remove this current vcore ID from the capsule's
                hash table */
                victim.remove_vcore(vid);

                return Ok(());
            },

            false => return Err(Cause::CapsuleCantRestart)
        }
    }
    else
    {
        Err(Cause::CapsuleBadID)
    }
}

/* recreate and restart the currently running capsule, if possible.
   it can be called multiple times per vcore. each vcore should call
   this within the capsule when it realizes the capsule is restarting.
   when all vcores have call this function, the capsule will restart proper.
   it's on the caller of restart_current() to reschedule another vcore to run.
   <= Ok for success, or an error code
*/
pub fn restart_current() -> Result<(), Cause>
{
    let (cid, vid) = match pcore::PhysicalCore::this().get_virtualcore_id()
    {
        Some(id) => (id.capsuleid, id.vcoreid),
        None =>
        {
            hvalert!("BUG: Can't find currently running capsule to restart");
            return Err(Cause::CapsuleBadID);
        }
    };

    restart(cid, vid)
}

/* return the state of the given capsule, identified by ID, or None for not found */
pub fn get_state(cid: CapsuleID) -> Option<CapsuleState>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(capsule) => Some(capsule.get().state),
        Vacant(_) => None
    }
}

/* get the current capsule's state, or None if no running capsule */
pub fn get_current_state() -> Option<CapsuleState>
{
    if let Some(cid) = pcore::PhysicalCore::get_capsule_id()
    {
        return get_state(cid);
    }

    None
}

/* return currently running capsule's auto-restart-on-crash flag as
   Some(true) or Some(false) as appropriate, or None for no running capsule */
pub fn is_current_autorestart() -> Option<bool>
{
    let cid = match pcore::PhysicalCore::get_capsule_id()
    {
        Some(id) => id,
        None => return None
    };

    match CAPSULES.lock().entry(cid)
    {
        Occupied(capsule) => return Some(capsule.get().restart_on_crash),
        Vacant(_) => None
    }
}

/* buffer a character from the guest kernel, and flush output if a newline */
pub fn debug_write(cid: CapsuleID, character: char) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut capsule) =>
        {
            let c = capsule.get_mut();

            if character != '\n'
            {
                /* buffer the character if we're not at a newline */
                c.debug_buffer.push(character);
            }
            else
            {
                /* flush the buffer and reinitialize it */
                hvdebug!("Capsule {}: {}", cid, c.debug_buffer);
                c.debug_buffer = String::new();
            }
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* allow a given capsule to offer a given service. if the capsule was already allowed the
   service then this returns without error.
    => cid = capsule ID
       sid = service ID
    <= Ok for success, or an error code */
pub fn allow_service(cid: CapsuleID, sid: ServiceID) -> Result<(), Cause>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(mut c) =>
        {
            c.get_mut().allowed_services.insert(sid);
            Ok(())
        },
        Vacant(_) => Err(Cause::CapsuleBadID)
    }
}

/* check whether a capsule is allowed to run the given service
    => cid = capsule ID to check
       sid = service ID to check
    <= Some true if the capsule exists and is allowed, or Some false
        if the capsule exists and is not allowed, or None if
        the capsule doesn't exist */
pub fn is_service_allowed(cid: CapsuleID, sid: ServiceID) -> Option<bool>
{
    match CAPSULES.lock().entry(cid)
    {
        Occupied(c) => Some(c.get().check_service(sid)),
        Vacant(_) => None
    }
}

/* add a memory mapping to a capsule
   cid = ID of capsule to add the mapping to
   to_map = memory mapping object to add
   Ok for success, or an error code
*/
pub fn map_memory(cid: CapsuleID, to_map: Mapping) -> Result<(), Cause>
{
    if let Occupied(mut c) = CAPSULES.lock().entry(cid)
    {
        c.get_mut().set_memory_mapping(to_map);
        return Ok(());
    }
    else
    {
        return Err(Cause::CapsuleBadID);
    }
}

/* enforce hardware security restrictions for the given capsule.
   supervisor-level code will only be able to access the physical
   RAM covered by that assigned to the given capsule. call this
   when switching to a capsule, so that the previous enforcement is
   replaced by enforcement of this capsule.
   => id = capsule to enforce
   <= true for success, or fail for failure
*/
pub fn enforce(id: CapsuleID) -> bool
{
    /* this is a filthy hardcode hack that I hate but it's needed for now */
    let mut index = 0;

    match CAPSULES.lock().entry(id)
    {
        Occupied(c) =>
        {
            for mapping in c.get().get_memory_mappings()
            {
                if let Some(r) = mapping.get_physical()
                {
                    if index > 0
                    {
                        panic!("TODO / FIXME: Capsules can't have more than one physical RAM region");
                    }

                    r.grant_access();
                    index = index + 1;
                }
            }
            return true
        },
        _ => false
    }
}
<|endoftext|>


<commit_before>extern crate rustbox;

use std::char;

use rustbox::{Style,Color};

fn main() {
    rustbox::init();
    rustbox::print(1, 1, Style::Bold, Color::White, Color::Black, "Hello, world!".to_string());
    rustbox::print(1, 3, Style::Bold, Color::White, Color::Black, "Press 'q' to quit.".to_string());
    rustbox::present();

    loop {
        match rustbox::poll_event() {
            rustbox::Event::KeyEvent(_, _, ch) => {
                match char::from_u32(ch) {
                    Some('q') => { break; },
                    _ => {}
                }
            },
            _ => { }
        }
    }
    rustbox::shutdown();
}
<commit_msg>Fix example.<commit_after>extern crate rustbox;

use std::char;

use rustbox::{Style,Color};

fn main() {
    rustbox::init();
    rustbox::print(1, 1, Style::Bold, Color::White, Color::Black, "Hello, world!");
    rustbox::print(1, 3, Style::Bold, Color::White, Color::Black, "Press 'q' to quit.");
    rustbox::present();

    loop {
        match rustbox::poll_event() {
            rustbox::Event::KeyEvent(_, _, ch) => {
                match char::from_u32(ch) {
                    Some('q') => { break; },
                    _ => {}
                }
            },
            _ => { }
        }
    }
    rustbox::shutdown();
}
<|endoftext|>