<commit_before>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH, ZOPFLI_WINDOW_SIZE};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct SmallerHashThing {
    prev: u16, /* Index to index of prev. occurrence of same hash. */
    hashval: i32, /* Index to hash value at this index. */
}

pub struct HashThing {
    head: [i32; 65536],  /* Hash value to index of its most recent occurrence. */
    prev_and_hashval: Vec<SmallerHashThing>,
    val: i32,  /* Current hash value. */
}

impl HashThing {
    fn new() -> HashThing {
        HashThing {
            head: [-1; 65536],
            prev_and_hashval: (0..ZOPFLI_WINDOW_SIZE as u16).map(|p| {
                SmallerHashThing {
                    prev: p,
                    hashval: -1,
                }
            }).collect(),
            val: 0,
        }
    }

    fn reset(&mut self) {
        self.val = 0;
        self.head = [-1; 65536];
        self.prev_and_hashval = (0..ZOPFLI_WINDOW_SIZE as u16).map(|p| {
            SmallerHashThing {
                prev: p,
                hashval: -1,
            }
        }).collect();
    }

    fn update(&mut self, hpos: usize) {
        let hashval = self.val;
        let index = self.val as usize;
        let head_index = self.head[index];
        let prev = if head_index != -1 && self.prev_and_hashval[head_index as usize].hashval == self.val {
            head_index as u16
        } else {
            hpos as u16
        };

        self.prev_and_hashval[hpos] = SmallerHashThing {
            prev: prev,
            hashval: hashval,
        };
        self.head[index] = hpos as i32;
    }
}

pub struct ZopfliHash {
    hash1: HashThing,
    hash2: HashThing,
    pub same: [u16; ZOPFLI_WINDOW_SIZE],  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new() -> ZopfliHash {
        ZopfliHash {
            hash1: HashThing::new(),
            hash2: HashThing::new(),
            same: [0; ZOPFLI_WINDOW_SIZE],
        }
    }

    pub fn reset(&mut self) {
        self.hash1.reset();
        self.hash2.reset();
        self.same = [0; ZOPFLI_WINDOW_SIZE];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.hash1.val = ((self.hash1.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;

        self.hash1.update(hpos);

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same - 1;
        }

        self.same[hpos] = amount;

        self.hash2.val = ((amount.wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.hash1.val as u16) as i32;

        self.hash2.update(hpos);
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.head[index],
            Which::Hash2 => self.hash2.head[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].prev,
            Which::Hash2 => self.hash2.prev_and_hashval[index].prev,
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].hashval,
            Which::Hash2 => self.hash2.prev_and_hashval[index].hashval,
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.val,
            Which::Hash2 => self.hash2.val,
        }
    }
}
<commit_msg>Use a Vec and store lots of data on the heap instead of the stack :(<commit_after>use util::{ZOPFLI_WINDOW_MASK, ZOPFLI_MIN_MATCH, ZOPFLI_WINDOW_SIZE};

const HASH_SHIFT: i32 = 5;
const HASH_MASK: i32 = 32767;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Which {
    Hash1,
    Hash2,
}

pub struct SmallerHashThing {
    prev: u16, /* Index to index of prev. occurrence of same hash. */
    hashval: i32, /* Index to hash value at this index. */
}

pub struct HashThing {
    head: Vec<i32>,  /* Hash value to index of its most recent occurrence. */
    prev_and_hashval: Vec<SmallerHashThing>,
    val: i32,  /* Current hash value. */
}

impl HashThing {
    fn new() -> HashThing {
        HashThing {
            head: vec![-1; 65536],
            prev_and_hashval: (0..ZOPFLI_WINDOW_SIZE as u16).map(|p| {
                SmallerHashThing {
                    prev: p,
                    hashval: -1,
                }
            }).collect(),
            val: 0,
        }
    }

    fn reset(&mut self) {
        self.val = 0;
        self.head.clear();
        self.head.resize(65536, -1);
        self.prev_and_hashval = (0..ZOPFLI_WINDOW_SIZE as u16).map(|p| {
            SmallerHashThing {
                prev: p,
                hashval: -1,
            }
        }).collect();
    }

    fn update(&mut self, hpos: usize) {
        let hashval = self.val;
        let index = self.val as usize;
        let head_index = self.head[index];
        let prev = if head_index != -1 && self.prev_and_hashval[head_index as usize].hashval == self.val {
            head_index as u16
        } else {
            hpos as u16
        };

        self.prev_and_hashval[hpos] = SmallerHashThing {
            prev: prev,
            hashval: hashval,
        };
        self.head[index] = hpos as i32;
    }
}

pub struct ZopfliHash {
    hash1: HashThing,
    hash2: HashThing,
    pub same: [u16; ZOPFLI_WINDOW_SIZE],  /* Amount of repetitions of same byte after this .*/
}

impl ZopfliHash {
    pub fn new() -> ZopfliHash {
        ZopfliHash {
            hash1: HashThing::new(),
            hash2: HashThing::new(),
            same: [0; ZOPFLI_WINDOW_SIZE],
        }
    }

    pub fn reset(&mut self) {
        self.hash1.reset();
        self.hash2.reset();
        self.same = [0; ZOPFLI_WINDOW_SIZE];
    }

    pub fn warmup(&mut self, arr: &[u8], pos: usize, end: usize) {
        let c = arr[pos];
        self.update_val(c);

        if pos + 1 < end {
            let c = arr[pos + 1];
            self.update_val(c);
        }
    }

    /// Update the sliding hash value with the given byte. All calls to this function
    /// must be made on consecutive input characters. Since the hash value exists out
    /// of multiple input bytes, a few warmups with this function are needed initially.
    fn update_val(&mut self, c: u8) {
        self.hash1.val = ((self.hash1.val << HASH_SHIFT) ^ c as i32) & HASH_MASK;
    }

    pub fn update(&mut self, array: &[u8], pos: usize) {
        let hash_value = array.get(pos + ZOPFLI_MIN_MATCH - 1).cloned().unwrap_or(0);
        self.update_val(hash_value);

        let hpos = pos & ZOPFLI_WINDOW_MASK;

        self.hash1.update(hpos);

        // Update "same".
        let mut amount = 0;
        let same_index = pos.wrapping_sub(1) & ZOPFLI_WINDOW_MASK;
        let same = self.same[same_index];
        if same > 1 {
            amount = same - 1;
        }

        self.same[hpos] = amount;

        self.hash2.val = ((amount.wrapping_sub(ZOPFLI_MIN_MATCH as u16) & 255) ^ self.hash1.val as u16) as i32;

        self.hash2.update(hpos);
    }

    pub fn head_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.head[index],
            Which::Hash2 => self.hash2.head[index],
        }
    }

    pub fn prev_at(&self, index: usize, which: Which) -> u16 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].prev,
            Which::Hash2 => self.hash2.prev_and_hashval[index].prev,
        }
    }

    pub fn hash_val_at(&self, index: usize, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.prev_and_hashval[index].hashval,
            Which::Hash2 => self.hash2.prev_and_hashval[index].hashval,
        }
    }

    pub fn val(&self, which: Which) -> i32 {
        match which {
            Which::Hash1 => self.hash1.val,
            Which::Hash2 => self.hash2.val,
        }
    }
}
<|endoftext|>


<commit_before>//! Tests of the interactive console installer

pub mod mock;

use std::env::consts::EXE_SUFFIX;
use std::io::Write;
use std::process::Stdio;

use rustup::for_host;
use rustup::test::this_host_triple;
use rustup::test::with_saved_path;
use rustup::utils::raw;

use crate::mock::clitools::{
    self, expect_ok, expect_stderr_ok, expect_stdout_ok, run, set_current_dist_date, Config,
    SanitizedOutput, Scenario,
};

fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput {
    run_input_with_env(config, args, input, &[])
}

fn run_input_with_env(
    config: &Config,
    args: &[&str],
    input: &str,
    env: &[(&str, &str)],
) -> SanitizedOutput {
    let mut cmd = clitools::cmd(config, args[0], &args[1..]);
    clitools::env(config, &mut cmd);

    for (key, value) in env.iter() {
        cmd.env(key, value);
    }

    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());
    let mut child = cmd.spawn().unwrap();

    child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(input.as_bytes())
        .unwrap();
    let out = child.wait_with_output().unwrap();

    SanitizedOutput {
        ok: out.status.success(),
        stdout: String::from_utf8(out.stdout).unwrap(),
        stderr: String::from_utf8(out.stderr).unwrap(),
    }
}

#[test]
fn update() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        with_saved_path(&|| {
            run_input(config, &["rustup-init"], "\n\n");
            let out = run_input(config, &["rustup-init"], "\n\n");
            assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr);
        })
    });
}

// Testing that the right number of blank lines are printed after the
// 'pre-install' message and before the 'post-install' message - overall smoke
// test for the install case.
#[test]
fn smoke_case_install_no_modify_path() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        assert!(out.ok);
        // During an interactive session, after "Press the Enter
        // key..."  the UI emits a blank line, then there is a blank
        // line that comes from the user pressing enter, then log
        // output on stderr, then an explicit blank line on stdout
        // before printing $toolchain installed
        assert!(
            out.stdout.contains(for_host!(
                r"
This path needs to be in your PATH environment variable,
but will not be added automatically.

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:

   default host triple: {0}
     default toolchain: stable (default)
               profile: default
  modify PATH variable: no

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>

  stable-{0} installed - 1.1.0 (hash-stable-1.1.0)

Rust is installed now. Great!

"
            )),
            format!("pattern not found in \"\"\"{}\"\"\"", out.stdout)
        );
        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn smoke_case_install_with_path_install() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        with_saved_path(&|| {
            let out = run_input(config, &["rustup-init"], "\n\n");
            assert!(out.ok);
            assert!(!out
                .stdout
                .contains("This path needs to be in your PATH environment variable"));
        })
    });
}

