<commit_before>//! History API

use std::collections::VecDeque;
use std::collections::vec_deque;
use std::fs::File;
use std::iter::DoubleEndedIterator;
use std::ops::Index;
use std::path::Path;

use super::Result;
use config::{Config, HistoryDuplicates};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
    Forward,
    Reverse,
}

/// Current state of the history.
pub struct History {
    entries: VecDeque<String>,
    max_len: usize,
    ignore_space: bool,
    ignore_dups: bool,
}

impl History {
    pub fn new(config: Config) -> History {
        History {
            entries: VecDeque::new(),
            max_len: config.max_history_size(),
            ignore_space: config.history_duplicates() == HistoryDuplicates::IgnoreConsecutive,
            ignore_dups: config.history_ignore_space(),
        }
    }

    /// Return the history entry at position `index`, starting from 0.
    pub fn get(&self, index: usize) -> Option<&String> {
        self.entries.get(index)
    }

    /// Return the last history entry (i.e. previous command)
    pub fn last(&self) -> Option<&String> {
        self.entries.back()
    }

    /// Add a new entry in the history.
    pub fn add<S: AsRef<str> + Into<String>>(&mut self, line: S) -> bool {
        if self.max_len == 0 {
            return false;
        }
        if line.as_ref().is_empty() ||
           (self.ignore_space && line.as_ref().chars().next().map_or(true, |c| c.is_whitespace())) {
            return false;
        }
        if self.ignore_dups {
            if let Some(s) = self.entries.back() {
                if s == line.as_ref() {
                    return false;
                }
            }
        }
        if self.entries.len() == self.max_len {
            self.entries.pop_front();
        }
        self.entries.push_back(line.into());
        true
    }

    /// Returns the number of entries in the history.
    pub fn len(&self) -> usize {
        self.entries.len()
    }
    /// Returns true if the history has no entry.
    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    /// Set the maximum length for the history. This function can be called even
    /// if there is already some history, the function will make sure to retain
    /// just the latest `len` elements if the new history length value is smaller
    /// than the amount of items already inside the history.
    pub fn set_max_len(&mut self, len: usize) {
        self.max_len = len;
        if len == 0 {
            self.entries.clear();
            return;
        }
        loop {
            if self.entries.len() <= len {
                break;
            }
            self.entries.pop_front();
        }
    }

    /// Save the history in the specified file.
    /// TODO append_history http://cnswww.cns.cwru.edu/php/chet/readline/history.html#IDX30
    /// TODO history_truncate_file http://cnswww.cns.cwru.edu/php/chet/readline/history.html#IDX31
    pub fn save<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<()> {
        use std::io::{BufWriter, Write};

        if self.is_empty() {
            return Ok(());
        }
        let file = try!(File::create(path));
        let mut wtr = BufWriter::new(file);
        for entry in &self.entries {
            try!(wtr.write_all(&entry.as_bytes()));
            try!(wtr.write_all(b"\n"));
        }
        Ok(())
    }

    /// Load the history from the specified file.
    ///
    /// # Failure
    /// Will return `Err` if path does not already exist.
    pub fn load<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<()> {
        use std::io::{BufRead, BufReader};

        let file = try!(File::open(&path));
        let rdr = BufReader::new(file);
        for line in rdr.lines() {
            self.add(try!(line).as_ref()); // TODO truncate to MAX_LINE
        }
        Ok(())
    }

    /// Clear history
    pub fn clear(&mut self) {
        self.entries.clear()
    }

    /// Search history (start position inclusive [0, len-1]).
    /// Return the absolute index of the nearest history entry that matches `term`.
    /// Return None if no entry contains `term` between [start, len -1] for forward search
    /// or between [0, start] for reverse search.
    pub fn search(&self, term: &str, start: usize, dir: Direction) -> Option<usize> {
        if term.is_empty() || start >= self.len() {
            return None;
        }
        match dir {
            Direction::Reverse => {
                let index = self.entries
                    .iter()
                    .rev()
                    .skip(self.entries.len() - 1 - start)
                    .position(|entry| entry.contains(term));
                index.and_then(|index| Some(start - index))
            }
            Direction::Forward => {
                let index = self.entries.iter().skip(start).position(|entry| entry.contains(term));
                index.and_then(|index| Some(index + start))
            }
        }
    }

    /// Return a forward iterator.
    pub fn iter(&self) -> Iter {
        Iter(self.entries.iter())
    }
}

impl Index<usize> for History {
    type Output = String;

    fn index(&self, index: usize) -> &String {
        &self.entries[index]
    }
}

impl<'a> IntoIterator for &'a History {
    type Item = &'a String;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

/// History iterator.
pub struct Iter<'a>(vec_deque::Iter<'a, String>);

impl<'a> Iterator for Iter<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.next()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

impl<'a> DoubleEndedIterator for Iter<'a> {
    fn next_back(&mut self) -> Option<&'a String> {
        self.0.next_back()
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use std::path::Path;
    use config::Config;
    use super::{Direction, History};

    fn init() -> History {
        let mut history = History::new(Config::default());
        assert!(history.add("line1"));
        assert!(history.add("line2"));
        assert!(history.add("line3"));
        history
    }

    #[test]
    fn new() {
        let config = Config::default();
        let history = History::new(config);
        assert_eq!(config.max_history_size(), history.max_len);
        assert_eq!(0, history.entries.len());
    }

    #[test]
    fn add() {
        let config = Config::builder()
            .history_ignore_space(true)
            .build();
        let mut history = History::new(config);
        assert!(history.add("line1"));
        assert!(history.add("line2"));
        assert!(!history.add("line2"));
        assert!(!history.add(""));
        assert!(!history.add(" line3"));
    }

    #[test]
    fn set_max_len() {
        let mut history = init();
        history.set_max_len(1);
        assert_eq!(1, history.entries.len());
        assert_eq!(Some(&"line3".to_string()), history.last());
    }

    #[test]
    fn save() {
        let mut history = init();
        let td = tempdir::TempDir::new_in(&Path::new("."), "histo").unwrap();
        let history_path = td.path().join(".history");

        history.save(&history_path).unwrap();
        history.load(&history_path).unwrap();
        td.close().unwrap();
    }

    #[test]
    fn search() {
        let history = init();
        assert_eq!(None, history.search("", 0, Direction::Forward));
        assert_eq!(None, history.search("none", 0, Direction::Forward));
        assert_eq!(None, history.search("line", 3, Direction::Forward));

        assert_eq!(Some(0), history.search("line", 0, Direction::Forward));
        assert_eq!(Some(1), history.search("line", 1, Direction::Forward));
        assert_eq!(Some(2), history.search("line3", 1, Direction::Forward));
    }

    #[test]
    fn reverse_search() {
        let history = init();
        assert_eq!(None, history.search("", 2, Direction::Reverse));
        assert_eq!(None, history.search("none", 2, Direction::Reverse));
        assert_eq!(None, history.search("line", 3, Direction::Reverse));

        assert_eq!(Some(2), history.search("line", 2, Direction::Reverse));
        assert_eq!(Some(1), history.search("line", 1, Direction::Reverse));
        assert_eq!(Some(0), history.search("line1", 1, Direction::Reverse));
    }
}
<commit_msg>Fix insecure history file creation.<commit_after>//! History API

use std::collections::VecDeque;
use std::collections::vec_deque;
use std::fs::File;
use std::iter::DoubleEndedIterator;
use std::ops::Index;
use std::path::Path;

use super::Result;
use config::{Config, HistoryDuplicates};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
    Forward,
    Reverse,
}

/// Current state of the history.
pub struct History {
    entries: VecDeque<String>,
    max_len: usize,
    ignore_space: bool,
    ignore_dups: bool,
}

impl History {
    pub fn new(config: Config) -> History {
        History {
            entries: VecDeque::new(),
            max_len: config.max_history_size(),
            ignore_space: config.history_duplicates() == HistoryDuplicates::IgnoreConsecutive,
            ignore_dups: config.history_ignore_space(),
        }
    }

    /// Return the history entry at position `index`, starting from 0.
    pub fn get(&self, index: usize) -> Option<&String> {
        self.entries.get(index)
    }

    /// Return the last history entry (i.e. previous command)
    pub fn last(&self) -> Option<&String> {
        self.entries.back()
    }

    /// Add a new entry in the history.
    pub fn add<S: AsRef<str> + Into<String>>(&mut self, line: S) -> bool {
        if self.max_len == 0 {
            return false;
        }
        if line.as_ref().is_empty() ||
           (self.ignore_space && line.as_ref().chars().next().map_or(true, |c| c.is_whitespace())) {
            return false;
        }
        if self.ignore_dups {
            if let Some(s) = self.entries.back() {
                if s == line.as_ref() {
                    return false;
                }
            }
        }
        if self.entries.len() == self.max_len {
            self.entries.pop_front();
        }
        self.entries.push_back(line.into());
        true
    }

    /// Returns the number of entries in the history.
    pub fn len(&self) -> usize {
        self.entries.len()
    }
    /// Returns true if the history has no entry.
    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    /// Set the maximum length for the history. This function can be called even
    /// if there is already some history, the function will make sure to retain
    /// just the latest `len` elements if the new history length value is smaller
    /// than the amount of items already inside the history.
    pub fn set_max_len(&mut self, len: usize) {
        self.max_len = len;
        if len == 0 {
            self.entries.clear();
            return;
        }
        loop {
            if self.entries.len() <= len {
                break;
            }
            self.entries.pop_front();
        }
    }

