<commit_before>use std::collections::BTreeMap;
use std::error;
use std::env;

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

use config;
use output;

use super::{DeployCommand, ParamsDeleteCommand, ParamsExecCommand, ParamsGetCommand,
            ParamsListCommand, ParamsPutCommand, RunTaskCommand, ScheduleTaskDeleteCommand,
            ScheduleTaskPutCommand};

pub struct MainCommand {}

impl MainCommand {
    fn validate_args_template_variables(tag_str: String) -> Result<(), String> {
        let pair: Vec<&str> = tag_str.split("=").collect();
        if pair.len() == 2 {
            Ok(())
        } else {
            Err(String::from(
                "The variable format should contain variable name and variable value, and those are seperated with `=` character",
            ))
        }
    }
    fn parse_args_template_variables(args: &ArgMatches) -> Option<BTreeMap<String, String>> {
        args.values_of("CONFIG_TEMPLATE_VARIABLES").map(|vars_str| {
            let mut data = BTreeMap::new();
            for var_str in vars_str {
                let pair: Vec<&str> = var_str.split("=").collect();
                data.insert(pair[0].to_owned(), pair[1].to_owned());
            }

            data
        })
    }

    fn config_file(args: &ArgMatches) -> String {
        if let Some(config_file) = args.value_of("CONFIG") {
            return config_file.to_owned();
        }
        if let Ok(config_file) = env::var("RACCO_CONFIG_PATH") {
            return config_file;
        }

        String::from("racco.yml")
    }

    pub fn run() -> Result<(), Box<error::Error>> {

        let matches = App::new("Racco")
            .version("0.1.0")
            .author("Daichi Sakai. <daisaru11@gmail.com>")
            .about("Deployment toolkit for AWS ECS")
            .arg(
                Arg::with_name("CONFIG")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .help("Specifies configuration file")
                    .takes_value(true),
            )
            .arg(
                Arg::with_name("CONFIG_TEMPLATE_VARIABLES")
                    .short("a")
                    .long("config-template-vars")
                    .value_name("NAME=VALUE")
                    .help("Variables to be rendered in config template.")
                    .takes_value(true)
                    .multiple(true)
                    .validator(MainCommand::validate_args_template_variables),
            )
            .subcommand(
                SubCommand::with_name("deploy")
                    .about("Deploys ECS service")
                    .arg(
                        Arg::with_name("NAME")
                            .help("Name of the entry in config")
                            .index(1),
                    ),
            )
            .subcommand(
                SubCommand::with_name("run-task")
                    .about("Runs single ESC task")
                    .arg(
                        Arg::with_name("NAME")
                            .help("Name of the entry in config")
                            .required(true)
                            .index(1),
                    ),
            )
            .subcommand(
                SubCommand::with_name("schedule-task")
                    .about("Deploys ECS task scheduled by Cloudwatch events")
                    .subcommand(
                        SubCommand::with_name("put")
                            .about("Puts a scheduled task")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the entry in config")
                                    .required(true)
                                    .index(1),
                            ),
                    )
                    .subcommand(
                        SubCommand::with_name("delete")
                            .about("Deletes a scheduled task")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the entry in config")
                                    .required(true)
                                    .index(1),
                            ),
                    ),
            )
            .subcommand(
                SubCommand::with_name("params")
                    .about("Manages parameters")
                    .subcommand(
                        SubCommand::with_name("get").about("Gets a parameter").arg(
                            Arg::with_name("NAME")
                                .help("Name of the parameter")
                                .required(true)
                                .index(1),
                        ),
                    )
                    .subcommand(SubCommand::with_name("list").about("Lists parameters"))
                    .subcommand(
                        SubCommand::with_name("put")
                            .about("Puts a parameter")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the parameter")
                                    .required(true)
                                    .index(1),
                            )
                            .arg(
                                Arg::with_name("VALUE")
                                    .help("Value of the parameter")
                                    .required(true)
                                    .index(2),
                            ),
                    )
                    .subcommand(
                        SubCommand::with_name("delete")
                            .about("Deletes a parameter")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the parameter")
                                    .required(true)
                                    .index(1),
                            ),
                    )
                    .subcommand(
                        SubCommand::with_name("exec")
                            .setting(AppSettings::AllowLeadingHyphen)
                            .about(
                                "Executes a command with the exported parameters as env variables",
                            )
                            .arg(
                                Arg::with_name("PROGRAM")
                                    .help("Program to be executed")
                                    .required(true)
                                    .index(1),
                            )
                            .arg(
                                Arg::with_name("ARGS")
                                    .help("Arguments passed to the program")
                                    .multiple(true)
                                    .index(2),
                            ),
                    ),
            )
            .get_matches();

        info!("start racco");

        let config_file = MainCommand::config_file(&matches);
        info!("config file: {}", config_file);

        let template_variables = MainCommand::parse_args_template_variables(&matches);

        match config::command::Config::from_file(config_file.as_str(), template_variables.as_ref()) {
            Err(error) => {
                output::PrintLine::error(&format!("Failed loading the configuration: {}", error));
            }
            Ok(config) => {

                // deploy
                if let Some(sub_matches) = matches.subcommand_matches("deploy") {

                    info!("start deploy");

                    let cmd = DeployCommand::from_args(&config, sub_matches);
                    match cmd.run() {
                        Ok(_) => {}
                        Err(error) => error!("deploy failed: {}", error),
                    }

                    info!("end deploy");
                }

                // run-task
                if let Some(sub_matches) = matches.subcommand_matches("run-task") {

                    info!("start run-task");

                    let cmd = RunTaskCommand::from_args(&config, sub_matches);
                    match cmd.run() {
                        Ok(_) => {}
                        Err(error) => {
                            output::PrintLine::error(&format!("Failed: {}", error));
                        }
                    }

                    info!("end run-task");
                }

                // schedule-task
                if let Some(sub0_matches) = matches.subcommand_matches("schedule-task") {
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                        info!("start schedule-task put");

                        let cmd = ScheduleTaskPutCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end schdule-task put");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                        info!("start schedule-task delete");

                        let cmd = ScheduleTaskDeleteCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end schedule-task delete");
                    }
                }

                // params
                if let Some(sub0_matches) = matches.subcommand_matches("params") {
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("get") {
                        info!("start params get");

                        let cmd = ParamsGetCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params get");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("list") {
                        info!("start params list");

                        let cmd = ParamsListCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params list");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                        info!("start params put");

                        let cmd = ParamsPutCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params put");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                        info!("start params delete");

                        let cmd = ParamsDeleteCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params delete");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("exec") {
                        info!("start params exec");

                        let cmd = ParamsExecCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params exec");
                    }
                }
            }
        };

        info!("end racco");

        Ok(())
    }
}
<commit_msg>Trivial changes of output messages<commit_after>use std::collections::BTreeMap;
use std::error;
use std::env;

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

use config;
use output;

use super::{DeployCommand, ParamsDeleteCommand, ParamsExecCommand, ParamsGetCommand,
            ParamsListCommand, ParamsPutCommand, RunTaskCommand, ScheduleTaskDeleteCommand,
            ScheduleTaskPutCommand};

pub struct MainCommand {}

impl MainCommand {
    fn validate_args_template_variables(tag_str: String) -> Result<(), String> {
        let pair: Vec<&str> = tag_str.split("=").collect();
        if pair.len() == 2 {
            Ok(())
        } else {
            Err(String::from(
                "The variable format should contain variable name and variable value, and those are seperated with `=` character",
            ))
        }
    }
    fn parse_args_template_variables(args: &ArgMatches) -> Option<BTreeMap<String, String>> {
        args.values_of("CONFIG_TEMPLATE_VARIABLES").map(|vars_str| {
            let mut data = BTreeMap::new();
            for var_str in vars_str {
                let pair: Vec<&str> = var_str.split("=").collect();
                data.insert(pair[0].to_owned(), pair[1].to_owned());
            }

            data
        })
    }

    fn config_file(args: &ArgMatches) -> String {
        if let Some(config_file) = args.value_of("CONFIG") {
            return config_file.to_owned();
        }
        if let Ok(config_file) = env::var("RACCO_CONFIG_PATH") {
            return config_file;
        }

        String::from("racco.yml")
    }