#[test]
fn blank_lines_around_stderr_log_output_update() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        expect_ok(config, &["rustup-init", "-y", "--no-modify-path"]);
        let out = run_input(
            config,
            &["rustup-init", "--no-update-default-toolchain"],
            "\n\n",
        );
        println!("-- stdout --\n {}", out.stdout);
        println!("-- stderr --\n {}", out.stderr);

        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn user_says_nope() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn with_no_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &[
                "rustup-init",
                "--no-modify-path",
                "--default-toolchain=none",
            ],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "no active toolchain");
    });
}

#[test]
fn with_non_default_toolchain_still_prompts() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &[
                "rustup-init",
                "--no-modify-path",
                "--default-toolchain=nightly",
            ],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn with_non_release_channel_non_default_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &[
                "rustup-init",
                "--no-modify-path",
                "--default-toolchain=nightly-2015-01-02",
            ],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
        expect_stdout_ok(config, &["rustup", "show"], "2015-01-02");
    });
}

#[test]
fn set_nightly_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\nnightly\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn set_no_modify_path() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\n\n\nno\n\n\n",
        );
        assert!(out.ok);

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn set_nightly_toolchain_and_unset() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "beta");
    });
}

#[test]
fn user_says_nope_after_advanced_install() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\n\n\n\nn\n\n\n",
        );
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn install_with_components() {
    fn go(comp_args: &[&str]) {
        let mut args = vec!["rustup-init", "-y", "--no-modify-path"];
        args.extend_from_slice(comp_args);

        clitools::setup(Scenario::SimpleV2, &|config| {
            expect_ok(config, &args);
            expect_stdout_ok(
                config,
                &["rustup", "component", "list"],
                "rust-src (installed)",
            );
            expect_stdout_ok(
                config,
                &["rustup", "component", "list"],
                &format!("rust-analysis-{} (installed)", this_host_triple()),
            );
        })
    }

    go(&["-c", "rust-src", "-c", "rust-analysis"]);
    go(&["-c", "rust-src,rust-analysis"]);
}

#[test]
fn install_forces_and_skips_rls() {
    clitools::setup(Scenario::UnavailableRls, &|config| {
        set_current_dist_date(config, "2015-01-01");

        let out = run_input(
            config,
            &[
                "rustup-init",
                "--profile",
                "complete",
                "--default-toolchain",
                "nightly",
                "--no-modify-path",
            ],
            "\n\n",
        );
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: Force-skipping unavailable component"));
    });
}

#[test]
fn test_warn_if_complete_profile_is_used() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        expect_stderr_ok(
            config,
            &[
                "rustup-init",
                "-y",
                "--profile",
                "complete",
                "--no-modify-path",
            ],
            "warning: downloading with complete profile",
        );
    });
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        config.create_rustup_sh_metadata();
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        config.create_rustup_sh_metadata();
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "no\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        config.create_rustup_sh_metadata();
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "yes\n\n\n");
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
        assert!(!out.stdout.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ));
        assert!(out.ok);
    })
}

#[test]
fn installing_when_already_installed_updates_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        expect_ok(config, &["rustup-init", "-y", "--no-modify-path"]);
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        println!("stdout:\n{}\n...\n", out.stdout);
        assert!(out
            .stdout
            .contains(for_host!("stable-{} unchanged - 1.1.0 (hash-stable-1.1.0)")));
    })
}

#[test]
fn install_stops_if_rustc_exists() {
    let temp_dir = tempfile::Builder::new()
        .prefix("fakebin")
        .tempdir()
        .unwrap();
    // Create fake executable
    let fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
    raw::append_file(&fake_exe, "").unwrap();
    let temp_dir_path = temp_dir.path().to_str().unwrap();

    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run(
            config,
            "rustup-init",
            &["--no-modify-path"],
            &[
                ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
                ("PATH", &temp_dir_path),
            ],
        );
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("it looks like you have an existing installation of Rust at:"));
        assert!(out
            .stderr
            .contains("If you are sure that you want both rustup and your already installed Rust"));
    });
}

#[test]
fn install_stops_if_cargo_exists() {
    let temp_dir = tempfile::Builder::new()
        .prefix("fakebin")
        .tempdir()
        .unwrap();
    // Create fake executable
    let fake_exe = temp_dir.path().join(&format!("{}{}", "cargo", EXE_SUFFIX));
    raw::append_file(&fake_exe, "").unwrap();
    let temp_dir_path = temp_dir.path().to_str().unwrap();

    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run(
            config,
            "rustup-init",
            &["--no-modify-path"],
            &[
                ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
                ("PATH", &temp_dir_path),
            ],
        );
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("it looks like you have an existing installation of Rust at:"));
        assert!(out
            .stderr
            .contains("If you are sure that you want both rustup and your already installed Rust"));
    });
}

#[test]
fn with_no_prompt_install_succeeds_if_rustc_exists() {
    let temp_dir = tempfile::Builder::new()
        .prefix("fakebin")
        .tempdir()
        .unwrap();
    // Create fake executable
    let fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
    raw::append_file(&fake_exe, "").unwrap();
    let temp_dir_path = temp_dir.path().to_str().unwrap();

    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run(
            config,
            "rustup-init",
            &["-y", "--no-modify-path"],
            &[
                ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
                ("PATH", &temp_dir_path),
            ],
        );
        assert!(out.ok);
    });
}
<commit_msg>tests: Ensure env file is written out<commit_after>//! Tests of the interactive console installer

pub mod mock;

use std::env::consts::EXE_SUFFIX;
use std::io::Write;
use std::process::Stdio;

use rustup::for_host;
use rustup::test::this_host_triple;
use rustup::test::with_saved_path;
use rustup::utils::raw;

use crate::mock::clitools::{
    self, expect_ok, expect_stderr_ok, expect_stdout_ok, run, set_current_dist_date, Config,
    SanitizedOutput, Scenario,
};

fn run_input(config: &Config, args: &[&str], input: &str) -> SanitizedOutput {
    run_input_with_env(config, args, input, &[])
}

fn run_input_with_env(
    config: &Config,
    args: &[&str],
    input: &str,
    env: &[(&str, &str)],
) -> SanitizedOutput {
    let mut cmd = clitools::cmd(config, args[0], &args[1..]);
    clitools::env(config, &mut cmd);

    for (key, value) in env.iter() {
        cmd.env(key, value);
    }

    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());
    let mut child = cmd.spawn().unwrap();

    child
        .stdin
        .as_mut()
        .unwrap()
        .write_all(input.as_bytes())
        .unwrap();
    let out = child.wait_with_output().unwrap();

    SanitizedOutput {
        ok: out.status.success(),
        stdout: String::from_utf8(out.stdout).unwrap(),
        stderr: String::from_utf8(out.stderr).unwrap(),
    }
}

#[test]
fn update() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        with_saved_path(&|| {
            run_input(config, &["rustup-init"], "\n\n");
            let out = run_input(config, &["rustup-init"], "\n\n");
            assert!(out.ok, "stdout:\n{}\nstderr:\n{}", out.stdout, out.stderr);
        })
    });
}

// Testing that the right number of blank lines are printed after the
// 'pre-install' message and before the 'post-install' message - overall smoke
// test for the install case.
#[test]
fn smoke_case_install_no_modify_path() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        assert!(out.ok);
        // During an interactive session, after "Press the Enter
        // key..."  the UI emits a blank line, then there is a blank
        // line that comes from the user pressing enter, then log
        // output on stderr, then an explicit blank line on stdout
        // before printing $toolchain installed
        assert!(
            out.stdout.contains(for_host!(
                r"
This path needs to be in your PATH environment variable,
but will not be added automatically.

You can uninstall at any time with rustup self uninstall and
these changes will be reverted.

Current installation options:

   default host triple: {0}
     default toolchain: stable (default)
               profile: default
  modify PATH variable: no

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>

  stable-{0} installed - 1.1.0 (hash-stable-1.1.0)

Rust is installed now. Great!

"
            )),
            format!("pattern not found in \"\"\"{}\"\"\"", out.stdout)
        );
        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
            assert!(config.cargodir.join("env").exists());
        }
    });
}

#[test]
fn smoke_case_install_with_path_install() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        with_saved_path(&|| {
            let out = run_input(config, &["rustup-init"], "\n\n");
            assert!(out.ok);
            assert!(!out
                .stdout
                .contains("This path needs to be in your PATH environment variable"));
        })
    });
}

#[test]
fn blank_lines_around_stderr_log_output_update() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        expect_ok(config, &["rustup-init", "-y", "--no-modify-path"]);
        let out = run_input(
            config,
            &["rustup-init", "--no-update-default-toolchain"],
            "\n\n",
        );
        println!("-- stdout --\n {}", out.stdout);
        println!("-- stderr --\n {}", out.stderr);

        assert!(out.stdout.contains(
            r"
3) Cancel installation
>

Rust is installed now. Great!
"
        ));
    });
}

#[test]
fn user_says_nope() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "n\n\n");
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn with_no_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &[
                "rustup-init",
                "--no-modify-path",
                "--default-toolchain=none",
            ],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "no active toolchain");
    });
}