    /// Save the history in the specified file.
    /// TODO append_history http://cnswww.cns.cwru.edu/php/chet/readline/history.html#IDX30
    /// TODO history_truncate_file http://cnswww.cns.cwru.edu/php/chet/readline/history.html#IDX31
    pub fn save<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<()> {
        use std::io::{BufWriter, Write};

        if self.is_empty() {
            return Ok(());
        }
        // TODO umask
        let file = try!(File::create(path));
        if cfg!(unix) {
            use libc;
            use std::os::unix::io::AsRawFd;
            unsafe {
                libc::fchmod(file.as_raw_fd(), libc::S_IRUSR | libc::S_IWUSR);
            }
        }
        let mut wtr = BufWriter::new(file);
        for entry in &self.entries {
            try!(wtr.write_all(&entry.as_bytes()));
            try!(wtr.write_all(b"\n"));
        }
        Ok(())
    }

    /// Load the history from the specified file.
    ///
    /// # Failure
    /// Will return `Err` if path does not already exist.
    pub fn load<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<()> {
        use std::io::{BufRead, BufReader};

        let file = try!(File::open(&path));
        let rdr = BufReader::new(file);
        for line in rdr.lines() {
            self.add(try!(line).as_ref()); // TODO truncate to MAX_LINE
        }
        Ok(())
    }

    /// Clear history
    pub fn clear(&mut self) {
        self.entries.clear()
    }

    /// Search history (start position inclusive [0, len-1]).
    /// Return the absolute index of the nearest history entry that matches `term`.
    /// Return None if no entry contains `term` between [start, len -1] for forward search
    /// or between [0, start] for reverse search.
    pub fn search(&self, term: &str, start: usize, dir: Direction) -> Option<usize> {
        if term.is_empty() || start >= self.len() {
            return None;
        }
        match dir {
            Direction::Reverse => {
                let index = self.entries
                    .iter()
                    .rev()
                    .skip(self.entries.len() - 1 - start)
                    .position(|entry| entry.contains(term));
                index.and_then(|index| Some(start - index))
            }
            Direction::Forward => {
                let index = self.entries.iter().skip(start).position(|entry| entry.contains(term));
                index.and_then(|index| Some(index + start))
            }
        }
    }

    /// Return a forward iterator.
    pub fn iter(&self) -> Iter {
        Iter(self.entries.iter())
    }
}

impl Index<usize> for History {
    type Output = String;

    fn index(&self, index: usize) -> &String {
        &self.entries[index]
    }
}

impl<'a> IntoIterator for &'a History {
    type Item = &'a String;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

/// History iterator.
pub struct Iter<'a>(vec_deque::Iter<'a, String>);

impl<'a> Iterator for Iter<'a> {
    type Item = &'a String;

    fn next(&mut self) -> Option<&'a String> {
        self.0.next()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

impl<'a> DoubleEndedIterator for Iter<'a> {
    fn next_back(&mut self) -> Option<&'a String> {
        self.0.next_back()
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use std::path::Path;
    use config::Config;
    use super::{Direction, History};

    fn init() -> History {
        let mut history = History::new(Config::default());
        assert!(history.add("line1"));
        assert!(history.add("line2"));
        assert!(history.add("line3"));
        history
    }

    #[test]
    fn new() {
        let config = Config::default();
        let history = History::new(config);
        assert_eq!(config.max_history_size(), history.max_len);
        assert_eq!(0, history.entries.len());
    }

    #[test]
    fn add() {
        let config = Config::builder()
            .history_ignore_space(true)
            .build();
        let mut history = History::new(config);
        assert!(history.add("line1"));
        assert!(history.add("line2"));
        assert!(!history.add("line2"));
        assert!(!history.add(""));
        assert!(!history.add(" line3"));
    }

    #[test]
    fn set_max_len() {
        let mut history = init();
        history.set_max_len(1);
        assert_eq!(1, history.entries.len());
        assert_eq!(Some(&"line3".to_string()), history.last());
    }

    #[test]
    fn save() {
        let mut history = init();
        let td = tempdir::TempDir::new_in(&Path::new("."), "histo").unwrap();
        let history_path = td.path().join(".history");

        history.save(&history_path).unwrap();
        history.load(&history_path).unwrap();
        td.close().unwrap();
    }

    #[test]
    fn search() {
        let history = init();
        assert_eq!(None, history.search("", 0, Direction::Forward));
        assert_eq!(None, history.search("none", 0, Direction::Forward));
        assert_eq!(None, history.search("line", 3, Direction::Forward));

        assert_eq!(Some(0), history.search("line", 0, Direction::Forward));
        assert_eq!(Some(1), history.search("line", 1, Direction::Forward));
        assert_eq!(Some(2), history.search("line3", 1, Direction::Forward));
    }

    #[test]
    fn reverse_search() {
        let history = init();
        assert_eq!(None, history.search("", 2, Direction::Reverse));
        assert_eq!(None, history.search("none", 2, Direction::Reverse));
        assert_eq!(None, history.search("line", 3, Direction::Reverse));

        assert_eq!(Some(2), history.search("line", 2, Direction::Reverse));
        assert_eq!(Some(1), history.search("line", 1, Direction::Reverse));
        assert_eq!(Some(0), history.search("line1", 1, Direction::Reverse));
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(test)]

extern crate test;
extern crate rand;

const RAND_BENCH_N: u64 = 1000;

use std::mem::size_of;
#[cfg(rustc_1_28)]
use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128};
use test::Bencher;
use std::time::Duration;

use rand::{Rng, FromEntropy};
use rand::rngs::SmallRng;
use rand::distributions::*;

macro_rules! distr_int {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_nz_int {
    ($fnn:ident, $tynz:ty, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $tynz = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x.get());
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_float {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum += x;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_duration {
    ($fnn:ident, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = Duration::new(0, 0);
                for _ in 0..::RAND_BENCH_N {
                    let x: Duration = distr.sample(&mut rng);
                    accum = accum.checked_add(x).unwrap_or(Duration::new(u64::max_value(), 999_999_999));
                }
                accum
            });
            b.bytes = size_of::<Duration>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0u32;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x as u32);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_arr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0u32;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x[0] as u32);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

// uniform
distr_int!(distr_uniform_i8, i8, Uniform::new(20i8, 100));
distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000));
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000));
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123));
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789));

distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319));
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319));

const LARGE_SEC: u64 = u64::max_value() / 1000;

distr_duration!(distr_uniform_duration_largest,
    Uniform::new_inclusive(Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999))
);
distr_duration!(distr_uniform_duration_large,
    Uniform::new(Duration::new(0, 0), Duration::new(LARGE_SEC, 1_000_000_000 / 2))
);
distr_duration!(distr_uniform_duration_one,
    Uniform::new(Duration::new(0, 0), Duration::new(1, 0))
);
distr_duration!(distr_uniform_duration_variety,
    Uniform::new(Duration::new(10000, 423423), Duration::new(200000, 6969954))
);
distr_duration!(distr_uniform_duration_edge,
    Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1))
);

// standard
distr_int!(distr_standard_i8, i8, Standard);
distr_int!(distr_standard_i16, i16, Standard);
distr_int!(distr_standard_i32, i32, Standard);
distr_int!(distr_standard_i64, i64, Standard);
distr_int!(distr_standard_i128, i128, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz8, NonZeroU8, u8, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz16, NonZeroU16, u16, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz32, NonZeroU32, u32, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz64, NonZeroU64, u64, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz128, NonZeroU128, u128, Standard);

distr!(distr_standard_bool, bool, Standard);
distr!(distr_standard_alphanumeric, char, Alphanumeric);
distr!(distr_standard_codepoint, char, Standard);

distr_float!(distr_standard_f32, f32, Standard);
distr_float!(distr_standard_f64, f64, Standard);
distr_float!(distr_open01_f32, f32, Open01);
distr_float!(distr_open01_f64, f64, Open01);
distr_float!(distr_openclosed01_f32, f32, OpenClosed01);
distr_float!(distr_openclosed01_f64, f64, OpenClosed01);

// distributions
distr_float!(distr_exp, f64, Exp::new(1.23 * 4.56));
distr_float!(distr_normal, f64, Normal::new(-1.23, 4.56));
distr_float!(distr_log_normal, f64, LogNormal::new(-1.23, 4.56));
distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0));
distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0));
distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9));
distr_int!(distr_binomial, u64, Binomial::new(20, 0.7));
distr_int!(distr_poisson, u64, Poisson::new(4.0));
distr!(distr_bernoulli, bool, Bernoulli::new(0.18));
distr_arr!(distr_circle, [f64; 2], UnitCircle::new());
distr_arr!(distr_sphere_surface, [f64; 3], UnitSphereSurface::new());