    pub fn run() -> Result<(), Box<error::Error>> {

        let matches = App::new("Racco")
            .version("0.1.0")
            .author("Daichi Sakai. <daisaru11@gmail.com>")
            .about("Deployment toolkit for AWS ECS")
            .arg(
                Arg::with_name("CONFIG")
                    .short("c")
                    .long("config")
                    .value_name("FILE")
                    .help("Specifies configuration file")
                    .takes_value(true),
            )
            .arg(
                Arg::with_name("CONFIG_TEMPLATE_VARIABLES")
                    .short("a")
                    .long("config-template-vars")
                    .value_name("NAME=VALUE")
                    .help("Variables to be rendered in config template.")
                    .takes_value(true)
                    .multiple(true)
                    .validator(MainCommand::validate_args_template_variables),
            )
            .subcommand(
                SubCommand::with_name("deploy")
                    .about("Deploys ECS service")
                    .arg(
                        Arg::with_name("NAME")
                            .help("Name of the entry in config")
                            .index(1),
                    ),
            )
            .subcommand(
                SubCommand::with_name("run-task")
                    .about("Runs single ESC task")
                    .arg(
                        Arg::with_name("NAME")
                            .help("Name of the entry in config")
                            .required(true)
                            .index(1),
                    ),
            )
            .subcommand(
                SubCommand::with_name("schedule-task")
                    .about("Deploys ECS task scheduled by Cloudwatch events")
                    .subcommand(
                        SubCommand::with_name("put")
                            .about("Puts a scheduled task")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the entry in config")
                                    .required(true)
                                    .index(1),
                            ),
                    )
                    .subcommand(
                        SubCommand::with_name("delete")
                            .about("Deletes a scheduled task")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the entry in config")
                                    .required(true)
                                    .index(1),
                            ),
                    ),
            )
            .subcommand(
                SubCommand::with_name("params")
                    .about("Manages parameters")
                    .subcommand(
                        SubCommand::with_name("get").about("Gets a parameter").arg(
                            Arg::with_name("NAME")
                                .help("Name of the parameter")
                                .required(true)
                                .index(1),
                        ),
                    )
                    .subcommand(SubCommand::with_name("list").about("Lists parameters"))
                    .subcommand(
                        SubCommand::with_name("put")
                            .about("Puts a parameter")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the parameter")
                                    .required(true)
                                    .index(1),
                            )
                            .arg(
                                Arg::with_name("VALUE")
                                    .help("Value of the parameter")
                                    .required(true)
                                    .index(2),
                            ),
                    )
                    .subcommand(
                        SubCommand::with_name("delete")
                            .about("Deletes a parameter")
                            .arg(
                                Arg::with_name("NAME")
                                    .help("Name of the parameter")
                                    .required(true)
                                    .index(1),
                            ),
                    )
                    .subcommand(
                        SubCommand::with_name("exec")
                            .setting(AppSettings::AllowLeadingHyphen)
                            .about(
                                "Executes a command with the exported parameters as env variables",
                            )
                            .arg(
                                Arg::with_name("PROGRAM")
                                    .help("Program to be executed")
                                    .required(true)
                                    .index(1),
                            )
                            .arg(
                                Arg::with_name("ARGS")
                                    .help("Arguments passed to the program")
                                    .multiple(true)
                                    .index(2),
                            ),
                    ),
            )
            .get_matches();

        info!("start racco");

        let config_file = MainCommand::config_file(&matches);
        info!("config file: {}", config_file);

        let template_variables = MainCommand::parse_args_template_variables(&matches);

        match config::command::Config::from_file(config_file.as_str(), template_variables.as_ref()) {
            Err(error) => {
                output::PrintLine::error(&format!("Failed loading the configuration: {}", error));
            }
            Ok(config) => {

                // deploy
                if let Some(sub_matches) = matches.subcommand_matches("deploy") {

                    info!("start deploy");

                    let cmd = DeployCommand::from_args(&config, sub_matches);
                    match cmd.run() {
                        Ok(_) => {}
                        Err(error) => {
                            output::PrintLine::error(&format!("Failed deployment: {}", error));
                        }
                    }

                    info!("end deploy");
                }

                // run-task
                if let Some(sub_matches) = matches.subcommand_matches("run-task") {

                    info!("start run-task");

                    let cmd = RunTaskCommand::from_args(&config, sub_matches);
                    match cmd.run() {
                        Ok(_) => {}
                        Err(error) => {
                            output::PrintLine::error(&format!("Failed running the task: {}", error));
                        }
                    }

                    info!("end run-task");
                }

                // schedule-task
                if let Some(sub0_matches) = matches.subcommand_matches("schedule-task") {
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                        info!("start schedule-task put");

                        let cmd = ScheduleTaskPutCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end schdule-task put");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                        info!("start schedule-task delete");

                        let cmd = ScheduleTaskDeleteCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end schedule-task delete");
                    }
                }

                // params
                if let Some(sub0_matches) = matches.subcommand_matches("params") {
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("get") {
                        info!("start params get");

                        let cmd = ParamsGetCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params get");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("list") {
                        info!("start params list");

                        let cmd = ParamsListCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params list");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                        info!("start params put");

                        let cmd = ParamsPutCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params put");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                        info!("start params delete");

                        let cmd = ParamsDeleteCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params delete");
                    }
                    if let Some(sub1_matches) = sub0_matches.subcommand_matches("exec") {
                        info!("start params exec");

                        let cmd = ParamsExecCommand::from_args(&config, sub1_matches);
                        match cmd.run() {
                            Ok(_) => {}
                            Err(error) => {
                                output::PrintLine::error(&format!("Failed: {}", error));
                            }
                        }

                        info!("end params exec");
                    }
                }
            }
        };

        info!("end racco");

        Ok(())
    }
}
<|endoftext|>


<commit_before>use chrono::{DateTime, Duration, Utc};
use serde_derive::{Deserialize, Serialize};
use uuid::Uuid;

use super::ProjectId;

#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct EntryId(Uuid);

impl EntryId {
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }

    pub fn value(&self) -> Uuid {
        self.0
    }
}