#[test]
fn with_non_default_toolchain_still_prompts() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &[
                "rustup-init",
                "--no-modify-path",
                "--default-toolchain=nightly",
            ],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn with_non_release_channel_non_default_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &[
                "rustup-init",
                "--no-modify-path",
                "--default-toolchain=nightly-2015-01-02",
            ],
            "\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
        expect_stdout_ok(config, &["rustup", "show"], "2015-01-02");
    });
}

#[test]
fn set_nightly_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\nnightly\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "nightly");
    });
}

#[test]
fn set_no_modify_path() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\n\n\nno\n\n\n",
        );
        assert!(out.ok);

        if cfg!(unix) {
            assert!(!config.homedir.join(".profile").exists());
        }
    });
}

#[test]
fn set_nightly_toolchain_and_unset() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\nnightly\n\n\n2\n\nbeta\n\n\n\n\n",
        );
        assert!(out.ok);

        expect_stdout_ok(config, &["rustup", "show"], "beta");
    });
}

#[test]
fn user_says_nope_after_advanced_install() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run_input(
            config,
            &["rustup-init", "--no-modify-path"],
            "2\n\n\n\n\nn\n\n\n",
        );
        assert!(out.ok);
        assert!(!config.cargodir.join("bin").exists());
    });
}

#[test]
fn install_with_components() {
    fn go(comp_args: &[&str]) {
        let mut args = vec!["rustup-init", "-y", "--no-modify-path"];
        args.extend_from_slice(comp_args);

        clitools::setup(Scenario::SimpleV2, &|config| {
            expect_ok(config, &args);
            expect_stdout_ok(
                config,
                &["rustup", "component", "list"],
                "rust-src (installed)",
            );
            expect_stdout_ok(
                config,
                &["rustup", "component", "list"],
                &format!("rust-analysis-{} (installed)", this_host_triple()),
            );
        })
    }

    go(&["-c", "rust-src", "-c", "rust-analysis"]);
    go(&["-c", "rust-src,rust-analysis"]);
}

#[test]
fn install_forces_and_skips_rls() {
    clitools::setup(Scenario::UnavailableRls, &|config| {
        set_current_dist_date(config, "2015-01-01");

        let out = run_input(
            config,
            &[
                "rustup-init",
                "--profile",
                "complete",
                "--default-toolchain",
                "nightly",
                "--no-modify-path",
            ],
            "\n\n",
        );
        assert!(out.ok);
        assert!(out
            .stderr
            .contains("warning: Force-skipping unavailable component"));
    });
}

#[test]
fn test_warn_if_complete_profile_is_used() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        expect_stderr_ok(
            config,
            &[
                "rustup-init",
                "-y",
                "--profile",
                "complete",
                "--no-modify-path",
            ],
            "warning: downloading with complete profile",
        );
    });
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_nothing() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        config.create_rustup_sh_metadata();
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_fail_if_rustup_sh_already_installed_reply_no() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        config.create_rustup_sh_metadata();
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "no\n");
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
    })
}

#[test]
fn test_prompt_succeed_if_rustup_sh_already_installed_reply_yes() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        config.create_rustup_sh_metadata();
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "yes\n\n\n");
        assert!(out
            .stderr
            .contains("warning: it looks like you have existing rustup.sh metadata"));
        assert!(out
            .stderr
            .contains("error: cannot install while rustup.sh is installed"));
        assert!(out.stdout.contains("Continue? (y/N)"));
        assert!(!out.stdout.contains(
            "warning: continuing (because the -y flag is set and the error is ignorable)"
        ));
        assert!(out.ok);
    })
}

#[test]
fn installing_when_already_installed_updates_toolchain() {
    clitools::setup(Scenario::SimpleV2, &|config| {
        expect_ok(config, &["rustup-init", "-y", "--no-modify-path"]);
        let out = run_input(config, &["rustup-init", "--no-modify-path"], "\n\n");
        println!("stdout:\n{}\n...\n", out.stdout);
        assert!(out
            .stdout
            .contains(for_host!("stable-{} unchanged - 1.1.0 (hash-stable-1.1.0)")));
    })
}

#[test]
fn install_stops_if_rustc_exists() {
    let temp_dir = tempfile::Builder::new()
        .prefix("fakebin")
        .tempdir()
        .unwrap();
    // Create fake executable
    let fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
    raw::append_file(&fake_exe, "").unwrap();
    let temp_dir_path = temp_dir.path().to_str().unwrap();

    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run(
            config,
            "rustup-init",
            &["--no-modify-path"],
            &[
                ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
                ("PATH", &temp_dir_path),
            ],
        );
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("it looks like you have an existing installation of Rust at:"));
        assert!(out
            .stderr
            .contains("If you are sure that you want both rustup and your already installed Rust"));
    });
}

#[test]
fn install_stops_if_cargo_exists() {
    let temp_dir = tempfile::Builder::new()
        .prefix("fakebin")
        .tempdir()
        .unwrap();
    // Create fake executable
    let fake_exe = temp_dir.path().join(&format!("{}{}", "cargo", EXE_SUFFIX));
    raw::append_file(&fake_exe, "").unwrap();
    let temp_dir_path = temp_dir.path().to_str().unwrap();

    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run(
            config,
            "rustup-init",
            &["--no-modify-path"],
            &[
                ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
                ("PATH", &temp_dir_path),
            ],
        );
        assert!(!out.ok);
        assert!(out
            .stderr
            .contains("it looks like you have an existing installation of Rust at:"));
        assert!(out
            .stderr
            .contains("If you are sure that you want both rustup and your already installed Rust"));
    });
}

#[test]
fn with_no_prompt_install_succeeds_if_rustc_exists() {
    let temp_dir = tempfile::Builder::new()
        .prefix("fakebin")
        .tempdir()
        .unwrap();
    // Create fake executable
    let fake_exe = temp_dir.path().join(&format!("{}{}", "rustc", EXE_SUFFIX));
    raw::append_file(&fake_exe, "").unwrap();
    let temp_dir_path = temp_dir.path().to_str().unwrap();

    clitools::setup(Scenario::SimpleV2, &|config| {
        let out = run(
            config,
            "rustup-init",
            &["-y", "--no-modify-path"],
            &[
                ("RUSTUP_INIT_SKIP_PATH_CHECK", "no"),
                ("PATH", &temp_dir_path),
            ],
        );
        assert!(out.ok);
    });
}
<|endoftext|>


<commit_before>
use std::collections::BTreeMap;
use std::error;
use std::fmt;
use std::mem;

use twelve_bit::u12::*;

use assembler::data_range::DataRange;
use assembler::directive::Directive;
use assembler::instruction::Instruction;
use assembler::parser::Node;
use assembler::source_file_location::SourceFileLocation;

// MARK: - Constants

lazy_static! {
  static ref BYTES_PER_INSTRUCTION: U12 = u12![2];
}