// Weighted
distr_int!(distr_weighted_i8, usize, WeightedIndex::new(&[1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_u32, usize, WeightedIndex::new(&[1u32, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_f64, usize, WeightedIndex::new(&[1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap());
distr_int!(distr_weighted_large_set, usize, WeightedIndex::new((0..10000).rev().chain(1..10001)).unwrap());

// construct and sample from a range
macro_rules! gen_range_int {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut accum: $ty = 0;
                for _ in 0..::RAND_BENCH_N {
                    accum = accum.wrapping_add(rng.gen_range($low, high));
                    // force recalculation of range each time
                    high = high.wrapping_add(1) & std::$ty::MAX;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_int!(gen_range_i8, i8, -20i8, 100);
gen_range_int!(gen_range_i16, i16, -500i16, 2000);
gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000);
gen_range_int!(gen_range_i64, i64, 3i64, 123_456_789_123);
gen_range_int!(gen_range_i128, i128, -12345678901234i128, 123_456_789_123_456_789);

// construct and sample from a floating-point range
macro_rules! gen_range_float {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut low = $low;
                let mut accum: $ty = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    accum += rng.gen_range(low, high);
                    // force recalculation of range each time
                    low += 0.9;
                    high += 1.1;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_float!(gen_range_f32, f32, -20000.0f32, 100000.0);
gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12);

#[bench]
fn dist_iter(b: &mut Bencher) {
    let mut rng = SmallRng::from_entropy();
    let distr = Normal::new(-2.71828, 3.14159);
    let mut iter = distr.sample_iter(&mut rng);

    b.iter(|| {
        let mut accum = 0.0;
        for _ in 0..::RAND_BENCH_N {
            accum += iter.next().unwrap();
        }
        accum
    });
    b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
}
<commit_msg>Binomial: Add benchmark for small n * p<commit_after>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(test)]

extern crate test;
extern crate rand;

const RAND_BENCH_N: u64 = 1000;

use std::mem::size_of;
#[cfg(rustc_1_28)]
use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128};
use test::Bencher;
use std::time::Duration;

use rand::{Rng, FromEntropy};
use rand::rngs::SmallRng;
use rand::distributions::*;

macro_rules! distr_int {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_nz_int {
    ($fnn:ident, $tynz:ty, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $tynz = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x.get());
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_float {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum += x;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_duration {
    ($fnn:ident, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = Duration::new(0, 0);
                for _ in 0..::RAND_BENCH_N {
                    let x: Duration = distr.sample(&mut rng);
                    accum = accum.checked_add(x).unwrap_or(Duration::new(u64::max_value(), 999_999_999));
                }
                accum
            });
            b.bytes = size_of::<Duration>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0u32;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x as u32);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_arr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0u32;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x[0] as u32);
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

// uniform
distr_int!(distr_uniform_i8, i8, Uniform::new(20i8, 100));
distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000));
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000));
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123));
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789));

distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319));
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319));

const LARGE_SEC: u64 = u64::max_value() / 1000;

distr_duration!(distr_uniform_duration_largest,
    Uniform::new_inclusive(Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999))
);
distr_duration!(distr_uniform_duration_large,
    Uniform::new(Duration::new(0, 0), Duration::new(LARGE_SEC, 1_000_000_000 / 2))
);
distr_duration!(distr_uniform_duration_one,
    Uniform::new(Duration::new(0, 0), Duration::new(1, 0))
);
distr_duration!(distr_uniform_duration_variety,
    Uniform::new(Duration::new(10000, 423423), Duration::new(200000, 6969954))
);
distr_duration!(distr_uniform_duration_edge,
    Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1))
);

// standard
distr_int!(distr_standard_i8, i8, Standard);
distr_int!(distr_standard_i16, i16, Standard);
distr_int!(distr_standard_i32, i32, Standard);
distr_int!(distr_standard_i64, i64, Standard);
distr_int!(distr_standard_i128, i128, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz8, NonZeroU8, u8, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz16, NonZeroU16, u16, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz32, NonZeroU32, u32, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz64, NonZeroU64, u64, Standard);
#[cfg(rustc_1_28)] distr_nz_int!(distr_standard_nz128, NonZeroU128, u128, Standard);

distr!(distr_standard_bool, bool, Standard);
distr!(distr_standard_alphanumeric, char, Alphanumeric);
distr!(distr_standard_codepoint, char, Standard);

distr_float!(distr_standard_f32, f32, Standard);
distr_float!(distr_standard_f64, f64, Standard);
distr_float!(distr_open01_f32, f32, Open01);
distr_float!(distr_open01_f64, f64, Open01);
distr_float!(distr_openclosed01_f32, f32, OpenClosed01);
distr_float!(distr_openclosed01_f64, f64, OpenClosed01);

// distributions
distr_float!(distr_exp, f64, Exp::new(1.23 * 4.56));
distr_float!(distr_normal, f64, Normal::new(-1.23, 4.56));
distr_float!(distr_log_normal, f64, LogNormal::new(-1.23, 4.56));
distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0));
distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0));
distr_float!(distr_cauchy, f64, Cauchy::new(4.2, 6.9));
distr_int!(distr_binomial, u64, Binomial::new(20, 0.7));
distr_int!(distr_binomial_small, u64, Binomial::new(1000000, 1e-30));
distr_int!(distr_poisson, u64, Poisson::new(4.0));
distr!(distr_bernoulli, bool, Bernoulli::new(0.18));
distr_arr!(distr_circle, [f64; 2], UnitCircle::new());
distr_arr!(distr_sphere_surface, [f64; 3], UnitSphereSurface::new());