impl From<Uuid> for EntryId {
    fn from(uuid: Uuid) -> Self {
        Self(uuid)
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Entry {
    pub id: EntryId,

    #[serde(flatten)]
    pub kind: EntryKind,
    pub timestamp: DateTime<Utc>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum EntryKind {
    Event(EventEntry),
    ProjectHeadway(ProjectHeadwayEntry),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EventEntry {
    pub name: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ProjectHeadwayEntry {
    pub project_id: ProjectId,
    pub hours_spent: f64,
}
<commit_msg>Remove unused import<commit_after>use chrono::{DateTime, Utc};
use serde_derive::{Deserialize, Serialize};
use uuid::Uuid;

use super::ProjectId;

#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct EntryId(Uuid);

impl EntryId {
    pub fn new() -> Self {
        Self(Uuid::new_v4())
    }

    pub fn value(&self) -> Uuid {
        self.0
    }
}

impl From<Uuid> for EntryId {
    fn from(uuid: Uuid) -> Self {
        Self(uuid)
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Entry {
    pub id: EntryId,

    #[serde(flatten)]
    pub kind: EntryKind,
    pub timestamp: DateTime<Utc>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum EntryKind {
    Event(EventEntry),
    ProjectHeadway(ProjectHeadwayEntry),
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EventEntry {
    pub name: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ProjectHeadwayEntry {
    pub project_id: ProjectId,
    pub hours_spent: f64,
}
<|endoftext|>


<commit_before>use std::fs;
use std::path::Path;

use crate::vm::VM;
use crate::vm::{exception_get_and_clear, Fct, FctId};
use dora_parser::ast::{self, Ast};
use dora_parser::error::msg::Msg;

use crate::driver::cmd;
use crate::object;
use crate::os;
use crate::timer::Timer;
use dora_parser::lexer::position::Position;
use dora_parser::lexer::reader::Reader;

use crate::semck;
use crate::semck::specialize::specialize_class_id;
use crate::ty::BuiltinType;
use dora_parser::parser::Parser;

pub fn start(content: Option<&str>) -> i32 {
    os::mem::init_page_size();
    let fuzzing = content.is_some();

    let args = if fuzzing {
        Default::default()
    } else {
        cmd::parse()
    };

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut ast = Ast::new();
    let empty = Ast::new();
    let mut vm = VM::new(args, &empty);

    if let Err(code) = parse_all_files(&mut vm, &mut ast, content) {
        return code;
    }

    vm.ast = *

    if vm.args.flag_emit_ast {
        ast::dump::dump(&vm.ast, &vm.interner);
    }

    semck::check(&mut vm);

    // register signal handler
    os::register_signals();

    let main = if vm.args.cmd_test {
        None
    } else {
        find_main(&vm)
    };

    if vm.diag.lock().has_errors() {
        vm.diag.lock().dump();
        let no_errors = vm.diag.lock().errors().len();

        if no_errors == 1 {
            println!("{} error found.", no_errors);
        } else {
            println!("{} errors found.", no_errors);
        }

        return 1;
    }

    // if --check given, stop after type/semantic check
    if vm.args.flag_check {
        return 0;
    }

    let mut timer = Timer::new(vm.args.flag_gc_stats);

    vm.threads.attach_current_thread();

    let code = if vm.args.cmd_test {
        run_tests(&vm)
    } else {
        run_main(&vm, main.unwrap())
    };

    vm.threads.detach_current_thread();
    vm.threads.join_all();

    os::unregister_signals();

    if vm.args.flag_gc_stats {
        let duration = timer.stop();
        vm.dump_gc_summary(duration);
    }

    code
}

fn parse_all_files(vm: &mut VM, ast: &mut Ast, content: Option<&str>) -> Result<(), i32> {
    let fuzzing = content.is_some();

    parse_dir("stdlib", vm, ast).and_then(|_| {
        if fuzzing {
            return parse_str(content.unwrap(), vm, ast);
        }

        let arg_file = vm.args.arg_file.clone();
        let path = Path::new(&arg_file);

        if path.is_file() {
            parse_file(&arg_file, vm, ast)
        } else if path.is_dir() {
            parse_dir(&arg_file, vm, ast)
        } else {
            println!("file or directory `{}` does not exist.", &arg_file);
            Err(1)
        }
    })
}

fn run_tests<'ast>(vm: &VM<'ast>) -> i32 {
    let mut tests = 0;
    let mut passed = 0;

    for fct in vm.fcts.iter() {
        let fct = fct.read();

        if !is_test_fct(vm, &*fct) {
            continue;
        }

        tests += 1;

        print!("test {} ... ", vm.interner.str(fct.name));

        if run_test(vm, fct.id) {
            passed += 1;
            println!("ok");
        } else {
            println!("failed");
        }
    }

    println!(
        "{} tests executed; {} passed; {} failed.",
        tests,
        passed,
        tests - passed
    );

    // if all tests passed exit with 0, otherwise 1
    if tests == passed {
        0
    } else {
        1
    }
}

fn run_test<'ast>(vm: &VM<'ast>, fct: FctId) -> bool {
    let testing_class = vm.vips.testing_class;
    let testing_class = specialize_class_id(vm, testing_class);
    let testing = object::alloc(vm, testing_class).cast();
    vm.run_test(fct, testing);

    // see if test failed with exception
    let exception = exception_get_and_clear();

    exception.is_null() && !testing.has_failed()
}

fn is_test_fct<'ast>(vm: &VM<'ast>, fct: &Fct<'ast>) -> bool {
    // tests need to be standalone functions, with no return type and a single parameter
    if !fct.parent.is_none() || !fct.return_type.is_unit() || fct.param_types.len() != 1 {
        return false;
    }

    // parameter needs to be of type Testing
    let testing_cls = vm.cls(vm.vips.testing_class);
    if fct.param_types[0] != testing_cls {
        return false;
    }

    // the functions name needs to start with `test`
    let fct_name = vm.interner.str(fct.name);
    fct_name.starts_with("test")
}

fn run_main<'ast>(vm: &VM<'ast>, main: FctId) -> i32 {
    let res = vm.run(main);
    let fct = vm.fcts.idx(main);
    let fct = fct.read();
    let is_unit = fct.return_type.is_unit();

    // main-fct without return value exits with status 0
    if is_unit {
        0

    // else use return value of main for exit status
    } else {
        res
    }
}

fn parse_dir(dirname: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let path = Path::new(dirname);

    if path.is_dir() {
        for entry in fs::read_dir(path).unwrap() {
            let path = entry.unwrap().path();

            if path.is_file() && path.extension().unwrap() == "dora" {
                parse_file(path.to_str().unwrap(), vm, ast)?;
            }
        }

        Ok(())
    } else {
        println!("directory `{}` does not exist.", dirname);

        Err(1)
    }
}

fn parse_file(filename: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = if filename == "-" {
        match Reader::from_input() {
            Err(_) => {
                println!("unable to read from stdin.");
                return Err(1);
            }

            Ok(reader) => reader,
        }
    } else {
        match Reader::from_file(filename) {
            Err(_) => {
                println!("unable to read file `{}`", filename);
                return Err(1);
            }

            Ok(reader) => reader,
        }
    };

    let parser = Parser::new(reader, &vm.id_generator, ast, &mut vm.interner);

    match parser.parse() {
        Ok(file) => {
            vm.files.push(file);
            Ok(())
        }

        Err(error) => {
            println!("{}", error);
            println!("1 error found.");

            Err(1)
        }
    }
}

fn parse_str(file: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = Reader::from_string(file);
    let parser = Parser::new(reader, &vm.id_generator, ast, &mut vm.interner);

    match parser.parse() {
        Ok(file) => {
            vm.files.push(file);
            Ok(())
        }

        Err(error) => {
            println!("{}", error);
            println!("1 error found.");

            Err(1)
        }
    }
}

fn find_main<'ast>(vm: &VM<'ast>) -> Option<FctId> {
    let name = vm.interner.intern("main");
    let fctid = match vm.sym.lock().get_fct(name) {
        Some(id) => id,
        None => {
            vm.diag
                .lock()
                .report_without_path(Position::new(1, 1), Msg::MainNotFound);
            return None;
        }
    };

    let fct = vm.fcts.idx(fctid);
    let fct = fct.read();
    let ret = fct.return_type;

    if (ret != BuiltinType::Unit && ret != BuiltinType::Int) || fct.params_without_self().len() > 0
    {
        let pos = fct.ast.pos;
        vm.diag
            .lock()
            .report_without_path(pos, Msg::WrongMainDefinition);
        return None;
    }

    Some(fctid)
}
<commit_msg>driver: unify parse_str and parse_file<commit_after>use std::fs;
use std::path::Path;

use crate::vm::VM;
use crate::vm::{exception_get_and_clear, Fct, FctId};
use dora_parser::ast::{self, Ast};
use dora_parser::error::msg::Msg;

use crate::driver::cmd;
use crate::object;
use crate::os;
use crate::timer::Timer;
use dora_parser::lexer::position::Position;
use dora_parser::lexer::reader::Reader;

use crate::semck;
use crate::semck::specialize::specialize_class_id;
use crate::ty::BuiltinType;
use dora_parser::parser::Parser;

pub fn start(content: Option<&str>) -> i32 {
    os::mem::init_page_size();
    let fuzzing = content.is_some();

    let args = if fuzzing {
        Default::default()
    } else {
        cmd::parse()
    };

    if args.flag_version {
        println!("dora v0.01b");
        return 0;
    }

    let mut ast = Ast::new();
    let empty = Ast::new();
    let mut vm = VM::new(args, &empty);

    if let Err(code) = parse_all_files(&mut vm, &mut ast, content) {
        return code;
    }

    vm.ast = *

    if vm.args.flag_emit_ast {
        ast::dump::dump(&vm.ast, &vm.interner);
    }

    semck::check(&mut vm);

    // register signal handler
    os::register_signals();

    let main = if vm.args.cmd_test {
        None
    } else {
        find_main(&vm)
    };

    if vm.diag.lock().has_errors() {
        vm.diag.lock().dump();
        let no_errors = vm.diag.lock().errors().len();

        if no_errors == 1 {
            println!("{} error found.", no_errors);
        } else {
            println!("{} errors found.", no_errors);
        }

        return 1;
    }

    // if --check given, stop after type/semantic check
    if vm.args.flag_check {
        return 0;
    }

    let mut timer = Timer::new(vm.args.flag_gc_stats);

    vm.threads.attach_current_thread();

    let code = if vm.args.cmd_test {
        run_tests(&vm)
    } else {
        run_main(&vm, main.unwrap())
    };

    vm.threads.detach_current_thread();
    vm.threads.join_all();

    os::unregister_signals();

    if vm.args.flag_gc_stats {
        let duration = timer.stop();
        vm.dump_gc_summary(duration);
    }

    code
}

fn parse_all_files(vm: &mut VM, ast: &mut Ast, content: Option<&str>) -> Result<(), i32> {
    let fuzzing = content.is_some();

    parse_dir("stdlib", vm, ast).and_then(|_| {
        if fuzzing {
            return parse_str(content.unwrap(), vm, ast);
        }

        let arg_file = vm.args.arg_file.clone();
        let path = Path::new(&arg_file);

        if path.is_file() {
            parse_file(&arg_file, vm, ast)
        } else if path.is_dir() {
            parse_dir(&arg_file, vm, ast)
        } else {
            println!("file or directory `{}` does not exist.", &arg_file);
            Err(1)
        }
    })
}

fn run_tests<'ast>(vm: &VM<'ast>) -> i32 {
    let mut tests = 0;
    let mut passed = 0;

    for fct in vm.fcts.iter() {
        let fct = fct.read();

        if !is_test_fct(vm, &*fct) {
            continue;
        }

        tests += 1;

        print!("test {} ... ", vm.interner.str(fct.name));

        if run_test(vm, fct.id) {
            passed += 1;
            println!("ok");
        } else {
            println!("failed");
        }
    }

    println!(
        "{} tests executed; {} passed; {} failed.",
        tests,
        passed,
        tests - passed
    );

    // if all tests passed exit with 0, otherwise 1
    if tests == passed {
        0
    } else {
        1
    }
}

fn run_test<'ast>(vm: &VM<'ast>, fct: FctId) -> bool {
    let testing_class = vm.vips.testing_class;
    let testing_class = specialize_class_id(vm, testing_class);
    let testing = object::alloc(vm, testing_class).cast();
    vm.run_test(fct, testing);

    // see if test failed with exception
    let exception = exception_get_and_clear();

    exception.is_null() && !testing.has_failed()
}

fn is_test_fct<'ast>(vm: &VM<'ast>, fct: &Fct<'ast>) -> bool {
    // tests need to be standalone functions, with no return type and a single parameter
    if !fct.parent.is_none() || !fct.return_type.is_unit() || fct.param_types.len() != 1 {
        return false;
    }

    // parameter needs to be of type Testing
    let testing_cls = vm.cls(vm.vips.testing_class);
    if fct.param_types[0] != testing_cls {
        return false;
    }

    // the functions name needs to start with `test`
    let fct_name = vm.interner.str(fct.name);
    fct_name.starts_with("test")
}

fn run_main<'ast>(vm: &VM<'ast>, main: FctId) -> i32 {
    let res = vm.run(main);
    let fct = vm.fcts.idx(main);
    let fct = fct.read();
    let is_unit = fct.return_type.is_unit();

    // main-fct without return value exits with status 0
    if is_unit {
        0

    // else use return value of main for exit status
    } else {
        res
    }
}

fn parse_dir(dirname: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let path = Path::new(dirname);

    if path.is_dir() {
        for entry in fs::read_dir(path).unwrap() {
            let path = entry.unwrap().path();

            if path.is_file() && path.extension().unwrap() == "dora" {
                parse_file(path.to_str().unwrap(), vm, ast)?;
            }
        }

        Ok(())
    } else {
        println!("directory `{}` does not exist.", dirname);

        Err(1)
    }
}

fn parse_file(filename: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = if filename == "-" {
        match Reader::from_input() {
            Ok(reader) => reader,

            Err(_) => {
                println!("unable to read from stdin.");
                return Err(1);
            }
        }
    } else {
        match Reader::from_file(filename) {
            Ok(reader) => reader,

            Err(_) => {
                println!("unable to read file `{}`", filename);
                return Err(1);
            }
        }
    };

    parse_reader(reader, vm, ast)
}

fn parse_str(file: &str, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let reader = Reader::from_string(file);
    parse_reader(reader, vm, ast)
}

fn parse_reader(reader: Reader, vm: &mut VM, ast: &mut Ast) -> Result<(), i32> {
    let parser = Parser::new(reader, &vm.id_generator, ast, &mut vm.interner);

    match parser.parse() {
        Ok(file) => {
            vm.files.push(file);
            Ok(())
        }

        Err(error) => {
            println!("{}", error);
            println!("1 error found.");

            Err(1)
        }
    }
}

fn find_main<'ast>(vm: &VM<'ast>) -> Option<FctId> {
    let name = vm.interner.intern("main");
    let fctid = match vm.sym.lock().get_fct(name) {
        Some(id) => id,
        None => {
            vm.diag
                .lock()
                .report_without_path(Position::new(1, 1), Msg::MainNotFound);
            return None;
        }
    };

    let fct = vm.fcts.idx(fctid);
    let fct = fct.read();
    let ret = fct.return_type;

    if (ret != BuiltinType::Unit && ret != BuiltinType::Int) || fct.params_without_self().len() > 0
    {
        let pos = fct.ast.pos;
        vm.diag
            .lock()
            .report_without_path(pos, Msg::WrongMainDefinition);
        return None;
    }

    Some(fctid)
}
<|endoftext|>