// MARK: - Semantic Error

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum SemanticError<'a> {
  /// Unable to process an invalid directive.
  DirectiveInvalid(SourceFileLocation<'a>, String),
  /// The directive processed exceeds the size limit of 4096 bytes.
  DirectiveTooLarge(SourceFileLocation<'a>, Directive),
  /// The directive at the specified location would cause the address counter to wrap.
  DirectiveWouldOverflow(SourceFileLocation<'a>, Directive),
  /// The instruction at the specified location would cause the addresss counter to wrap.
  InstructionWouldOverlow(SourceFileLocation<'a>),
  /// The label specified has already been defined.
  RedefinitionOfLabel(SourceFileLocation<'a>, String),
  /// Unable to assemble the specified instruction.
  AssemblyFailed(SourceFileLocation<'a>, String)
}

impl<'a> error::Error for SemanticError<'a> {
  /// Returns a string slice with a general description of a semantic error.
  /// No specific information is contained. To obtain a printable representation,
  /// use the `fmt::Display` attribute.
  fn description(&self) -> &str {
    match self {
      &SemanticError::DirectiveInvalid(_,_)       => "Directive invalid",
      &SemanticError::DirectiveTooLarge(_,_)      => "Directive size exceeds 4096 bytes",
      &SemanticError::DirectiveWouldOverflow(_,_) => "Directive would cause the 12-bit address counter to overflow",
      &SemanticError::InstructionWouldOverlow(_)  => "Instruction would cause address counter to overflow $FFF",
      &SemanticError::RedefinitionOfLabel(_,_)    => "Attempted re-definition of label",
      &SemanticError::AssemblyFailed(_,_)         => "Unable to assemble instruction",
    }
  }
}

impl<'a> fmt::Display for SemanticError<'a> {
  /// Formats the receiver for display purposes. Incorporates specific information
  /// relating to this particular error instance where applicable.
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      &SemanticError::DirectiveInvalid(ref loc, ref reason) =>
        write!(f, "{}: error: {}", loc, reason),
      &SemanticError::DirectiveTooLarge(ref loc, ref directive) =>
        write!(f, "{}: error: Directive ({}) size {} exceeds 4096 bytes.", loc, directive, directive.size()),
      &SemanticError::DirectiveWouldOverflow(ref loc, ref directive) =>
        write!(f, "{}: error: Directive ({}) would cause the 12-bit address counter to overflow.", loc, directive),
      &SemanticError::RedefinitionOfLabel(ref loc, ref label) =>
        write!(f, "{}: error: Attempted re-definition of label '{}'.", loc, label),
      &SemanticError::InstructionWouldOverlow(ref loc) =>
        write!(f, "{}: error: {}.", loc, (self as &error::Error).description()),
      &SemanticError::AssemblyFailed(ref loc, ref reason) =>
        write!(f, "{}: error: {}.", loc, reason),
    }
  }
}

// MARK: - Pass 1: Define Labels

/**
 In a 2-pass assembler, the first pass is used to resolve the address of all labels
 declared in the assembly. This involves walking the ASL, computing the size of instructions
 and directives in the output stream and using that information to establish the label-address map.
 @param syntax_list The un-filtered syntax list.
 @return The map of labels to their corresponding addresses on success or a string describing
     the reason the first pass failed.
 */
fn define_labels<'a>(syntax_list: &Vec<Node<'a>>) -> Result<BTreeMap<&'a str, U12>, SemanticError<'a>> {
  let mut label_address_map = BTreeMap::new();
  let mut current_address = u12![0];

  // Define all labels and process '.org' directives.
  for node in syntax_list.iter() {
    match *node {
      Node::Directive(ref data) => {
        // Validate the directive and that the arguments match the identifier.
        let directive = try!(
          Directive::from_identifier_and_parameters(data.identifier, &data.arguments)
            .map_err(|reason| {
              SemanticError::DirectiveInvalid(data.location.clone(), reason)
            })
        );

        // Ensure the directive is not too large to emit.
        let directive_size: U12 = try!(
          directive
            .size()
            .failable_into()
            .ok_or(SemanticError::DirectiveTooLarge(data.location.clone(), directive.clone()))
        );

        // Offset the current address by the size of the directive being processed.
        current_address = try!(
          current_address
            .checked_add(directive_size)
            .ok_or(SemanticError::DirectiveWouldOverflow(data.location.clone(), directive.clone()))
        );

        // The origin directive changes the current address.
        if let Directive::Org(address) = directive {
          current_address = address;
        }
      }

      Node::Label(ref data) => {
        // Map the label to the current address and remove from the ASL.
        if label_address_map.contains_key(&data.identifier) {
          return Err(SemanticError::RedefinitionOfLabel(data.location.clone(), String::from(data.identifier)));
        } else {
          label_address_map.insert(data.identifier, current_address);
        }
      }

      Node::Instruction(ref data) => {
        // All Chip8 instructions are the same length.
        current_address = try!(
          current_address
            .checked_add(*BYTES_PER_INSTRUCTION)
            .ok_or(SemanticError::InstructionWouldOverlow(data.location.clone()))
        );
      }
    }
  }

  // Semantic analysis for directives and labels is complete.
  Ok(label_address_map)
}

// MARK: - Pass 2: Emit Bytes

fn emit_data_ranges<'a>(syntax_list: Vec<Node<'a>>, label_address_map: &BTreeMap<&'a str, U12>) -> Result<Vec<DataRange>, SemanticError<'a>> {
  let mut data_ranges = Vec::new();
  let mut current_range = DataRange::new(u12![0]);

  for node in syntax_list {
    match node {

      Node::Directive(data) => {
        match Directive::from_identifier_and_parameters(data.identifier, &data.arguments).unwrap() {

          // Process the origin directive to build a new data range.
          Directive::Org(address) => {
            let new_range = DataRange::new(address);
            let previous_range = mem::replace(&mut current_range, new_range);
            if previous_range.len() > 0 {
              data_ranges.push(previous_range);
            }
          }

          // Emit the directive into the range.
          directive @ _ => {
            assert_eq!(current_range.append(directive.into_vec().as_slice()), true);
          }

        }
      }

      Node::Instruction(data) => {
        // Verify the semantics and append the instruction to the output buffer.
        match Instruction::from_mnemonic_and_parameters(data.mnemonic, &data.fields, label_address_map) {
          Ok(instruction) => {
            let bytes = instruction.into_vec();
            let appended = current_range.append(&bytes);
            assert!(appended);
          }

          Err(reason) => {
            return Err(SemanticError::AssemblyFailed(data.location.clone(), reason));
          }
        }
      }

      Node::Label(_) => {
        // Processed in Pass 1.
      }

    }
  }

  // Push the last range into the list.
  if current_range.len() > 0 {
    data_ranges.push(current_range);
  }

  Ok(data_ranges)
}

// MARK: - Public API

/**
 Analyze the ASL for the assembly and convert it into an output byte stream.
 */
pub fn assemble<'a>(syntax_list: Vec<Node<'a>>) -> Result<Vec<DataRange>, String> {
  let label_address_map = try!(
    define_labels(&syntax_list).map_err(|error|
      format!("{}", error)
    )
  );

  emit_data_ranges(syntax_list, &label_address_map).map_err(|error|
    format!("{}", error)
  )
}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use twelve_bit::u12::*;

  use assembler::parser::*;
  use assembler::source_file_location::SourceFileLocation;

  use super::define_labels;

  // MARK: - Helpers

  /// Creates a Label type node with associated data at location "-:seq:1-name.len()".
  fn make_label_node<'a>(seq: usize, name: &'a str) -> Node<'a> {
    Node::Label (
      LabelData {
        location: SourceFileLocation::new("-", seq, 1, name.len()),
        identifier: name
      }
    )
  }

  /// Creates a Directive type node with associated data at location "-:seq:2-name.len()+1".
  fn make_directive_node<'a>(seq: usize, name: &'a str, args: Vec<Literal<'a>>) -> Node<'a> {
    Node::Directive (
      DirectiveData {
        location: SourceFileLocation::new("-", seq, 2, name.len()),
        identifier: name,
        arguments: args
      }
    )
  }

  /// Creates an Instruction type node with associated data at location "-:seq:1-name.len()".
  fn make_instruction_node<'a>(seq: usize, name: &'a str, fields: Vec<InstructionField<'a>>) -> Node<'a> {
    Node::Instruction (
      InstructionData {
        location: SourceFileLocation::new("-", seq, 1, name.len()),
        mnemonic: name,
        fields: fields
      }
    )
  }

  // MARK: - Pass 1 Tests

  #[test]
  fn test_define_labels() {
    let program = vec![
      make_directive_node   (1, "org", vec![Literal::Numeric(0x100)]),
      make_label_node       (2, "label1"),
      make_directive_node   (3, "db", vec![Literal::Numeric(0xFF)]),
      make_label_node       (4, "label2"),
      make_instruction_node (5, "trap", vec![]),
      make_label_node       (6, "label3"),
    ];

    let result = define_labels(&program);

    // Assert that semantic analysis passed.
    if let Err(reason) = result {
      assert!(false, "Unexpected failure in assembler pass 1: {}", reason);
      return;
    }

    // Verify that the labels are defined as expected.
    let label_map = result.unwrap();
    assert_eq!(label_map.len(), 3);
    assert_eq!(label_map.get("label1").unwrap(), &(u12![0x100]));
    assert_eq!(label_map.get("label2").unwrap(), &(u12![0x101]));
    assert_eq!(label_map.get("label3").unwrap(), &(u12![0x103]));
  }

  #[test]
  fn test_define_labels_does_semantic_analysis_on_org() {
    let program = vec![
      make_directive_node(1, "org", vec![])
    ];

    let result = define_labels(&program);
    assert_eq!(format!("{}", result.unwrap_err()), "-:1:2-4: error: Incorrect number of parameters (0) for directive .org, expecting 1.");
  }

  #[test]
  fn test_define_labels_does_semantic_analysis_on_db() {
    let program = vec![
      make_directive_node(1, "db", vec![])
    ];

    let result = define_labels(&program);
    assert_eq!(format!("{}", result.unwrap_err()), "-:1:2-3: error: Incorrect number of parameters (0) for directive .db, expecting 1 or more.");
  }

  #[test]
  fn test_define_labels_fails_on_redefinition() {
    let program = vec![
      make_label_node(1, "L1"),
      make_label_node(2, "L1"),
    ];

    let result = define_labels(&program);
    assert_eq!(format!("{}", result.unwrap_err()), "-:2:1-2: error: Attempted re-definition of label 'L1'.");
  }

}
<commit_msg>Improved readability of some complex statements leveraging Deref.<commit_after>
use std::collections::BTreeMap;
use std::error;
use std::fmt;
use std::mem;

use twelve_bit::u12::*;

use assembler::data_range::DataRange;
use assembler::directive::Directive;
use assembler::instruction::Instruction;
use assembler::parser::Node;
use assembler::source_file_location::SourceFileLocation;

// MARK: - Constants

lazy_static! {
  static ref BYTES_PER_INSTRUCTION: U12 = u12![2];
}