// Weighted
distr_int!(distr_weighted_i8, usize, WeightedIndex::new(&[1i8, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_u32, usize, WeightedIndex::new(&[1u32, 2, 3, 4, 12, 0, 2, 1]).unwrap());
distr_int!(distr_weighted_f64, usize, WeightedIndex::new(&[1.0f64, 0.001, 1.0/3.0, 4.01, 0.0, 3.3, 22.0, 0.001]).unwrap());
distr_int!(distr_weighted_large_set, usize, WeightedIndex::new((0..10000).rev().chain(1..10001)).unwrap());

// construct and sample from a range
macro_rules! gen_range_int {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut accum: $ty = 0;
                for _ in 0..::RAND_BENCH_N {
                    accum = accum.wrapping_add(rng.gen_range($low, high));
                    // force recalculation of range each time
                    high = high.wrapping_add(1) & std::$ty::MAX;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_int!(gen_range_i8, i8, -20i8, 100);
gen_range_int!(gen_range_i16, i16, -500i16, 2000);
gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000);
gen_range_int!(gen_range_i64, i64, 3i64, 123_456_789_123);
gen_range_int!(gen_range_i128, i128, -12345678901234i128, 123_456_789_123_456_789);

// construct and sample from a floating-point range
macro_rules! gen_range_float {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = SmallRng::from_entropy();

            b.iter(|| {
                let mut high = $high;
                let mut low = $low;
                let mut accum: $ty = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    accum += rng.gen_range(low, high);
                    // force recalculation of range each time
                    low += 0.9;
                    high += 1.1;
                }
                accum
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_float!(gen_range_f32, f32, -20000.0f32, 100000.0);
gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12);

#[bench]
fn dist_iter(b: &mut Bencher) {
    let mut rng = SmallRng::from_entropy();
    let distr = Normal::new(-2.71828, 3.14159);
    let mut iter = distr.sample_iter(&mut rng);

    b.iter(|| {
        let mut accum = 0.0;
        for _ in 0..::RAND_BENCH_N {
            accum += iter.next().unwrap();
        }
        accum
    });
    b.bytes = size_of::<f64>() as u64 * ::RAND_BENCH_N;
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use lazy_static::lazy_static;
use maplit::hashmap;
use specs::{Entities, ReadExpect, ReadStorage, System, WriteStorage};

use crate::combat::components::collision::{CollisionBox, Points};
use crate::combat::components::intent::{AttackType, DefendType};
use crate::combat::components::state::HitType;
use crate::combat::components::{
    Action, Body, Collided, Draw, Facing, Health, Position, State, UnitType, Velocity, Weapon,
};
use crate::combat::damage::DamageTables;
use crate::combat::systems::animation::action_to_animation;
use crate::files::collide::CollisionBoxes;
use crate::objects::TextureAtlas;
use crate::rect::{Interval, Point, Rect};
use crate::scenes::encounter::EncounterTextures;

pub struct EntityEntityCollision;

impl<'a> System<'a> for EntityEntityCollision {
    type SystemData = (
        ReadStorage<'a, Body>,
        ReadStorage<'a, Position>,
        WriteStorage<'a, Velocity>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (body_storage, position_storage, mut velocity_storage, entities): Self::SystemData,
    ) {
        use specs::Join;
        for (body, position_1, velocity, entity_1) in (
            &body_storage,
            &position_storage,
            &mut velocity_storage,
            &*entities,
        )
            .join()
        {
            for (other, position_2, _entity_2) in (&body_storage, &position_storage, &*entities)
                .join()
                .filter(|(.., entity_2)| entity_2.id() != entity_1.id())
            {
                if let (Some(body_rect), Some(other_rect)) = (body.rect, other.rect) {
                    let y_delta: i32 = position_1.y as i32 - position_2.y as i32;
                    let new_rect = body_rect + (velocity.x, velocity.y);
                    if y_delta.abs() <= 20 {
                        if new_rect.intersects(&other_rect) {
                            let other_x = Interval {
                                a: other_rect.x,
                                b: other_rect.x + other_rect.w as i32,
                            };
                            if velocity.x < 0 && other_x.contains_point(new_rect.x) {
                                velocity.x = 0;
                            } else if velocity.x > 0
                                && other_x.contains_point(new_rect.x + new_rect.w as i32)
                            {
                                velocity.x = 0;
                            }

                            let other_y = Interval {
                                a: other_rect.y,
                                b: other_rect.y + new_rect.h as i32,
                            };
                            if velocity.y < 0 && other_y.contains_point(new_rect.y) {
                                velocity.y = 0;
                            } else if velocity.y > 0
                                && other_y.contains_point(new_rect.y + new_rect.h as i32)
                            {
                                velocity.y = 0;
                            }
                        }
                    }

                    // let y_delta: i32 = position_1.y as i32 - position_2.y as i32;
                    // if y_delta.abs() <= 10 && new_y.intersects(&other_y) {
                    //     println!("{:?}", new_y);
                    //     velocity.y = 0;
                    // }
                }
            }
        }
    }
}

pub struct UpdateBoundingBoxes;

impl<'a> System<'a> for UpdateBoundingBoxes {
    type SystemData = (
        ReadExpect<'a, CollisionBoxes>,
        ReadExpect<'a, EncounterTextures>,
        ReadStorage<'a, Draw>,
        ReadStorage<'a, Position>,
        WriteStorage<'a, Body>,
        WriteStorage<'a, Weapon>,
    );

    fn run(
        &mut self,
        (
            collision_boxes,
            encounter_textures,
            draw,
            position,
            mut body,
            mut weapon,
        ): Self::SystemData,
    ) {
        use specs::Join;
        let collision_data = &collision_boxes.data;
        let textures = &encounter_textures.data;

        for (draw, position, body) in (&draw, &position, &mut body).join() {
            let mut body_boxes: Vec<CollisionBox> = vec![];
            for image in draw.frame.images.iter().filter(|i| i.is_collidee()) {
                if let Some(texture) = textures.get(&image.sheet) {
                    let rect = &texture.rects[image.image];
                    let mut image_global_x: i32 =
                        position.x as i32 + image.x * draw.direction as i32;
                    image_global_x = match draw.direction {
                        Facing::Left => image_global_x - rect.w as i32,
                        Facing::Right => image_global_x,
                    };
                    body_boxes.push(CollisionBox {
                        rect: Rect {
                            x: image_global_x,
                            y: position.y as i32 + image.y,
                            w: rect.w,
                            h: rect.h,
                        },
                        sheet: image.sheet.clone(),
                        image_num: image.image as u32,
                    })
                }
            }

            if body_boxes.is_empty() {
                body.collision_boxes = None;
            } else {
                let mut contains_all: Option<Rect> = None;
                for body_box in &body_boxes {
                    match contains_all {
                        Some(ref mut rect) => {
                            if body_box.rect.w > rect.w {
                                rect.w = body_box.rect.w
                            }
                            if body_box.rect.h > rect.h {
                                rect.h = body_box.rect.h
                            }
                            if body_box.rect.x < rect.x {
                                rect.x = body_box.rect.x
                            }
                            if body_box.rect.y < rect.x {
                                rect.y = body_box.rect.y
                            }
                        }
                        None => contains_all = Some(body_box.rect.clone()),
                    }
                }
                body.collision_boxes = Some(body_boxes);
                body.rect = contains_all;
            }
        }

        for (draw, position, weapon) in (&draw, &position, &mut weapon).join() {
            let mut weapon_boxes: Vec<Points> = vec![];
            for image in draw.frame.images.iter().filter(|i| i.is_collider()) {
                if let Some(Some(points)) = &collision_data
                    .get(&image.sheet)
                    .and_then(|v| v.points.get(image.image))
                {
                    let direction = draw.direction as i32;
                    let image_global_x: i32 = position.x as i32 + image.x * direction;
                    let image_global_y: i32 = position.y as i32 + image.y;
                    let rect_x = match draw.direction {
                        Facing::Left => image_global_x - points.max_x as i32,
                        Facing::Right => image_global_x,
                    };
                    weapon_boxes.push(Points {
                        bounding: Rect {
                            x: rect_x,
                            y: image_global_y,
                            w: points.max_x,
                            h: points.max_y,
                        },
                        points: points
                            .data
                            .iter()
                            .map(|p| Point {
                                x: image_global_x + p.x * direction,
                                y: image_global_y + p.y,
                            })
                            .collect(),
                    })
                }
            }

            if weapon_boxes.is_empty() {
                weapon.collision_points = None;
            } else {
                weapon.collision_points = Some(weapon_boxes);
            }
        }
    }
}

pub struct CheckCollisions;

impl<'a> System<'a> for CheckCollisions {
    type SystemData = (
        ReadExpect<'a, EncounterTextures>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, Body>,
        ReadStorage<'a, Weapon>,
        WriteStorage<'a, Collided>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (encounter_textures, position, bodies, weapons, mut collided, entities): Self::SystemData,
    ) {
        use specs::Join;
        let textures = &encounter_textures.data;
        // for (_state, att_pos, weapon, attacker) in (&state, &position, &weapons, &*entities)
        for (att_pos, weapon, attacker) in (&position, &weapons, &*entities).join()
        // .filter(|(state, ..)| state.action.is_attack())
        {
            for (defender, body, _defender_position) in (&*entities, &bodies, &position)
                .join()
                // ignore self for collision checking.
                .filter(|(defender, ..)| attacker.id() != defender.id())
                // ignore entities outside a 10 pixel y range.
                .filter(|(_, _, d_pos)| (att_pos.y as i32 - d_pos.y as i32).abs() < 10)
            {
                let hit = CheckCollisions::check_collision(&textures, weapon, body);
                if hit {
                    let _result = collided.insert(attacker, Collided { target: defender });
                }
            }
        }
    }
}

impl CheckCollisions {
    fn check_collision(
        textures: &HashMap<String, TextureAtlas>,
        weapon: &Weapon,
        body: &Body,
    ) -> bool {
        for weapon_part in weapon.collision_points.iter().flat_map(|v| v) {
            // each attacking image in the attacker
            for body_part in body
                .collision_boxes
                .iter() // Option.iter()
                .flat_map(|v| v)
                .filter(|b| weapon_part.bounding.intersects(&b.rect))
            {
                // check that against each defending image in the defender
                for point in weapon_part
                    .points
                    .iter()
                    .filter(|p| body_part.rect.contains_point(p))
                {
                    // check the points collide and the pixel in the image is a part of the target
                    let hit_x = point.x - body_part.rect.x;
                    let hit_y = point.y - body_part.rect.y;
                    let texture = textures
                        .get(&body_part.sheet)
                        .expect("Encounter hasn't loaded correct textures to as world resource");
                    let collision_lookup = hit_x as usize * texture.image.width + hit_y as usize;
                    let pixel = texture.image.pixels[collision_lookup];
                    if pixel > 0 {
                        return true;
                    }
                }
            }
        }
        false
    }
}

lazy_static! {
    static ref blocks_attack: HashMap<Action, Action> = hashmap! {
        Action::Attack(AttackType::Swing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::BackSwing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::Thrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::Chop) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::UpThrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::ThrowDagger) => Action::Defend(DefendType::Dodge),
    };
}

pub struct ResolveCollisions;

impl<'a> System<'a> for ResolveCollisions {
    type SystemData = (
        ReadExpect<'a, DamageTables>,
        ReadStorage<'a, UnitType>,
        WriteStorage<'a, Collided>,
        WriteStorage<'a, Health>,
        WriteStorage<'a, State>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (
            damage_tables,
            unit_type_storage,
            mut collided_storage,
            mut health_storage,
            mut state_storage,
            entities,
        ): Self::SystemData,
    ) {
        use specs::Join;

        for (unit_type, collided, entity) in
            (&unit_type_storage, collided_storage.drain(), &*entities).join()
        {
            let mut has_defended = false;
            let mut target_used_block = false;

            let target = &collided.target;
            {
                let state: Option<&State> = state_storage.get(entity);
                let target_state: Option<&State> = state_storage.get(*target);
                if let (Some(state), Some(target_state)) = (state, target_state) {
                    has_defended = blocks_attack
                        .get(&state.action)
                        .and_then(|a| Some(a == &target_state.action))
                        .expect(&format!(
                            "attack {:?} not in blocks_attack lookup",
                            &state.action
                        ));
                    target_used_block = target_state.action == Action::Defend(DefendType::Block);
                    if target_used_block && target_state.direction == state.direction {
                        has_defended = false;
                    }
                }
            }

            {
                if !has_defended {
                    // set defender animation
                    let target = &collided.target;
                    let state_action = state_storage
                        .get(entity)
                        .and_then(|s| Some(s.action.clone()));
                    let target_health: Option<&mut Health> = health_storage.get_mut(*target);
                    let target_state: Option<&mut State> = state_storage.get_mut(*target);
                    if let (Some(target_health), Some(target_state)) = (target_health, target_state)
                    {
                        let damage = match state_action {
                            Some(action) => calculate_damage(&damage_tables, unit_type, action),
                            None => 3, //TODO since daggers have no state
                        };
                        target_health.points -= damage as i32;
                        match target_state.action {
                            Action::Death | Action::Dead => (),
                            _ => {
                                target_state.action = Action::Hit(HitType::Sliced);
                                target_state.ticks = 0;
                            }
                        }
                    }
                }
            }
            {
                // set attacker state
                let state: Option<&mut State> = state_storage.get_mut(entity);
                if let Some(state) = state {
                    if !has_defended || target_used_block {
                        match state.action {
                            Action::Attack(AttackType::UpThrust) => (),
                            _ => {
                                state.action = Action::AttackRecovery;
                                state.ticks = 0;
                            }
                        }
                    }
                } else {
                    // entiites without state are deleted after a collision
                    // this is currently for thrown daggers, it would be better
                    // to make this explicit, perhaps add a DeleteOnCollision component.
                    println!("deleting entity {:?}", entity);
                    entities.delete(entity);
                }
            }
        }
    }
}

fn calculate_damage(
    damage_tables: &DamageTables,
    unit_type: &UnitType,
    attacker_action: Action,
) -> u32 {
    let mut raw_damage = *damage_tables
        .0
        .get(&unit_type.name)
        .and_then(|t| {
            action_to_animation
                .get(&attacker_action)
                .and_then(|name| t.get(name))
        })
        .expect("missing value from damage tables");

    if attacker_action == Action::Attack(AttackType::Chop) {
        raw_damage *= 2;
    }
    raw_damage
}
<commit_msg>updated recovery/hit animations after all collisions are processed.<commit_after>use std::collections::HashMap;

use lazy_static::lazy_static;
use maplit::hashmap;
use specs::world::Index;
use specs::{Entities, ReadExpect, ReadStorage, System, WriteStorage};

use crate::combat::components::collision::{CollisionBox, Points};
use crate::combat::components::intent::{AttackType, DefendType};
use crate::combat::components::state::HitType;
use crate::combat::components::{
    Action, Body, Collided, Draw, Facing, Health, Position, State, UnitType, Velocity, Weapon,
};
use crate::combat::damage::DamageTables;
use crate::combat::systems::animation::action_to_animation;
use crate::files::collide::CollisionBoxes;
use crate::objects::TextureAtlas;
use crate::rect::{Interval, Point, Rect};
use crate::scenes::encounter::EncounterTextures;

pub struct EntityEntityCollision;

impl<'a> System<'a> for EntityEntityCollision {
    type SystemData = (
        ReadStorage<'a, Body>,
        ReadStorage<'a, Position>,
        WriteStorage<'a, Velocity>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (body_storage, position_storage, mut velocity_storage, entities): Self::SystemData,
    ) {
        use specs::Join;
        for (body, position_1, velocity, entity_1) in (
            &body_storage,
            &position_storage,
            &mut velocity_storage,
            &*entities,
        )
            .join()
        {
            for (other, position_2, _entity_2) in (&body_storage, &position_storage, &*entities)
                .join()
                .filter(|(.., entity_2)| entity_2.id() != entity_1.id())
            {
                if let (Some(body_rect), Some(other_rect)) = (body.rect, other.rect) {
                    let y_delta: i32 = position_1.y as i32 - position_2.y as i32;
                    let new_rect = body_rect + (velocity.x, velocity.y);
                    if y_delta.abs() <= 20 {
                        if new_rect.intersects(&other_rect) {
                            let other_x = Interval {
                                a: other_rect.x,
                                b: other_rect.x + other_rect.w as i32,
                            };
                            if velocity.x < 0 && other_x.contains_point(new_rect.x) {
                                velocity.x = 0;
                            } else if velocity.x > 0
                                && other_x.contains_point(new_rect.x + new_rect.w as i32)
                            {
                                velocity.x = 0;
                            }

                            let other_y = Interval {
                                a: other_rect.y,
                                b: other_rect.y + new_rect.h as i32,
                            };
                            if velocity.y < 0 && other_y.contains_point(new_rect.y) {
                                velocity.y = 0;
                            } else if velocity.y > 0
                                && other_y.contains_point(new_rect.y + new_rect.h as i32)
                            {
                                velocity.y = 0;
                            }
                        }
                    }

                    // let y_delta: i32 = position_1.y as i32 - position_2.y as i32;
                    // if y_delta.abs() <= 10 && new_y.intersects(&other_y) {
                    //     println!("{:?}", new_y);
                    //     velocity.y = 0;
                    // }
                }
            }
        }
    }
}

pub struct UpdateBoundingBoxes;

impl<'a> System<'a> for UpdateBoundingBoxes {
    type SystemData = (
        ReadExpect<'a, CollisionBoxes>,
        ReadExpect<'a, EncounterTextures>,
        ReadStorage<'a, Draw>,
        ReadStorage<'a, Position>,
        WriteStorage<'a, Body>,
        WriteStorage<'a, Weapon>,
    );

    fn run(
        &mut self,
        (
            collision_boxes,
            encounter_textures,
            draw,
            position,
            mut body,
            mut weapon,
        ): Self::SystemData,
    ) {
        use specs::Join;
        let collision_data = &collision_boxes.data;
        let textures = &encounter_textures.data;

        for (draw, position, body) in (&draw, &position, &mut body).join() {
            let mut body_boxes: Vec<CollisionBox> = vec![];
            for image in draw.frame.images.iter().filter(|i| i.is_collidee()) {
                if let Some(texture) = textures.get(&image.sheet) {
                    let rect = &texture.rects[image.image];
                    let mut image_global_x: i32 =
                        position.x as i32 + image.x * draw.direction as i32;
                    image_global_x = match draw.direction {
                        Facing::Left => image_global_x - rect.w as i32,
                        Facing::Right => image_global_x,
                    };
                    body_boxes.push(CollisionBox {
                        rect: Rect {
                            x: image_global_x,
                            y: position.y as i32 + image.y,
                            w: rect.w,
                            h: rect.h,
                        },
                        sheet: image.sheet.clone(),
                        image_num: image.image as u32,
                    })
                }
            }

            if body_boxes.is_empty() {
                body.collision_boxes = None;
            } else {
                let mut contains_all: Option<Rect> = None;
                for body_box in &body_boxes {
                    match contains_all {
                        Some(ref mut rect) => {
                            if body_box.rect.w > rect.w {
                                rect.w = body_box.rect.w
                            }
                            if body_box.rect.h > rect.h {
                                rect.h = body_box.rect.h
                            }
                            if body_box.rect.x < rect.x {
                                rect.x = body_box.rect.x
                            }
                            if body_box.rect.y < rect.x {
                                rect.y = body_box.rect.y
                            }
                        }
                        None => contains_all = Some(body_box.rect.clone()),
                    }
                }
                body.collision_boxes = Some(body_boxes);
                body.rect = contains_all;
            }
        }

        for (draw, position, weapon) in (&draw, &position, &mut weapon).join() {
            let mut weapon_boxes: Vec<Points> = vec![];
            for image in draw.frame.images.iter().filter(|i| i.is_collider()) {
                if let Some(Some(points)) = &collision_data
                    .get(&image.sheet)
                    .and_then(|v| v.points.get(image.image))
                {
                    let direction = draw.direction as i32;
                    let image_global_x: i32 = position.x as i32 + image.x * direction;
                    let image_global_y: i32 = position.y as i32 + image.y;
                    let rect_x = match draw.direction {
                        Facing::Left => image_global_x - points.max_x as i32,
                        Facing::Right => image_global_x,
                    };
                    weapon_boxes.push(Points {
                        bounding: Rect {
                            x: rect_x,
                            y: image_global_y,
                            w: points.max_x,
                            h: points.max_y,
                        },
                        points: points
                            .data
                            .iter()
                            .map(|p| Point {
                                x: image_global_x + p.x * direction,
                                y: image_global_y + p.y,
                            })
                            .collect(),
                    })
                }
            }

            if weapon_boxes.is_empty() {
                weapon.collision_points = None;
            } else {
                weapon.collision_points = Some(weapon_boxes);
            }
        }
    }
}

pub struct CheckCollisions;

impl<'a> System<'a> for CheckCollisions {
    type SystemData = (
        ReadExpect<'a, EncounterTextures>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, Body>,
        ReadStorage<'a, Weapon>,
        WriteStorage<'a, Collided>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (encounter_textures, position, bodies, weapons, mut collided, entities): Self::SystemData,
    ) {
        use specs::Join;
        let textures = &encounter_textures.data;
        // for (_state, att_pos, weapon, attacker) in (&state, &position, &weapons, &*entities)
        for (att_pos, weapon, attacker) in (&position, &weapons, &*entities).join()
        // .filter(|(state, ..)| state.action.is_attack())
        {
            for (defender, body, _defender_position) in (&*entities, &bodies, &position)
                .join()
                // ignore self for collision checking.
                .filter(|(defender, ..)| attacker.id() != defender.id())
                // ignore entities outside a 10 pixel y range.
                .filter(|(_, _, d_pos)| (att_pos.y as i32 - d_pos.y as i32).abs() < 10)
            {
                let hit = CheckCollisions::check_collision(&textures, weapon, body);
                if hit {
                    let _result = collided.insert(attacker, Collided { target: defender });
                }
            }
        }
    }
}

impl CheckCollisions {
    fn check_collision(
        textures: &HashMap<String, TextureAtlas>,
        weapon: &Weapon,
        body: &Body,
    ) -> bool {
        for weapon_part in weapon.collision_points.iter().flat_map(|v| v) {
            // each attacking image in the attacker
            for body_part in body
                .collision_boxes
                .iter() // Option.iter()
                .flat_map(|v| v)
                .filter(|b| weapon_part.bounding.intersects(&b.rect))
            {
                // check that against each defending image in the defender
                for point in weapon_part
                    .points
                    .iter()
                    .filter(|p| body_part.rect.contains_point(p))
                {
                    // check the points collide and the pixel in the image is a part of the target
                    let hit_x = point.x - body_part.rect.x;
                    let hit_y = point.y - body_part.rect.y;
                    let texture = textures
                        .get(&body_part.sheet)
                        .expect("Encounter hasn't loaded correct textures to as world resource");
                    let collision_lookup = hit_x as usize * texture.image.width + hit_y as usize;
                    let pixel = texture.image.pixels[collision_lookup];
                    if pixel > 0 {
                        return true;
                    }
                }
            }
        }
        false
    }
}

lazy_static! {
    static ref blocks_attack: HashMap<Action, Action> = hashmap! {
        Action::Attack(AttackType::Swing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::BackSwing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::Thrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::Chop) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::UpThrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::ThrowDagger) => Action::Defend(DefendType::Dodge),
    };
}

pub struct ResolveCollisions;

impl<'a> System<'a> for ResolveCollisions {
    type SystemData = (
        ReadExpect<'a, DamageTables>,
        ReadStorage<'a, UnitType>,
        WriteStorage<'a, Collided>,
        WriteStorage<'a, Health>,
        WriteStorage<'a, State>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (
            damage_tables,
            unit_type_storage,
            mut collided_storage,
            mut health_storage,
            mut state_storage,
            entities,
        ): Self::SystemData,
    ) {
        use specs::Join;

        let mut set_recovery: Vec<Index> = Vec::new();
        let mut set_sliced: Vec<Index> = Vec::new();
        let mut set_chopped: Vec<Index> = Vec::new();

        for (unit_type, collided, entity) in
            (&unit_type_storage, collided_storage.drain(), &*entities).join()
        {
            let mut has_defended = false;
            let mut target_used_block = false;

            let target = &collided.target;
            {
                let state: Option<&State> = state_storage.get(entity);
                let target_state: Option<&State> = state_storage.get(*target);
                if let (Some(state), Some(target_state)) = (state, target_state) {
                    has_defended = blocks_attack
                        .get(&state.action)
                        .and_then(|a| Some(a == &target_state.action))
                        .expect(&format!(
                            "attack {:?} not in blocks_attack lookup",
                            &state.action
                        ));
                    target_used_block = target_state.action == Action::Defend(DefendType::Block);
                    if target_used_block && target_state.direction == state.direction {
                        has_defended = false;
                    }
                }
            }

            {
                if !has_defended {
                    // set defender animation
                    let target = &collided.target;
                    let state_action = state_storage
                        .get(entity)
                        .and_then(|s| Some(s.action.clone()));
                    let target_health: Option<&mut Health> = health_storage.get_mut(*target);
                    let target_state: Option<&mut State> = state_storage.get_mut(*target);
                    if let (Some(target_health), Some(target_state)) = (target_health, target_state)
                    {
                        let damage = match state_action {
                            Some(action) => calculate_damage(&damage_tables, unit_type, action),
                            None => 3, //TODO since daggers have no state
                        };
                        target_health.points -= damage as i32;
                        match target_state.action {
                            Action::Death | Action::Dead => (),
                            _ => {
                                set_sliced.push(target.id());
                                // target_state.action = Action::Hit(HitType::Sliced);
                                // target_state.ticks = 0;
                            }
                        }
                    }
                }
            }
            {
                // set attacker state
                let state: Option<&mut State> = state_storage.get_mut(entity);
                if let Some(state) = state {
                    if !has_defended || target_used_block {
                        match state.action {
                            Action::Attack(AttackType::UpThrust) => (),
                            _ => {
                                set_recovery.push(target.id());
                            }
                        }
                    }
                } else {
                    // entiites without state are deleted after a collision
                    // this is currently for thrown daggers, it would be better
                    // to make this explicit, perhaps add a DeleteOnCollision component.
                    println!("deleting entity {:?}", entity);
                    entities.delete(entity);
                }
            }
        }

        for i in set_recovery {
            let entity = entities.entity(i);
            let state: Option<&mut State> = state_storage.get_mut(entity);
            if let Some(state) = state {
                state.action = Action::AttackRecovery;
                state.ticks = 0;
            }
        }

        for i in set_sliced {
            let entity = entities.entity(i);
            let state: Option<&mut State> = state_storage.get_mut(entity);
            if let Some(state) = state {
                state.action = Action::Hit(HitType::Sliced);
                state.ticks = 0;
            }
        }

        for i in set_chopped {
            let entity = entities.entity(i);
            let state: Option<&mut State> = state_storage.get_mut(entity);
            if let Some(state) = state {
                state.action = Action::Hit(HitType::Chopped);
                state.ticks = 0;
            }
        }
    }
}

fn calculate_damage(
    damage_tables: &DamageTables,
    unit_type: &UnitType,
    attacker_action: Action,
) -> u32 {
    let mut raw_damage = *damage_tables
        .0
        .get(&unit_type.name)
        .and_then(|t| {
            action_to_animation
                .get(&attacker_action)
                .and_then(|name| t.get(name))
        })
        .expect(&format!(
            "missing value from damage tables {}",
            &unit_type.name
        ));

    if attacker_action == Action::Attack(AttackType::Chop) {
        raw_damage *= 2;
    }
    raw_damage
}
<|endoftext|>


<commit_before>use ladspa::Data;

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};