<commit_before>//! Mesh generation from primitives like cubes and spheres.
//!
//! This module provides unit primitives that can be used to form complex
//! iterator expressions to generate meshes via a stream of topology and
//! geometry. This data can be collected into simple buffers for rendering or a
//! graph (half-edge) for further manipulation.
//!
//! Iterator expressions begin with a unit primitive and manipulate its
//! components like vertices, lines, and polygons. Generation and decomposition
//! operations are exposed via traits.
//!
//! # Examples
//!
//! Generating position and index buffers for a scaled sphere:
//!
//! ```rust
//! use plexus::generate::prelude::*;
//! use plexus::generate::sphere::UVSphere;
//!
//! let sphere = UVSphere::<f32>::with_unit_radius(16, 16);
//! let positions: Vec<_> = sphere
//!     .vertices_with_position() // Generate the unique set of positional vertices.
//!     .map(|Triplet(x, y, z)| Triplet(x * 10.0, y * 10.0, z * 10.0)) // Scale the positions by 10.
//!     .collect();
//! let indeces: Vec<_> = sphere
//!     .polygons_with_index() // Generate polygons indexing the unique set of vertices.
//!     .triangulate() // Decompose the polygons into triangles.
//!     .vertices() // Decompose the triangles into vertices (indeces).
//!     .collect();
//! ```

// TODO: Primitives are parameterized over the type of scalars used for spatial
//       data. This can be interpreted as the vector space and affects the
//       internal data describing the primitive. See the `Unit` trait. Other
//       data, like texture coordinates, are not parameterized at all. It may
//       be more consistent to parameterize all of this data, either as
//       individual type parameters or via a trait (like `Geometry`). Default
//       type parameters are also provided.

pub mod cube;
mod decompose;
mod generate;
mod geometry;
mod index;
pub mod sphere;
mod topology;

pub(crate) use self::decompose::{IntoTriangles, IntoVertices};
pub(crate) use self::geometry::Unit;
pub(crate) use self::index::{FromIndexer, Indexer};

pub use self::decompose::{Lines, Subdivide, Tetrahedrons, Triangulate, Vertices};
pub use self::generate::{PolygonsWithIndex, PolygonsWithPosition, PolygonsWithTexture,
                         VerticesWithPosition};
pub use self::geometry::{Duplet, Triplet};
pub use self::index::{CollectWithIndexer, HashIndexer, IndexVertices, LruIndexer};
pub use self::topology::{Line, MapVertices, Polygon, Polygonal, Quad, Rotate, Topological,
                         Triangle};

pub mod prelude {
    pub use super::{CollectWithIndexer, Duplet, MapVertices, PolygonsWithIndex,
                    PolygonsWithPosition, PolygonsWithTexture, Triangulate, Triplet, Vertices,
                    VerticesWithPosition};
}
<commit_msg>Expose generator traits to allow querying vertex and polygon counts.<commit_after>//! Mesh generation from primitives like cubes and spheres.
//!
//! This module provides unit primitives that can be used to form complex
//! iterator expressions to generate meshes via a stream of topology and
//! geometry. This data can be collected into simple buffers for rendering or a
//! graph (half-edge) for further manipulation.
//!
//! Iterator expressions begin with a unit primitive and manipulate its
//! components like vertices, lines, and polygons. Generation and decomposition
//! operations are exposed via traits.
//!
//! # Examples
//!
//! Generating position and index buffers for a scaled sphere:
//!
//! ```rust
//! use plexus::generate::prelude::*;
//! use plexus::generate::sphere::UVSphere;
//!
//! let sphere = UVSphere::<f32>::with_unit_radius(16, 16);
//! let positions: Vec<_> = sphere
//!     .vertices_with_position() // Generate the unique set of positional vertices.
//!     .map(|Triplet(x, y, z)| Triplet(x * 10.0, y * 10.0, z * 10.0)) // Scale the positions by 10.
//!     .collect();
//! let indeces: Vec<_> = sphere
//!     .polygons_with_index() // Generate polygons indexing the unique set of vertices.
//!     .triangulate() // Decompose the polygons into triangles.
//!     .vertices() // Decompose the triangles into vertices (indeces).
//!     .collect();
//! ```

// TODO: Primitives are parameterized over the type of scalars used for spatial
//       data. This can be interpreted as the vector space and affects the
//       internal data describing the primitive. See the `Unit` trait. Other
//       data, like texture coordinates, are not parameterized at all. It may
//       be more consistent to parameterize all of this data, either as
//       individual type parameters or via a trait (like `Geometry`). Default
//       type parameters are also provided.

pub mod cube;
mod decompose;
mod generate;
mod geometry;
mod index;
pub mod sphere;
mod topology;

pub(crate) use self::decompose::{IntoTriangles, IntoVertices};
pub(crate) use self::geometry::Unit;
pub(crate) use self::index::{FromIndexer, Indexer};

pub use self::decompose::{Lines, Subdivide, Tetrahedrons, Triangulate, Vertices};
pub use self::generate::{PolygonGenerator, PolygonsWithIndex, PolygonsWithPosition,
                         PolygonsWithTexture, VertexGenerator, VerticesWithPosition};