// MARK: - Semantic Error

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum SemanticError<'a> {
  /// Unable to process an invalid directive.
  DirectiveInvalid(SourceFileLocation<'a>, String),
  /// The directive processed exceeds the size limit of 4096 bytes.
  DirectiveTooLarge(SourceFileLocation<'a>, Directive),
  /// The directive at the specified location would cause the address counter to wrap.
  DirectiveWouldOverflow(SourceFileLocation<'a>, Directive),
  /// The instruction at the specified location would cause the addresss counter to wrap.
  InstructionWouldOverlow(SourceFileLocation<'a>),
  /// The label specified has already been defined.
  RedefinitionOfLabel(SourceFileLocation<'a>, String),
  /// Unable to assemble the specified instruction.
  AssemblyFailed(SourceFileLocation<'a>, String)
}

impl<'a> error::Error for SemanticError<'a> {
  /// Returns a string slice with a general description of a semantic error.
  /// No specific information is contained. To obtain a printable representation,
  /// use the `fmt::Display` attribute.
  fn description(&self) -> &str {
    match self {
      &SemanticError::DirectiveInvalid(_,_)       => "Directive invalid",
      &SemanticError::DirectiveTooLarge(_,_)      => "Directive size exceeds 4096 bytes",
      &SemanticError::DirectiveWouldOverflow(_,_) => "Directive would cause the 12-bit address counter to overflow",
      &SemanticError::InstructionWouldOverlow(_)  => "Instruction would cause address counter to overflow $FFF",
      &SemanticError::RedefinitionOfLabel(_,_)    => "Attempted re-definition of label",
      &SemanticError::AssemblyFailed(_,_)         => "Unable to assemble instruction",
    }
  }
}

impl<'a> fmt::Display for SemanticError<'a> {
  /// Formats the receiver for display purposes. Incorporates specific information
  /// relating to this particular error instance where applicable.
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match self {
      &SemanticError::DirectiveInvalid(ref loc, ref reason) =>
        write!(f, "{}: error: {}", loc, reason),
      &SemanticError::DirectiveTooLarge(ref loc, ref directive) =>
        write!(f, "{}: error: Directive ({}) size {} exceeds 4096 bytes.", loc, directive, directive.size()),
      &SemanticError::DirectiveWouldOverflow(ref loc, ref directive) =>
        write!(f, "{}: error: Directive ({}) would cause the 12-bit address counter to overflow.", loc, directive),
      &SemanticError::RedefinitionOfLabel(ref loc, ref label) =>
        write!(f, "{}: error: Attempted re-definition of label '{}'.", loc, label),
      &SemanticError::InstructionWouldOverlow(ref loc) =>
        write!(f, "{}: error: {}.", loc, (self as &error::Error).description()),
      &SemanticError::AssemblyFailed(ref loc, ref reason) =>
        write!(f, "{}: error: {}.", loc, reason),
    }
  }
}

// MARK: - Pass 1: Define Labels

/**
 In a 2-pass assembler, the first pass is used to resolve the address of all labels
 declared in the assembly. This involves walking the ASL, computing the size of instructions
 and directives in the output stream and using that information to establish the label-address map.
 @param syntax_list The un-filtered syntax list.
 @return The map of labels to their corresponding addresses on success or a string describing
     the reason the first pass failed.
 */
fn define_labels<'a>(syntax_list: &Vec<Node<'a>>) -> Result<BTreeMap<&'a str, U12>, SemanticError<'a>> {
  let mut label_address_map = BTreeMap::new();
  let mut current_address = u12![0];

  // Define all labels and process '.org' directives.
  for node in syntax_list.iter() {
    match *node {
      Node::Directive(ref data) => {
        // Validate the directive and that the arguments match the identifier.
        let directive = try!(
          Directive::from_identifier_and_parameters(data.identifier, &data.arguments)
            .map_err(|reason| {
              SemanticError::DirectiveInvalid(data.location.clone(), reason)
            })
        );

        // Ensure the directive is not too large to emit.
        let directive_size: U12 = try!(
          directive
            .size()
            .failable_into()
            .ok_or(SemanticError::DirectiveTooLarge(data.location.clone(), directive.clone()))
        );

        // Offset the current address by the size of the directive being processed.
        current_address = try!(
          current_address
            .checked_add(directive_size)
            .ok_or(SemanticError::DirectiveWouldOverflow(data.location.clone(), directive.clone()))
        );

        // The origin directive changes the current address.
        if let Directive::Org(address) = directive {
          current_address = address;
        }
      }

      Node::Label(ref data) => {
        // Map the label to the current address and remove from the ASL.
        if label_address_map.contains_key(&data.identifier) {
          return Err(SemanticError::RedefinitionOfLabel(data.location.clone(), String::from(data.identifier)));
        } else {
          label_address_map.insert(data.identifier, current_address);
        }
      }

      Node::Instruction(ref data) => {
        // All Chip8 instructions are the same length.
        current_address = try!(
          current_address
            .checked_add(*BYTES_PER_INSTRUCTION)
            .ok_or(SemanticError::InstructionWouldOverlow(data.location.clone()))
        );
      }
    }
  }

  // Semantic analysis for directives and labels is complete.
  Ok(label_address_map)
}

// MARK: - Pass 2: Emit Bytes

fn emit_data_ranges<'a>(syntax_list: Vec<Node<'a>>, label_address_map: &BTreeMap<&'a str, U12>) -> Result<Vec<DataRange>, SemanticError<'a>> {
  let mut data_ranges = Vec::new();
  let mut current_range = DataRange::new(u12![0]);

  for node in syntax_list {
    match node {

      Node::Directive(data) => {
        match Directive::from_identifier_and_parameters(data.identifier, &data.arguments).unwrap() {

          // Process the origin directive to build a new data range.
          Directive::Org(address) => {
            let new_range = DataRange::new(address);
            let previous_range = mem::replace(&mut current_range, new_range);
            if previous_range.len() > 0 {
              data_ranges.push(previous_range);
            }
          }

          // Emit the directive into the range.
          directive @ _ => {
            let bytes = directive.into_vec();
            assert!(current_range.append(&bytes));
          }

        }
      }

      Node::Instruction(data) => {
        // Verify the semantics and append the instruction to the output buffer.
        match Instruction::from_mnemonic_and_parameters(data.mnemonic, &data.fields, label_address_map) {
          Ok(instruction) => {
            let bytes = instruction.into_vec();
            assert!(current_range.append(&bytes));
          }

          Err(reason) => {
            return Err(SemanticError::AssemblyFailed(data.location.clone(), reason));
          }
        }
      }

      Node::Label(_) => {
        // Processed in Pass 1.
      }

    }
  }

  // Push the last range into the list.
  if current_range.len() > 0 {
    data_ranges.push(current_range);
  }

  Ok(data_ranges)
}

// MARK: - Public API

/**
 Analyze the ASL for the assembly and convert it into an output byte stream.
 */
pub fn assemble<'a>(syntax_list: Vec<Node<'a>>) -> Result<Vec<DataRange>, String> {
  let label_address_map = try!(
    define_labels(&syntax_list).map_err(|error|
      format!("{}", error)
    )
  );

  emit_data_ranges(syntax_list, &label_address_map).map_err(|error|
    format!("{}", error)
  )
}

// MARK: - Tests

#[cfg(test)]
mod tests {

  use twelve_bit::u12::*;

  use assembler::parser::*;
  use assembler::source_file_location::SourceFileLocation;

  use super::define_labels;

  // MARK: - Helpers

  /// Creates a Label type node with associated data at location "-:seq:1-name.len()".
  fn make_label_node<'a>(seq: usize, name: &'a str) -> Node<'a> {
    Node::Label (
      LabelData {
        location: SourceFileLocation::new("-", seq, 1, name.len()),
        identifier: name
      }
    )
  }

  /// Creates a Directive type node with associated data at location "-:seq:2-name.len()+1".
  fn make_directive_node<'a>(seq: usize, name: &'a str, args: Vec<Literal<'a>>) -> Node<'a> {
    Node::Directive (
      DirectiveData {
        location: SourceFileLocation::new("-", seq, 2, name.len()),
        identifier: name,
        arguments: args
      }
    )
  }

  /// Creates an Instruction type node with associated data at location "-:seq:1-name.len()".
  fn make_instruction_node<'a>(seq: usize, name: &'a str, fields: Vec<InstructionField<'a>>) -> Node<'a> {
    Node::Instruction (
      InstructionData {
        location: SourceFileLocation::new("-", seq, 1, name.len()),
        mnemonic: name,
        fields: fields
      }
    )
  }

  // MARK: - Pass 1 Tests

  #[test]
  fn test_define_labels() {
    let program = vec![
      make_directive_node   (1, "org", vec![Literal::Numeric(0x100)]),
      make_label_node       (2, "label1"),
      make_directive_node   (3, "db", vec![Literal::Numeric(0xFF)]),
      make_label_node       (4, "label2"),
      make_instruction_node (5, "trap", vec![]),
      make_label_node       (6, "label3"),
    ];

    let result = define_labels(&program);

    // Assert that semantic analysis passed.
    if let Err(reason) = result {
      assert!(false, "Unexpected failure in assembler pass 1: {}", reason);
      return;
    }

    // Verify that the labels are defined as expected.
    let label_map = result.unwrap();
    assert_eq!(label_map.len(), 3);
    assert_eq!(label_map.get("label1").unwrap(), &(u12![0x100]));
    assert_eq!(label_map.get("label2").unwrap(), &(u12![0x101]));
    assert_eq!(label_map.get("label3").unwrap(), &(u12![0x103]));
  }

  #[test]
  fn test_define_labels_does_semantic_analysis_on_org() {
    let program = vec![
      make_directive_node(1, "org", vec![])
    ];

    let result = define_labels(&program);
    assert_eq!(format!("{}", result.unwrap_err()), "-:1:2-4: error: Incorrect number of parameters (0) for directive .org, expecting 1.");
  }

  #[test]
  fn test_define_labels_does_semantic_analysis_on_db() {
    let program = vec![
      make_directive_node(1, "db", vec![])
    ];

    let result = define_labels(&program);
    assert_eq!(format!("{}", result.unwrap_err()), "-:1:2-3: error: Incorrect number of parameters (0) for directive .db, expecting 1 or more.");
  }

  #[test]
  fn test_define_labels_fails_on_redefinition() {
    let program = vec![
      make_label_node(1, "L1"),
      make_label_node(2, "L1"),
    ];

    let result = define_labels(&program);
    assert_eq!(format!("{}", result.unwrap_err()), "-:2:1-2: error: Attempted re-definition of label 'L1'.");
  }

}
<|endoftext|>