pub const BUFFER_SIZE: usize = 1024;
pub const BYTE_BUFFER_SIZE: usize = 8216; // data + data size + timestamp

#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct Packet {
    ldata: Vec<Data>,
    rdata: Vec<Data>,
    timestamp: u64,
}

impl Packet {
    pub fn parse(bytes: &[u8]) -> Packet {
        decode(bytes).unwrap()
    }

    pub fn new(ldata: &[Data], rdata: &[Data], time: u64) -> Packet {
        assert_eq!(ldata.len(), BUFFER_SIZE);
        assert_eq!(rdata.len(), BUFFER_SIZE);

        let mut packet = Packet {
            ldata: vec![0f32; BUFFER_SIZE],
            rdata: vec![0f32; BUFFER_SIZE],
            timestamp: time,
        };
        (&mut packet.ldata[..]).clone_from_slice(ldata);
        (&mut packet.rdata[..]).clone_from_slice(rdata);

        packet
    }

    pub fn as_bytes(&self) -> Vec<u8> {
        encode(self, SizeLimit::Infinite).unwrap()
    }

    pub fn get_ldata(&self) -> &[Data] {
        &self.ldata[..]
    }

    pub fn get_rdata(&self) -> &[Data] {
        &self.rdata[..]
    }

    pub fn read(&self, time: u64) -> (Data, Data) {
        if !self.active(time) {
            return (0_f32, 0_f32);
        }
        let position = (time - self.timestamp) as usize;
        let ldata = self.ldata[position];
        let rdata = self.rdata[position];
        (ldata, rdata)
    }

