<commit_before>use std::io;

/// Print the menu followed by the prompt
fn print_both(menu: &[&str], prompt: &str) {

    // Iterate through array and print index, period, and menu item
    for (i, item) in menu.iter().enumerate() {
        println!("{}. {}", i, item);
    }

    // Print the prompt
    println!("{}", prompt);
}

/// Grab the next line of input
fn next_input() -> Option<usize> {
    let mut in_s = String::new();
    if let Ok(_) = io::stdin().read_line(&mut in_s) {
        in_s.trim().parse().ok()
    } else {
        None
    }
}

fn select<'a>(menu: &'a [&str], prompt: &str) -> &'a str {

    // Check if menu is empty
    if menu.len() == 0 {
        return "";
    }

    // Loop until user inputs a valid menu index
    loop {

        print_both(menu, prompt);

        let input = next_input();

        let num = match input {
            Some(num) => num,
            None => continue,
        };

        if num < menu.len() {
            return menu[num];
        }
    }
}

fn main() {
    let prompt = "Choose one.";
    let menu = &["fee fie", "huff and puff", "mirror mirror", "tick tock"];
    println!("{}", select(menu, prompt));
}

#[test]
fn test_empty_menu() {
    let prompt = "Choose one.";
    let menu = &[];
    assert_eq!(select(menu, prompt), "");
}
<commit_msg>cleanup menu<commit_after>use std::io;

/// Print the menu followed by the prompt
fn print_both(menu: &[&str], prompt: &str) {
    // Iterate through array and print index, period, and menu item
    for (i, item) in menu.iter().enumerate() {
        println!("{}. {}", i, item);
    }

    // Print the prompt
    println!("{}", prompt);
}

/// Grab the next line of input
fn next_input() -> Option<usize> {
    let mut in_s = String::new();
    if io::stdin().read_line(&mut in_s).is_ok() {
        in_s.trim().parse().ok()
    } else {
        None
    }
}

fn select<'a>(menu: &'a [&str], prompt: &str) -> &'a str {
    if menu.is_empty() {
        return "";
    }

    // Loop until user inputs a valid menu index
    loop {
        print_both(menu, prompt);

        let input = next_input();

        let num = match input {
            Some(num) => num,
            None => continue,
        };

        if num < menu.len() {
            return menu[num];
        }
    }
}

fn main() {
    let prompt = "Choose one.";
    let menu = &["fee fie", "huff and puff", "mirror mirror", "tick tock"];
    println!("{}", select(menu, prompt));
}

#[test]
fn test_empty_menu() {
    let prompt = "Choose one.";
    let menu = &[];
    assert_eq!(select(menu, prompt), "");
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{accounts, context::Context};
use diem_api_types::{Error, LedgerInfo};

use std::convert::Infallible;
use warp::{http::StatusCode, reply, Filter, Rejection, Reply};

pub fn routes(context: Context) -> impl Filter<Extract = impl Reply, Error = Infallible> + Clone {
    index(context.clone())
        .or(accounts::routes(context))
        .recover(handle_rejection)
}

// GET /
pub fn index(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
    warp::path::end()
        .and(warp::get())
        .and(context.filter())
        .and_then(handle_index)
}

pub async fn handle_index(context: Context) -> Result<impl Reply, Rejection> {
    let ledger_info = context.get_latest_ledger_info().map_err(Error::internal)?;
    let chain_id = context.chain_id().id();
    let ledger_version = ledger_info.ledger_info().version().into();
    let ledger_timestamp = ledger_info.ledger_info().timestamp_usecs().into();

    let info = LedgerInfo {
        chain_id,
        ledger_version,
        ledger_timestamp,
    };
    Ok(warp::reply::json(&info))
}

async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
    let code;
    let body;
    if let Some(error) = err.find::<Error>() {
        code = error.status_code();
        body = reply::json(error);
    } else {
        code = StatusCode::INTERNAL_SERVER_ERROR;
        body = reply::json(&Error::new(code, format!("unexpected error: {:?}", err)));
    }
    Ok(reply::with_status(body, code))
}

#[cfg(test)]
mod test {
    use crate::test_utils::{new_test_context, send_request};
    use serde_json::json;

    #[tokio::test]
    async fn test_get_ledger_info() {
        let context = new_test_context();
        let resp = send_request(context.clone(), "GET", "/", 200).await;

        let ledger_info = context.get_latest_ledger_info().unwrap();
        let expected = json!({
            "chain_id": 4,
            "ledger_version": ledger_info.ledger_info().version().to_string(),
            "ledger_timestamp": ledger_info.ledger_info().timestamp_usecs().to_string(),
        });

        assert_eq!(expected, resp);
    }
}
<commit_msg>[api] handle invalid URL path properly<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{accounts, context::Context};
use diem_api_types::{Error, LedgerInfo};

use std::convert::Infallible;
use warp::{http::StatusCode, reply, Filter, Rejection, Reply};

pub fn routes(context: Context) -> impl Filter<Extract = impl Reply, Error = Infallible> + Clone {
    index(context.clone())
        .or(accounts::routes(context))
        .recover(handle_rejection)
}

// GET /
pub fn index(context: Context) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
    warp::path::end()
        .and(warp::get())
        .and(context.filter())
        .and_then(handle_index)
}

pub async fn handle_index(context: Context) -> Result<impl Reply, Rejection> {
    let ledger_info = context.get_latest_ledger_info().map_err(Error::internal)?;
    let chain_id = context.chain_id().id();
    let ledger_version = ledger_info.ledger_info().version().into();
    let ledger_timestamp = ledger_info.ledger_info().timestamp_usecs().into();

    let info = LedgerInfo {
        chain_id,
        ledger_version,
        ledger_timestamp,
    };
    Ok(warp::reply::json(&info))
}

async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
    let code;
    let body;
    if err.is_not_found() {
        code = StatusCode::NOT_FOUND;
        body = reply::json(&Error::new(code, "Not Found".to_owned()));
    } else if let Some(error) = err.find::<Error>() {
        code = error.status_code();
        body = reply::json(error);
    } else {
        code = StatusCode::INTERNAL_SERVER_ERROR;
        body = reply::json(&Error::new(code, format!("unexpected error: {:?}", err)));
    }
    Ok(reply::with_status(body, code))
}

#[cfg(test)]
mod test {
    use crate::test_utils::{new_test_context, send_request};
    use serde_json::json;

    #[tokio::test]
    async fn test_get_ledger_info() {
        let context = new_test_context();
        let resp = send_request(context.clone(), "GET", "/", 200).await;

        let ledger_info = context.get_latest_ledger_info().unwrap();
        let expected = json!({
            "chain_id": 4,
            "ledger_version": ledger_info.ledger_info().version().to_string(),
            "ledger_timestamp": ledger_info.ledger_info().timestamp_usecs().to_string(),
        });

        assert_eq!(expected, resp);
    }

    #[tokio::test]
    async fn test_returns_not_found_for_the_invalid_path() {
        let context = new_test_context();
        let resp = send_request(context.clone(), "GET", "/invalid_path", 404).await;
        assert_eq!(json!({"code": 404, "message": "Not Found"}), resp)
    }
}
<|endoftext|>


<commit_before>use yaml_rust::{YamlLoader, Yaml};
use yaml_rust::yaml::Hash;

static EN:&'static str = include_str!("../../lang/en.yaml");
static FR:&'static str = include_str!("../../lang/fr.yaml");

/// Get the hashmap for a given language
pub fn get_hash(lang: &str) -> Hash {
    let docs = match lang {
        "fr" => YamlLoader::load_from_str(FR).unwrap(),
        _ => YamlLoader::load_from_str(EN).unwrap(),
    };
    let elem = docs.into_iter().next().unwrap();
    if let Yaml::Hash(hash) = elem {
        hash
    } else {
        panic!("Yaml file for language {} didn't contain a hash", lang);
    }
}
<commit_msg>Forgot te commit lang.rs in previous commit<commit_after>use yaml_rust::{YamlLoader, Yaml};
use yaml_rust::yaml::Hash;

static EN:&'static str = include_str!("../../lang/en.yaml");
static FR:&'static str = include_str!("../../lang/fr.yaml");

/// Get the hashmap for a given language
pub fn get_hash(lang: &str) -> Hash {
    let docs = match lang {
        "fr" => YamlLoader::load_from_str(FR).unwrap(),
        _ => YamlLoader::load_from_str(EN).unwrap(),
    };
    let elem = docs.into_iter().next().unwrap();
    if let Yaml::Hash(hash) = elem {
        hash
    } else {
        panic!("Yaml file for language {} didn't contain a hash", lang);
    }
}

/// Get a string for a given language
pub fn get_str(lang: &str, s: &str) -> String {
    let hash = get_hash(lang);
    let yaml = hash.get(&Yaml::String(s.to_owned()))
        .expect(&format!("Could not find translation for {} in language {}", s, lang));
    if let &Yaml::String(ref result) = yaml {
        result.clone()
    } else {
        panic!("Yaml for {} in lang {} is not a String!", s, lang);
    }
}
<|endoftext|>


<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

use sysinfo::{Pid, PidExt, ProcessExt, SystemExt};

#[test]
fn test_process() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);
    s.refresh_processes();
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    assert!(!s.processes().is_empty());
    #[cfg(not(windows))]
    assert!(s
        .processes()
        .values()
        .any(|p| !p.exe().to_str().unwrap_or("").is_empty()));
}

#[test]
fn test_cwd() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("CwdSignal")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");

    let processes = s.processes();
    let p = processes.get(&pid);

    if let Some(p) = p {
        assert_eq!(p.pid(), pid);
        assert_eq!(p.cwd(), std::env::current_dir().unwrap());
    } else {
        panic!("Process not found!");
    }
}

#[test]
fn test_cmd() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("CmdSignal")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };
    std::thread::sleep(std::time::Duration::from_millis(500));
    let mut s = sysinfo::System::new();
    assert!(s.processes().is_empty());
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");
    assert!(!s.processes().is_empty());
    if let Some(process) = s.process(Pid::from_u32(p.id() as u32)) {
        if cfg!(target_os = "windows") {
            // Sometimes, we get the full path instead for some reasons... So just in case,
            // we check for the command independently that from the arguments.
            assert!(process.cmd()[0].contains("waitfor"));
            assert_eq!(&process.cmd()[1..], &["/t", "3", "CmdSignal"]);
        } else {
            assert_eq!(process.cmd(), &["sleep", "3"]);
        }
    } else {
        panic!("Process not found!");
    }
}