pub use self::geometry::{Duplet, Triplet};
pub use self::index::{CollectWithIndexer, HashIndexer, IndexVertices, LruIndexer};
pub use self::topology::{Line, MapVertices, Polygon, Polygonal, Quad, Rotate, Topological,
                         Triangle};

pub mod prelude {
    pub use super::{CollectWithIndexer, Duplet, MapVertices, PolygonGenerator, PolygonsWithIndex,
                    PolygonsWithPosition, PolygonsWithTexture, Triangulate, Triplet,
                    VertexGenerator, Vertices, VerticesWithPosition};
}
<|endoftext|>


<commit_before>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::RawImage;

extern crate time;
extern crate toml;
extern crate serde;
extern crate serde_yaml;
use self::serde::Serialize;

extern crate bincode;
extern crate sha2;
use self::sha2::Digest;

use std::fmt::Debug;

extern crate multicache;
use self::multicache::MultiCache;
use std::sync::Arc;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

// Use SHA256 as the hash for buffers
type BufHasher = sha2::Sha256;
type BufHash = [u8;32];

pub trait ImageOp<'a>: Debug+Serialize {
  fn name(&self) -> &str;
  fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash);
  fn to_settings(&self) -> String {
    serde_yaml::to_string(self).unwrap()
  }
  fn hash(&self, hasher: &mut BufHasher) {
    let encoded = self::bincode::serialize(self, self::bincode::Infinite).unwrap();
    hasher.input(&encoded);
  }
}

#[derive(Debug)]
pub struct PipelineGlobals<'a> {
  cache: MultiCache<BufHash, OpBuffer>,
  maxwidth: usize,
  maxheight: usize,
  linear: bool,
  image: &'a RawImage,
}

#[derive(Debug)]
pub struct PipelineOps {
  gofloat: gofloat::OpGoFloat,
  demosaic: demosaic::OpDemosaic,
  level: level::OpLevel,
  tolab: colorspaces::OpToLab,
  basecurve: curves::OpBaseCurve,
  fromlab: colorspaces::OpFromLab,
  gamma: gamma::OpGamma,
  transform: transform::OpTransform,
}

macro_rules! for_vals {
  ([$($val:expr),*] |$x:pat, $i:ident| $body:expr) => {
    let mut pos = 0;
    $({
      let $x = $val;
      pos += 1;
      let $i = pos-1;
      $body
    })*
  }
}

macro_rules! all_ops {
  ($ops:expr, |$x:pat, $i:ident| $body:expr) => {
    for_vals!([
      $ops.gofloat,
      $ops.demosaic,
      $ops.level,
      $ops.tolab,
      $ops.basecurve,
      $ops.fromlab,
      $ops.gamma,
      $ops.transform
    ] |$x, $i| {
      $body
    });
  }
}

#[derive(Debug)]
pub struct Pipeline<'a> {
  globals: PipelineGlobals<'a>,
  ops: PipelineOps,
}

impl<'a> Pipeline<'a> {
  pub fn new(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> Pipeline {
    // Check if the image's orientation results in a rotation that
    // swaps the maximum width with the maximum height
    let (transpose, ..) = img.orientation.to_flips();
    let (maxwidth, maxheight) = if transpose {
      (maxheight, maxwidth)
    } else {
      (maxwidth, maxheight)
    };

    Pipeline {
      globals: PipelineGlobals {
        cache: MultiCache::new(1),
        maxwidth,
        maxheight,
        linear,
        image: img,
      },
      ops: PipelineOps {
        gofloat: gofloat::OpGoFloat::new(img),
        demosaic: demosaic::OpDemosaic::new(img),
        level: level::OpLevel::new(img),
        tolab: colorspaces::OpToLab::new(img),
        basecurve: curves::OpBaseCurve::new(img),
        fromlab: colorspaces::OpFromLab::new(img),
        gamma: gamma::OpGamma::new(img),
        transform: transform::OpTransform::new(img),
      },
    }
  }

  pub fn run(&mut self) -> Arc<OpBuffer> {
    // Generate all the hashes for the operations
    let mut hasher = BufHasher::default();
    let mut ophashes = Vec::new();
    let mut startpos = 0;
    all_ops!(self.ops, |ref op, i| {
      // Hash the name first as a zero sized struct doesn't actually do any hashing
      hasher.input(op.name().as_bytes());
      op.hash(&mut hasher);
      let mut result = BufHash::default();
      for (i, byte) in hasher.result().into_iter().enumerate() {
        result[i] = byte;
      }
      ophashes.push(result);

      // Set the latest op for which we already have the calculated buffer
      if self.globals.cache.contains_key(&result) {
        startpos = i+1;
      }
    });

    // Do the operations, starting with a dummy buffer id as gofloat doesn't use it
    let mut bufin = BufHash::default();
    all_ops!(self.ops, |ref op, i| {
      let hash = ophashes[i];
      if i >= startpos { // We're at the point where we need to start calculating ops
        let globals = &mut self.globals;
        do_timing(op.name(), ||op.run(globals, bufin, hash));
      }
      bufin = hash;
    });
    self.globals.cache.get(bufin).unwrap()
  }
}

fn simple_decode_full(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> OpBuffer {
  let buf = {
    let mut pipeline = Pipeline::new(img, maxwidth, maxheight, linear);
    // pipeline.run(); // Used for testing if the pipeline is caching properly
    pipeline.run()
  };

  // Since we've kept the pipeline to ourselves unwraping always works
  Arc::try_unwrap(buf).unwrap()
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, false)
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, true)
}
<commit_msg>imageops: make ImageOp require Deserialize<commit_after>extern crate rayon;
use self::rayon::prelude::*;

pub mod gofloat;
pub mod demosaic;
pub mod level;
pub mod colorspaces;
pub mod curves;
pub mod gamma;
pub mod transform;

use decoders::RawImage;

extern crate time;
extern crate toml;
extern crate serde;
extern crate serde_yaml;
use self::serde::{Serialize,Deserialize};

extern crate bincode;
extern crate sha2;
use self::sha2::Digest;

use std::fmt::Debug;

extern crate multicache;
use self::multicache::MultiCache;
use std::sync::Arc;

#[derive(Debug, Clone, PartialEq)]
pub struct OpBuffer {
  pub width: usize,
  pub height: usize,
  pub colors: usize,
  pub data: Vec<f32>,
}

impl OpBuffer {
  pub fn new(width: usize, height: usize, colors: usize) -> OpBuffer {
    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: vec![0.0; width*height*(colors as usize)],
    }
  }

  pub fn mutate_lines<F>(&mut self, closure: &F)
    where F : Fn(&mut [f32], usize)+Sync {

    self.data.par_chunks_mut(self.width*self.colors).enumerate().for_each(|(row, line)| {
      closure(line, row);
    });
  }

  pub fn process_into_new<F>(&self, colors: usize, closure: &F) -> OpBuffer
    where F : Fn(&mut [f32], &[f32])+Sync {

    let mut out = OpBuffer::new(self.width, self.height, colors);
    out.data.par_chunks_mut(out.width*out.colors).enumerate().for_each(|(row, line)| {
      closure(line, &self.data[self.width*self.colors*row..]);
    });
    out
  }

  /// Helper function to allow human readable creation of `OpBuffer` instances
  pub fn from_rgb_str_vec(data: Vec<&str>) -> OpBuffer {
    let width = data.first().expect("Invalid data for rgb helper function").len();
    let height = data.len();
    let colors = 3;

    let mut pixel_data: Vec<f32> = Vec::with_capacity(width * height * colors);
    for row in data {
      for col in row.chars() {
        let (r, g, b) = match col {
            'R' => (1.0, 0.0, 0.0),
            'G' => (0.0, 1.0, 0.0),
            'B' => (0.0, 0.0, 1.0),
            'O' => (1.0, 1.0, 1.0),
            ' ' => (0.0, 0.0, 0.0),
            c @ _ => panic!(format!(
              "Invalid color '{}' sent to rgb expected any of 'RGBO '", c)),
        };

        pixel_data.push(r);
        pixel_data.push(g);
        pixel_data.push(b);
      }
    }

    OpBuffer {
      width: width,
      height: height,
      colors: colors,
      data: pixel_data,
    }
  }
}

fn do_timing<O, F: FnMut() -> O>(name: &str, mut closure: F) -> O {
  let from_time = time::precise_time_ns();
  let ret = closure();
  let to_time = time::precise_time_ns();
  println!("{} ms for '{}'", (to_time - from_time)/1000000, name);

  ret
}

// Use SHA256 as the hash for buffers
type BufHasher = sha2::Sha256;
type BufHash = [u8;32];