    pub fn active(&self, time: u64) -> bool {
        time >= self.timestamp && !self.complete(time)
    }

    pub fn complete(&self, time: u64) -> bool {
        time >= self.timestamp + BUFFER_SIZE as u64
    }
}

#[test]
fn test_packet_serialize() {
    let ldata = vec![1.0; BUFFER_SIZE];
    let rdata = vec![2.0; BUFFER_SIZE];
    let new = Packet::new(&ldata, &rdata, 0);
    assert_eq!(new.get_ldata(), ldata.as_slice());
    assert_eq!(new.get_rdata(), rdata.as_slice());
    let parsed = Packet::parse(&new.as_bytes()[..]);
    assert_eq!(parsed.get_ldata(), ldata.as_slice());
    assert_eq!(parsed.get_rdata(), rdata.as_slice());
    assert_eq!(&new.as_bytes()[..], &parsed.as_bytes()[..]);
    println!("{}", new.as_bytes().len());
}

#[test]
#[should_panic]
fn test_packet_wrong_size() {
    let ldata = vec![1.0; 5];
    let rdata = vec![2.0; 6];
    Packet::new(&ldata, &rdata, 0);
}

#[test]
fn test_packet_read() {
    let ldata = vec![1.0; BUFFER_SIZE];
    let rdata = vec![2.0; BUFFER_SIZE];
    let packet = Packet::new(&ldata, &rdata, 100);
    assert_eq!((0.0, 0.0), packet.read(0));
    assert_eq!((0.0, 0.0), packet.read(99));
    assert_eq!((1.0, 2.0), packet.read(100));
    assert_eq!((1.0, 2.0), packet.read(100 + BUFFER_SIZE as u64 - 1));
    assert_eq!((0.0, 0.0), packet.read(100 + BUFFER_SIZE as u64));
}

#[test]
fn test_packet_active_complete() {
    let ldata = vec![1.0; BUFFER_SIZE];
    let rdata = vec![2.0; BUFFER_SIZE];
    let packet = Packet::new(&ldata, &rdata, 100);
    assert!(!packet.active(0));
    assert!(!packet.complete(0));
    assert!(!packet.active(99));
    assert!(!packet.complete(99));
    assert!(packet.active(100));
    assert!(!packet.complete(100));
    assert!(packet.active(100 + BUFFER_SIZE as u64 - 1));
    assert!(!packet.complete(100 + BUFFER_SIZE as u64 - 1));
    assert!(!packet.active(100 + BUFFER_SIZE as u64));
    assert!(packet.complete(100 + BUFFER_SIZE as u64));
}
<commit_msg>Revert "revert, it didn't work"<commit_after>use ladspa::Data;

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};