#[test]
fn test_environ() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("EnvironSignal")
            .stdout(std::process::Stdio::null())
            .env("FOO", "BAR")
            .env("OTHER", "VALUE")
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .env("FOO", "BAR")
            .env("OTHER", "VALUE")
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");

    let processes = s.processes();
    let p = processes.get(&pid);

    if let Some(p) = p {
        assert_eq!(p.pid(), pid);
        // FIXME: instead of ignoring the test on CI, try to find out what's wrong...
        if std::env::var("APPLE_CI").is_err() {
            assert!(p.environ().iter().any(|e| e == "FOO=BAR"));
            assert!(p.environ().iter().any(|e| e == "OTHER=VALUE"));
        }
    } else {
        panic!("Process not found!");
    }
}

#[test]
fn test_process_refresh() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    s.refresh_process(sysinfo::get_current_pid().expect("failed to get current pid"));
    assert!(s
        .process(sysinfo::get_current_pid().expect("failed to get current pid"))
        .is_some(),);
}

#[test]
fn test_process_disk_usage() {
    use std::fs;
    use std::fs::File;
    use std::io::prelude::*;
    use sysinfo::{get_current_pid, ProcessExt, SystemExt};

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    if std::env::var("FREEBSD_CI").is_ok() {
        // For an unknown reason, when running this test on Cirrus CI, it fails. It works perfectly
        // locally though... Dark magic...
        return;
    }

    fn inner() -> sysinfo::System {
        {
            let mut file = File::create("test.txt").expect("failed to create file");
            file.write_all(b"This is a test file\nwith test data.\n")
                .expect("failed to write to file");
        }
        fs::remove_file("test.txt").expect("failed to remove file");
        // Waiting a bit just in case...
        std::thread::sleep(std::time::Duration::from_millis(250));
        let mut system = sysinfo::System::new();
        assert!(system.processes().is_empty());
        system.refresh_processes();
        assert!(!system.processes().is_empty());
        system
    }

    let mut system = inner();
    let mut p = system
        .process(get_current_pid().expect("Failed retrieving current pid."))
        .expect("failed to get process");

    if cfg!(any(target_os = "macos", target_os = "ios")) && p.disk_usage().total_written_bytes == 0
    {
        // For whatever reason, sometimes, mac doesn't work on the first time when running
        // `cargo test`. Two solutions, either run with "cargo test -- --test-threads 1", or
        // check twice...
        system = inner();
        p = system
            .process(get_current_pid().expect("Failed retrieving current pid."))
            .expect("failed to get process");
    }

    assert!(
        p.disk_usage().total_written_bytes > 0,
        "found {} total written bytes...",
        p.disk_usage().total_written_bytes
    );
    assert!(
        p.disk_usage().written_bytes > 0,
        "found {} written bytes...",
        p.disk_usage().written_bytes
    );
}

#[test]
fn cpu_usage_is_not_nan() {
    let mut system = sysinfo::System::new();
    system.refresh_processes();

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }

    let first_pids = system
        .processes()
        .iter()
        .take(10)
        .map(|(&pid, _)| pid)
        .collect::<Vec<_>>();
    let mut checked = 0;

    first_pids.into_iter().for_each(|pid| {
        system.refresh_process(pid);
        if let Some(p) = system.process(pid) {
            assert!(!p.cpu_usage().is_nan());
            checked += 1;
        }
    });
    assert!(checked > 0);
}

#[test]
fn test_process_times() {
    use std::time::{SystemTime, UNIX_EPOCH};

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("ProcessTimes")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");

    if let Some(p) = s.process(pid) {
        assert_eq!(p.pid(), pid);
        assert!(p.run_time() >= 1);
        assert!(p.run_time() <= 2);
        assert!(p.start_time() > p.run_time());
        // On linux, for whatever reason, the uptime seems to be older than the boot time, leading
        // to this weird `+ 3` to ensure the test is passing as it should...
        assert!(
            p.start_time() + 3
                > SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_secs(),
        );
        assert!(p.start_time() >= s.boot_time());
    } else {
        panic!("Process not found!");
    }
}

// Checks that `refresh_processes` is removing dead processes.
#[test]
fn test_refresh_processes() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("300")
            .arg("RefreshProcesses")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("300")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));

    // Checks that the process is listed as it should.
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    assert!(s.process(pid).is_some());

    // Check that the process name is not empty.
    assert!(!s.process(pid).unwrap().name().is_empty());

    p.kill().expect("Unable to kill process.");
    // We need this, otherwise the process will still be around as a zombie on linux.
    let _ = p.wait();
    // Let's give some time to the system to clean up...
    std::thread::sleep(std::time::Duration::from_secs(1));

    s.refresh_processes();
    // Checks that the process isn't listed anymore.
    assert!(s.process(pid).is_none());
}

// Checks that `refresh_process` is NOT removing dead processes.
#[test]
fn test_refresh_process() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("300")
            .arg("RefreshProcess")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("300")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));

    // Checks that the process is listed as it should.
    let mut s = sysinfo::System::new();
    s.refresh_process(pid);
    assert!(s.process(pid).is_some());

    // Check that the process name is not empty.
    assert!(!s.process(pid).unwrap().name().is_empty());

    p.kill().expect("Unable to kill process.");
    // We need this, otherwise the process will still be around as a zombie on linux.
    let _ = p.wait();
    // Let's give some time to the system to clean up...
    std::thread::sleep(std::time::Duration::from_secs(1));

    assert!(!s.refresh_process(pid));
    // Checks that the process is still listed.
    assert!(s.process(pid).is_some());
}
<commit_msg>Remove unnecessary cfg attribute.<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

use sysinfo::{Pid, PidExt, ProcessExt, SystemExt};

#[test]
fn test_process() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);
    s.refresh_processes();
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    assert!(!s.processes().is_empty());
    assert!(s
        .processes()
        .values()
        .any(|p| !p.exe().to_str().unwrap_or("").is_empty()));
}

#[test]
fn test_cwd() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("CwdSignal")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");

    let processes = s.processes();
    let p = processes.get(&pid);

    if let Some(p) = p {
        assert_eq!(p.pid(), pid);
        assert_eq!(p.cwd(), std::env::current_dir().unwrap());
    } else {
        panic!("Process not found!");
    }
}

#[test]
fn test_cmd() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("CmdSignal")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };
    std::thread::sleep(std::time::Duration::from_millis(500));
    let mut s = sysinfo::System::new();
    assert!(s.processes().is_empty());
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");
    assert!(!s.processes().is_empty());
    if let Some(process) = s.process(Pid::from_u32(p.id() as u32)) {
        if cfg!(target_os = "windows") {
            // Sometimes, we get the full path instead for some reasons... So just in case,
            // we check for the command independently that from the arguments.
            assert!(process.cmd()[0].contains("waitfor"));
            assert_eq!(&process.cmd()[1..], &["/t", "3", "CmdSignal"]);
        } else {
            assert_eq!(process.cmd(), &["sleep", "3"]);
        }
    } else {
        panic!("Process not found!");
    }
}

#[test]
fn test_environ() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("EnvironSignal")
            .stdout(std::process::Stdio::null())
            .env("FOO", "BAR")
            .env("OTHER", "VALUE")
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .env("FOO", "BAR")
            .env("OTHER", "VALUE")
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");

    let processes = s.processes();
    let p = processes.get(&pid);

    if let Some(p) = p {
        assert_eq!(p.pid(), pid);
        // FIXME: instead of ignoring the test on CI, try to find out what's wrong...
        if std::env::var("APPLE_CI").is_err() {
            assert!(p.environ().iter().any(|e| e == "FOO=BAR"));
            assert!(p.environ().iter().any(|e| e == "OTHER=VALUE"));
        }
    } else {
        panic!("Process not found!");
    }
}

#[test]
fn test_process_refresh() {
    let mut s = sysinfo::System::new();
    assert_eq!(s.processes().len(), 0);

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    s.refresh_process(sysinfo::get_current_pid().expect("failed to get current pid"));
    assert!(s
        .process(sysinfo::get_current_pid().expect("failed to get current pid"))
        .is_some(),);
}

#[test]
fn test_process_disk_usage() {
    use std::fs;
    use std::fs::File;
    use std::io::prelude::*;
    use sysinfo::{get_current_pid, ProcessExt, SystemExt};

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    if std::env::var("FREEBSD_CI").is_ok() {
        // For an unknown reason, when running this test on Cirrus CI, it fails. It works perfectly
        // locally though... Dark magic...
        return;
    }

    fn inner() -> sysinfo::System {
        {
            let mut file = File::create("test.txt").expect("failed to create file");
            file.write_all(b"This is a test file\nwith test data.\n")
                .expect("failed to write to file");
        }
        fs::remove_file("test.txt").expect("failed to remove file");
        // Waiting a bit just in case...
        std::thread::sleep(std::time::Duration::from_millis(250));
        let mut system = sysinfo::System::new();
        assert!(system.processes().is_empty());
        system.refresh_processes();
        assert!(!system.processes().is_empty());
        system
    }

    let mut system = inner();
    let mut p = system
        .process(get_current_pid().expect("Failed retrieving current pid."))
        .expect("failed to get process");

    if cfg!(any(target_os = "macos", target_os = "ios")) && p.disk_usage().total_written_bytes == 0
    {
        // For whatever reason, sometimes, mac doesn't work on the first time when running
        // `cargo test`. Two solutions, either run with "cargo test -- --test-threads 1", or
        // check twice...
        system = inner();
        p = system
            .process(get_current_pid().expect("Failed retrieving current pid."))
            .expect("failed to get process");
    }

    assert!(
        p.disk_usage().total_written_bytes > 0,
        "found {} total written bytes...",
        p.disk_usage().total_written_bytes
    );
    assert!(
        p.disk_usage().written_bytes > 0,
        "found {} written bytes...",
        p.disk_usage().written_bytes
    );
}

#[test]
fn cpu_usage_is_not_nan() {
    let mut system = sysinfo::System::new();
    system.refresh_processes();

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }

    let first_pids = system
        .processes()
        .iter()
        .take(10)
        .map(|(&pid, _)| pid)
        .collect::<Vec<_>>();
    let mut checked = 0;

    first_pids.into_iter().for_each(|pid| {
        system.refresh_process(pid);
        if let Some(p) = system.process(pid) {
            assert!(!p.cpu_usage().is_nan());
            checked += 1;
        }
    });
    assert!(checked > 0);
}