pub trait ImageOp<'a>: Debug+Serialize+Deserialize<'a> {
  fn name(&self) -> &str;
  fn run(&self, pipeline: &mut PipelineGlobals, inid: BufHash, outid: BufHash);
  fn to_settings(&self) -> String {
    serde_yaml::to_string(self).unwrap()
  }
  fn hash(&self, hasher: &mut BufHasher) {
    let encoded = self::bincode::serialize(self, self::bincode::Infinite).unwrap();
    hasher.input(&encoded);
  }
}

#[derive(Debug)]
pub struct PipelineGlobals<'a> {
  cache: MultiCache<BufHash, OpBuffer>,
  maxwidth: usize,
  maxheight: usize,
  linear: bool,
  image: &'a RawImage,
}

#[derive(Debug)]
pub struct PipelineOps {
  gofloat: gofloat::OpGoFloat,
  demosaic: demosaic::OpDemosaic,
  level: level::OpLevel,
  tolab: colorspaces::OpToLab,
  basecurve: curves::OpBaseCurve,
  fromlab: colorspaces::OpFromLab,
  gamma: gamma::OpGamma,
  transform: transform::OpTransform,
}

macro_rules! for_vals {
  ([$($val:expr),*] |$x:pat, $i:ident| $body:expr) => {
    let mut pos = 0;
    $({
      let $x = $val;
      pos += 1;
      let $i = pos-1;
      $body
    })*
  }
}

macro_rules! all_ops {
  ($ops:expr, |$x:pat, $i:ident| $body:expr) => {
    for_vals!([
      $ops.gofloat,
      $ops.demosaic,
      $ops.level,
      $ops.tolab,
      $ops.basecurve,
      $ops.fromlab,
      $ops.gamma,
      $ops.transform
    ] |$x, $i| {
      $body
    });
  }
}

#[derive(Debug)]
pub struct Pipeline<'a> {
  globals: PipelineGlobals<'a>,
  ops: PipelineOps,
}

impl<'a> Pipeline<'a> {
  pub fn new(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> Pipeline {
    // Check if the image's orientation results in a rotation that
    // swaps the maximum width with the maximum height
    let (transpose, ..) = img.orientation.to_flips();
    let (maxwidth, maxheight) = if transpose {
      (maxheight, maxwidth)
    } else {
      (maxwidth, maxheight)
    };

    Pipeline {
      globals: PipelineGlobals {
        cache: MultiCache::new(1),
        maxwidth,
        maxheight,
        linear,
        image: img,
      },
      ops: PipelineOps {
        gofloat: gofloat::OpGoFloat::new(img),
        demosaic: demosaic::OpDemosaic::new(img),
        level: level::OpLevel::new(img),
        tolab: colorspaces::OpToLab::new(img),
        basecurve: curves::OpBaseCurve::new(img),
        fromlab: colorspaces::OpFromLab::new(img),
        gamma: gamma::OpGamma::new(img),
        transform: transform::OpTransform::new(img),
      },
    }
  }

  pub fn run(&mut self) -> Arc<OpBuffer> {
    // Generate all the hashes for the operations
    let mut hasher = BufHasher::default();
    let mut ophashes = Vec::new();
    let mut startpos = 0;
    all_ops!(self.ops, |ref op, i| {
      // Hash the name first as a zero sized struct doesn't actually do any hashing
      hasher.input(op.name().as_bytes());
      op.hash(&mut hasher);
      let mut result = BufHash::default();
      for (i, byte) in hasher.result().into_iter().enumerate() {
        result[i] = byte;
      }
      ophashes.push(result);

      // Set the latest op for which we already have the calculated buffer
      if self.globals.cache.contains_key(&result) {
        startpos = i+1;
      }
    });

    // Do the operations, starting with a dummy buffer id as gofloat doesn't use it
    let mut bufin = BufHash::default();
    all_ops!(self.ops, |ref op, i| {
      let hash = ophashes[i];
      if i >= startpos { // We're at the point where we need to start calculating ops
        let globals = &mut self.globals;
        do_timing(op.name(), ||op.run(globals, bufin, hash));
      }
      bufin = hash;
    });
    self.globals.cache.get(bufin).unwrap()
  }
}

fn simple_decode_full(img: &RawImage, maxwidth: usize, maxheight: usize, linear: bool) -> OpBuffer {
  let buf = {
    let mut pipeline = Pipeline::new(img, maxwidth, maxheight, linear);
    // pipeline.run(); // Used for testing if the pipeline is caching properly
    pipeline.run()
  };

  // Since we've kept the pipeline to ourselves unwraping always works
  Arc::try_unwrap(buf).unwrap()
}

pub fn simple_decode(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, false)
}

pub fn simple_decode_linear(img: &RawImage, maxwidth: usize, maxheight: usize) -> OpBuffer {
  simple_decode_full(img, maxwidth, maxheight, true)
}
<|endoftext|>


<commit_before>use cap::{CPoolHalf, Cap, CPool, CapWriteObject, CapReadObject};
use util::{SharedReadGuard, SharedWriteGuard, RefGuard, RefMutGuard, IndexedSharedReadGuard, IndexedSharedWriteGuard};
use core::ops::{Deref, DerefMut};
use core::marker::{PhantomData};

pub trait IntoFull<Half, M> where CapFull<Half, M>: Into<Cap> {
    unsafe fn into_full(mut self, cpool: CPoolHalf, cpool_index: usize) -> CapFull<Half, M>;
}

pub struct CapNearlyFull<Half, M> {
    half: Half,
    holdings: M
}

impl<Half, M> CapNearlyFull<Half, M> {
    pub fn new(half: Half, holdings: M) -> Self {
        CapNearlyFull {
            half: half,
            holdings: holdings
        }
    }
}

impl<'a, Half> IntoFull<Half, [MDB; 1]> for CapNearlyFull<Half, [Option<&'a mut MDB>; 1]>
    where CapFull<Half, [MDB; 1]>: Into<Cap> {
    unsafe fn into_full(mut self, cpool: CPoolHalf, cpool_index: usize) -> CapFull<Half, [MDB; 1]> {
        let mut cap = CapFull::new(self.half, [ MDB::default() ]);
        let mut index = 0;
        for hold in self.holdings.iter_mut() {
            unsafe { cap.set_mdb(cpool.clone(), cpool_index); }
            if hold.is_some() {
                let hold = hold.take().unwrap();
                cap.mdb_mut(index).associate(hold);
            }
            index += 1;
        }
        cap
    }
}

impl<Half, M> Deref for CapNearlyFull<Half, M> {
    type Target = Half;
    fn deref(&self) -> &Half {
        &self.half
    }
}

impl<Half, M> DerefMut for CapNearlyFull<Half, M> {
    fn deref_mut(&mut self) -> &mut Half {
        &mut self.half
    }
}

#[derive(Debug)]
pub struct CapFull<Half, M> {
    half: Half,
    mdbs: M,
    deleted: bool,
}

impl<Half, M> CapFull<Half, M> {
    pub fn new(half: Half, mdbs: M) -> Self {
        CapFull {
            half: half,
            mdbs: mdbs,
            deleted: false,
        }
    }

    pub fn mark_deleted(&mut self) {
        self.deleted = true;
    }
}

impl<Half> CapFull<Half, [MDB; 1]> {
    pub unsafe fn set_mdb(&mut self, cpool: CPoolHalf, cpool_index: usize) {
        let mut mdb_index = 0;
        for mdb in self.mdbs.iter_mut() {
            mdb.set(MDBAddr {
                cpool: cpool.clone(),
                cpool_index: cpool_index,
                mdb_index: mdb_index,
            });
            mdb_index += 1;
        }
    }

    pub fn mdb(&self, index: usize) -> &MDB {
        &self.mdbs[index]
    }

    pub fn mdb_mut(&mut self, index: usize) -> &mut MDB {
        &mut self.mdbs[index]
    }
}

impl<'a, Half> IntoFull<Half, [MDB; 1]> for CapFull<Half, [MDB; 1]>
    where CapFull<Half, [MDB; 1]>: Into<Cap> {
    unsafe fn into_full(mut self, cpool: CPoolHalf, cpool_index: usize) -> CapFull<Half, [MDB; 1]> {
        self.set_mdb(cpool.clone(), cpool_index);
        self
    }
}

impl<Half, M> Deref for CapFull<Half, M> {
    type Target = Half;
    fn deref(&self) -> &Half {
        &self.half
    }
}

impl<Half, M> DerefMut for CapFull<Half, M> {
    fn deref_mut(&mut self) -> &mut Half {
        &mut self.half
    }
}

impl<Half, M> Drop for CapFull<Half, M> {
    fn drop(&mut self) {
        assert!(self.deleted, "attempt to drop unmarked CapFull.");
    }
}

#[derive(Clone, Debug)]
pub struct MDBAddr {
    cpool: CPoolHalf,
    cpool_index: usize,
    mdb_index: usize,
}