<commit_before>//! GC heap allocator.
//!
//! ### Safety
//!
//! First, a few general observations about safety in Rust.
//!
//! *   If a `mut` reference to a value exists, and other references to the value
//!     also exist, we can definitely crash.
//!
//!     One way it can happen is that the `mut` value is or contains an
//!     `enum`. Using the other reference, we can borrow a refrence to a
//!     current field of the `enum`. Then, using the `mut` reference, we can
//!     assign some other variant to the `enum`, invalidating the reference.
//!     Rust does not detect that the reference is invalid, so when we use it,
//!     we are accessing gibberish.
//!
//!     Another way is if a callback is called while we're in the middle of
//!     mutating an object, while it's in an invalid state. When you read
//!     "callback", think of `deref` and `drop` methods, which are called
//!     implicitly all over the place. These callbacks are normally safe,
//!     because they simply can't have a reference to the object that's in an
//!     intermediate state. But if another reference exists, it might be used
//!     to try to read from the half-mutated object.
//!
//! *   If a data structure contains two paths to an object, under Rust's usual
//!     rules, you can get two `mut` references to that object.
//!
//!     This is why you tend to build ownership trees in Rust and it's why GC
//!     is particularly challenging in Rust: GCs build an arbitrary graph of
//!     objects and references.
//!
//! This GC takes the following approach to ensure safety.
//!
//! *   Minimize access to values stored in the heap. For the most part,
//!     application code *never* sees a direct reference to any value that is
//!     physically stored someplace where it's subject to GC.
//!
//! *   Minimize the times when direct references to in-heap values exist at
//!     all, and during these operations, prevent control from escaping to
//!     arbitrary application code.
//!
//! *   Ensure that when any direct references to in-heap values exist, they
//!     obey Rust's rules: for any given value, either only non-`mut`
//!     references, or at most one `mut` reference, exists at a time.
//!
//! Thus we are particularly keen to avoid the possibility of "reentering" the
//! heap, creating new references to in-heap values while others already exist.
//!
//! References to heap values therefore exist only during the following
//! operations:
//!
//! *   Allocation - That is, moving values into the heap. This is safe because
//!     it never triggers any user code at all while heap references exist.
//!
//! * - Heap reads and writes - The only way to do these is via macro-generated
//!     accessors which do not expose references.  Reads call `from_heap()` on
//!     in-heap values, which is dangerous because `from_heap()` receives a
//!     direct reference.  Writes call `drop()`, which is even more dangerous:
//!     (1) it receives a direct `mut` reference; and (2) it leaves in-heap
//!     values uninitialized.
//!
//! *   GC marking - The code for this is all macro-generated.
//!
//! *   GC sweeping - This calls `drop()`, which is dangerous for the reasons
//!     noted above.
//!
//! To make this scheme safe, `from_heap()` and `drop()` must be tightly controlled.
//! `from_heap()` is therefore in an unsafe trait; users are expected to use
//! the `gc_heap_type!` to autogenerate instances.
//!
//! However, **we leave it up to the user to exercise care with `drop()`.**
//! We suggest *never* implementing `Drop` for a heap type. If you must,
//! avoid reading pointer fields while dropping, and avoid calling into
//! arbitrary code.

use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::ptr;
use traits::{IntoHeap, IntoHeapAllocation};
use pages::{heap_type_id, PageHeader, TypedPage, PageBox};
use gcref::GCRef;

// What does this do? You'll never guess!
pub type HeapId<'h> = PhantomData<::std::cell::Cell<&'h mut ()>>;

pub struct Heap<'h> {
    id: HeapId<'h>,
    pages: HashMap<usize, PageBox<'h>>,
    pins: RefCell<HashMap<*mut (), usize>>
}

/// Create a heap, pass it to a callback, then destroy the heap.
///
/// The heap's lifetime is directly tied to this function call, for safety. (So
/// the API is a little wonky --- but how many heaps were you planning on
/// creating?)
pub fn with_heap<F, O>(f: F) -> O
    where F: for<'h> FnOnce(&mut Heap<'h>) -> O
{
    f(&mut Heap::new())
}

impl<'h> Heap<'h> {
    fn new() -> Heap<'h> {
        Heap {
            id: PhantomData,
            pages: HashMap::new(),
            pins: RefCell::new(HashMap::new())
        }
    }

    fn get_page<T: IntoHeap<'h>>(&mut self) -> &mut TypedPage<'h, T> {
        let key = heap_type_id::<T>();
        let heap_ptr = self as *mut Heap<'h>;
        self.pages
            .entry(key)
            .or_insert_with(|| PageBox::new::<T>(heap_ptr))
            .downcast_mut::<T>()
            .unwrap()
    }

    /// Add the object `*p` to the root set, protecting it from GC.
    ///
    /// An object that has been pinned *n* times stays in the root set
    /// until it has been unpinned *n* times.
    ///
    /// (Unsafe because if the argument is garbage, a later GC will
    /// crash. Called only from `impl GCRef`.)
    pub unsafe fn pin<T: IntoHeap<'h>>(&self, p: *mut T::In) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    /// Unpin an object (see `pin`).
    ///
    /// (Unsafe because unpinning an object that other code is still using
    /// causes dangling pointers. Called only from `impl GCRef`.)
    pub unsafe fn unpin<T: IntoHeap<'h>>(&self, p: *mut T::In) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p as *mut ()).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    pub fn try_alloc<T: IntoHeapAllocation<'h>>(&mut self, value: T) -> Option<T::Ref> {
        // For now, this is done very early, so that if it panics, the heap is
        // left in an OK state. Better wrapping of raw pointers would make it
        // possible to do this later, closer to the `ptr::write()` call. (And
        // the compiler might optimize away this temporary if we do it that
        // way.) Looking forward to placement new!
        let u = value.into_heap();
        unsafe {
            let alloc = self.get_page::<T>().try_alloc();
            alloc
                .or_else(|| {
                    self.gc();
                    self.get_page::<T>().try_alloc()
                })
                .map(move |p| {
                    ptr::write(p, u);
                    T::wrap_gcref(GCRef::new(p))
                })
        }
    }

    pub unsafe fn from_allocation<T: IntoHeap<'h>>(ptr: *const T::In) -> *const Heap<'h> {
        (*TypedPage::<'h, T>::find(ptr)).header.heap
    }

    pub unsafe fn get_mark_bit<T: IntoHeap<'h>>(ptr: *const T::In) -> bool {
        (*TypedPage::<'h, T>::find(ptr)).get_mark_bit(ptr)
    }

    pub unsafe fn set_mark_bit<T: IntoHeap<'h>>(ptr: *const T::In) {
        (*TypedPage::<'h, T>::find(ptr)).set_mark_bit(ptr);
    }

    pub fn alloc<T: IntoHeapAllocation<'h>>(&mut self, value: T) -> T::Ref {
        self.try_alloc(value).expect("out of memory (gc did not collect anything)")
    }

    unsafe fn gc(&mut self) {
        // mark phase
        for (_, page) in self.pages.iter_mut() {
            page.clear_mark_bits();
        }
        for (&ptr, _) in self.pins.borrow().iter() {
            (*PageHeader::<'h>::find(ptr)).mark(ptr);
        }

        // sweep phase
        for (_, page) in self.pages.iter_mut() {
            page.sweep();
        }
    }

    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

impl<'h> Drop for Heap<'h> {
    fn drop(&mut self) {
        // Perform a final GC to call destructors on any remaining allocations.
        assert!(self.pins.borrow().is_empty());
        unsafe { self.gc(); }

        for (_, page) in self.pages.iter() {
            assert!(page.is_empty());
        }
    }
}
<commit_msg>Change "object" to "value" in some comments.<commit_after>//! GC heap allocator.
//!
//! ### Safety
//!
//! First, a few general observations about safety in Rust.
//!
//! *   If a `mut` reference to a value exists, and other references to the value
//!     also exist, we can definitely crash.
//!
//!     One way it can happen is that the `mut` value is or contains an
//!     `enum`. Using the other reference, we can borrow a refrence to a
//!     current field of the `enum`. Then, using the `mut` reference, we can
//!     assign some other variant to the `enum`, invalidating the reference.
//!     Rust does not detect that the reference is invalid, so when we use it,
//!     we are accessing gibberish.
//!
//!     Another way is if a callback is called while we're in the middle of
//!     mutating a value, while it's in an invalid state. When you read
//!     "callback", think of `deref` and `drop` methods, which are called
//!     implicitly all over the place. These callbacks are normally safe,
//!     because they simply can't have a reference to the value that's in an
//!     intermediate state. But if another reference exists, it might be used
//!     to try to read from the half-mutated value.
//!
//! *   If a data structure contains two paths to a value, under Rust's usual
//!     rules, you can get two `mut` references to that value.
//!
//!     This is why you tend to build ownership trees in Rust and it's why GC
//!     is particularly challenging in Rust: GCs build an arbitrary graph of
//!     values and references.
//!
//! This GC takes the following approach to ensure safety.
//!
//! *   Minimize access to values stored in the heap. For the most part,
//!     application code *never* sees a direct reference to any value that is
//!     physically stored someplace where it's subject to GC.
//!
//! *   Minimize the times when direct references to in-heap values exist at
//!     all, and during these operations, prevent control from escaping to
//!     arbitrary application code.
//!
//! *   Ensure that when any direct references to in-heap values exist, they
//!     obey Rust's rules: for any given value, either only non-`mut`
//!     references, or at most one `mut` reference, exists at a time.
//!
//! Thus we are particularly keen to avoid the possibility of "reentering" the
//! heap, creating new references to in-heap values while others already exist.
//!
//! References to heap values therefore exist only during the following
//! operations:
//!
//! *   Allocation - That is, moving values into the heap. This is safe because
//!     it never triggers any user code at all while heap references exist.
//!
//! * - Heap reads and writes - The only way to do these is via macro-generated
//!     accessors which do not expose references.  Reads call `from_heap()` on
//!     in-heap values, which is dangerous because `from_heap()` receives a
//!     direct reference.  Writes call `drop()`, which is even more dangerous:
//!     (1) it receives a direct `mut` reference; and (2) it leaves in-heap
//!     values uninitialized.
//!
//! *   GC marking - The code for this is all macro-generated.
//!
//! *   GC sweeping - This calls `drop()`, which is dangerous for the reasons
//!     noted above.
//!
//! To make this scheme safe, `from_heap()` and `drop()` must be tightly controlled.
//! `from_heap()` is therefore in an unsafe trait; users are expected to use
//! the `gc_heap_type!` to autogenerate instances.
//!
//! However, **we leave it up to the user to exercise care with `drop()`.**
//! We suggest *never* implementing `Drop` for a heap type. If you must,
//! avoid reading pointer fields while dropping, and avoid calling into
//! arbitrary code.

use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::ptr;
use traits::{IntoHeap, IntoHeapAllocation};
use pages::{heap_type_id, PageHeader, TypedPage, PageBox};
use gcref::GCRef;

// What does this do? You'll never guess!
pub type HeapId<'h> = PhantomData<::std::cell::Cell<&'h mut ()>>;

pub struct Heap<'h> {
    id: HeapId<'h>,
    pages: HashMap<usize, PageBox<'h>>,
    pins: RefCell<HashMap<*mut (), usize>>
}