#[test]
fn test_process_times() {
    use std::time::{SystemTime, UNIX_EPOCH};

    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("3")
            .arg("ProcessTimes")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("3")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    p.kill().expect("Unable to kill process.");

    if let Some(p) = s.process(pid) {
        assert_eq!(p.pid(), pid);
        assert!(p.run_time() >= 1);
        assert!(p.run_time() <= 2);
        assert!(p.start_time() > p.run_time());
        // On linux, for whatever reason, the uptime seems to be older than the boot time, leading
        // to this weird `+ 3` to ensure the test is passing as it should...
        assert!(
            p.start_time() + 3
                > SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_secs(),
        );
        assert!(p.start_time() >= s.boot_time());
    } else {
        panic!("Process not found!");
    }
}

// Checks that `refresh_processes` is removing dead processes.
#[test]
fn test_refresh_processes() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("300")
            .arg("RefreshProcesses")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("300")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));

    // Checks that the process is listed as it should.
    let mut s = sysinfo::System::new();
    s.refresh_processes();
    assert!(s.process(pid).is_some());

    // Check that the process name is not empty.
    assert!(!s.process(pid).unwrap().name().is_empty());

    p.kill().expect("Unable to kill process.");
    // We need this, otherwise the process will still be around as a zombie on linux.
    let _ = p.wait();
    // Let's give some time to the system to clean up...
    std::thread::sleep(std::time::Duration::from_secs(1));

    s.refresh_processes();
    // Checks that the process isn't listed anymore.
    assert!(s.process(pid).is_none());
}

// Checks that `refresh_process` is NOT removing dead processes.
#[test]
fn test_refresh_process() {
    if !sysinfo::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
        return;
    }
    let mut p = if cfg!(target_os = "windows") {
        std::process::Command::new("waitfor")
            .arg("/t")
            .arg("300")
            .arg("RefreshProcess")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    } else {
        std::process::Command::new("sleep")
            .arg("300")
            .stdout(std::process::Stdio::null())
            .spawn()
            .unwrap()
    };

    let pid = Pid::from_u32(p.id() as u32);
    std::thread::sleep(std::time::Duration::from_secs(1));

    // Checks that the process is listed as it should.
    let mut s = sysinfo::System::new();
    s.refresh_process(pid);
    assert!(s.process(pid).is_some());

    // Check that the process name is not empty.
    assert!(!s.process(pid).unwrap().name().is_empty());

    p.kill().expect("Unable to kill process.");
    // We need this, otherwise the process will still be around as a zombie on linux.
    let _ = p.wait();
    // Let's give some time to the system to clean up...
    std::thread::sleep(std::time::Duration::from_secs(1));

    assert!(!s.refresh_process(pid));
    // Checks that the process is still listed.
    assert!(s.process(pid).is_some());
}
<|endoftext|>


<commit_before>// Functions dealing with attributes and meta_items

import std::vec;
import std::option;
import front::ast;

export attr_metas;
export find_linkage_metas;
export find_attrs_by_name;
export find_meta_items_by_name;
export contains;
export sort_meta_items;
export remove_meta_items_by_name;
export get_attr_name;

// From a list of crate attributes get only the meta_items that impact crate
// linkage
fn find_linkage_metas(vec[ast::attribute] attrs) -> vec[@ast::meta_item] {
    let vec[@ast::meta_item] metas = [];
    for (ast::attribute attr in find_attrs_by_name(attrs, "link")) {
        alt (attr.node.value.node) {
            case (ast::meta_list(_, ?items)) {
                metas += items;
            }
            case (_) {
                // FIXME: Maybe need a warning that this attr isn't
                // being used for linkage
            }
        }
    }
    ret metas;
}

// Search a list of attributes and return only those with a specific name
fn find_attrs_by_name(vec[ast::attribute] attrs,
                      ast::ident name) -> vec[ast::attribute] {
    auto filter = bind fn(&ast::attribute a,
                          ast::ident name) -> option::t[ast::attribute] {
        if (get_attr_name(a) == name) {
            option::some(a)
        } else {
            option::none
        }
    } (_, name);
    ret vec::filter_map(filter, attrs);
}

fn get_attr_name(&ast::attribute attr) -> ast::ident {
    get_meta_item_name(@attr.node.value)
}

fn find_meta_items_by_name(vec[@ast::meta_item] metas,
                           ast::ident name) -> vec[@ast::meta_item] {
    auto filter = bind fn(&@ast::meta_item m,
                          ast::ident name) -> option::t[@ast::meta_item] {
        if (get_meta_item_name(m) == name) {
            option::some(m)
        } else {
            option::none
        }
    } (_, name);
    ret vec::filter_map(filter, metas);
}

fn get_meta_item_name(&@ast::meta_item meta) -> ast::ident {
    alt (meta.node) {
        case (ast::meta_word(?n)) { n }
        case (ast::meta_name_value(?n, _)) { n }
        case (ast::meta_list(?n, _)) { n }
    }
}

fn attr_meta(&ast::attribute attr) -> @ast::meta_item { @attr.node.value }

// Get the meta_items from inside an attribute
fn attr_metas(&vec[ast::attribute] attrs) -> vec[@ast::meta_item] {
    ret vec::map(attr_meta, attrs);
}

fn eq(@ast::meta_item a, @ast::meta_item b) -> bool {
    ret alt (a.node) {
        case (ast::meta_word(?na)) {
            alt (b.node) {
                case(ast::meta_word(?nb)) { na == nb }
                case(_) { false }
            }
        }
        case (ast::meta_name_value(?na, ?va)) {
            alt (b.node) {
                case (ast::meta_name_value(?nb, ?vb)) { na == nb && va == vb }
                case (_) { false }
            }
        }
        case (ast::meta_list(?na, ?la)) {
            // FIXME (#487): This involves probably sorting the list by name
            fail "unimplemented meta_item variant"
        }
    }
}

fn contains(&vec[@ast::meta_item] haystack, @ast::meta_item needle) -> bool {
    log #fmt("looking for %s", pretty::pprust::meta_item_to_str(*needle));
    for (@ast::meta_item item in haystack) {
        log #fmt("looking in %s", pretty::pprust::meta_item_to_str(*item));
        if (eq(item, needle)) {
            log "found it!";
            ret true;
        }
    }
    log "found it not :(";
    ret false;
}

fn sort_meta_items(&vec[@ast::meta_item] items) -> vec[@ast::meta_item] {
    fn lteq(&@ast::meta_item ma, &@ast::meta_item mb) -> bool {
        fn key(&@ast::meta_item m) -> ast::ident {
            alt (m.node) {
                case (ast::meta_word(?name)) {
                    name
                }
                case (ast::meta_name_value(?name, _)) {
                    name
                }
                case (ast::meta_list(?name, _)) {
                    name
                }
            }
        }
        ret key(ma) <= key(mb);
    }

    // This is sort of stupid here, converting to a vec of mutables and back
    let vec[mutable @ast::meta_item] v = [mutable ];
    for (@ast::meta_item mi in items) {
        v += [mutable mi];
    }

    std::sort::quick_sort(lteq, v);

    let vec[@ast::meta_item] v2 = [];
    for (@ast::meta_item mi in v) {
        v2 += [mi]
    }
    ret v2;
}