#[derive(Debug)]
pub struct MDB {
    this: Option<MDBAddr>,
    first_child: Option<MDBAddr>,
    parent: Option<MDBAddr>,
    prev: Option<MDBAddr>,
    next: Option<MDBAddr>
}

impl Default for MDB {
    fn default() -> Self {
        MDB {
            this: None,
            first_child: None,
            parent: None,
            prev: None,
            next: None
        }
    }
}

impl MDB {
    pub fn children<'a, 'b>(&'a self) -> MDBChildIter<'b> {
        MDBChildIter {
            next_child: self.first_child.clone(),
            _marker: PhantomData,
        }
    }

    pub fn children_mut<'a, 'b>(&'a mut self) -> MDBChildIterMut<'b> {
        MDBChildIterMut {
            next_child: self.first_child.clone(),
            _marker: PhantomData,
        }
    }

    pub fn associate(&mut self, holding_parent: &mut MDB) {
        assert!(self.parent.is_none() &&
                self.next.is_none() &&
                self.prev.is_none() &&
                self.this.is_some() &&
                holding_parent.this.is_some());

        if holding_parent.first_child.is_some() {
            let mut first_child = holding_parent.first_child.clone().unwrap();
            let mut full_option = first_child.cpool.write(first_child.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(first_child.mdb_index).prev = self.this.clone();
        }
        self.next = holding_parent.first_child.clone();
        self.parent = holding_parent.this.clone();
        holding_parent.first_child = self.this.clone();
    }

    pub unsafe fn set(&mut self, addr: MDBAddr) {
        self.this = Some(addr.clone());

        // Update prev
        if self.prev.is_some() {
            let mut prev = self.prev.clone().unwrap();
            let mut full_option = prev.cpool.write(prev.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(prev.mdb_index).next = Some(addr.clone());
        }

        // Update children
        let mut current_child_option = self.first_child.clone();
        while current_child_option.is_some() {
            let mut current_child = current_child_option.clone().unwrap();
            let mut full_option = current_child.cpool.write(current_child.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(current_child.mdb_index).parent = Some(addr.clone());
            current_child_option = full.mdb(current_child.mdb_index).next.clone();
        }

        // Update next
        if self.next.is_some() {
            let mut next = self.next.clone().unwrap();
            let mut full_option = next.cpool.write(next.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(next.mdb_index).prev = Some(addr.clone());
        }
    }
}

pub struct MDBChildIter<'a> {
    next_child: Option<MDBAddr>,
    _marker: PhantomData<SharedReadGuard<'a, Option<Cap>>>,
}

impl<'a> Iterator for MDBChildIter<'a> {
    type Item = SharedReadGuard<'a, Option<Cap>>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.next_child.is_some() {
            let next_child = self.next_child.take().unwrap();
            let full_option = next_child.cpool.read(next_child.cpool_index);
            {
                let full = full_option.as_ref().unwrap();
                self.next_child = full.mdb(next_child.mdb_index).next.clone();
            }
            Some(full_option)
        } else {
            None
        }
    }
}

pub struct MDBChildIterMut<'a> {
    next_child: Option<MDBAddr>,
    _marker: PhantomData<SharedWriteGuard<'a, Option<Cap>>>,
}

impl<'a> Iterator for MDBChildIterMut<'a> {
    type Item = SharedWriteGuard<'a, Option<Cap>>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.next_child.is_some() {
            let mut next_child = self.next_child.take().unwrap();
            let full_option = next_child.cpool.write(next_child.cpool_index);
            {
                let full = full_option.as_ref().unwrap();
                self.next_child = full.mdb(next_child.mdb_index).next.clone();
            }
            Some(full_option)
        } else {
            None
        }
    }
}
<commit_msg>Make MDB access not overflow<commit_after>use cap::{CPoolHalf, Cap, CPool, CapWriteObject, CapReadObject};
use util::{SharedReadGuard, SharedWriteGuard, RefGuard, RefMutGuard, IndexedSharedReadGuard, IndexedSharedWriteGuard};
use core::ops::{Deref, DerefMut};
use core::marker::{PhantomData};

pub trait IntoFull<Half, M> where CapFull<Half, M>: Into<Cap> {
    unsafe fn into_full(mut self, cpool: CPoolHalf, cpool_index: usize) -> CapFull<Half, M>;
}

pub struct CapNearlyFull<Half, M> {
    half: Half,
    holdings: M
}

impl<Half, M> CapNearlyFull<Half, M> {
    pub fn new(half: Half, holdings: M) -> Self {
        CapNearlyFull {
            half: half,
            holdings: holdings
        }
    }
}

impl<'a, Half> IntoFull<Half, [MDB; 1]> for CapNearlyFull<Half, [Option<&'a mut MDB>; 1]>
    where CapFull<Half, [MDB; 1]>: Into<Cap> {
    unsafe fn into_full(mut self, cpool: CPoolHalf, cpool_index: usize) -> CapFull<Half, [MDB; 1]> {
        let mut cap = CapFull::new(self.half, [ MDB::default() ]);
        let mut index = 0;
        for hold in self.holdings.iter_mut() {
            unsafe { cap.set_mdb(cpool.clone(), cpool_index); }
            if hold.is_some() {
                let hold = hold.take().unwrap();
                cap.mdb_mut(index).associate(hold);
            }
            index += 1;
        }
        cap
    }
}

impl<Half, M> Deref for CapNearlyFull<Half, M> {
    type Target = Half;
    fn deref(&self) -> &Half {
        &self.half
    }
}

impl<Half, M> DerefMut for CapNearlyFull<Half, M> {
    fn deref_mut(&mut self) -> &mut Half {
        &mut self.half
    }
}

#[derive(Debug)]
pub struct CapFull<Half, M> {
    half: Half,
    mdbs: M,
    deleted: bool,
}

impl<Half, M> CapFull<Half, M> {
    pub fn new(half: Half, mdbs: M) -> Self {
        CapFull {
            half: half,
            mdbs: mdbs,
            deleted: false,
        }
    }

    pub fn mark_deleted(&mut self) {
        self.deleted = true;
    }
}

impl<Half> CapFull<Half, [MDB; 1]> {
    pub unsafe fn set_mdb(&mut self, cpool: CPoolHalf, cpool_index: usize) {
        let mut mdb_index = 0;
        for mdb in self.mdbs.iter_mut() {
            mdb.set(MDBAddr {
                cpool: cpool.clone(),
                cpool_index: cpool_index,
                mdb_index: mdb_index,
            });
            mdb_index += 1;
        }
    }

    pub fn mdb(&self, index: usize) -> &MDB {
        assert!(index < 1);
        &self.mdbs[index]
    }

    pub fn mdb_mut(&mut self, index: usize) -> &mut MDB {
        assert!(index < 1);
        &mut self.mdbs[index]
    }
}

impl<'a, Half> IntoFull<Half, [MDB; 1]> for CapFull<Half, [MDB; 1]>
    where CapFull<Half, [MDB; 1]>: Into<Cap> {
    unsafe fn into_full(mut self, cpool: CPoolHalf, cpool_index: usize) -> CapFull<Half, [MDB; 1]> {
        self.set_mdb(cpool.clone(), cpool_index);
        self
    }
}

impl<Half, M> Deref for CapFull<Half, M> {
    type Target = Half;
    fn deref(&self) -> &Half {
        &self.half
    }
}

impl<Half, M> DerefMut for CapFull<Half, M> {
    fn deref_mut(&mut self) -> &mut Half {
        &mut self.half
    }
}

impl<Half, M> Drop for CapFull<Half, M> {
    fn drop(&mut self) {
        assert!(self.deleted, "attempt to drop unmarked CapFull.");
    }
}

#[derive(Clone, Debug)]
pub struct MDBAddr {
    cpool: CPoolHalf,
    cpool_index: usize,
    mdb_index: usize,
}

#[derive(Debug)]
pub struct MDB {
    this: Option<MDBAddr>,
    first_child: Option<MDBAddr>,
    parent: Option<MDBAddr>,
    prev: Option<MDBAddr>,
    next: Option<MDBAddr>
}

impl Default for MDB {
    fn default() -> Self {
        MDB {
            this: None,
            first_child: None,
            parent: None,
            prev: None,
            next: None
        }
    }
}