/// Create a heap, pass it to a callback, then destroy the heap.
///
/// The heap's lifetime is directly tied to this function call, for safety. (So
/// the API is a little wonky --- but how many heaps were you planning on
/// creating?)
pub fn with_heap<F, O>(f: F) -> O
    where F: for<'h> FnOnce(&mut Heap<'h>) -> O
{
    f(&mut Heap::new())
}

impl<'h> Heap<'h> {
    fn new() -> Heap<'h> {
        Heap {
            id: PhantomData,
            pages: HashMap::new(),
            pins: RefCell::new(HashMap::new())
        }
    }

    fn get_page<T: IntoHeap<'h>>(&mut self) -> &mut TypedPage<'h, T> {
        let key = heap_type_id::<T>();
        let heap_ptr = self as *mut Heap<'h>;
        self.pages
            .entry(key)
            .or_insert_with(|| PageBox::new::<T>(heap_ptr))
            .downcast_mut::<T>()
            .unwrap()
    }

    /// Add the value `*p` to the root set, protecting it from GC.
    ///
    /// A value that has been pinned *n* times stays in the root set
    /// until it has been unpinned *n* times.
    ///
    /// # Safety
    ///
    /// `p` must point to a live allocation of type `T` in this heap.
    pub unsafe fn pin<T: IntoHeap<'h>>(&self, p: *mut T::In) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        let entry = pins.entry(p).or_insert(0);
        *entry += 1;
    }

    /// Unpin a heap-allocated value (see `pin`).
    ///
    /// # Safety
    ///
    /// `p` must point to a pinned allocation of type `T` in this heap.
    pub unsafe fn unpin<T: IntoHeap<'h>>(&self, p: *mut T::In) {
        let p = p as *mut ();
        let mut pins = self.pins.borrow_mut();
        if {
            let entry = pins.entry(p as *mut ()).or_insert(0);
            assert!(*entry != 0);
            *entry -= 1;
            *entry == 0
        } {
            pins.remove(&p);
        }
    }

    pub fn try_alloc<T: IntoHeapAllocation<'h>>(&mut self, value: T) -> Option<T::Ref> {
        // For now, this is done very early, so that if it panics, the heap is
        // left in an OK state. Better wrapping of raw pointers would make it
        // possible to do this later, closer to the `ptr::write()` call. (And
        // the compiler might optimize away this temporary if we do it that
        // way.) Looking forward to placement new!
        let u = value.into_heap();
        unsafe {
            let alloc = self.get_page::<T>().try_alloc();
            alloc
                .or_else(|| {
                    self.gc();
                    self.get_page::<T>().try_alloc()
                })
                .map(move |p| {
                    ptr::write(p, u);
                    T::wrap_gcref(GCRef::new(p))
                })
        }
    }

    pub unsafe fn from_allocation<T: IntoHeap<'h>>(ptr: *const T::In) -> *const Heap<'h> {
        (*TypedPage::<'h, T>::find(ptr)).header.heap
    }

    pub unsafe fn get_mark_bit<T: IntoHeap<'h>>(ptr: *const T::In) -> bool {
        (*TypedPage::<'h, T>::find(ptr)).get_mark_bit(ptr)
    }

    pub unsafe fn set_mark_bit<T: IntoHeap<'h>>(ptr: *const T::In) {
        (*TypedPage::<'h, T>::find(ptr)).set_mark_bit(ptr);
    }

    pub fn alloc<T: IntoHeapAllocation<'h>>(&mut self, value: T) -> T::Ref {
        self.try_alloc(value).expect("out of memory (gc did not collect anything)")
    }

    unsafe fn gc(&mut self) {
        // mark phase
        for (_, page) in self.pages.iter_mut() {
            page.clear_mark_bits();
        }
        for (&ptr, _) in self.pins.borrow().iter() {
            (*PageHeader::<'h>::find(ptr)).mark(ptr);
        }

        // sweep phase
        for (_, page) in self.pages.iter_mut() {
            page.sweep();
        }
    }

    pub fn force_gc(&mut self) {
        unsafe { self.gc(); }
    }
}

impl<'h> Drop for Heap<'h> {
    fn drop(&mut self) {
        // Perform a final GC to call destructors on any remaining allocations.
        assert!(self.pins.borrow().is_empty());
        unsafe { self.gc(); }

        for (_, page) in self.pages.iter() {
            assert!(page.is_empty());
        }
    }
}
<|endoftext|>


<commit_before>use quote::{Tokens, Ident};
use syn::Visibility;
use structs::Struct;

pub fn derive(input: &Struct) -> Tokens {
    let name = &input.name;
    let visibility = &input.visibility;
    let detail_mod = Ident::from(format!("__detail_{}", name));
    let vec_name = &input.vec_name();
    let slice_name = &input.slice_name();
    let slice_mut_name = &input.slice_mut_name();
    let ref_name = &input.ref_name();
    let ref_mut_name = &input.ref_mut_name();

    let fields_names = input.fields.iter()
                                   .map(|field| field.ident.clone().unwrap())
                                   .collect::<Vec<_>>();
    let fields_names_1 = &fields_names;
    let fields_names_2 = &fields_names;
    let first_field = &fields_names[0];

    let fields_types = &input.fields.iter()
                                    .map(|field| &field.ty)
                                    .collect::<Vec<_>>();

    let mut generated = quote! {
        #[allow(non_snake_case, dead_code)]
        mod #detail_mod {
            use super::*;
            use std::slice;

            #visibility struct Iter<'a> {
                #(#fields_names_1: slice::Iter<'a, #fields_types>,)*
            }

            impl<'a> Iterator for Iter<'a> {
                type Item = #ref_name<'a>;
                fn next(&mut self) -> Option<#ref_name<'a>> {
                    #(let #fields_names_1 = self.#fields_names_2.next();)*
                    if #first_field.is_none() {
                        None
                    } else {
                        Some(#ref_name {
                            #(#fields_names_1: #fields_names_2.unwrap(),)*
                        })
                    }
                }
            }

            impl #vec_name {
                #visibility fn iter(&self) -> Iter {
                    Iter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }
            }

            impl<'a> #slice_name<'a> {
                #visibility fn iter(&self) -> Iter {
                    Iter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }
            }

            #visibility struct IterMut<'a> {
                #(#fields_names_1: slice::IterMut<'a, #fields_types>,)*
            }

            impl<'a> Iterator for IterMut<'a> {
                type Item = #ref_mut_name<'a>;
                fn next(&mut self) -> Option<#ref_mut_name<'a>> {
                    #(let #fields_names_1 = self.#fields_names_2.next();)*
                    if #first_field.is_none() {
                        None
                    } else {
                        Some(#ref_mut_name {
                            #(#fields_names_1: #fields_names_2.unwrap(),)*
                        })
                    }
                }
            }

            impl #vec_name {
                #visibility fn iter_mut(&mut self) -> IterMut {
                    IterMut {
                        #(#fields_names_1: self.#fields_names_2.iter_mut(),)*
                    }
                }
            }

            impl<'a> #slice_mut_name<'a> {
                #visibility fn iter(&mut self) -> Iter {
                    Iter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }

                #visibility fn iter_mut(&mut self) -> IterMut {
                    IterMut {
                        #(#fields_names_1: self.#fields_names_2.iter_mut(),)*
                    }
                }
            }
        }
    };

    if let Visibility::Public = *visibility {
        generated.append(quote!{
            impl<'a> IntoIterator for &'a #slice_name<'a> {
                type Item = #ref_name<'a>;
                type IntoIter = #detail_mod::Iter<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    self.iter()
                }
            }

            impl<'a> IntoIterator for &'a #vec_name {
                type Item = #ref_name<'a>;
                type IntoIter = #detail_mod::Iter<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    self.iter()
                }
            }

            impl<'a> IntoIterator for &'a mut #slice_mut_name<'a> {
                type Item = #ref_mut_name<'a>;
                type IntoIter = #detail_mod::IterMut<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    self.iter_mut()
                }
            }

            impl<'a> IntoIterator for &'a mut #vec_name {
                type Item = #ref_mut_name<'a>;
                type IntoIter = #detail_mod::IterMut<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    self.iter_mut()
                }
            }
        });
    }

    return generated;
}
<commit_msg>We do not need the references to implement IntoIterator<commit_after>use quote::{Tokens, Ident};
use syn::Visibility;
use structs::Struct;