fn remove_meta_items_by_name(&vec[@ast::meta_item] items,
                             str name) -> vec[@ast::meta_item] {

    auto filter = bind fn(&@ast::meta_item item,
                          str name) -> option::t[@ast::meta_item] {
        if (get_meta_item_name(item) != name) {
            option::some(item)
        } else {
            option::none
        }
    } (_, name);

    ret vec::filter_map(filter, items);
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Add some helper functions to front::attr<commit_after>// Functions dealing with attributes and meta_items

import std::vec;
import std::option;
import front::ast;
import util::common;

export attr_metas;
export find_linkage_metas;
export find_attrs_by_name;
export find_meta_items_by_name;
export contains;
export sort_meta_items;
export remove_meta_items_by_name;
export get_attr_name;
export mk_name_value_item;
export mk_link_item;
export mk_word_item;

// From a list of crate attributes get only the meta_items that impact crate
// linkage
fn find_linkage_metas(vec[ast::attribute] attrs) -> vec[@ast::meta_item] {
    let vec[@ast::meta_item] metas = [];
    for (ast::attribute attr in find_attrs_by_name(attrs, "link")) {
        alt (attr.node.value.node) {
            case (ast::meta_list(_, ?items)) {
                metas += items;
            }
            case (_) {
                // FIXME: Maybe need a warning that this attr isn't
                // being used for linkage
            }
        }
    }
    ret metas;
}

// Search a list of attributes and return only those with a specific name
fn find_attrs_by_name(vec[ast::attribute] attrs,
                      ast::ident name) -> vec[ast::attribute] {
    auto filter = bind fn(&ast::attribute a,
                          ast::ident name) -> option::t[ast::attribute] {
        if (get_attr_name(a) == name) {
            option::some(a)
        } else {
            option::none
        }
    } (_, name);
    ret vec::filter_map(filter, attrs);
}

fn get_attr_name(&ast::attribute attr) -> ast::ident {
    get_meta_item_name(@attr.node.value)
}

fn find_meta_items_by_name(vec[@ast::meta_item] metas,
                           ast::ident name) -> vec[@ast::meta_item] {
    auto filter = bind fn(&@ast::meta_item m,
                          ast::ident name) -> option::t[@ast::meta_item] {
        if (get_meta_item_name(m) == name) {
            option::some(m)
        } else {
            option::none
        }
    } (_, name);
    ret vec::filter_map(filter, metas);
}

fn get_meta_item_name(&@ast::meta_item meta) -> ast::ident {
    alt (meta.node) {
        case (ast::meta_word(?n)) { n }
        case (ast::meta_name_value(?n, _)) { n }
        case (ast::meta_list(?n, _)) { n }
    }
}

fn attr_meta(&ast::attribute attr) -> @ast::meta_item { @attr.node.value }

// Get the meta_items from inside an attribute
fn attr_metas(&vec[ast::attribute] attrs) -> vec[@ast::meta_item] {
    ret vec::map(attr_meta, attrs);
}

fn eq(@ast::meta_item a, @ast::meta_item b) -> bool {
    ret alt (a.node) {
        case (ast::meta_word(?na)) {
            alt (b.node) {
                case(ast::meta_word(?nb)) { na == nb }
                case(_) { false }
            }
        }
        case (ast::meta_name_value(?na, ?va)) {
            alt (b.node) {
                case (ast::meta_name_value(?nb, ?vb)) { na == nb && va == vb }
                case (_) { false }
            }
        }
        case (ast::meta_list(?na, ?la)) {
            // FIXME (#487): This involves probably sorting the list by name
            fail "unimplemented meta_item variant"
        }
    }
}

fn contains(&vec[@ast::meta_item] haystack, @ast::meta_item needle) -> bool {
    log #fmt("looking for %s", pretty::pprust::meta_item_to_str(*needle));
    for (@ast::meta_item item in haystack) {
        log #fmt("looking in %s", pretty::pprust::meta_item_to_str(*item));
        if (eq(item, needle)) {
            log "found it!";
            ret true;
        }
    }
    log "found it not :(";
    ret false;
}

fn sort_meta_items(&vec[@ast::meta_item] items) -> vec[@ast::meta_item] {
    fn lteq(&@ast::meta_item ma, &@ast::meta_item mb) -> bool {
        fn key(&@ast::meta_item m) -> ast::ident {
            alt (m.node) {
                case (ast::meta_word(?name)) {
                    name
                }
                case (ast::meta_name_value(?name, _)) {
                    name
                }
                case (ast::meta_list(?name, _)) {
                    name
                }
            }
        }
        ret key(ma) <= key(mb);
    }

    // This is sort of stupid here, converting to a vec of mutables and back
    let vec[mutable @ast::meta_item] v = [mutable ];
    for (@ast::meta_item mi in items) {
        v += [mutable mi];
    }

    std::sort::quick_sort(lteq, v);

    let vec[@ast::meta_item] v2 = [];
    for (@ast::meta_item mi in v) {
        v2 += [mi]
    }
    ret v2;
}

fn remove_meta_items_by_name(&vec[@ast::meta_item] items,
                             str name) -> vec[@ast::meta_item] {

    auto filter = bind fn(&@ast::meta_item item,
                          str name) -> option::t[@ast::meta_item] {
        if (get_meta_item_name(item) != name) {
            option::some(item)
        } else {
            option::none
        }
    } (_, name);

    ret vec::filter_map(filter, items);
}

fn span[T](&T item) -> common::spanned[T] {
    ret rec(node=item, span=rec(lo=0u, hi=0u));
}

fn mk_name_value_item(ast::ident name, str value) -> @ast::meta_item {
    ret @span(ast::meta_name_value(name, value));
}

fn mk_list_item(ast::ident name, &vec[@ast::meta_item] items) -> @ast::meta_item {
    ret @span(ast::meta_list(name, items));
}

fn mk_word_item(ast::ident name) -> @ast::meta_item {
    ret @span(ast::meta_word(name));
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>use std::mem;
use std::sync;

pub struct Lazy<T> {
    pub lock: sync::Once,
    pub ptr: *const T,
}

impl<T> Lazy<T> {
    pub fn get(&self, init: || -> T) -> &'static T {
        unsafe {
            self.lock.doit(|| {
                mem::transmute::<&Lazy<T>, &mut Lazy<T>>(self).ptr = mem::transmute(box init())
            });
            mem::transmute(self.ptr)
        }
    }
}

pub const ONCE_INIT: sync::Once = sync::ONCE_INIT;
<commit_msg>Fix compilation after changes in rust master<commit_after>use std::mem;
use std::sync;

pub struct Lazy<T> {
    pub lock: sync::Once,
    pub ptr: *const T,
}

impl<T> Lazy<T> {
    pub fn get(&'static self, init: || -> T) -> &'static T {
        unsafe {
            self.lock.doit(|| {
                mem::transmute::<&Lazy<T>, &mut Lazy<T>>(self).ptr = mem::transmute(box init())
            });
            mem::transmute(self.ptr)
        }
    }
}

pub const ONCE_INIT: sync::Once = sync::ONCE_INIT;
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::rc::Rc;

use baseline::fct::{Bailouts, BailoutInfo, CatchType, Comments, Comment, ExHandler, JitFct,
                    LineNumberTable, GcPoints, GcPoint};
use baseline::codegen::CondCode;
use byteorder::{LittleEndian, WriteBytesExt};
use cpu::{Reg, SCRATCH};
use ctxt::FctId;
use dseg::DSeg;
use lexer::position::Position;
use os::signal::Trap;
use ty::MachineMode;

#[cfg(target_arch = "x86_64")]
pub use self::x64::*;

#[cfg(target_arch = "x86_64")]
pub mod x64;

#[cfg(target_arch = "aarch64")]
pub use self::arm64::*;

#[cfg(target_arch = "aarch64")]
pub mod arm64;

pub struct MacroAssembler {
    data: Vec<u8>,
    labels: Vec<Option<usize>>,
    jumps: Vec<ForwardJump>,
    bailouts: Vec<(Label, Trap, Position)>,
    bailout_infos: Bailouts,
    dseg: DSeg,
    gcpoints: GcPoints,
    comments: Comments,
    linenos: LineNumberTable,
    exception_handlers: Vec<ExHandler>,
    scratch_registers: ScratchRegisters,
}

impl MacroAssembler {
    pub fn new() -> MacroAssembler {
        MacroAssembler {
            data: Vec::new(),
            labels: Vec::new(),
            jumps: Vec::new(),
            bailouts: Vec::new(),
            bailout_infos: Bailouts::new(),
            dseg: DSeg::new(),
            gcpoints: GcPoints::new(),
            comments: Comments::new(),
            linenos: LineNumberTable::new(),
            exception_handlers: Vec::new(),
            scratch_registers: ScratchRegisters::new(),
        }
    }

    pub fn jit(mut self, id: FctId, stacksize: i32) -> JitFct {
        self.finish();

        JitFct::from_buffer(id,
                            &self.dseg,
                            &self.data,
                            self.bailout_infos,
                            self.gcpoints,
                            stacksize,
                            self.comments,
                            self.linenos,
                            self.exception_handlers)
    }

    pub fn data(mut self) -> Vec<u8> {
        self.finish();

        self.data
    }

    fn finish(&mut self) {
        let bailouts = self.bailouts.drain(0..).collect::<Vec<_>>();

        for bailout in &bailouts {
            let (lbl, trap, pos) = *bailout;

            self.bind_label(lbl);
            self.emit_lineno(pos.line as i32);
            self.trap(trap);
        }

        self.fix_forward_jumps();
    }

    pub fn add_addr(&mut self, ptr: *const u8) -> i32 {
        self.dseg.add_addr(ptr)
    }

    pub fn pos(&self) -> usize {
        self.data.len()
    }

    pub fn test_if_nil_bailout(&mut self, pos: Position, reg: Reg, trap: Trap) {
        let lbl = self.test_if_nil(reg);
        self.emit_bailout(lbl, trap, pos);
    }

    pub fn test_if_nil(&mut self, reg: Reg) -> Label {
        self.cmp_zero(MachineMode::Ptr, reg);

        let lbl = self.create_label();
        self.jump_if(CondCode::Equal, lbl);

        lbl
    }

    pub fn emit_lineno(&mut self, lineno: i32) {
        let pos = self.pos() as i32;
        self.linenos.insert(pos, lineno);
    }

    pub fn emit_gcpoint(&mut self, gcpoint: GcPoint) {
        let pos = self.pos() as i32;
        self.gcpoints.insert(pos, gcpoint);
    }

    pub fn emit_bailout_info(&mut self, info: BailoutInfo) {
        let pos = self.pos() as i32;
        self.bailout_infos.insert(pos, info);
    }

    pub fn create_label(&mut self) -> Label {
        let idx = self.labels.len();
        self.labels.push(None);

        Label(idx)
    }

    pub fn emit_comment(&mut self, comment: Comment) {
        let pos = self.pos() as i32;
        self.comments.insert(pos, comment);
    }

    pub fn bind_label(&mut self, lbl: Label) {
        let lbl_idx = lbl.index();

        assert!(self.labels[lbl_idx].is_none());
        self.labels[lbl_idx] = Some(self.pos());
    }

    pub fn emit_bailout(&mut self, lbl: Label, trap: Trap, pos: Position) {
        self.bailouts.push((lbl, trap, pos));
    }

    pub fn emit_bailout_inplace(&mut self, trap: Trap, pos: Position) {
        self.emit_lineno(pos.line as i32);
        self.trap(trap);
    }

    pub fn emit_exception_handler(&mut self,
                                  span: (usize, usize),
                                  catch: usize,
                                  offset: Option<i32>,
                                  catch_type: CatchType) {
        self.exception_handlers.push(ExHandler {
            try_start: span.0,
            try_end: span.1,
            catch: catch,
            offset: offset,
            catch_type: catch_type,
        });
    }

    pub fn get_scratch(&self) -> ScratchReg {
        self.scratch_registers.get()
    }

    pub fn emit_u8(&mut self, value: u8) {
        self.data.write_u8(value).unwrap();
    }

    pub fn emit_u32(&mut self, value: u32) {
        self.data.write_u32::<LittleEndian>(value).unwrap();
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Label(usize);

impl Label {
    pub fn index(&self) -> usize {
        self.0
    }
}

#[derive(Clone, Debug)]
pub struct ScratchRegisters {
    regs: &'static [Reg],
    value: Rc<Cell<u32>>,
}

impl ScratchRegisters {
    pub fn new() -> ScratchRegisters {
        ScratchRegisters {
            regs: &SCRATCH,
            value: Rc::new(Cell::new(0))
        }
    }

    #[cfg(test)]
    pub fn with_regs(regs: &'static [Reg]) -> ScratchRegisters {
        ScratchRegisters {
            regs: regs,
            value: Rc::new(Cell::new(0))
        }
    }

    pub fn get(&self) -> ScratchReg {
        let value = self.value.get();

        for (ind, &reg) in self.regs.iter().enumerate() {
            if (value >> ind) & 1 == 0 {
                let bitmask = 1 << ind;
                self.value.set(value | bitmask);

                return ScratchReg {
                    ind: ind as u32,
                    reg: reg,
                    scratch: self.clone(),
                };
            }
        }

        panic!("all scratch registers used");
    }

    fn free(&self, reg: &ScratchReg) {
        let value = self.value.get();
        let bitmask = !(1 << reg.ind);

        self.value.set(value & bitmask);
    }
}

#[derive(Debug)]
pub struct ScratchReg {
    ind: u32,
    reg: Reg,
    scratch: ScratchRegisters,
}

impl ScratchReg {
    pub fn reg(&self) -> Reg {
        self.reg
    }
}

impl Drop for ScratchReg {
    fn drop(&mut self) {
        self.scratch.free(self);
    }
}

impl<'a> From<&'a ScratchReg> for Reg {
    fn from(value: &'a ScratchReg) -> Reg {
        value.reg
    }
}

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

    #[test]
    fn test_label() {
        let mut masm = MacroAssembler::new();

        assert_eq!(Label(0), masm.create_label());
        assert_eq!(Label(1), masm.create_label());
    }

    #[test]
    #[should_panic]
    fn test_bind_label_twice() {
        let mut masm = MacroAssembler::new();
        let lbl = masm.create_label();

        masm.bind_label(lbl);
        masm.bind_label(lbl);
    }

    static SCRATCH_REGS1: [Reg; 1] = [Reg(0)];
    static SCRATCH_REGS2: [Reg; 1] = [Reg(1)];

    #[test]
    #[should_panic]
    #[allow(unused_variables)]
    fn test_scratch_fail() {
        let masm = ScratchRegisters::with_regs(&SCRATCH_REGS1);

        let scratch1 = masm.get();
        let scratch2 = masm.get();
    }

    #[test]
    fn test_scratch_drop() {
        let masm = ScratchRegisters::with_regs(&SCRATCH_REGS2);

        {
            let scratch1 = masm.get();
            assert_eq!(scratch1.reg(), Reg(1));
        }

        {
            let scratch1 = masm.get();
            assert_eq!(scratch1.reg(), Reg(1));
        }
    }
}
<commit_msg>implement Deref for ScratchReg<commit_after>use std::cell::Cell;
use std::ops::Deref;
use std::rc::Rc;

use baseline::fct::{Bailouts, BailoutInfo, CatchType, Comments, Comment, ExHandler, JitFct,
                    LineNumberTable, GcPoints, GcPoint};
use baseline::codegen::CondCode;
use byteorder::{LittleEndian, WriteBytesExt};
use cpu::{Reg, SCRATCH};
use ctxt::FctId;
use dseg::DSeg;
use lexer::position::Position;
use os::signal::Trap;
use ty::MachineMode;

#[cfg(target_arch = "x86_64")]
pub use self::x64::*;

#[cfg(target_arch = "x86_64")]
pub mod x64;

#[cfg(target_arch = "aarch64")]
pub use self::arm64::*;

#[cfg(target_arch = "aarch64")]
pub mod arm64;

pub struct MacroAssembler {
    data: Vec<u8>,
    labels: Vec<Option<usize>>,
    jumps: Vec<ForwardJump>,
    bailouts: Vec<(Label, Trap, Position)>,
    bailout_infos: Bailouts,
    dseg: DSeg,
    gcpoints: GcPoints,
    comments: Comments,
    linenos: LineNumberTable,
    exception_handlers: Vec<ExHandler>,
    scratch_registers: ScratchRegisters,
}

impl MacroAssembler {
    pub fn new() -> MacroAssembler {
        MacroAssembler {
            data: Vec::new(),
            labels: Vec::new(),
            jumps: Vec::new(),
            bailouts: Vec::new(),
            bailout_infos: Bailouts::new(),
            dseg: DSeg::new(),
            gcpoints: GcPoints::new(),
            comments: Comments::new(),
            linenos: LineNumberTable::new(),
            exception_handlers: Vec::new(),
            scratch_registers: ScratchRegisters::new(),
        }
    }

    pub fn jit(mut self, id: FctId, stacksize: i32) -> JitFct {
        self.finish();

        JitFct::from_buffer(id,
                            &self.dseg,
                            &self.data,
                            self.bailout_infos,
                            self.gcpoints,
                            stacksize,
                            self.comments,
                            self.linenos,
                            self.exception_handlers)
    }

    pub fn data(mut self) -> Vec<u8> {
        self.finish();

        self.data
    }

    fn finish(&mut self) {
        let bailouts = self.bailouts.drain(0..).collect::<Vec<_>>();

        for bailout in &bailouts {
            let (lbl, trap, pos) = *bailout;

            self.bind_label(lbl);
            self.emit_lineno(pos.line as i32);
            self.trap(trap);
        }

        self.fix_forward_jumps();
    }

    pub fn add_addr(&mut self, ptr: *const u8) -> i32 {
        self.dseg.add_addr(ptr)
    }

    pub fn pos(&self) -> usize {
        self.data.len()
    }

    pub fn test_if_nil_bailout(&mut self, pos: Position, reg: Reg, trap: Trap) {
        let lbl = self.test_if_nil(reg);
        self.emit_bailout(lbl, trap, pos);
    }

    pub fn test_if_nil(&mut self, reg: Reg) -> Label {
        self.cmp_zero(MachineMode::Ptr, reg);

        let lbl = self.create_label();
        self.jump_if(CondCode::Equal, lbl);

        lbl
    }

    pub fn emit_lineno(&mut self, lineno: i32) {
        let pos = self.pos() as i32;
        self.linenos.insert(pos, lineno);
    }

    pub fn emit_gcpoint(&mut self, gcpoint: GcPoint) {
        let pos = self.pos() as i32;
        self.gcpoints.insert(pos, gcpoint);
    }

    pub fn emit_bailout_info(&mut self, info: BailoutInfo) {
        let pos = self.pos() as i32;
        self.bailout_infos.insert(pos, info);
    }

    pub fn create_label(&mut self) -> Label {
        let idx = self.labels.len();
        self.labels.push(None);

        Label(idx)
    }

    pub fn emit_comment(&mut self, comment: Comment) {
        let pos = self.pos() as i32;
        self.comments.insert(pos, comment);
    }

    pub fn bind_label(&mut self, lbl: Label) {
        let lbl_idx = lbl.index();

        assert!(self.labels[lbl_idx].is_none());
        self.labels[lbl_idx] = Some(self.pos());
    }

    pub fn emit_bailout(&mut self, lbl: Label, trap: Trap, pos: Position) {
        self.bailouts.push((lbl, trap, pos));
    }

    pub fn emit_bailout_inplace(&mut self, trap: Trap, pos: Position) {
        self.emit_lineno(pos.line as i32);
        self.trap(trap);
    }

    pub fn emit_exception_handler(&mut self,
                                  span: (usize, usize),
                                  catch: usize,
                                  offset: Option<i32>,
                                  catch_type: CatchType) {
        self.exception_handlers.push(ExHandler {
            try_start: span.0,
            try_end: span.1,
            catch: catch,
            offset: offset,
            catch_type: catch_type,
        });
    }

    pub fn get_scratch(&self) -> ScratchReg {
        self.scratch_registers.get()
    }

    pub fn emit_u8(&mut self, value: u8) {
        self.data.write_u8(value).unwrap();
    }

    pub fn emit_u32(&mut self, value: u32) {
        self.data.write_u32::<LittleEndian>(value).unwrap();
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Label(usize);

impl Label {
    pub fn index(&self) -> usize {
        self.0
    }
}

#[derive(Clone, Debug)]
pub struct ScratchRegisters {
    regs: &'static [Reg],
    value: Rc<Cell<u32>>,
}

impl ScratchRegisters {
    pub fn new() -> ScratchRegisters {
        ScratchRegisters {
            regs: &SCRATCH,
            value: Rc::new(Cell::new(0))
        }
    }

    #[cfg(test)]
    pub fn with_regs(regs: &'static [Reg]) -> ScratchRegisters {
        ScratchRegisters {
            regs: regs,
            value: Rc::new(Cell::new(0))
        }
    }

    pub fn get(&self) -> ScratchReg {
        let value = self.value.get();

        for (ind, &reg) in self.regs.iter().enumerate() {
            if (value >> ind) & 1 == 0 {
                let bitmask = 1 << ind;
                self.value.set(value | bitmask);

                return ScratchReg {
                    ind: ind as u32,
                    reg: reg,
                    scratch: self.clone(),
                };
            }
        }

        panic!("all scratch registers used");
    }

    fn free(&self, reg: &ScratchReg) {
        let value = self.value.get();
        let bitmask = !(1 << reg.ind);

        self.value.set(value & bitmask);
    }
}

#[derive(Debug)]
pub struct ScratchReg {
    ind: u32,
    reg: Reg,
    scratch: ScratchRegisters,
}

impl ScratchReg {
    pub fn reg(&self) -> Reg {
        self.reg
    }
}

impl Drop for ScratchReg {
    fn drop(&mut self) {
        self.scratch.free(self);
    }
}

impl Deref for ScratchReg {
    type Target = Reg;

    fn deref(&self) -> &Reg {
        &self.reg
    }
}

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

    #[test]
    fn test_label() {
        let mut masm = MacroAssembler::new();

        assert_eq!(Label(0), masm.create_label());
        assert_eq!(Label(1), masm.create_label());
    }

    #[test]
    #[should_panic]
    fn test_bind_label_twice() {
        let mut masm = MacroAssembler::new();
        let lbl = masm.create_label();

        masm.bind_label(lbl);
        masm.bind_label(lbl);
    }

    static SCRATCH_REGS1: [Reg; 1] = [Reg(0)];
    static SCRATCH_REGS2: [Reg; 1] = [Reg(1)];

    #[test]
    #[should_panic]
    #[allow(unused_variables)]
    fn test_scratch_fail() {
        let masm = ScratchRegisters::with_regs(&SCRATCH_REGS1);

        let scratch1 = masm.get();
        let scratch2 = masm.get();
    }

    #[test]
    fn test_scratch_drop() {
        let masm = ScratchRegisters::with_regs(&SCRATCH_REGS2);

        {
            let scratch1 = masm.get();
            assert_eq!(*scratch1, Reg(1));
        }

        {
            let scratch1 = masm.get();
            assert_eq!(*scratch1, Reg(1));
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{Read, Write};
use std::fs::OpenOptions;
use std::cmp;

use point::Point;

/// Holds the text data and the filepath, if any.
/// This buffer has no knowledge of the editor.
pub struct Buffer {
    /// Holds the actual data.
    /// This is a *very* inefficient data structure and will eventually be replace by a GapBuffer or Rope.
    /// This will also end up private so don't really too much on it.
    pub data: Vec<String>,

    /// The file path associated with that buffer.
    pub filepath: Option<String>,
}

impl Buffer {
    /// Make a new empty `Buffer`.
    pub fn new() -> Buffer {
        Buffer {
            data: vec!("".into()),
            filepath: None,
        }
    }

    /// Number of line in the buffer. Returns 1 if the buffer is empty.
    pub fn line_len(&self) -> usize {
        cmp::max(1, self.data.len())
    }

    /// Find the line at the given row.
    pub fn line_at(&mut self, y: usize) -> Option<&String> {
        self.data.get(y)
    }

    /// Find the last column that is non empty.
    /// Returns 0 if the whole line is empty.
    pub fn last_non_empty_col(&mut self, location: Point) -> usize {
        let x = self.line_at(location.y)
            .and_then(|line| match line.chars().nth(location.x) {
                Some(_) => Some(location.x as isize),
                None => Some(line.len() as isize - 2),
            }).unwrap();

        cmp::max(0, x) as usize
    }

    /// Find a char at a specific column/row.
    pub fn char_at(&mut self, location: Point) -> Option<char> {
        self.line_at(location.y).and_then(|line| line.chars().nth(location.x))
    }

    /// Insert a row at the specified point, pushing the other characters to the right.
    pub fn insert(&mut self, location: Point, c: char) {
        self.data[location.y].insert(location.x, c);
    }

    /// Replace a specific character.
    pub fn replace(&mut self, location: Point, c: char) {
        self.insert(location, c);
        self.delete(location.offset(1, 0));
    }

    /// Replace a character at the given location or insert it.
    pub fn upsert(&mut self, location: Point, c: char) {
        if let Some('\n') = self.char_at(location) {
            self.insert(location, c);
        } else {
            self.replace(location, c);
        }
    }

    /// Delete a character at the given location, shifting the rest of the line to the left.
    pub fn delete(&mut self, location: Point) -> char {
        self.data[location.y].remove(location.x)
    }

    /// Insert several characters at the given location. See `insert`.
    pub fn insert_text(&mut self, location: Point, string: String) {
        for (x, c) in string.chars().enumerate() {
            self.data[location.y].insert(location.x + x, c);
        }
    }

    /// Insert an empty line at the given location, shifting the subsequent lines down if any.
    pub fn new_line(&mut self, location: Point) {
        self.data.insert(location.y + 1, "\n".into());
    }

    /// Delete a line at the given location, shifting the subsequent lines up if any.
    pub fn delete_line(&mut self, location: Point) -> String {
        self.data.remove(location.y)
    }

    /// Split a line in half, inserting the second half as a new line below the first half.
    pub fn split_line(&mut self, location: Point) {
        let line = self.data[location.y].clone();
        let (left, right) = line.split_at(location.x);
        self.data[location.y] = String::from(left);
        self.data.insert(location.y + 1, right.into());
    }

    /// Load a file from a path and populate the internal data buffer.
    /// Unfortunately replaces the original line endings with CRs for now.
    /// # Panics
    /// When the file can't be loaded, e.g. if it doesn't exist.
    pub fn load_file(&mut self, path: String) {
        let mut file = OpenOptions::new().read(true).write(true).create(true).open(path).unwrap();
        let mut s = String::new();
        let _ = file.read_to_string(&mut s);
        let mut buf = Vec::new();

        for line in s.lines() {
            let mut l = String::from(line);
            l.push('\n');
            buf.push(l);
        }

        if buf.is_empty() {
            buf.push("".into());
        }

        self.data = buf;
    }

    /// Save the buffer at the internal filepath, returning the number of bytes written.
    ///
    /// Probably doesn't handle UTF-8 very well du to the `char` to `u8` conversion happening.
    /// # Panics
    /// When no filepath was given when creating the `Buffer`.
    pub fn save_file(&mut self) -> u64 {
        if self.filepath.is_none() { return 0 } // TODO: choose filepath

        let path = self.filepath.clone().unwrap();
        let mut file = OpenOptions::new().read(true).write(true).create(true).open(path).unwrap();
        for line in &self.data {
            let _ = file.write_all(line.as_bytes());
        }

        file.metadata().unwrap().len()
    }
}
<commit_msg>Fix crash when creating empty buffers<commit_after>use std::io::{Read, Write};
use std::fs::OpenOptions;
use std::cmp;

use point::Point;

/// Holds the text data and the filepath, if any.
/// This buffer has no knowledge of the editor.
pub struct Buffer {
    /// Holds the actual data.
    /// This is a *very* inefficient data structure and will eventually be replace by a GapBuffer or Rope.
    /// This will also end up private so don't really too much on it.
    pub data: Vec<String>,

    /// The file path associated with that buffer.
    pub filepath: Option<String>,
}

impl Buffer {
    /// Make a new empty `Buffer`.
    pub fn new() -> Buffer {
        Buffer {
            data: vec!("\n".into()),
            filepath: None,
        }
    }

    /// Number of line in the buffer. Returns 1 if the buffer is empty.
    pub fn line_len(&self) -> usize {
        cmp::max(1, self.data.len())
    }

    /// Find the line at the given row.
    pub fn line_at(&mut self, y: usize) -> Option<&String> {
        self.data.get(y)
    }

    /// Find the last column that is non empty.
    /// Returns 0 if the whole line is empty.
    pub fn last_non_empty_col(&mut self, location: Point) -> usize {
        let x = self.line_at(location.y)
            .and_then(|line| match line.chars().nth(location.x) {
                Some(_) => Some(location.x as isize),
                None => Some(line.len() as isize - 2),
            }).unwrap();

        cmp::max(0, x) as usize
    }

    /// Find a char at a specific column/row.
    pub fn char_at(&mut self, location: Point) -> Option<char> {
        self.line_at(location.y).and_then(|line| line.chars().nth(location.x))
    }

    /// Insert a row at the specified point, pushing the other characters to the right.
    pub fn insert(&mut self, location: Point, c: char) {
        self.data[location.y].insert(location.x, c);
    }

    /// Replace a specific character.
    pub fn replace(&mut self, location: Point, c: char) {
        self.insert(location, c);
        self.delete(location.offset(1, 0));
    }

    /// Replace a character at the given location or insert it.
    pub fn upsert(&mut self, location: Point, c: char) {
        if let Some('\n') = self.char_at(location) {
            self.insert(location, c);
        } else {
            self.replace(location, c);
        }
    }

    /// Delete a character at the given location, shifting the rest of the line to the left.
    pub fn delete(&mut self, location: Point) -> char {
        self.data[location.y].remove(location.x)
    }

    /// Insert several characters at the given location. See `insert`.
    pub fn insert_text(&mut self, location: Point, string: String) {
        for (x, c) in string.chars().enumerate() {
            self.data[location.y].insert(location.x + x, c);
        }
    }

    /// Insert an empty line at the given location, shifting the subsequent lines down if any.
    pub fn new_line(&mut self, location: Point) {
        self.data.insert(location.y + 1, "\n".into());
    }

    /// Delete a line at the given location, shifting the subsequent lines up if any.
    pub fn delete_line(&mut self, location: Point) -> String {
        self.data.remove(location.y)
    }

    /// Split a line in half, inserting the second half as a new line below the first half.
    pub fn split_line(&mut self, location: Point) {
        let line = self.data[location.y].clone();
        let (left, right) = line.split_at(location.x);
        self.data[location.y] = String::from(left);
        self.data[location.y].push('\n');
        self.data.insert(location.y + 1, right.into());
    }

    /// Load a file from a path and populate the internal data buffer.
    /// Unfortunately replaces the original line endings with CRs for now.
    /// # Panics
    /// When the file can't be loaded, e.g. if it doesn't exist.
    pub fn load_file(&mut self, path: String) {
        let mut file = OpenOptions::new().read(true).write(true).create(true).open(path).unwrap();
        let mut s = String::new();
        let _ = file.read_to_string(&mut s);
        let mut buf = Vec::new();

        for line in s.lines() {
            let mut l = String::from(line);
            l.push('\n');
            buf.push(l);
        }

        if buf.is_empty() {
            buf.push("\n".into());
        }

        self.data = buf;
    }

    /// Save the buffer at the internal filepath, returning the number of bytes written.
    ///
    /// Probably doesn't handle UTF-8 very well du to the `char` to `u8` conversion happening.
    /// # Panics
    /// When no filepath was given when creating the `Buffer`.
    pub fn save_file(&mut self) -> u64 {
        if self.filepath.is_none() { return 0 } // TODO: choose filepath

        let path = self.filepath.clone().unwrap();
        let mut file = OpenOptions::new().read(true).write(true).create(true).open(path).unwrap();
        for line in &self.data {
            let _ = file.write_all(line.as_bytes());
        }

        file.metadata().unwrap().len()
    }
}
<|endoftext|>


<commit_before>// Claxon -- A FLAC decoding library in Rust
// Copyright 2014 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! The `metadata` module deals with metadata at the beginning of a FLAC stream.

use error::{Result, fmt_err};
use input::ReadBytes;

#[derive(Clone, Copy)]
struct MetadataBlockHeader {
    is_last: bool,
    block_type: u8,
    length: u32,
}

/// The streaminfo metadata block, with important information about the stream.
#[derive(Clone, Copy, Debug)]
pub struct StreamInfo {
    // TODO: "size" would better be called "duration" for clarity.

    /// The minimum block size (in samples) used in the stream.
    pub min_block_size: u16,
    /// The maximum block size (in samples) used in the stream.
    pub max_block_size: u16,
    /// The minimum frame size (in bytes) used in the stream.
    pub min_frame_size: Option<u32>,
    /// The maximum frame size (in bytes) used in the stream.
    pub max_frame_size: Option<u32>,
    /// The sample rate in Hz.
    pub sample_rate: u32,
    /// The number of channels.
    pub channels: u32,
    /// The number of bits per sample.
    pub bits_per_sample: u32,
    /// The total number of inter-channel samples in the stream.
    pub samples: Option<u64>,
    /// MD5 signature of the unencoded audio data.
    pub md5sum: [u8; 16],
}

/// A seek point in the seek table.
#[derive(Clone, Copy)]
pub struct SeekPoint {
    /// Sample number of the first sample in the target frame, or 2^64 - 1 for a placeholder.
    pub sample: u64,
    /// Offset in bytes from the first byte of the first frame header to the first byte of the
    /// target frame's header.
    pub offset: u64,
    /// Number of samples in the target frame.
    pub samples: u16,
}

/// A seek table to aid seeking in the stream.
pub struct SeekTable {
    /// The seek points, sorted in ascending order by sample number.
    #[allow(dead_code)] // TODO: Implement seeking.
    seekpoints: Vec<SeekPoint>,
}

/// A metadata about the flac stream.
pub enum MetadataBlock {
    /// A stream info block.
    StreamInfo(StreamInfo),
    /// A padding block (with no meaningful data).
    Padding {
        /// The number of padding bytes.
        length: u32,
    },
    /// An application block with application-specific data.
    Application {
        /// The registered application ID.
        id: u32,
        /// The contents of the application block.
        data: Vec<u8>,
    },
    /// A seek table block.
    SeekTable(SeekTable),
    /// A Vorbis comment block, also known as FLAC tags.
    VorbisComment, // TODO
    /// A CUE sheet block.
    CueSheet, // TODO
    /// A picture block.
    Picture, // TODO
    /// A block with a reserved block type, not supported by this library.
    Reserved,
}

fn read_metadata_block_header<R: ReadBytes>(input: &mut R) -> Result<MetadataBlockHeader> {
    let byte = try!(input.read_u8());

    // The first bit specifies whether this is the last block, the next 7 bits
    // specify the type of the metadata block to follow.
    let is_last = (byte >> 7) == 1;
    let block_type = byte & 0b0111_1111;

    // The length field is 24 bits, or 3 bytes.
    let length = try!(input.read_be_u24());

    let header = MetadataBlockHeader {
        is_last: is_last,
        block_type: block_type,
        length: length,
    };
    Ok(header)
}

/// Read a single metadata block header and body from the input.
///
/// When reading a regular flac stream, there is no need to use this function
/// directly; constructing a `FlacReader` will read the header and its metadata
/// blocks.
///
/// When a flac stream is embedded in a container format, this function can be
/// used to decode a single metadata block. For instance, the Ogg format embeds
/// metadata blocks including their header verbatim in packets. This function
/// can be used to decode that raw data.
pub fn read_metadata_block_with_header<R: ReadBytes>(input: &mut R)
                                                     -> Result<MetadataBlock> {
  let header = try!(read_metadata_block_header(input));
  read_metadata_block(input, header.block_type, header.length)
}

/// Read a single metadata block of the given type and length from the input.
///
/// When reading a regular flac stream, there is no need to use this function
/// directly; constructing a `FlacReader` will read the header and its metadata
/// blocks.
///
/// When a flac stream is embedded in a container format, this function can be
/// used to decode a single metadata block. For instance, the MP4 format sports
/// a “FLAC Specific Box” which contains the block type and the raw data. This
/// function can be used to decode that raw data.
pub fn read_metadata_block<R: ReadBytes>(input: &mut R,
                                         block_type: u8,
                                         length: u32)
                                         -> Result<MetadataBlock> {
    match block_type {
        0 => {
            // The streaminfo block has a fixed size of 34 bytes.
            if length == 34 {
                let streaminfo = try!(read_streaminfo_block(input));
                Ok(MetadataBlock::StreamInfo(streaminfo))
            } else {
                fmt_err("invalid streaminfo metadata block length")
            }
        }
        1 => {
            try!(read_padding_block(input, length));
            Ok(MetadataBlock::Padding { length: length })
        }
        2 => {
            let (id, data) = try!(read_application_block(input, length));
            Ok(MetadataBlock::Application {
                id: id,
                data: data,
            })
        }
        3 => {
            // TODO: implement seektable reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        4 => {
            // TODO: implement Vorbis comment reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        5 => {
            // TODO: implement CUE sheet reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        6 => {
            // TODO: implement picture reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        127 => {
            // This code is invalid to avoid confusion with a frame sync code.
            fmt_err("invalid metadata block type")
        }
        _ => {
            // Any other block type is 'reserved' at the moment of writing. The
            // reference implementation reads it as an 'unknown' block. That is
            // one way of handling it, but maybe there should be some kind of
            // 'strict' mode (configurable at compile time?) so that this can
            // be an error if desired.
            try!(input.skip(length));
            Ok(MetadataBlock::Reserved)
        }
    }
}

fn read_streaminfo_block<R: ReadBytes>(input: &mut R) -> Result<StreamInfo> {
    let min_block_size = try!(input.read_be_u16());
    let max_block_size = try!(input.read_be_u16());

    // The frame size fields are 24 bits, or 3 bytes.
    let min_frame_size = try!(input.read_be_u24());
    let max_frame_size = try!(input.read_be_u24());

    // Next up are 20 bits that determine the sample rate.
    let sample_rate_msb = try!(input.read_be_u16());
    let sample_rate_lsb = try!(input.read_u8());

    // Stitch together the value from the first 16 bits,
    // and then the 4 most significant bits of the next byte.
    let sample_rate = (sample_rate_msb as u32) << 4 | (sample_rate_lsb as u32) >> 4;

    // Next three bits are the number of channels - 1. Mask them out and add 1.
    let n_channels_bps = sample_rate_lsb;
    let n_channels = ((n_channels_bps >> 1) & 0b0000_0111) + 1;

    // The final bit is the most significant of bits per sample - 1. Bits per
    // sample - 1 is 5 bits in total.
    let bps_msb = n_channels_bps & 1;
    let bps_lsb_n_samples = try!(input.read_u8());

    // Stitch together these values, add 1 because # - 1 is stored.
    let bits_per_sample = (bps_msb << 4 | (bps_lsb_n_samples >> 4)) + 1;

    // Number of samples in 36 bits, we have 4 already, 32 to go.
    let n_samples_msb = bps_lsb_n_samples & 0b0000_1111;
    let n_samples_lsb = try!(input.read_be_u32());
    let n_samples = (n_samples_msb as u64) << 32 | n_samples_lsb as u64;

    // Next are 128 bits (16 bytes) of MD5 signature.
    let mut md5sum = [0u8; 16];
    try!(input.read_into(&mut md5sum));

    // Lower bounds can never be larger than upper bounds. Note that 0 indicates
    // unknown for the frame size. Also, the block size must be at least 16.
    if min_block_size > max_block_size {
        return fmt_err("inconsistent bounds, min block size > max block size");
    }
    if min_block_size < 16 {
        return fmt_err("invalid block size, must be at least 16");
    }
    if min_frame_size > max_frame_size && max_frame_size != 0 {
        return fmt_err("inconsistent bounds, min frame size > max frame size");
    }

    // A sample rate of 0 is invalid, and the maximum sample rate is limited by
    // the structure of the frame headers to 655350 Hz.
    if sample_rate == 0 || sample_rate > 655350 {
        return fmt_err("invalid sample rate");
    }

    let stream_info = StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: if min_frame_size == 0 {
            None
        } else {
            Some(min_frame_size)
        },
        max_frame_size: if max_frame_size == 0 {
            None
        } else {
            Some(max_frame_size)
        },
        sample_rate: sample_rate,
        channels: n_channels as u32,
        bits_per_sample: bits_per_sample as u32,
        samples: if n_samples == 0 {
            None
        } else {
            Some(n_samples)
        },
        md5sum: md5sum,
    };
    Ok(stream_info)
}

fn read_padding_block<R: ReadBytes>(input: &mut R, length: u32) -> Result<()> {
    // The specification dictates that all bits of the padding block must be 0.
    // However, the reference implementation does not issue an error when this
    // is not the case, and frankly, when you are going to skip over these
    // bytes and do nothing with them whatsoever, why waste all those CPU
    // cycles checking that the padding is valid?
    Ok(try!(input.skip(length)))
}

fn read_application_block<R: ReadBytes>(input: &mut R, length: u32) -> Result<(u32, Vec<u8>)> {
    if length < 4 {
        return fmt_err("application block length must be at least 4 bytes.")
    }

    let id = try!(input.read_be_u32());

    // Four bytes of the block have been used for the ID, the rest is payload.
    // Create a vector of uninitialized memory, and read the block into it. The
    // uninitialized memory is never exposed: read_into will either fill the
    // buffer completely, or return an err, in which case the memory is not
    // exposed.
    let mut data = Vec::with_capacity(length as usize - 4);
    unsafe { data.set_len(length as usize - 4); }
    try!(input.read_into(&mut data));

    Ok((id, data))
}

/// Reads metadata blocks from a stream and exposes them as an iterator.
///
/// It is assumed that the next byte that the reader will read, is the first
/// byte of a metadata block header. This means that the iterator will yield at
/// least a single value. If the iterator ever yields an error, then no more
/// data will be read thereafter, and the next value will be `None`.
pub struct MetadataBlockReader<R: ReadBytes> {
    input: R,
    done: bool,
}

/// Either a `MetadataBlock` or an `Error`.
pub type MetadataBlockResult = Result<MetadataBlock>;

impl<R: ReadBytes> MetadataBlockReader<R> {
    /// Creates a metadata block reader that will yield at least one element.
    pub fn new(input: R) -> MetadataBlockReader<R> {
        MetadataBlockReader {
            input: input,
            done: false,
        }
    }

    fn read_next(&mut self) -> MetadataBlockResult {
        let header = try!(read_metadata_block_header(&mut self.input));
        let block = try!(read_metadata_block(&mut self.input, header.block_type, header.length));
        self.done = header.is_last;
        Ok(block)
    }
}

impl<R: ReadBytes> Iterator for MetadataBlockReader<R> {
    type Item = MetadataBlockResult;

    fn next(&mut self) -> Option<MetadataBlockResult> {
        if self.done {
            None
        } else {
            let block = self.read_next();

            // After a failure, no more attempts to read will be made,
            // because we don't know where we are in the stream.
            if !block.is_ok() {
                self.done = true;
            }

            Some(block)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        // When done, there will be no more blocks,
        // when not done, there will be at least one more.
        if self.done { (0, Some(0)) } else { (1, None) }
    }
}
<commit_msg>Clarify docs for StreamInfo<commit_after>// Claxon -- A FLAC decoding library in Rust
// Copyright 2014 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! The `metadata` module deals with metadata at the beginning of a FLAC stream.

use error::{Result, fmt_err};
use input::ReadBytes;

#[derive(Clone, Copy)]
struct MetadataBlockHeader {
    is_last: bool,
    block_type: u8,
    length: u32,
}

/// The streaminfo metadata block, with important information about the stream.
#[derive(Clone, Copy, Debug)]
pub struct StreamInfo {
    // TODO: "size" would better be called "duration" for clarity.

    /// The minimum block size (in inter-channel samples) used in the stream.
    ///
    /// To get the minimum block duration in seconds, divide this by the sample
    /// rate.
    pub min_block_size: u16,
    /// The maximum block size (in inter-channel samples) used in the stream.
    ///
    /// To get the maximum block duration in seconds, divide this by the sample
    /// rate.
    pub max_block_size: u16,
    /// The minimum frame size (in bytes) used in the stream.
    pub min_frame_size: Option<u32>,
    /// The maximum frame size (in bytes) used in the stream.
    pub max_frame_size: Option<u32>,
    /// The sample rate in Hz.
    pub sample_rate: u32,
    /// The number of channels.
    pub channels: u32,
    /// The number of bits per sample.
    pub bits_per_sample: u32,
    /// The total number of inter-channel samples in the stream.
    pub samples: Option<u64>,
    /// MD5 signature of the unencoded audio data.
    pub md5sum: [u8; 16],
}

/// A seek point in the seek table.
#[derive(Clone, Copy)]
pub struct SeekPoint {
    /// Sample number of the first sample in the target frame, or 2^64 - 1 for a placeholder.
    pub sample: u64,
    /// Offset in bytes from the first byte of the first frame header to the first byte of the
    /// target frame's header.
    pub offset: u64,
    /// Number of samples in the target frame.
    pub samples: u16,
}

/// A seek table to aid seeking in the stream.
pub struct SeekTable {
    /// The seek points, sorted in ascending order by sample number.
    #[allow(dead_code)] // TODO: Implement seeking.
    seekpoints: Vec<SeekPoint>,
}

/// A metadata about the flac stream.
pub enum MetadataBlock {
    /// A stream info block.
    StreamInfo(StreamInfo),
    /// A padding block (with no meaningful data).
    Padding {
        /// The number of padding bytes.
        length: u32,
    },
    /// An application block with application-specific data.
    Application {
        /// The registered application ID.
        id: u32,
        /// The contents of the application block.
        data: Vec<u8>,
    },
    /// A seek table block.
    SeekTable(SeekTable),
    /// A Vorbis comment block, also known as FLAC tags.
    VorbisComment, // TODO
    /// A CUE sheet block.
    CueSheet, // TODO
    /// A picture block.
    Picture, // TODO
    /// A block with a reserved block type, not supported by this library.
    Reserved,
}

fn read_metadata_block_header<R: ReadBytes>(input: &mut R) -> Result<MetadataBlockHeader> {
    let byte = try!(input.read_u8());

    // The first bit specifies whether this is the last block, the next 7 bits
    // specify the type of the metadata block to follow.
    let is_last = (byte >> 7) == 1;
    let block_type = byte & 0b0111_1111;

    // The length field is 24 bits, or 3 bytes.
    let length = try!(input.read_be_u24());

    let header = MetadataBlockHeader {
        is_last: is_last,
        block_type: block_type,
        length: length,
    };
    Ok(header)
}

/// Read a single metadata block header and body from the input.
///
/// When reading a regular flac stream, there is no need to use this function
/// directly; constructing a `FlacReader` will read the header and its metadata
/// blocks.
///
/// When a flac stream is embedded in a container format, this function can be
/// used to decode a single metadata block. For instance, the Ogg format embeds
/// metadata blocks including their header verbatim in packets. This function
/// can be used to decode that raw data.
pub fn read_metadata_block_with_header<R: ReadBytes>(input: &mut R)
                                                     -> Result<MetadataBlock> {
  let header = try!(read_metadata_block_header(input));
  read_metadata_block(input, header.block_type, header.length)
}

/// Read a single metadata block of the given type and length from the input.
///
/// When reading a regular flac stream, there is no need to use this function
/// directly; constructing a `FlacReader` will read the header and its metadata
/// blocks.
///
/// When a flac stream is embedded in a container format, this function can be
/// used to decode a single metadata block. For instance, the MP4 format sports
/// a “FLAC Specific Box” which contains the block type and the raw data. This
/// function can be used to decode that raw data.
pub fn read_metadata_block<R: ReadBytes>(input: &mut R,
                                         block_type: u8,
                                         length: u32)
                                         -> Result<MetadataBlock> {
    match block_type {
        0 => {
            // The streaminfo block has a fixed size of 34 bytes.
            if length == 34 {
                let streaminfo = try!(read_streaminfo_block(input));
                Ok(MetadataBlock::StreamInfo(streaminfo))
            } else {
                fmt_err("invalid streaminfo metadata block length")
            }
        }
        1 => {
            try!(read_padding_block(input, length));
            Ok(MetadataBlock::Padding { length: length })
        }
        2 => {
            let (id, data) = try!(read_application_block(input, length));
            Ok(MetadataBlock::Application {
                id: id,
                data: data,
            })
        }
        3 => {
            // TODO: implement seektable reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        4 => {
            // TODO: implement Vorbis comment reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        5 => {
            // TODO: implement CUE sheet reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        6 => {
            // TODO: implement picture reading. For now, pretend it is padding.
            try!(input.skip(length));
            Ok(MetadataBlock::Padding { length: length })
        }
        127 => {
            // This code is invalid to avoid confusion with a frame sync code.
            fmt_err("invalid metadata block type")
        }
        _ => {
            // Any other block type is 'reserved' at the moment of writing. The
            // reference implementation reads it as an 'unknown' block. That is
            // one way of handling it, but maybe there should be some kind of
            // 'strict' mode (configurable at compile time?) so that this can
            // be an error if desired.
            try!(input.skip(length));
            Ok(MetadataBlock::Reserved)
        }
    }
}

fn read_streaminfo_block<R: ReadBytes>(input: &mut R) -> Result<StreamInfo> {
    let min_block_size = try!(input.read_be_u16());
    let max_block_size = try!(input.read_be_u16());

    // The frame size fields are 24 bits, or 3 bytes.
    let min_frame_size = try!(input.read_be_u24());
    let max_frame_size = try!(input.read_be_u24());

    // Next up are 20 bits that determine the sample rate.
    let sample_rate_msb = try!(input.read_be_u16());
    let sample_rate_lsb = try!(input.read_u8());

    // Stitch together the value from the first 16 bits,
    // and then the 4 most significant bits of the next byte.
    let sample_rate = (sample_rate_msb as u32) << 4 | (sample_rate_lsb as u32) >> 4;

    // Next three bits are the number of channels - 1. Mask them out and add 1.
    let n_channels_bps = sample_rate_lsb;
    let n_channels = ((n_channels_bps >> 1) & 0b0000_0111) + 1;

    // The final bit is the most significant of bits per sample - 1. Bits per
    // sample - 1 is 5 bits in total.
    let bps_msb = n_channels_bps & 1;
    let bps_lsb_n_samples = try!(input.read_u8());

    // Stitch together these values, add 1 because # - 1 is stored.
    let bits_per_sample = (bps_msb << 4 | (bps_lsb_n_samples >> 4)) + 1;

    // Number of samples in 36 bits, we have 4 already, 32 to go.
    let n_samples_msb = bps_lsb_n_samples & 0b0000_1111;
    let n_samples_lsb = try!(input.read_be_u32());
    let n_samples = (n_samples_msb as u64) << 32 | n_samples_lsb as u64;

    // Next are 128 bits (16 bytes) of MD5 signature.
    let mut md5sum = [0u8; 16];
    try!(input.read_into(&mut md5sum));

    // Lower bounds can never be larger than upper bounds. Note that 0 indicates
    // unknown for the frame size. Also, the block size must be at least 16.
    if min_block_size > max_block_size {
        return fmt_err("inconsistent bounds, min block size > max block size");
    }
    if min_block_size < 16 {
        return fmt_err("invalid block size, must be at least 16");
    }
    if min_frame_size > max_frame_size && max_frame_size != 0 {
        return fmt_err("inconsistent bounds, min frame size > max frame size");
    }

    // A sample rate of 0 is invalid, and the maximum sample rate is limited by
    // the structure of the frame headers to 655350 Hz.
    if sample_rate == 0 || sample_rate > 655350 {
        return fmt_err("invalid sample rate");
    }

    let stream_info = StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: if min_frame_size == 0 {
            None
        } else {
            Some(min_frame_size)
        },
        max_frame_size: if max_frame_size == 0 {
            None
        } else {
            Some(max_frame_size)
        },
        sample_rate: sample_rate,
        channels: n_channels as u32,
        bits_per_sample: bits_per_sample as u32,
        samples: if n_samples == 0 {
            None
        } else {
            Some(n_samples)
        },
        md5sum: md5sum,
    };
    Ok(stream_info)
}

fn read_padding_block<R: ReadBytes>(input: &mut R, length: u32) -> Result<()> {
    // The specification dictates that all bits of the padding block must be 0.
    // However, the reference implementation does not issue an error when this
    // is not the case, and frankly, when you are going to skip over these
    // bytes and do nothing with them whatsoever, why waste all those CPU
    // cycles checking that the padding is valid?
    Ok(try!(input.skip(length)))
}

fn read_application_block<R: ReadBytes>(input: &mut R, length: u32) -> Result<(u32, Vec<u8>)> {
    if length < 4 {
        return fmt_err("application block length must be at least 4 bytes.")
    }

    let id = try!(input.read_be_u32());

    // Four bytes of the block have been used for the ID, the rest is payload.
    // Create a vector of uninitialized memory, and read the block into it. The
    // uninitialized memory is never exposed: read_into will either fill the
    // buffer completely, or return an err, in which case the memory is not
    // exposed.
    let mut data = Vec::with_capacity(length as usize - 4);
    unsafe { data.set_len(length as usize - 4); }
    try!(input.read_into(&mut data));

    Ok((id, data))
}

/// Reads metadata blocks from a stream and exposes them as an iterator.
///
/// It is assumed that the next byte that the reader will read, is the first
/// byte of a metadata block header. This means that the iterator will yield at
/// least a single value. If the iterator ever yields an error, then no more
/// data will be read thereafter, and the next value will be `None`.
pub struct MetadataBlockReader<R: ReadBytes> {
    input: R,
    done: bool,
}

/// Either a `MetadataBlock` or an `Error`.
pub type MetadataBlockResult = Result<MetadataBlock>;

impl<R: ReadBytes> MetadataBlockReader<R> {
    /// Creates a metadata block reader that will yield at least one element.
    pub fn new(input: R) -> MetadataBlockReader<R> {
        MetadataBlockReader {
            input: input,
            done: false,
        }
    }

    fn read_next(&mut self) -> MetadataBlockResult {
        let header = try!(read_metadata_block_header(&mut self.input));
        let block = try!(read_metadata_block(&mut self.input, header.block_type, header.length));
        self.done = header.is_last;
        Ok(block)
    }
}

impl<R: ReadBytes> Iterator for MetadataBlockReader<R> {
    type Item = MetadataBlockResult;

    fn next(&mut self) -> Option<MetadataBlockResult> {
        if self.done {
            None
        } else {
            let block = self.read_next();

            // After a failure, no more attempts to read will be made,
            // because we don't know where we are in the stream.
            if !block.is_ok() {
                self.done = true;
            }

            Some(block)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        // When done, there will be no more blocks,
        // when not done, there will be at least one more.
        if self.done { (0, Some(0)) } else { (1, None) }
    }
}
<|endoftext|>