impl MDB {
    pub fn children<'a, 'b>(&'a self) -> MDBChildIter<'b> {
        MDBChildIter {
            next_child: self.first_child.clone(),
            _marker: PhantomData,
        }
    }

    pub fn children_mut<'a, 'b>(&'a mut self) -> MDBChildIterMut<'b> {
        MDBChildIterMut {
            next_child: self.first_child.clone(),
            _marker: PhantomData,
        }
    }

    pub fn associate(&mut self, holding_parent: &mut MDB) {
        assert!(self.parent.is_none() &&
                self.next.is_none() &&
                self.prev.is_none() &&
                self.this.is_some() &&
                holding_parent.this.is_some());

        if holding_parent.first_child.is_some() {
            let mut first_child = holding_parent.first_child.clone().unwrap();
            let mut full_option = first_child.cpool.write(first_child.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(first_child.mdb_index).prev = self.this.clone();
        }
        self.next = holding_parent.first_child.clone();
        self.parent = holding_parent.this.clone();
        holding_parent.first_child = self.this.clone();
    }

    pub unsafe fn set(&mut self, addr: MDBAddr) {
        self.this = Some(addr.clone());

        // Update prev
        if self.prev.is_some() {
            let mut prev = self.prev.clone().unwrap();
            let mut full_option = prev.cpool.write(prev.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(prev.mdb_index).next = Some(addr.clone());
        }

        // Update children
        let mut current_child_option = self.first_child.clone();
        while current_child_option.is_some() {
            let mut current_child = current_child_option.clone().unwrap();
            let mut full_option = current_child.cpool.write(current_child.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(current_child.mdb_index).parent = Some(addr.clone());
            current_child_option = full.mdb(current_child.mdb_index).next.clone();
        }

        // Update next
        if self.next.is_some() {
            let mut next = self.next.clone().unwrap();
            let mut full_option = next.cpool.write(next.cpool_index);
            let full = full_option.as_mut().unwrap();
            full.mdb_mut(next.mdb_index).prev = Some(addr.clone());
        }
    }
}

pub struct MDBChildIter<'a> {
    next_child: Option<MDBAddr>,
    _marker: PhantomData<SharedReadGuard<'a, Option<Cap>>>,
}

impl<'a> Iterator for MDBChildIter<'a> {
    type Item = SharedReadGuard<'a, Option<Cap>>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.next_child.is_some() {
            let next_child = self.next_child.take().unwrap();
            let full_option = next_child.cpool.read(next_child.cpool_index);
            {
                let full = full_option.as_ref().unwrap();
                self.next_child = full.mdb(next_child.mdb_index).next.clone();
            }
            Some(full_option)
        } else {
            None
        }
    }
}

pub struct MDBChildIterMut<'a> {
    next_child: Option<MDBAddr>,
    _marker: PhantomData<SharedWriteGuard<'a, Option<Cap>>>,
}

impl<'a> Iterator for MDBChildIterMut<'a> {
    type Item = SharedWriteGuard<'a, Option<Cap>>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.next_child.is_some() {
            let mut next_child = self.next_child.take().unwrap();
            let full_option = next_child.cpool.write(next_child.cpool_index);
            {
                let full = full_option.as_ref().unwrap();
                self.next_child = full.mdb(next_child.mdb_index).next.clone();
            }
            Some(full_option)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>//! Low level functions for special x86 instructions.

use segmentation;

/// Enable Interrupts.
pub unsafe fn enable() {
    asm!("sti");
}

/// Disable Interrupts.
pub unsafe fn disable() {
    asm!("cli");
}

/// Generate a software interrupt.
/// This is a macro because the argument needs to be an immediate.
#[macro_export]
macro_rules! int {
    ( $x:expr ) => {
        {
            asm!("int $0" :: "N" ($x));
        }
    };
}

/// A struct describing a pointer to a descriptor table (GDT / IDT).
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
#[derive(Debug)]
#[repr(C, packed)]
pub struct DescriptorTablePointer {
    /// Size of the DT.
    pub limit: u16,
    /// Pointer to the memory region containing the DT.
    pub base: u64,
}

/// Load GDT table.
pub unsafe fn lgdt(gdt: &DescriptorTablePointer) {
    asm!("lgdt ($0)" :: "r" (gdt) : "memory");
}

/// Load LDT table.
pub unsafe fn lldt(ldt: &DescriptorTablePointer) {
    asm!("lldt ($0)" :: "r" (ldt) : "memory");
}

/// Load IDT table.
pub unsafe fn lidt(idt: &DescriptorTablePointer) {
    asm!("lidt ($0)" :: "r" (idt) : "memory");
}

/// Load the task state register.
pub unsafe fn load_tss(sel: segmentation::SegmentSelector) {
    asm!("ltr $0" :: "r" (sel.bits()));
}

/// Halts the CPU by executing the `hlt` instruction.
#[inline(always)]
pub unsafe fn halt() {
    asm!("hlt" :::: "volatile");
}

/// Read the time stamp counter using the `RDTSC` instruction.
///
/// The `RDTSC` instruction is not a serializing instruction.
/// It does not necessarily wait until all previous instructions
/// have been executed before reading the counter. Similarly,
/// subsequent instructions may begin execution before the
/// read operation is performed. If software requires `RDTSC` to be
/// executed only after all previous instructions have completed locally,
/// it can either use `RDTSCP` or execute the sequence `LFENCE;RDTSC`.
pub fn rdtsc() -> u64 {
    let low: u32;
    let high: u32;
    unsafe {
        asm!("rdtsc" : "={eax}" (low), "={edx}" (high));
    }
    ((u64::from(high)) << 32) | (u64::from(low))
}

/// Read the time stamp counter using the `RDTSCP` instruction.
///
/// The `RDTSCP` instruction waits until all previous instructions
/// have been executed before reading the counter.
/// However, subsequent instructions may begin execution
/// before the read operation is performed.
///
/// Volatile is used here because the function may be used to act as
/// an instruction barrier.
pub fn rdtscp() -> u64 {
    let low: u32;
    let high: u32;
    unsafe {
        asm!("rdtscp" : "={eax}" (low), "={edx}" (high) ::: "volatile");
    }
    ((high as u64) << 32) | (low as u64)
}
<commit_msg>Rename interrupt enable/disable functions and add better docs<commit_after>//! Low level functions for special x86 instructions.

use segmentation;

/// Enable hardware interrupts using the `sti` instruction.
pub unsafe fn enable_interrupts() {
    asm!("sti");
}

/// Disable hardware interrupts using the `cli` instruction.
pub unsafe fn disable_interrupts() {
    asm!("cli");
}

/// Generate a software interrupt.
/// This is a macro because the argument needs to be an immediate.
#[macro_export]
macro_rules! int {
    ( $x:expr ) => {
        {
            asm!("int $0" :: "N" ($x));
        }
    };
}

/// A struct describing a pointer to a descriptor table (GDT / IDT).
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
#[derive(Debug)]
#[repr(C, packed)]
pub struct DescriptorTablePointer {
    /// Size of the DT.
    pub limit: u16,
    /// Pointer to the memory region containing the DT.
    pub base: u64,
}

/// Load GDT table.
pub unsafe fn lgdt(gdt: &DescriptorTablePointer) {
    asm!("lgdt ($0)" :: "r" (gdt) : "memory");
}

/// Load LDT table.
pub unsafe fn lldt(ldt: &DescriptorTablePointer) {
    asm!("lldt ($0)" :: "r" (ldt) : "memory");
}

/// Load IDT table.
pub unsafe fn lidt(idt: &DescriptorTablePointer) {
    asm!("lidt ($0)" :: "r" (idt) : "memory");
}

/// Load the task state register.
pub unsafe fn load_tss(sel: segmentation::SegmentSelector) {
    asm!("ltr $0" :: "r" (sel.bits()));
}

/// Halts the CPU by executing the `hlt` instruction.
#[inline(always)]
pub unsafe fn halt() {
    asm!("hlt" :::: "volatile");
}

/// Read time stamp counters

/// Read the time stamp counter using the `RDTSC` instruction.
///
/// The `RDTSC` instruction is not a serializing instruction.
/// It does not necessarily wait until all previous instructions
/// have been executed before reading the counter. Similarly,
/// subsequent instructions may begin execution before the
/// read operation is performed. If software requires `RDTSC` to be
/// executed only after all previous instructions have completed locally,
/// it can either use `RDTSCP` or execute the sequence `LFENCE;RDTSC`.
pub fn rdtsc() -> u64 {
    let low: u32;
    let high: u32;
    unsafe {
        asm!("rdtsc" : "={eax}" (low), "={edx}" (high));
    }
    ((u64::from(high)) << 32) | (u64::from(low))
}

/// Read the time stamp counter using the `RDTSCP` instruction.
///
/// The `RDTSCP` instruction waits until all previous instructions
/// have been executed before reading the counter.
/// However, subsequent instructions may begin execution
/// before the read operation is performed.
///
/// Volatile is used here because the function may be used to act as
/// an instruction barrier.
pub fn rdtscp() -> u64 {
    let low: u32;
    let high: u32;
    unsafe {
        asm!("rdtscp" : "={eax}" (low), "={edx}" (high) ::: "volatile");
    }
    ((high as u64) << 32) | (low as u64)
}
<|endoftext|>