pub const BUFFER_SIZE: usize = 1024;
pub const BYTE_BUFFER_SIZE: usize = BUFFER_SIZE * 4 * 2 + 8 * 2 + 8; // data + data size + timestamp

#[derive(RustcEncodable, RustcDecodable, Clone)]
pub struct Packet {
    ldata: Vec<Data>,
    rdata: Vec<Data>,
    timestamp: u64,
}

impl Packet {
    pub fn parse(bytes: &[u8]) -> Packet {
        decode(bytes).unwrap()
    }

    pub fn new(ldata: &[Data], rdata: &[Data], time: u64) -> Packet {
        assert_eq!(ldata.len(), BUFFER_SIZE);
        assert_eq!(rdata.len(), BUFFER_SIZE);

        let mut packet = Packet {
            ldata: vec![0f32; BUFFER_SIZE],
            rdata: vec![0f32; BUFFER_SIZE],
            timestamp: time,
        };
        (&mut packet.ldata[..]).clone_from_slice(ldata);
        (&mut packet.rdata[..]).clone_from_slice(rdata);

        packet
    }

    pub fn as_bytes(&self) -> Vec<u8> {
        encode(self, SizeLimit::Infinite).unwrap()
    }

    pub fn get_ldata(&self) -> &[Data] {
        &self.ldata[..]
    }

    pub fn get_rdata(&self) -> &[Data] {
        &self.rdata[..]
    }

    pub fn read(&self, time: u64) -> (Data, Data) {
        if !self.active(time) {
            return (0_f32, 0_f32);
        }
        let position = (time - self.timestamp) as usize;
        let ldata = self.ldata[position];
        let rdata = self.rdata[position];
        (ldata, rdata)
    }

    pub fn active(&self, time: u64) -> bool {
        time >= self.timestamp && !self.complete(time)
    }

    pub fn complete(&self, time: u64) -> bool {
        time >= self.timestamp + BUFFER_SIZE as u64
    }
}

#[test]
fn test_packet_serialize() {
    let ldata = vec![1.0; BUFFER_SIZE];
    let rdata = vec![2.0; BUFFER_SIZE];
    let new = Packet::new(&ldata, &rdata, 0);
    assert_eq!(new.get_ldata(), ldata.as_slice());
    assert_eq!(new.get_rdata(), rdata.as_slice());
    let parsed = Packet::parse(&new.as_bytes()[..]);
    assert_eq!(parsed.get_ldata(), ldata.as_slice());
    assert_eq!(parsed.get_rdata(), rdata.as_slice());
    assert_eq!(&new.as_bytes()[..], &parsed.as_bytes()[..]);
    println!("{}", new.as_bytes().len());
}

#[test]
#[should_panic]
fn test_packet_wrong_size() {
    let ldata = vec![1.0; 5];
    let rdata = vec![2.0; 6];
    Packet::new(&ldata, &rdata, 0);
}

#[test]
fn test_packet_read() {
    let ldata = vec![1.0; BUFFER_SIZE];
    let rdata = vec![2.0; BUFFER_SIZE];
    let packet = Packet::new(&ldata, &rdata, 100);
    assert_eq!((0.0, 0.0), packet.read(0));
    assert_eq!((0.0, 0.0), packet.read(99));
    assert_eq!((1.0, 2.0), packet.read(100));
    assert_eq!((1.0, 2.0), packet.read(100 + BUFFER_SIZE as u64 - 1));
    assert_eq!((0.0, 0.0), packet.read(100 + BUFFER_SIZE as u64));
}

#[test]
fn test_packet_active_complete() {
    let ldata = vec![1.0; BUFFER_SIZE];
    let rdata = vec![2.0; BUFFER_SIZE];
    let packet = Packet::new(&ldata, &rdata, 100);
    assert!(!packet.active(0));
    assert!(!packet.complete(0));
    assert!(!packet.active(99));
    assert!(!packet.complete(99));
    assert!(packet.active(100));
    assert!(!packet.complete(100));
    assert!(packet.active(100 + BUFFER_SIZE as u64 - 1));
    assert!(!packet.complete(100 + BUFFER_SIZE as u64 - 1));
    assert!(!packet.active(100 + BUFFER_SIZE as u64));
    assert!(packet.complete(100 + BUFFER_SIZE as u64));
}
<|endoftext|>


<commit_before>extern crate libgbc;

use std::io::{self, Read, Write};
use std::io::BufRead;

#[derive(Default)]
pub struct Cli {
    gbc: libgbc::Gbc,
}

impl Cli {
    pub fn new() -> Cli {
        Default::default()
    }

    pub fn run(&mut self, rom: &mut Read) -> bool {
        if let Err(e) = self.gbc.load_rom(rom) {
            println!("Failed to load rom: {}", e);
            return false;
        }

        let mut line = String::new();
        let stdin = io::stdin();

        loop {
            print_prompt();
            match stdin.lock().read_line(&mut line) {
                Err(e) => {
                    println!("{}", e);
                    return false;
                }
                Ok(0) => {
                    println!("");
                    return true;
                }
                _ => self.run_command(&line),
            }
        }
    }

    fn run_command(&mut self, line: &String) {
        let _ = line;
    }
}

fn print_prompt() {
    print!("> ");
    let _ = io::stdout().flush();
}
<commit_msg>Outline cli structure<commit_after>extern crate libgbc;

use std::collections::BTreeMap;
use std::io::{self, Read, Write};
use std::io::BufRead;

trait Cmd {
    fn help(&self) -> &'static str;
    fn name(&self) -> &'static str;
    fn run(&mut self, gbc: &mut libgbc::Gbc, cmd: &[&str]);
}

#[derive(Default)]
pub struct Cli {
    gbc: libgbc::Gbc,
    last_cmd: String,
    cmds: BTreeMap<String, Box<Cmd>>,
}

impl Cli {
    pub fn new() -> Cli {
        let mut val: Cli = Default::default();
        val.register_cmds();
        val
    }

    fn register_cmds(&mut self) {
        let help = Box::new(CmdMem {});
        self.cmds.insert(help.name().to_string(), help);
    }

    pub fn run(&mut self, rom: &mut Read) -> bool {
        if let Err(e) = self.gbc.load_rom(rom) {
            println!("Failed to load rom: {}", e);
            return false;
        }

        let mut line = String::new();
        let stdin = io::stdin();

        loop {
            print_prompt();
            line.clear();
            match stdin.lock().read_line(&mut line) {
                Err(e) => {
                    println!("{}", e);
                    return false;
                }
                Ok(0) => {
                    println!("");
                    return true;
                }
                _ => self.run_command(&line),
            }
        }
    }

    fn run_command(&mut self, line: &str) {
        // Repeat last command if command is empty
        let cmd_str = if line == "\n" {
            self.last_cmd.clone()
        } else {
            line.to_string()
        };
        let cmd: Vec<&str> = cmd_str.split_whitespace().collect();
        if cmd.len() == 0 {
            return;
        }

        if cmd[0] == "help" {
            self.print_help();
            return;
        }

        match self.cmds.get_mut(cmd[0]) {
            None => {
                println!("Unknown command: {}. Try running `help`", cmd[0]);
                return;
            }
            Some(cmd_handler) => cmd_handler.run(&mut self.gbc, &cmd[1..]),
        }

        self.last_cmd = line.to_string();
    }

    fn print_help(&self) {
        println!("Commands:\n");
        for (name, cmd) in &self.cmds {
            print!("{} {}", name, cmd.help());
        }
        println!("");
    }
}

fn print_prompt() {
    print!("> ");
    let _ = io::stdout().flush();
}

#[derive(Default)]
struct CmdMem {
}

impl Cmd for CmdMem {
    fn help(&self) -> &'static str {
        // TODO
        "cmd mem help"
    }
    fn name(&self) -> &'static str {
        "mem"
    }
    fn run(&mut self, gbc: &mut libgbc::Gbc, cmd: &[&str]) {
        // TODO
        let _ = gbc;
        let _ = cmd;
        println!("cmd mem");
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str, ptr};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

extern "C" {
    fn register_writer_tx(tx: *mut Sender<Vec<u8>>);
    fn register_stop_tx(tx: *mut Sender<()>);
}

#[no_mangle]
pub extern "C" fn testing_fn() -> c_int {
    78 as c_int
}