pub fn derive(input: &Struct) -> Tokens {
    let name = &input.name;
    let visibility = &input.visibility;
    let detail_mod = Ident::from(format!("__detail_{}", name));
    let vec_name = &input.vec_name();
    let slice_name = &input.slice_name();
    let slice_mut_name = &input.slice_mut_name();
    let ref_name = &input.ref_name();
    let ref_mut_name = &input.ref_mut_name();

    let fields_names = input.fields.iter()
                                   .map(|field| field.ident.clone().unwrap())
                                   .collect::<Vec<_>>();
    let fields_names_1 = &fields_names;
    let fields_names_2 = &fields_names;
    let first_field = &fields_names[0];

    let fields_types = &input.fields.iter()
                                    .map(|field| &field.ty)
                                    .collect::<Vec<_>>();

    let mut generated = quote! {
        #[allow(non_snake_case, dead_code)]
        mod #detail_mod {
            use super::*;
            use std::slice;

            #visibility struct Iter<'a> {
                #(pub(super) #fields_names_1: slice::Iter<'a, #fields_types>,)*
            }

            impl<'a> Iterator for Iter<'a> {
                type Item = #ref_name<'a>;
                fn next(&mut self) -> Option<#ref_name<'a>> {
                    #(let #fields_names_1 = self.#fields_names_2.next();)*
                    if #first_field.is_none() {
                        None
                    } else {
                        Some(#ref_name {
                            #(#fields_names_1: #fields_names_2.unwrap(),)*
                        })
                    }
                }
            }

            impl #vec_name {
                #visibility fn iter(&self) -> Iter {
                    Iter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }
            }

            impl<'a> #slice_name<'a> {
                #visibility fn iter(&self) -> Iter {
                    Iter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }
            }

            #visibility struct IterMut<'a> {
                #(pub(super) #fields_names_1: slice::IterMut<'a, #fields_types>,)*
            }

            impl<'a> Iterator for IterMut<'a> {
                type Item = #ref_mut_name<'a>;
                fn next(&mut self) -> Option<#ref_mut_name<'a>> {
                    #(let #fields_names_1 = self.#fields_names_2.next();)*
                    if #first_field.is_none() {
                        None
                    } else {
                        Some(#ref_mut_name {
                            #(#fields_names_1: #fields_names_2.unwrap(),)*
                        })
                    }
                }
            }

            impl #vec_name {
                #visibility fn iter_mut(&mut self) -> IterMut {
                    IterMut {
                        #(#fields_names_1: self.#fields_names_2.iter_mut(),)*
                    }
                }
            }

            impl<'a> #slice_mut_name<'a> {
                #visibility fn iter(&mut self) -> Iter {
                    Iter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }

                #visibility fn iter_mut(&mut self) -> IterMut {
                    IterMut {
                        #(#fields_names_1: self.#fields_names_2.iter_mut(),)*
                    }
                }
            }
        }
    };

    if let Visibility::Public = *visibility {
        generated.append(quote!{
            impl<'a> IntoIterator for #slice_name<'a> {
                type Item = #ref_name<'a>;
                type IntoIter = #detail_mod::Iter<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    Self::IntoIter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }
            }

            impl<'a> IntoIterator for &'a #vec_name {
                type Item = #ref_name<'a>;
                type IntoIter = #detail_mod::Iter<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    Self::IntoIter {
                        #(#fields_names_1: self.#fields_names_2.iter(),)*
                    }
                }
            }

            impl<'a> IntoIterator for #slice_mut_name<'a> {
                type Item = #ref_mut_name<'a>;
                type IntoIter = #detail_mod::IterMut<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    Self::IntoIter {
                        #(#fields_names_1: self.#fields_names_2.iter_mut(),)*
                    }
                }
            }

            impl<'a> IntoIterator for &'a mut #vec_name {
                type Item = #ref_mut_name<'a>;
                type IntoIter = #detail_mod::IterMut<'a>;

                fn into_iter(self) -> Self::IntoIter {
                    Self::IntoIter {
                        #(#fields_names_1: self.#fields_names_2.iter_mut(),)*
                    }
                }
            }
        });
    }

    return generated;
}
<|endoftext|>


<commit_before>//! This module initializes a project.

use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};

use rustc_serialize::json;
use git2::Repository;

use Error;
use project_types::Project;

/// Creates a folder. The folder must not exist or must be empty.
///
/// Impure.
pub fn make_project_folder(root: &Path) -> Result<(), Error> {
    // Make the folder - ignore error.
    let _ = fs::create_dir(root);

    // Check that the folder is empty
    let count = try!(fs::read_dir(root)).count();
    if count == 0 {
        Ok(())
    } else {
        Err(Error::FolderNotEmpty(root.to_str().unwrap().to_owned(), count))
    }
}

/// Writes an empty Protonfile to the root.
///
/// Impure.
pub fn make_protonfile(root: &Path) -> Result<(), Error> {
    // Create content
    let project = Project::empty();
    let pretty_json = json::as_pretty_json(&project);

    // Make path
    let mut path = PathBuf::from(root);
    path.push("Protonfile.json");

    // Write file
    let mut protonfile = try!(File::create(&path));
    Ok(try!(write!(&mut protonfile, "{}\n", pretty_json)))
}

/// Initializes a git repository at root.
///
/// Impure.
pub fn make_repository(root: &Path) -> Result<Repository, Error> {
    Ok(try!(Repository::init(root)))
}

/// Stages the Protonfile and makes an initial commit.
///
/// Impure.
pub fn initial_commit(repo: &Repository) -> Result<(), Error> {
    let path = Path::new("Protonfile.json");

    Ok(try!(repo.index()
        .and_then(|mut index| index.add_path(&path).map(|_| index))
        .and_then(|mut index| index.write())))
}
<commit_msg>Converted to map_errs<commit_after>//! This module initializes a project.

use std::fs;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};

use rustc_serialize::json;
use git2::Repository;

use Error;
use project_types::Project;

/// Creates a folder. The folder must not exist or must be empty.
///
/// Impure.
pub fn make_project_folder(root: &Path) -> Result<(), Error> {
    // Make the folder - ignore error.
    let _ = fs::create_dir(root);

    // Check that the folder is empty
    fs::read_dir(root)
        .map(|iter| iter.count())
        .map_err(Error::Io)
        .and_then(|count|
            if count == 0 {
                Ok(())
            } else {
                Err(folder_not_empty(root, count))
            })
}

fn folder_not_empty(root: &Path, count: usize) -> Error {
    Error::FolderNotEmpty(root.to_str().unwrap().to_owned(), count)
}

/// Writes an empty Protonfile to the root.
///
/// Impure.
pub fn make_protonfile(root: &Path) -> Result<(), Error> {
    // Create content
    let project = Project::empty();
    let pretty_json = json::as_pretty_json(&project);

    // Make path
    let mut path = PathBuf::from(root);
    path.push("Protonfile.json");

    File::create(&path)
        .and_then(|mut protonfile| write!(protonfile, "{}\n", pretty_json))
        .map_err(Error::Io)
}

/// Initializes a git repository at root.
///
/// Impure.
pub fn make_repository(root: &Path) -> Result<Repository, Error> {
    Repository::init(root)
        .map_err(Error::Git)
}

/// Stages the Protonfile and makes an initial commit.
///
/// Impure.
pub fn initial_commit(repo: &Repository) -> Result<(), Error> {
    let path = Path::new("Protonfile.json");

    Ok(try!(repo.index()
        .and_then(|mut index| index.add_path(&path).map(|_| index))
        .and_then(|mut index| index.write())))
}
<|endoftext|>


<commit_before>use serde_json;

pub struct Line {
    pub text: String,
    pub selection: Option<(u64, u64)>,
    cursor: Option<u64>,
}

impl Line {
    pub fn from_value(value: &serde_json::Value) -> Line {
        let line_arr = value.as_array().unwrap();
        let mut line = Line {
            text: line_arr[0].as_str().unwrap().to_string(),
            cursor: None,
            selection: None,
        };
        for annotation in line_arr.iter().skip(1).map(|a| a.as_array().unwrap()) {
            match annotation[0].as_str().unwrap() {
                "cursor" => {
                    line.cursor = Some(annotation[1].as_u64().unwrap());
                },
                "sel" => {
                    line.selection = Some((annotation[1].as_u64().unwrap(), annotation[2].as_u64().unwrap()));
                },
                _ => {
                    error!("unknown annotation");
                }
            }
        }
        line
    }
}
<commit_msg>Updates Line interface/parsing to match current backend protocol<commit_after>use serde_json;

pub struct Line {
    text: Option<String>,
    cursor: Option<Vec<u64>>,
    styles: Option<Vec<i64>>,
}

impl Line {
    pub fn from_value(value: &serde_json::Value) -> Line {
        let obj = value.as_object().unwrap();
        let text = match obj.get("text") {
            Some(text) => Some(text.as_str().unwrap().to_string()),
            _ => None,
        };
        let cursor = match obj.get("cursor") {
            Some(cursor) => {
                Some(cursor.as_array().unwrap().iter().map(|c| c.as_u64().unwrap()).collect())
            },
            _ => None,
        };
        let styles = match obj.get("styles") {
            Some(styles) => {
                Some(styles.as_array().unwrap().iter().map(|s| s.as_i64().unwrap()).collect())
            },
            _ => None,
        };
        Line {
            text: text,
            cursor: cursor,
            styles: styles,
        }
    }
}
<|endoftext|>