/// Connects to the provided address, (eg "123.123.123.123:3000") and registers
/// the on data received handler
#[no_mangle]
pub extern "C" fn connect(address: *const c_char,
    handler: extern fn(*const c_char, c_int),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    println!("Rust - connect");

    78 as c_int

    // let mut r_address;
    // unsafe {
    //     r_address = CStr::from_ptr(address);
    // }
    // let s_address = r_address.to_bytes();
    // let host_address = match str::from_utf8(s_address) {
    //     Ok(safe_str) => safe_str,
    //     Err(_) => {
    //         println!("Invalid host address");
    //         return -1 as c_int;
    //     }
    // };
    //
    // // Create and register a way to kill this client
    // let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    // let kill_tx = k_tx.clone();
    // let mut k_tx_ptr = Box::new(k_tx);
    // unsafe {
    //     register_stop_tx(&mut *k_tx_ptr);
    // }
    //
    // // Writer thread's channel
    // let (w_tx, w_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // let mut w_tx_ptr = Box::new(w_tx);
    // unsafe {
    //     register_writer_tx(&mut *w_tx_ptr);
    // }
    //
    // println!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     println!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // println!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    //
    // let r_client = client.clone();
    // let r_kill_tx = kill_tx.clone();
    //
    // let w_client = client.clone();
    // let w_kill_tx = kill_tx.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, handler, r_kill_tx)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(w_rx, w_client, w_kill_tx)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         println!("Error on kill channel: {}", e);
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();
    //
    // // Exit out in standard C fashion
    // 0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn send_to_writer(w_tx: *mut Sender<Vec<u8>>,
                                 buffer: *const c_char,
                                 count: c_int,
                                 k_tx: *mut Sender<()>) -> c_int {
    if count < 1 {
        println!("Error - count must be greater than zero");
        return -1 as c_int;
    }

    let num_elts = count as usize;
    let mut n_buffer = Vec::<u8>::with_capacity(num_elts);
    for x in 0..num_elts as isize {
        unsafe {
            n_buffer.push(ptr::read(buffer.offset(x)) as u8);
        }
    }

    unsafe {
        match (*w_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                println!("Error sending buffer: {}", e);
                let _ = (*k_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream,
                 event_handler: extern fn(*const c_char, c_int),
                 kill_tx: Sender<()>) {

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        event_handler(c_buffer.as_ptr(), buffer.len() as c_int);
                    }).unwrap();
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Reader thread finished");
    let _ = kill_tx.send(());
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream, kill_tx: Sender<()>) {
    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Writer thread finished");
    let _ = kill_tx.send(());
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str, ptr};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

extern "C" {
    fn register_writer_tx(tx: *mut Sender<Vec<u8>>);
    fn register_stop_tx(tx: *mut Sender<()>);
}

#[no_mangle]
pub extern "C" fn testing_fn() -> c_int {
    println!("Hello from Rust :)");
    78 as c_int
}

/// Connects to the provided address, (eg "123.123.123.123:3000") and registers
/// the on data received handler
#[no_mangle]
pub extern "C" fn connect(address: *const c_char,
    handler: extern fn(*const c_char, c_int),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    println!("Rust - connect");

    78 as c_int

    // let mut r_address;
    // unsafe {
    //     r_address = CStr::from_ptr(address);
    // }
    // let s_address = r_address.to_bytes();
    // let host_address = match str::from_utf8(s_address) {
    //     Ok(safe_str) => safe_str,
    //     Err(_) => {
    //         println!("Invalid host address");
    //         return -1 as c_int;
    //     }
    // };
    //
    // // Create and register a way to kill this client
    // let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    // let kill_tx = k_tx.clone();
    // let mut k_tx_ptr = Box::new(k_tx);
    // unsafe {
    //     register_stop_tx(&mut *k_tx_ptr);
    // }
    //
    // // Writer thread's channel
    // let (w_tx, w_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // let mut w_tx_ptr = Box::new(w_tx);
    // unsafe {
    //     register_writer_tx(&mut *w_tx_ptr);
    // }
    //
    // println!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     println!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // println!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    //
    // let r_client = client.clone();
    // let r_kill_tx = kill_tx.clone();
    //
    // let w_client = client.clone();
    // let w_kill_tx = kill_tx.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, handler, r_kill_tx)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(w_rx, w_client, w_kill_tx)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         println!("Error on kill channel: {}", e);
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();
    //
    // // Exit out in standard C fashion
    // 0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn send_to_writer(w_tx: *mut Sender<Vec<u8>>,
                                 buffer: *const c_char,
                                 count: c_int,
                                 k_tx: *mut Sender<()>) -> c_int {
    if count < 1 {
        println!("Error - count must be greater than zero");
        return -1 as c_int;
    }

    let num_elts = count as usize;
    let mut n_buffer = Vec::<u8>::with_capacity(num_elts);
    for x in 0..num_elts as isize {
        unsafe {
            n_buffer.push(ptr::read(buffer.offset(x)) as u8);
        }
    }

    unsafe {
        match (*w_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                println!("Error sending buffer: {}", e);
                let _ = (*k_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream,
                 event_handler: extern fn(*const c_char, c_int),
                 kill_tx: Sender<()>) {

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        event_handler(c_buffer.as_ptr(), buffer.len() as c_int);
                    }).unwrap();
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Reader thread finished");
    let _ = kill_tx.send(());
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream, kill_tx: Sender<()>) {
    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        };
    }
    println!("Writer thread finished");
    let _ = kill_tx.send(());
}
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub fn parse(parser: &mut Parser) -> Describe {
    parse_describe("sup", parser)
}

pub fn parse_describe(name: &str, parser: &mut Parser) -> Describe {
    let brace = token::CloseDelim(token::Brace);

    let mut before = vec![];
    let mut after = vec![];
    let mut blocks = vec![];

    loop {
        if parser.token == brace || parser.token == token::Eof {
            break
        }

        let span = parser.span;
        let ident = parser.parse_ident().unwrap();

        match ident.as_str() {
            "describe" | "context" => {
                let (name, _) = parser.parse_str().unwrap();
                parser.expect(&token::OpenDelim(token::Brace)).unwrap();
                let block = Block::Describe(parse_describe(&name, parser));
                parser.expect(&token::CloseDelim(token::Brace)).unwrap();
                blocks.push(block);
            },

            "it" | "test" => {
                let (name, _) = parser.parse_str().unwrap();
                let block = parse_block(parser);

                blocks.push(Block::It(It {
                    name: name.to_string(),
                    block: block
                }))
            },

            "bench" => {
                let (name, _) = parser.parse_str().unwrap();
                parser.expect(&token::BinOp(token::Or)).unwrap();
                let ident = parser.parse_ident().unwrap();
                parser.expect(&token::BinOp(token::Or)).unwrap();
                let block = parse_block(parser);

                blocks.push(Block::Bench(Bench {
                    name: name.to_string(),
                    ident: ident,
                    block: block
                }))
            },

            "before" => {
                before.push(parse_block(parser));
            },

            "after" => {
                after.push(parse_block(parser));
            },

            otherwise => {
                let message = format!("Expected `describe`, `context`, \
`before`, `after`, `it`, `test`, or `bench`, found `{}`", otherwise);
                panic!("{}", parser.span_fatal(span, &message))
            }
        }
    }

    Describe {
        name: name.to_string(),
        before: before,
        after: after,
        blocks: blocks
    }
}

fn parse_block(parser: &mut Parser) -> P<ast::Block> {
    let span = parser.span;
    let block = parser.parse_block().unwrap();
    if block.expr.is_some() {
        panic!("{}", parser.span_fatal(
            span,
            "last expression in this block must be terminated by `;`"))
    }
    block
}
<commit_msg>`parser::parse_describe` doesn't need to be public.<commit_after>use syntax::ast;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub fn parse(parser: &mut Parser) -> Describe {
    parse_describe("sup", parser)
}

fn parse_describe(name: &str, parser: &mut Parser) -> Describe {
    let brace = token::CloseDelim(token::Brace);

    let mut before = vec![];
    let mut after = vec![];
    let mut blocks = vec![];

    loop {
        if parser.token == brace || parser.token == token::Eof {
            break
        }

        let span = parser.span;
        let ident = parser.parse_ident().unwrap();

        match ident.as_str() {
            "describe" | "context" => {
                let (name, _) = parser.parse_str().unwrap();
                parser.expect(&token::OpenDelim(token::Brace)).unwrap();
                let block = Block::Describe(parse_describe(&name, parser));
                parser.expect(&token::CloseDelim(token::Brace)).unwrap();
                blocks.push(block);
            },

            "it" | "test" => {
                let (name, _) = parser.parse_str().unwrap();
                let block = parse_block(parser);

                blocks.push(Block::It(It {
                    name: name.to_string(),
                    block: block
                }))
            },

            "bench" => {
                let (name, _) = parser.parse_str().unwrap();
                parser.expect(&token::BinOp(token::Or)).unwrap();
                let ident = parser.parse_ident().unwrap();
                parser.expect(&token::BinOp(token::Or)).unwrap();
                let block = parse_block(parser);

                blocks.push(Block::Bench(Bench {
                    name: name.to_string(),
                    ident: ident,
                    block: block
                }))
            },

            "before" => {
                before.push(parse_block(parser));
            },

            "after" => {
                after.push(parse_block(parser));
            },

            otherwise => {
                let message = format!("Expected `describe`, `context`, \
`before`, `after`, `it`, `test`, or `bench`, found `{}`", otherwise);
                panic!("{}", parser.span_fatal(span, &message))
            }
        }
    }

    Describe {
        name: name.to_string(),
        before: before,
        after: after,
        blocks: blocks
    }
}

fn parse_block(parser: &mut Parser) -> P<ast::Block> {
    let span = parser.span;
    let block = parser.parse_block().unwrap();
    if block.expr.is_some() {
        panic!("{}", parser.span_fatal(
            span,
            "last expression in this block must be terminated by `;`"))
    }
    block
}
<|endoftext|>