<commit_before>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * 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/
*/

use std::collections::HashMap;
use std::ops::{Index, IndexMut};

use ::potentials::PairPotential;
use ::types::Vector3D;

use super::Particle;
use super::UnitCell;
use super::interactions::Interactions;

/// The Universe type hold all the data about a system. This data contains:
///   - an unit cell, containing the system;
///   - a list of particles in the system;
///   - a list of interactions, associating particles types and potentials
///   - a hash map associating particles names and particles types.
pub struct Universe {
    /// Unit cell of the universe
    cell: UnitCell,
    /// List of particles in the system
    particles: Vec<Particle>,
    /// Particles types, associating particles names and indexes
    types: HashMap<String, usize>,
    /// Interactions is a hash map associating particles types and potentials
    interactions: Interactions,
}

impl Universe {
    /// Create a new empty Universe
    pub fn new() -> Universe {
        Universe{
            particles: Vec::new(),
            types: HashMap::new(),
            interactions: Interactions::new(),
            cell: UnitCell::new(),
        }
    }

    /// Create an empty universe with a specific UnitCell
    pub fn from_cell(cell: UnitCell) -> Universe {
        let mut universe = Universe::new();
        universe.set_cell(cell);
        return universe;
    }

    /// Get the universe unit cell
    pub fn cell<'a>(&'a self) -> &'a UnitCell {&self.cell}
    /// Set the universe unit cell
    pub fn set_cell(&mut self, cell: UnitCell) {self.cell = cell;}

    /// Insert a particle at the end of the internal list
    pub fn add_particle(&mut self, p: Particle) {self.particles.push(p);}
    /// Get the number of particles in this universe
    pub fn size(&self) -> usize {self.particles.len()}

    /// Get the list of pair interaction betweent the atom i and the atom j
    pub fn pairs<'a>(&'a self, i: usize, j: usize) -> &'a Vec<Box<PairPotential>> {
        let itype = self.types[self.particles[i].name()];
        let jtype = self.types[self.particles[j].name()];
        &self.interactions.pairs[&(itype, jtype)]
    }

    /// Add an interaction between the particles with names `names`
    pub fn add_pair_interaction<T>(&mut self, i: &str, j: &str, pot: T) where T: PairPotential + 'static {
        let itype = self.get_type(i);
        let jtype = self.get_type(j);

        if !self.interactions.pairs.contains_key(&(itype, jtype)) {
            self.interactions.pairs.insert((itype, jtype), Vec::new());
        }
        let pairs = self.interactions.pairs.get_mut(&(itype, jtype)).unwrap();
        pairs.push(Box::new(pot));
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn distance(&self, i: usize, j:usize) -> f64 {
        self.cell.distance(self.particles[i].position(), self.particles[j].position())
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn wrap_vector(&self, i: usize, j:usize) -> Vector3D {
        let mut res = *self.particles[i].position() - *self.particles[j].position();
        self.cell.wrap_vector(&mut res);
        return res;
    }

    /// Get or create the usize type for the particle `name`
    fn get_type(&mut self, name: &str) -> usize {
        if self.types.contains_key(name) {
            self.types[name]
        } else {
            let index = self.types.len();
            self.types.insert(name.to_string(), index);
            return index;
        }
    }
}

impl Index<usize> for Universe {
    type Output = Particle;
    fn index<'a>(&'a self, index: usize) -> &'a Particle {
        &self.particles[index]
    }
}

impl IndexMut<usize> for Universe {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Particle {
        &mut self.particles[index]
    }
}

#[cfg(test)]
mod tests {
    use ::universe::*;
    use ::types::*;
    use ::potentials::*;

    #[test]
    fn particles() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));
        universe.add_particle(Particle::new("H"));

        assert_eq!(universe.size(), 3);
        assert_eq!(universe[0].name(), "O");
        assert_eq!(universe[1].name(), "H");
        assert_eq!(universe[2].name(), "H");
    }

    #[test]
    fn distances() {
        let mut universe = Universe::from_cell(UnitCell::cubic(5.0));
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));

        universe[0].set_position(Vector3D::new(9.0, 0.0, 0.0));
        universe[1].set_position(Vector3D::new(0.0, 0.0, 0.0));
        assert_eq!(universe.distance(0, 1), 1.0);

        universe.set_cell(UnitCell::new());
        assert_eq!(universe.distance(0, 1), 9.0);
    }

    #[test]
    fn pairs() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("He"));

        universe.add_pair_interaction("He", "He", LennardJones{sigma: 0.3, epsilon: 2.0});
        universe.add_pair_interaction("He", "He", Harmonic{k: 100.0, r0: 1.1});

        assert_eq!(universe.pairs(0, 0).len(), 2);
    }
}
<commit_msg>Use the Particle::kind to remove a lookup in Universe::pairs<commit_after>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * 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/
*/

use std::collections::HashMap;
use std::ops::{Index, IndexMut};

use ::potentials::PairPotential;
use ::types::Vector3D;

use super::Particle;
use super::UnitCell;
use super::interactions::Interactions;

/// The Universe type hold all the data about a system. This data contains:
///   - an unit cell, containing the system;
///   - a list of particles in the system;
///   - a list of interactions, associating particles kinds and potentials
///   - a hash map associating particles names and particles kinds.
pub struct Universe {
    /// Unit cell of the universe
    cell: UnitCell,
    /// List of particles in the system
    particles: Vec<Particle>,
    /// Particles kinds, associating particles names and indexes
    kinds: HashMap<String, usize>,
    /// Interactions is a hash map associating particles kinds and potentials
    interactions: Interactions,
}

impl Universe {
    /// Create a new empty Universe
    pub fn new() -> Universe {
        Universe{
            particles: Vec::new(),
            kinds: HashMap::new(),
            interactions: Interactions::new(),
            cell: UnitCell::new(),
        }
    }

    /// Create an empty universe with a specific UnitCell
    pub fn from_cell(cell: UnitCell) -> Universe {
        let mut universe = Universe::new();
        universe.set_cell(cell);
        return universe;
    }

    /// Get the universe unit cell
    pub fn cell<'a>(&'a self) -> &'a UnitCell {&self.cell}
    /// Set the universe unit cell
    pub fn set_cell(&mut self, cell: UnitCell) {self.cell = cell;}

    /// Insert a particle at the end of the internal list
    pub fn add_particle(&mut self, p: Particle) {
        let mut part = p;
        if part.kind() == usize::max_value() {
            // If no value have been precised, set one from the internal list
            // of particles kinds.
            let kind = self.get_kind(part.name());
            part.set_kind(kind);
        }
        self.particles.push(part);
    }
    /// Get the number of particles in this universe
    pub fn size(&self) -> usize {self.particles.len()}

    /// Get the list of pair interaction between the atom i and the atom j
    pub fn pairs<'a>(&'a self, i: usize, j: usize) -> &'a Vec<Box<PairPotential>> {
        let ikind = self.particles[i].kind();
        let jkind = self.particles[j].kind();
        &self.interactions.pairs[&(ikind, jkind)]
    }

    /// Add an interaction between the particles with names `names`
    pub fn add_pair_interaction<T>(&mut self, i: &str, j: &str, pot: T)
    where T: PairPotential + 'static {
        let ikind = self.get_kind(i);
        let jkind = self.get_kind(j);

        if !self.interactions.pairs.contains_key(&(ikind, jkind)) {
            self.interactions.pairs.insert((ikind, jkind), Vec::new());
        }
        let pairs = self.interactions.pairs.get_mut(&(ikind, jkind)).unwrap();
        pairs.push(Box::new(pot));
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn distance(&self, i: usize, j:usize) -> f64 {
        self.cell.distance(self.particles[i].position(), self.particles[j].position())
    }

    /// Get the distance between the particles at indexes `i` and `j`
    pub fn wrap_vector(&self, i: usize, j:usize) -> Vector3D {
        let mut res = *self.particles[i].position() - *self.particles[j].position();
        self.cell.wrap_vector(&mut res);
        return res;
    }

    /// Get or create the usize kind index for the name `name` of a particle
    fn get_kind(&mut self, name: &str) -> usize {
        if self.kinds.contains_key(name) {
            self.kinds[name]
        } else {
            let index = self.kinds.len();
            self.kinds.insert(name.to_string(), index);
            return index;
        }
    }
}

impl Index<usize> for Universe {
    type Output = Particle;
    fn index<'a>(&'a self, index: usize) -> &'a Particle {
        &self.particles[index]
    }
}

impl IndexMut<usize> for Universe {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Particle {
        &mut self.particles[index]
    }
}

#[cfg(test)]
mod tests {
    use ::universe::*;
    use ::types::*;
    use ::potentials::*;

    #[test]
    fn particles() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));
        universe.add_particle(Particle::new("H"));

        assert_eq!(universe.size(), 3);
        assert_eq!(universe[0].name(), "O");
        assert_eq!(universe[1].name(), "H");
        assert_eq!(universe[2].name(), "H");
    }

    #[test]
    fn distances() {
        let mut universe = Universe::from_cell(UnitCell::cubic(5.0));
        universe.add_particle(Particle::new("O"));
        universe.add_particle(Particle::new("H"));

        universe[0].set_position(Vector3D::new(9.0, 0.0, 0.0));
        universe[1].set_position(Vector3D::new(0.0, 0.0, 0.0));
        assert_eq!(universe.distance(0, 1), 1.0);

        universe.set_cell(UnitCell::new());
        assert_eq!(universe.distance(0, 1), 9.0);
    }

    #[test]
    fn pairs() {
        let mut universe = Universe::new();
        universe.add_particle(Particle::new("He"));

        universe.add_pair_interaction("He", "He", LennardJones{sigma: 0.3, epsilon: 2.0});
        universe.add_pair_interaction("He", "He", Harmonic{k: 100.0, r0: 1.1});

        assert_eq!(universe.pairs(0, 0).len(), 2);
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Akira Hayakawa <ruby.wktk@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) PREFIXaa

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::char;
use std::fs::{File, OpenOptions};
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;

static NAME: &str = "split";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args.collect_str();

    let mut opts = getopts::Options::new();

    opts.optopt(
        "a",
        "suffix-length",
        "use suffixes of length N (default 2)",
        "N",
    );
    opts.optopt("b", "bytes", "put SIZE bytes per output file", "SIZE");
    opts.optopt(
        "C",
        "line-bytes",
        "put at most SIZE bytes of lines per output file",
        "SIZE",
    );
    opts.optflag(
        "d",
        "numeric-suffixes",
        "use numeric suffixes instead of alphabetic",
    );
    opts.optopt("l", "lines", "put NUMBER lines per output file", "NUMBER");
    opts.optflag(
        "",
        "verbose",
        "print a diagnostic just before each output file is opened",
    );
    opts.optflag("h", "help", "display help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f),
    };

    if matches.opt_present("h") {
        let msg = format!(
            "{0} {1}

Usage:
  {0} [OPTION]... [INPUT [PREFIX]]

Output fixed-size pieces of INPUT to PREFIXaa, PREFIX ab, ...; default
size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is
-, read standard input.",
            NAME, VERSION
        );

        println!(
            "{}\nSIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.",
            opts.usage(&msg)
        );
        return 0;
    }

    if matches.opt_present("V") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let mut settings = Settings {
        prefix: "".to_owned(),
        numeric_suffix: false,
        suffix_length: 0,
        input: "".to_owned(),
        strategy: "".to_owned(),
        strategy_param: "".to_owned(),
        verbose: false,
    };

    settings.numeric_suffix = matches.opt_present("d");

    settings.suffix_length = match matches.opt_str("a") {
        Some(n) => match n.parse() {
            Ok(m) => m,
            Err(e) => crash!(1, "cannot parse num: {}", e),
        },
        None => 2,
    };

    settings.verbose = matches.opt_present("verbose");

    settings.strategy = "l".to_owned();
    settings.strategy_param = "1000".to_owned();
    let strategies = vec!["b", "C", "l"];
    for e in &strategies {
        if let Some(a) = matches.opt_str(*e) {
            if settings.strategy == "l" {
                settings.strategy = (*e).to_owned();
                settings.strategy_param = a;
            } else {
                crash!(1, "{}: cannot split in more than one way", NAME)
            }
        }
    }

    let mut v = matches.free.iter();
    let (input, prefix) = match (v.next(), v.next()) {
        (Some(a), None) => (a.to_owned(), "x".to_owned()),
        (Some(a), Some(b)) => (a.clone(), b.clone()),
        (None, _) => ("-".to_owned(), "x".to_owned()),
    };
    settings.input = input;
    settings.prefix = prefix;

    split(&settings)
}

struct Settings {
    prefix: String,
    numeric_suffix: bool,
    suffix_length: usize,
    input: String,
    strategy: String,
    strategy_param: String,
    verbose: bool,
}

struct SplitControl {
    current_line: String,   // Don't touch
    request_new_file: bool, // Splitter implementation requests new file
}

trait Splitter {
    // Consume the current_line and return the consumed string
    fn consume(&mut self, _: &mut SplitControl) -> String;
}

struct LineSplitter {
    saved_lines_to_write: usize,
    lines_to_write: usize,
}

impl LineSplitter {
    fn new(settings: &Settings) -> LineSplitter {
        let n = match settings.strategy_param.parse() {
            Ok(a) => a,
            Err(e) => crash!(1, "invalid number of lines: {}", e),
        };
        LineSplitter {
            saved_lines_to_write: n,
            lines_to_write: n,
        }
    }
}

impl Splitter for LineSplitter {
    fn consume(&mut self, control: &mut SplitControl) -> String {
        self.lines_to_write -= 1;
        if self.lines_to_write == 0 {
            self.lines_to_write = self.saved_lines_to_write;
            control.request_new_file = true;
        }
        control.current_line.clone()
    }
}

struct ByteSplitter {
    saved_bytes_to_write: usize,
    bytes_to_write: usize,
    break_on_line_end: bool,
    require_whole_line: bool,
}

impl ByteSplitter {
    fn new(settings: &Settings) -> ByteSplitter {
        let mut strategy_param: Vec<char> = settings.strategy_param.chars().collect();
        let suffix = strategy_param.pop().unwrap();
        let multiplier = match suffix {
            '0'..='9' => 1usize,
            'b' => 512usize,
            'k' => 1024usize,
            'm' => 1024usize * 1024usize,
            _ => crash!(1, "invalid number of bytes"),
        };
        let n = if suffix.is_alphabetic() {
            match strategy_param
                .iter()
                .cloned()
                .collect::<String>()
                .parse::<usize>()
            {
                Ok(a) => a,
                Err(e) => crash!(1, "invalid number of bytes: {}", e),
            }
        } else {
            match settings.strategy_param.parse::<usize>() {
                Ok(a) => a,
                Err(e) => crash!(1, "invalid number of bytes: {}", e),
            }
        };
        ByteSplitter {
            saved_bytes_to_write: n * multiplier,
            bytes_to_write: n * multiplier,
            break_on_line_end: settings.strategy == "b",
            require_whole_line: false,
        }
    }
}

impl Splitter for ByteSplitter {
    fn consume(&mut self, control: &mut SplitControl) -> String {
        let line = control.current_line.clone();
        let n = std::cmp::min(line.chars().count(), self.bytes_to_write);
        if self.require_whole_line && n < line.chars().count() {
            self.bytes_to_write = self.saved_bytes_to_write;
            control.request_new_file = true;
            self.require_whole_line = false;
            return line[0..0].to_owned();
        }
        self.bytes_to_write -= n;
        if n == 0 {
            self.bytes_to_write = self.saved_bytes_to_write;
            control.request_new_file = true;
        }
        if self.break_on_line_end && n == line.chars().count() {
            self.require_whole_line = self.break_on_line_end;
        }
        line[..n].to_owned()
    }
}

// (1, 3) -> "aab"
#[allow(clippy::many_single_char_names)]
fn str_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 26usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_u32((r as u32) + 97).unwrap());
    }
    c
}

// (1, 3) -> "001"
#[allow(clippy::many_single_char_names)]
fn num_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 10usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_digit(r as u32, 10).unwrap());
    }
    c
}

fn split(settings: &Settings) -> i32 {
    let mut reader = BufReader::new(if settings.input == "-" {
        Box::new(stdin()) as Box<dyn Read>
    } else {
        let r = match File::open(Path::new(&settings.input)) {
            Ok(a) => a,
            Err(_) => crash!(
                1,
                "cannot open '{}' for reading: No such file or directory",
                settings.input
            ),
        };
        Box::new(r) as Box<dyn Read>
    });

    let mut splitter: Box<dyn Splitter> = match settings.strategy.as_ref() {
        "l" => Box::new(LineSplitter::new(settings)),
        "b" | "C" => Box::new(ByteSplitter::new(settings)),
        a => crash!(1, "strategy {} not supported", a),
    };

    let mut control = SplitControl {
        current_line: "".to_owned(), // Request new line
        request_new_file: true,      // Request new file
    };

    let mut writer = BufWriter::new(Box::new(stdout()) as Box<dyn Write>);
    let mut fileno = 0;
    loop {
        if control.current_line.chars().count() == 0 {
            match reader.read_line(&mut control.current_line) {
                Ok(0) | Err(_) => break,
                _ => {}
            }
        }

        if control.request_new_file {
            let mut filename = settings.prefix.clone();
            filename.push_str(
                if settings.numeric_suffix {
                    num_prefix(fileno, settings.suffix_length)
                } else {
                    str_prefix(fileno, settings.suffix_length)
                }
                .as_ref(),
            );

            if fileno != 0 {
                crash_if_err!(1, writer.flush());
            }
            fileno += 1;
            writer = BufWriter::new(Box::new(
                OpenOptions::new()
                    .write(true)
                    .create(true)
                    .open(Path::new(&filename))
                    .unwrap(),
            ) as Box<dyn Write>);
            control.request_new_file = false;
            if settings.verbose {
                println!("creating file '{}'", filename);
            }
        }

        let consumed = splitter.consume(&mut control);
        crash_if_err!(1, writer.write_all(consumed.as_bytes()));

        let advance = consumed.chars().count();
        let clone = control.current_line.clone();
        let sl = clone;
        control.current_line = sl[advance..sl.chars().count()].to_owned();
    }
    0
}
<commit_msg>refactor/polosh ~ fix `cargo clippy` complaint (reversed_empty_ranges)<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Akira Hayakawa <ruby.wktk@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) PREFIXaa

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::char;
use std::fs::{File, OpenOptions};
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
use std::path::Path;

static NAME: &str = "split";
static VERSION: &str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args.collect_str();

    let mut opts = getopts::Options::new();

    opts.optopt(
        "a",
        "suffix-length",
        "use suffixes of length N (default 2)",
        "N",
    );
    opts.optopt("b", "bytes", "put SIZE bytes per output file", "SIZE");
    opts.optopt(
        "C",
        "line-bytes",
        "put at most SIZE bytes of lines per output file",
        "SIZE",
    );
    opts.optflag(
        "d",
        "numeric-suffixes",
        "use numeric suffixes instead of alphabetic",
    );
    opts.optopt("l", "lines", "put NUMBER lines per output file", "NUMBER");
    opts.optflag(
        "",
        "verbose",
        "print a diagnostic just before each output file is opened",
    );
    opts.optflag("h", "help", "display help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "{}", f),
    };

    if matches.opt_present("h") {
        let msg = format!(
            "{0} {1}

Usage:
  {0} [OPTION]... [INPUT [PREFIX]]

Output fixed-size pieces of INPUT to PREFIXaa, PREFIX ab, ...; default
size is 1000, and default PREFIX is 'x'. With no INPUT, or when INPUT is
-, read standard input.",
            NAME, VERSION
        );

        println!(
            "{}\nSIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.",
            opts.usage(&msg)
        );
        return 0;
    }

    if matches.opt_present("V") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let mut settings = Settings {
        prefix: "".to_owned(),
        numeric_suffix: false,
        suffix_length: 0,
        input: "".to_owned(),
        strategy: "".to_owned(),
        strategy_param: "".to_owned(),
        verbose: false,
    };

    settings.numeric_suffix = matches.opt_present("d");

    settings.suffix_length = match matches.opt_str("a") {
        Some(n) => match n.parse() {
            Ok(m) => m,
            Err(e) => crash!(1, "cannot parse num: {}", e),
        },
        None => 2,
    };

    settings.verbose = matches.opt_present("verbose");

    settings.strategy = "l".to_owned();
    settings.strategy_param = "1000".to_owned();
    let strategies = vec!["b", "C", "l"];
    for e in &strategies {
        if let Some(a) = matches.opt_str(*e) {
            if settings.strategy == "l" {
                settings.strategy = (*e).to_owned();
                settings.strategy_param = a;
            } else {
                crash!(1, "{}: cannot split in more than one way", NAME)
            }
        }
    }

    let mut v = matches.free.iter();
    let (input, prefix) = match (v.next(), v.next()) {
        (Some(a), None) => (a.to_owned(), "x".to_owned()),
        (Some(a), Some(b)) => (a.clone(), b.clone()),
        (None, _) => ("-".to_owned(), "x".to_owned()),
    };
    settings.input = input;
    settings.prefix = prefix;

    split(&settings)
}

struct Settings {
    prefix: String,
    numeric_suffix: bool,
    suffix_length: usize,
    input: String,
    strategy: String,
    strategy_param: String,
    verbose: bool,
}

struct SplitControl {
    current_line: String,   // Don't touch
    request_new_file: bool, // Splitter implementation requests new file
}

trait Splitter {
    // Consume the current_line and return the consumed string
    fn consume(&mut self, _: &mut SplitControl) -> String;
}

struct LineSplitter {
    saved_lines_to_write: usize,
    lines_to_write: usize,
}

impl LineSplitter {
    fn new(settings: &Settings) -> LineSplitter {
        let n = match settings.strategy_param.parse() {
            Ok(a) => a,
            Err(e) => crash!(1, "invalid number of lines: {}", e),
        };
        LineSplitter {
            saved_lines_to_write: n,
            lines_to_write: n,
        }
    }
}

impl Splitter for LineSplitter {
    fn consume(&mut self, control: &mut SplitControl) -> String {
        self.lines_to_write -= 1;
        if self.lines_to_write == 0 {
            self.lines_to_write = self.saved_lines_to_write;
            control.request_new_file = true;
        }
        control.current_line.clone()
    }
}

struct ByteSplitter {
    saved_bytes_to_write: usize,
    bytes_to_write: usize,
    break_on_line_end: bool,
    require_whole_line: bool,
}

impl ByteSplitter {
    fn new(settings: &Settings) -> ByteSplitter {
        let mut strategy_param: Vec<char> = settings.strategy_param.chars().collect();
        let suffix = strategy_param.pop().unwrap();
        let multiplier = match suffix {
            '0'..='9' => 1usize,
            'b' => 512usize,
            'k' => 1024usize,
            'm' => 1024usize * 1024usize,
            _ => crash!(1, "invalid number of bytes"),
        };
        let n = if suffix.is_alphabetic() {
            match strategy_param
                .iter()
                .cloned()
                .collect::<String>()
                .parse::<usize>()
            {
                Ok(a) => a,
                Err(e) => crash!(1, "invalid number of bytes: {}", e),
            }
        } else {
            match settings.strategy_param.parse::<usize>() {
                Ok(a) => a,
                Err(e) => crash!(1, "invalid number of bytes: {}", e),
            }
        };
        ByteSplitter {
            saved_bytes_to_write: n * multiplier,
            bytes_to_write: n * multiplier,
            break_on_line_end: settings.strategy == "b",
            require_whole_line: false,
        }
    }
}

impl Splitter for ByteSplitter {
    fn consume(&mut self, control: &mut SplitControl) -> String {
        let line = control.current_line.clone();
        let n = std::cmp::min(line.chars().count(), self.bytes_to_write);
        if self.require_whole_line && n < line.chars().count() {
            self.bytes_to_write = self.saved_bytes_to_write;
            control.request_new_file = true;
            self.require_whole_line = false;
            return "".to_owned();
        }
        self.bytes_to_write -= n;
        if n == 0 {
            self.bytes_to_write = self.saved_bytes_to_write;
            control.request_new_file = true;
        }
        if self.break_on_line_end && n == line.chars().count() {
            self.require_whole_line = self.break_on_line_end;
        }
        line[..n].to_owned()
    }
}

// (1, 3) -> "aab"
#[allow(clippy::many_single_char_names)]
fn str_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 26usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_u32((r as u32) + 97).unwrap());
    }
    c
}

// (1, 3) -> "001"
#[allow(clippy::many_single_char_names)]
fn num_prefix(i: usize, width: usize) -> String {
    let mut c = "".to_owned();
    let mut n = i;
    let mut w = width;
    while w > 0 {
        w -= 1;
        let div = 10usize.pow(w as u32);
        let r = n / div;
        n -= r * div;
        c.push(char::from_digit(r as u32, 10).unwrap());
    }
    c
}

fn split(settings: &Settings) -> i32 {
    let mut reader = BufReader::new(if settings.input == "-" {
        Box::new(stdin()) as Box<dyn Read>
    } else {
        let r = match File::open(Path::new(&settings.input)) {
            Ok(a) => a,
            Err(_) => crash!(
                1,
                "cannot open '{}' for reading: No such file or directory",
                settings.input
            ),
        };
        Box::new(r) as Box<dyn Read>
    });

    let mut splitter: Box<dyn Splitter> = match settings.strategy.as_ref() {
        "l" => Box::new(LineSplitter::new(settings)),
        "b" | "C" => Box::new(ByteSplitter::new(settings)),
        a => crash!(1, "strategy {} not supported", a),
    };

    let mut control = SplitControl {
        current_line: "".to_owned(), // Request new line
        request_new_file: true,      // Request new file
    };

    let mut writer = BufWriter::new(Box::new(stdout()) as Box<dyn Write>);
    let mut fileno = 0;
    loop {
        if control.current_line.chars().count() == 0 {
            match reader.read_line(&mut control.current_line) {
                Ok(0) | Err(_) => break,
                _ => {}
            }
        }

        if control.request_new_file {
            let mut filename = settings.prefix.clone();
            filename.push_str(
                if settings.numeric_suffix {
                    num_prefix(fileno, settings.suffix_length)
                } else {
                    str_prefix(fileno, settings.suffix_length)
                }
                .as_ref(),
            );

            if fileno != 0 {
                crash_if_err!(1, writer.flush());
            }
            fileno += 1;
            writer = BufWriter::new(Box::new(
                OpenOptions::new()
                    .write(true)
                    .create(true)
                    .open(Path::new(&filename))
                    .unwrap(),
            ) as Box<dyn Write>);
            control.request_new_file = false;
            if settings.verbose {
                println!("creating file '{}'", filename);
            }
        }

        let consumed = splitter.consume(&mut control);
        crash_if_err!(1, writer.write_all(consumed.as_bytes()));

        let advance = consumed.chars().count();
        let clone = control.current_line.clone();
        let sl = clone;
        control.current_line = sl[advance..sl.chars().count()].to_owned();
    }
    0
}
<|endoftext|>


<commit_before>//! System operations instructions

use util::address::Address;
use util::bigint::{U256, M256};
use util::gas::Gas;
use vm::{Memory, Log, Transaction};
use super::{Control, State};

use std::cmp::min;
use tiny_keccak::Keccak;
use vm::eval::util::copy_from_memory;

pub fn suicide<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, address: Address);
    let balance = state.account_state.balance(state.context.address).unwrap();
    if !state.removed.contains(&state.context.address) {
        state.removed.push(state.context.address);
    }
    state.account_state.increase_balance(address, balance);

    let balance = state.account_state.balance(state.context.address).unwrap();
    state.account_state.decrease_balance(state.context.address, balance);
}

pub fn log<M: Memory + Default>(state: &mut State<M>, topic_len: usize) {
    pop!(state, index, len);
    let data = copy_from_memory(&state.memory, index, len);
    let mut topics = Vec::new();
    for _ in 0..topic_len {
        topics.push(state.stack.pop().unwrap());
    }

    state.logs.push(Log {
        address: state.context.address,
        data: data,
        topics: topics,
    });
}

pub fn sha3<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, from, len);
    let data = copy_from_memory(&state.memory, from, len);
    let mut sha3 = Keccak::new_keccak256();
    sha3.update(data.as_slice());
    let mut ret = [0u8; 32];
    sha3.finalize(&mut ret);
    push!(state, M256::from(ret.as_ref()));
}

macro_rules! try_callstack_limit {
    ( $state:expr, $gas:expr ) => {
        if $state.depth > $state.patch.callstack_limit {
            push!($state, M256::zero());
            return None;
        }
    }
}

macro_rules! try_balance {
    ( $state:expr, $value:expr, $gas:expr ) => {
        if $state.account_state.balance($state.context.address).unwrap() < $value {
            push!($state, M256::zero());
            return None;
        }
    }
}

pub fn create<M: Memory + Default>(state: &mut State<M>, after_gas: Gas) -> Option<Control> {
    pop!(state, value: U256);
    pop!(state, init_start, init_len);

    try_callstack_limit!(state, Gas::zero());
    try_balance!(state, value, Gas::zero());

    let init = copy_from_memory(&state.memory, init_start, init_len);
    let transaction = Transaction::ContractCreation {
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: after_gas,
        value: value,
        init: init,
    };
    let context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &mut state.account_state, true
    ).unwrap();

    push!(state, context.address.into());
    Some(Control::InvokeCreate(context))
}

pub fn call<M: Memory + Default>(state: &mut State<M>, stipend_gas: Gas, after_gas: Gas, as_self: bool) -> Option<Control> {
    pop!(state, gas: Gas, to: Address, value: U256);
    pop!(state, in_start, in_len, out_start, out_len);
    let gas_limit = min(gas, after_gas) + stipend_gas;

    try_callstack_limit!(state, gas_limit);
    try_balance!(state, value, gas_limit);

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let transaction = Transaction::MessageCall {
        address: to,
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: value,
        data: input,
    };

    let mut context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &mut state.account_state, true
    ).unwrap();
    if as_self {
        context.address = state.context.address;
    }

    push!(state, M256::from(1u64));
    Some(Control::InvokeCall(context, (out_start, out_len)))
}

pub fn delegate_call<M: Memory + Default>(state: &mut State<M>, after_gas: Gas) -> Option<Control> {
    pop!(state, gas: Gas, to: Address);
    pop!(state, in_start, in_len, out_start, out_len);
    let gas_limit = min(gas, after_gas);

    try_callstack_limit!(state, gas_limit);

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let transaction = Transaction::MessageCall {
        address: to,
        caller: state.context.caller,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: state.context.value,
        data: input,
    };

    let mut context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &mut state.account_state, true
    ).unwrap();
    context.value = U256::zero();
    context.address = state.context.address;

    push!(state, M256::from(1u64));
    Some(Control::InvokeCall(context, (out_start, out_len)))
}
<commit_msg>SUICIDE: performance issue for SUICIDE opcode attack<commit_after>//! System operations instructions

use util::address::Address;
use util::bigint::{U256, M256};
use util::gas::Gas;
use vm::{Memory, Log, Transaction};
use super::{Control, State};

use std::cmp::min;
use tiny_keccak::Keccak;
use vm::eval::util::copy_from_memory;

pub fn suicide<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, address: Address);
    let balance = state.account_state.balance(state.context.address).unwrap();
    if !state.removed.contains(&state.context.address) {
        state.removed.push(state.context.address);
    }

    // If balance is zero, ignoring balance state change.
    if balance != U256::zero() {
        state.account_state.increase_balance(address, balance);

        let balance = state.account_state.balance(state.context.address).unwrap();
        state.account_state.decrease_balance(state.context.address, balance);
    }
}

pub fn log<M: Memory + Default>(state: &mut State<M>, topic_len: usize) {
    pop!(state, index, len);
    let data = copy_from_memory(&state.memory, index, len);
    let mut topics = Vec::new();
    for _ in 0..topic_len {
        topics.push(state.stack.pop().unwrap());
    }

    state.logs.push(Log {
        address: state.context.address,
        data: data,
        topics: topics,
    });
}

pub fn sha3<M: Memory + Default>(state: &mut State<M>) {
    pop!(state, from, len);
    let data = copy_from_memory(&state.memory, from, len);
    let mut sha3 = Keccak::new_keccak256();
    sha3.update(data.as_slice());
    let mut ret = [0u8; 32];
    sha3.finalize(&mut ret);
    push!(state, M256::from(ret.as_ref()));
}

macro_rules! try_callstack_limit {
    ( $state:expr, $gas:expr ) => {
        if $state.depth > $state.patch.callstack_limit {
            push!($state, M256::zero());
            return None;
        }
    }
}

macro_rules! try_balance {
    ( $state:expr, $value:expr, $gas:expr ) => {
        if $state.account_state.balance($state.context.address).unwrap() < $value {
            push!($state, M256::zero());
            return None;
        }
    }
}

pub fn create<M: Memory + Default>(state: &mut State<M>, after_gas: Gas) -> Option<Control> {
    pop!(state, value: U256);
    pop!(state, init_start, init_len);

    try_callstack_limit!(state, Gas::zero());
    try_balance!(state, value, Gas::zero());

    let init = copy_from_memory(&state.memory, init_start, init_len);
    let transaction = Transaction::ContractCreation {
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: after_gas,
        value: value,
        init: init,
    };
    let context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &mut state.account_state, true
    ).unwrap();

    push!(state, context.address.into());
    Some(Control::InvokeCreate(context))
}

pub fn call<M: Memory + Default>(state: &mut State<M>, stipend_gas: Gas, after_gas: Gas, as_self: bool) -> Option<Control> {
    pop!(state, gas: Gas, to: Address, value: U256);
    pop!(state, in_start, in_len, out_start, out_len);
    let gas_limit = min(gas, after_gas) + stipend_gas;

    try_callstack_limit!(state, gas_limit);
    try_balance!(state, value, gas_limit);

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let transaction = Transaction::MessageCall {
        address: to,
        caller: state.context.address,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: value,
        data: input,
    };

    let mut context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &mut state.account_state, true
    ).unwrap();
    if as_self {
        context.address = state.context.address;
    }

    push!(state, M256::from(1u64));
    Some(Control::InvokeCall(context, (out_start, out_len)))
}

pub fn delegate_call<M: Memory + Default>(state: &mut State<M>, after_gas: Gas) -> Option<Control> {
    pop!(state, gas: Gas, to: Address);
    pop!(state, in_start, in_len, out_start, out_len);
    let gas_limit = min(gas, after_gas);

    try_callstack_limit!(state, gas_limit);

    let input = copy_from_memory(&state.memory, in_start, in_len);
    let transaction = Transaction::MessageCall {
        address: to,
        caller: state.context.caller,
        gas_price: state.context.gas_price,
        gas_limit: gas_limit,
        value: state.context.value,
        data: input,
    };

    let mut context = transaction.into_context(
        Gas::zero(), Some(state.context.origin), &mut state.account_state, true
    ).unwrap();
    context.value = U256::zero();
    context.address = state.context.address;

    push!(state, M256::from(1u64));
    Some(Control::InvokeCall(context, (out_start, out_len)))
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

macro_rules! id {
    ($e:expr) => { $e }
}

fn main() {
    id!(x?);  //~ error: the `?` operator is not stable (see issue #31436)
    //~^ help: add #![feature(question_mark)] to the crate attributes to enable
    y?;  //~ error: the `?` operator is not stable (see issue #31436)
    //~^ help: add #![feature(question_mark)] to the crate attributes to enable
}
<commit_msg>Fix beta branch<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

macro_rules! id {
    ($e:expr) => { $e }
}

fn main() {
    id!(x?);  //~ error: the `?` operator is not stable (see issue #31436)
    y?;  //~ error: the `?` operator is not stable (see issue #31436)
}
<|endoftext|>


<commit_before>const INPUT: &'static str = "01111001100111011";
const PART1_LENGTH: usize = 272;

fn main() {
    let expand = |mut data: String| {
        let b = data.chars().rev().map(|c| if c == '1' { '0' } else { '1' }).collect::<String>();
        data.push('0');
        data.push_str(&b);
        data
    };

    let calc_checksum = |data: &String| {
        data.as_bytes()
            .chunks(2)
            .map(|pair| if pair[0] == pair[1] { '1' } else { '0' })
            .collect::<String>()
    };

    let mut data: String = INPUT.into();
    while data.len() < PART1_LENGTH {
        data = expand(data);
    }

    data.truncate(PART1_LENGTH);
    let mut checksum = calc_checksum(&data);

    while checksum.len() % 2 == 0 {
        checksum = calc_checksum(&checksum);
    }

    println!("part 1: {}", checksum);
}
<commit_msg>rust day16 part2<commit_after>const INPUT: &'static str = "01111001100111011";
const PART1_LENGTH: usize = 272;
const PART2_LENGTH: usize = 35651584;

fn dragon_checksum(data: &str, disk_size: usize) -> String {
    let expand = |mut data: String| {
        let b = data.chars().rev().map(|c| if c == '1' { '0' } else { '1' }).collect::<String>();
        data.push('0');
        data.push_str(&b);
        data
    };

    let calc_checksum = |data: &String| {
        data.as_bytes()
            .chunks(2)
            .map(|pair| if pair[0] == pair[1] { '1' } else { '0' })
            .collect::<String>()
    };

    let mut data: String = data.into();
    while data.len() < disk_size {
        data = expand(data);
    }

    data.truncate(disk_size);
    let mut checksum = calc_checksum(&data);

    while checksum.len() % 2 == 0 {
        checksum = calc_checksum(&checksum);
    }

    checksum
}

fn main() {
    println!("part 1: {}", dragon_checksum(INPUT, PART1_LENGTH));
    println!("part 2: {}", dragon_checksum(INPUT, PART2_LENGTH));
}
<|endoftext|>


<commit_before>extern crate time;
extern crate glob;

use std::fs::{metadata, remove_file};
use std::path::{Path, PathBuf};
use std::os::unix::fs::MetadataExt;
use std::collections::BTreeSet;

use time::{Duration, get_time};
use glob::glob;

static KNOWN_STAMPS: [&'static str; 6] = [
    "/var/lib/systemd/timers/stamp-cronjob-daily.timer",
    "/var/lib/systemd/timers/stamp-cronjob-weekly.timer",
    "/var/lib/systemd/timers/stamp-cronjob-monthly.timer",
    "/var/lib/systemd/timers/stamp-cronjob-quarterly.timer",
    "/var/lib/systemd/timers/stamp-cronjob-semi-annually.timer",
    "/var/lib/systemd/timers/stamp-cronjob-yearly.timer"
];

static ACTUAL_STAMPS_GLOB: &'static str = "/var/lib/systemd/timers/stamp-cronjob-*.timer";
static TIMER_STAMPS_GLOB: &'static str = "/run/systemd/generator/cronjob-*.timer";

fn cleanup<P: AsRef<Path>, I: IntoIterator<Item=P>>(iter: I) {
    let ten_days_ago = get_time() - Duration::days(10);
    for stamp in iter {
        if let Ok(meta) = metadata(&stamp) {
            if meta.mtime() < ten_days_ago.sec {
                let _ = remove_file(&stamp);
            }
        }
    }
}

fn main() {
    let stale_stamps =
        &(&glob(ACTUAL_STAMPS_GLOB).unwrap()
            .flat_map(Result::into_iter)
            .collect::<BTreeSet<_>>() -
        &glob(TIMER_STAMPS_GLOB).unwrap()
            .flat_map(Result::into_iter)
            .map(|s| PathBuf::from(s.to_string_lossy().replace("/run/systemd/generator/cronjob-", "/var/lib/systemd/timers/stamp-cronjob-")))
            .collect::<BTreeSet<_>>()) -
        &KNOWN_STAMPS.iter()
            .map(PathBuf::from)
            .collect::<BTreeSet<_>>();

    cleanup(&stale_stamps);
}
<commit_msg>make it compile for 32-bit arch<commit_after>extern crate time;
extern crate glob;

use std::fs::{metadata, remove_file};
use std::path::{Path, PathBuf};
use std::os::unix::fs::MetadataExt;
use std::collections::BTreeSet;

use time::{Duration, get_time};
use glob::glob;

static KNOWN_STAMPS: [&'static str; 6] = [
    "/var/lib/systemd/timers/stamp-cronjob-daily.timer",
    "/var/lib/systemd/timers/stamp-cronjob-weekly.timer",
    "/var/lib/systemd/timers/stamp-cronjob-monthly.timer",
    "/var/lib/systemd/timers/stamp-cronjob-quarterly.timer",
    "/var/lib/systemd/timers/stamp-cronjob-semi-annually.timer",
    "/var/lib/systemd/timers/stamp-cronjob-yearly.timer"
];

static ACTUAL_STAMPS_GLOB: &'static str = "/var/lib/systemd/timers/stamp-cronjob-*.timer";
static TIMER_STAMPS_GLOB: &'static str = "/run/systemd/generator/cronjob-*.timer";

fn cleanup<P: AsRef<Path>, I: IntoIterator<Item=P>>(iter: I) {
    let ten_days_ago = get_time() - Duration::days(10);
    for stamp in iter {
        if let Ok(meta) = metadata(&stamp) {
            if (meta.mtime() as i64) < ten_days_ago.sec {
                let _ = remove_file(&stamp);
            }
        }
    }
}

fn main() {
    let stale_stamps =
        &(&glob(ACTUAL_STAMPS_GLOB).unwrap()
            .flat_map(Result::into_iter)
            .collect::<BTreeSet<_>>() -
        &glob(TIMER_STAMPS_GLOB).unwrap()
            .flat_map(Result::into_iter)
            .map(|s| PathBuf::from(s.to_string_lossy().replace("/run/systemd/generator/cronjob-", "/var/lib/systemd/timers/stamp-cronjob-")))
            .collect::<BTreeSet<_>>()) -
        &KNOWN_STAMPS.iter()
            .map(PathBuf::from)
            .collect::<BTreeSet<_>>();

    cleanup(&stale_stamps);
}
<|endoftext|>


<commit_before>use super::{Selection, UIState, WatchState};
use crate::err::{self, Result};
use crate::util;
use chrono::{Duration, Utc};
use smallvec::SmallVec;
use snafu::ResultExt;
use std::collections::VecDeque;
use std::fmt;
use std::io;
use std::sync::mpsc;
use std::thread;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};
use tui::backend::{self, Backend};
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::terminal::{Frame, Terminal};
use tui::widgets::{Block, Borders, Paragraph, SelectableList, Text, Widget};

macro_rules! create_stat_list {
    ($($header:expr => $value:expr),+) => {
        [$(
            create_stat_list!(h $header),
            create_stat_list!(v $header.len(), $value),
        )+]
    };

    (h $header:expr) => {
        Text::styled(format!("{}\n", $header), Style::default().modifier(Modifier::BOLD))
    };

    (v $len:expr, $value:expr) => {
        Text::styled(format!("{:^width$}\n\n", $value, width = $len), Style::default().modifier(Modifier::ITALIC))
    };
}

pub struct UI<'a, B>
where
    B: Backend,
{
    terminal: Terminal<B>,
    status_log: StatusLog<'a>,
}

impl<'a, B> UI<'a, B>
where
    B: Backend,
{
    pub fn clear(&mut self) -> Result<()> {
        self.terminal.clear().context(err::IO)
    }

    pub fn push_log_status<S>(&mut self, text: S)
    where
        S: Into<String>,
    {
        self.status_log.push(text);
    }

    pub fn log_capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.status_log.capture(text, f);
    }

    pub fn draw(&mut self, state: &UIState) -> Result<()> {
        let status_log = &mut self.status_log;

        self.terminal
            .draw(|mut frame| {
                // Top panels for series information
                let horiz_splitter = Layout::default()
                    .direction(Direction::Horizontal)
                    .constraints(
                        [
                            Constraint::Min(20),
                            Constraint::Min(8),
                            Constraint::Percentage(60),
                        ]
                        .as_ref(),
                    )
                    .split(frame.size());

                UI::draw_top_panels(state, &horiz_splitter, &mut frame);

                // Series info panel vertical splitter
                let info_panel_splitter = Layout::default()
                    .direction(Direction::Vertical)
                    .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref())
                    .split(horiz_splitter[2]);

                UI::draw_info_panel(state, &info_panel_splitter, &mut frame);
                UI::draw_status_bar(status_log, info_panel_splitter[1], &mut frame);
            })
            .context(err::IO)
    }

    fn draw_top_panels(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        let mut series_list = SelectableList::default()
            .block(Block::default().title("Series").borders(Borders::ALL))
            .items(state.series_names.as_ref())
            .select(Some(state.selected_series))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC));

        if state.selection == Selection::Series {
            series_list = series_list.highlight_symbol(">");
        }

        series_list.render(frame, layout[0]);

        let season_nums = (1..=state.series.num_seasons)
            .map(|i| i.to_string())
            .collect::<SmallVec<[_; 4]>>();

        let mut season_list = SelectableList::default()
            .block(Block::default().title("Season").borders(Borders::ALL))
            .items(season_nums.as_ref())
            .select(Some(state.series.season.season_num))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC));

        if state.selection == Selection::Season {
            season_list = season_list.highlight_symbol(">");
        }

        season_list.render(frame, layout[1]);
    }

    fn draw_info_panel(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        Block::default()
            .title("Info")
            .borders(Borders::ALL)
            .render(frame, layout[0]);

        let info_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(layout[0]);

        let season = &state.series.season;
        let info = &season.series.info;
        let entry = &season.tracker.state;

        // Series title
        {
            let text_items = {
                let mut items = SmallVec::<[_; 2]>::new();

                items.push(Text::styled(
                    &info.title,
                    Style::default().modifier(Modifier::BOLD),
                ));

                if entry.needs_sync() {
                    items.push(Text::styled(
                        " [*]",
                        Style::default().modifier(Modifier::ITALIC),
                    ));
                }

                items
            };

            Paragraph::new(text_items.iter())
                .alignment(Alignment::Center)
                .render(frame, info_layout[0]);
        }

        // Items in panel
        let stat_layout = Layout::default()
            .direction(Direction::Horizontal)
            .constraints(
                [
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                ]
                .as_ref(),
            )
            .split(info_layout[1]);

        {
            let left_items = create_stat_list!(
                "Watch Time" => season.value_cache.watch_time,
                "Time Left" => season.value_cache.watch_time_left,
                "Episode Length" => season.value_cache.episode_length
            );

            Paragraph::new(left_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[0]);
        }

        {
            let center_items = create_stat_list!(
                "Progress" => season.value_cache.progress,
                "Score" => season.value_cache.score,
                "Status" => entry.status()
            );

            Paragraph::new(center_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[1]);
        }

        {
            let right_items = create_stat_list!(
                "Start Date" => season.value_cache.start_date,
                "Finish Date" => season.value_cache.end_date,
                "Rewatched" => entry.times_rewatched()
            );

            Paragraph::new(right_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[2]);
        }

        // Watch time needed indicator at bottom
        match season.watch_state {
            WatchState::Idle => (),
            WatchState::Watching(_, progress_time, _) => {
                let watch_time = progress_time - Utc::now();
                let watch_secs = watch_time.num_seconds();

                if watch_secs > 0 {
                    let remaining_mins = watch_secs as f32 / 60.0;

                    let text_str = format!(
                        "{} Remaining Until Progression",
                        util::ms_from_mins(remaining_mins)
                    );

                    let text = Text::styled(text_str, Style::default().modifier(Modifier::BOLD));

                    Paragraph::new([text].iter())
                        .alignment(Alignment::Center)
                        .render(frame, info_layout[2]);
                }
            }
        }
    }

    fn draw_status_bar(log: &mut StatusLog, layout: Rect, frame: &mut Frame<B>) {
        log.prepare_to_draw(layout);

        Paragraph::new(log.draw_items().iter())
            .block(Block::default().title("Status").borders(Borders::ALL))
            .render(frame, layout);
    }
}

pub type TermionBackend = backend::TermionBackend<RawTerminal<io::Stdout>>;

impl<'a> UI<'a, TermionBackend> {
    pub fn init() -> Result<UI<'a, TermionBackend>> {
        let stdout = io::stdout().into_raw_mode().context(err::IO)?;
        let backend = TermionBackend::new(stdout);
        let mut terminal = Terminal::new(backend).context(err::IO)?;

        terminal.clear().context(err::IO)?;
        terminal.hide_cursor().context(err::IO)?;

        Ok(UI {
            terminal,
            status_log: StatusLog::new(),
        })
    }
}

pub enum Event {
    Input(Key),
    Tick,
}

pub struct Events(mpsc::Receiver<Event>);

impl Events {
    pub fn new(tick_rate: Duration) -> Events {
        let (tx, rx) = mpsc::channel();

        Events::spawn_key_handler(tx.clone());
        Events::spawn_tick_handler(tick_rate, tx);

        Events(rx)
    }

    fn spawn_key_handler(tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let stdin = io::stdin();

        thread::spawn(move || {
            for event in stdin.keys() {
                if let Ok(key) = event {
                    tx.send(Event::Input(key)).unwrap();
                }
            }
        })
    }

    fn spawn_tick_handler(tick_rate: Duration, tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let tick_rate = tick_rate
            .to_std()
            .unwrap_or_else(|_| std::time::Duration::from_secs(1));

        thread::spawn(move || loop {
            thread::sleep(tick_rate);
            tx.send(Event::Tick).unwrap();
        })
    }

    pub fn next(&self) -> Result<Event> {
        self.0.recv().context(err::MPSCRecv)
    }
}

pub enum LogItemStatus {
    Ok,
    Pending,
    Failed,
}

impl LogItemStatus {
    pub fn is_resolved(&self) -> bool {
        match self {
            LogItemStatus::Ok | LogItemStatus::Failed => true,
            LogItemStatus::Pending => false,
        }
    }
}

impl fmt::Display for LogItemStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LogItemStatus::Ok => write!(f, "ok"),
            LogItemStatus::Pending => write!(f, "pending"),
            LogItemStatus::Failed => write!(f, "failed"),
        }
    }
}

pub struct LogItem {
    pub text: String,
    pub status: LogItemStatus,
}

impl LogItem {
    pub fn new<S>(text: S) -> LogItem
    where
        S: Into<String>,
    {
        LogItem {
            text: text.into(),
            status: LogItemStatus::Pending,
        }
    }
}

impl<T> From<T> for LogItem
where
    T: Into<String>,
{
    fn from(value: T) -> Self {
        LogItem::new(value)
    }
}

pub struct StatusLog<'a> {
    items: VecDeque<LogItem>,
    formatted: Vec<Text<'a>>,
    max_items: u16,
}

impl<'a> StatusLog<'a> {
    pub fn new() -> StatusLog<'a> {
        StatusLog {
            items: VecDeque::new(),
            formatted: Vec::new(),
            max_items: 0,
        }
    }

    fn rebuild(&mut self) {
        self.formatted.clear();

        for item in &self.items {
            let value_text = if item.status.is_resolved() {
                format!("{}... ", item.text)
            } else {
                format!("{}...\n", item.text)
            };

            self.formatted.push(Text::raw(value_text));

            if !item.status.is_resolved() {
                continue;
            }

            let status_color = match item.status {
                LogItemStatus::Ok => Color::Green,
                LogItemStatus::Pending => Color::Yellow,
                LogItemStatus::Failed => Color::Red,
            };

            let status = Text::styled(
                format!("{}\n", item.status),
                Style::default().fg(status_color),
            );

            self.formatted.push(status);
        }
    }

    pub fn push<I>(&mut self, item: I)
    where
        I: Into<LogItem>,
    {
        self.trim();
        self.items.push_back(item.into());
        self.rebuild();
    }

    pub fn set_status_on_last(&mut self, status: LogItemStatus) {
        let last = match self.items.get_mut(self.items.len() - 1) {
            Some(last) => last,
            None => return,
        };

        last.status = status;
        self.rebuild();
    }

    pub fn capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.push(text);

        let status = match f() {
            Ok(_) => LogItemStatus::Ok,
            // TODO: display error
            Err(_) => LogItemStatus::Failed,
        };

        self.set_status_on_last(status);
    }

    pub fn prepare_to_draw(&mut self, size: Rect) {
        // We assume a border is around the list, so subtract 2 from the height
        self.max_items = size.height.saturating_sub(2);
        self.trim();
    }

    fn trim(&mut self) {
        while self.items.len() >= self.max_items as usize && !self.items.is_empty() {
            self.items.pop_front();
        }
    }

    /// Returns the log items in a ready-to-draw form.
    ///
    /// Note that this is intended by used with the Paragraph widget type.
    pub fn draw_items(&self) -> &Vec<Text<'a>> {
        &self.formatted
    }
}
<commit_msg>[anup] Display errors in TUI status log<commit_after>use super::{Selection, UIState, WatchState};
use crate::err::{self, Result};
use crate::util;
use chrono::{Duration, Utc};
use smallvec::SmallVec;
use snafu::ResultExt;
use std::collections::VecDeque;
use std::fmt;
use std::io;
use std::sync::mpsc;
use std::thread;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};
use tui::backend::{self, Backend};
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style};
use tui::terminal::{Frame, Terminal};
use tui::widgets::{Block, Borders, Paragraph, SelectableList, Text, Widget};

macro_rules! create_stat_list {
    ($($header:expr => $value:expr),+) => {
        [$(
            create_stat_list!(h $header),
            create_stat_list!(v $header.len(), $value),
        )+]
    };

    (h $header:expr) => {
        Text::styled(format!("{}\n", $header), Style::default().modifier(Modifier::BOLD))
    };

    (v $len:expr, $value:expr) => {
        Text::styled(format!("{:^width$}\n\n", $value, width = $len), Style::default().modifier(Modifier::ITALIC))
    };
}

pub struct UI<'a, B>
where
    B: Backend,
{
    terminal: Terminal<B>,
    status_log: StatusLog<'a>,
}

impl<'a, B> UI<'a, B>
where
    B: Backend,
{
    pub fn clear(&mut self) -> Result<()> {
        self.terminal.clear().context(err::IO)
    }

    pub fn push_log_status<S>(&mut self, text: S)
    where
        S: Into<String>,
    {
        self.status_log.push(text);
    }

    pub fn log_capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.status_log.capture(text, f);
    }

    pub fn draw(&mut self, state: &UIState) -> Result<()> {
        let status_log = &mut self.status_log;

        self.terminal
            .draw(|mut frame| {
                // Top panels for series information
                let horiz_splitter = Layout::default()
                    .direction(Direction::Horizontal)
                    .constraints(
                        [
                            Constraint::Min(20),
                            Constraint::Min(8),
                            Constraint::Percentage(60),
                        ]
                        .as_ref(),
                    )
                    .split(frame.size());

                UI::draw_top_panels(state, &horiz_splitter, &mut frame);

                // Series info panel vertical splitter
                let info_panel_splitter = Layout::default()
                    .direction(Direction::Vertical)
                    .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref())
                    .split(horiz_splitter[2]);

                UI::draw_info_panel(state, &info_panel_splitter, &mut frame);
                UI::draw_status_bar(status_log, info_panel_splitter[1], &mut frame);
            })
            .context(err::IO)
    }

    fn draw_top_panels(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        let mut series_list = SelectableList::default()
            .block(Block::default().title("Series").borders(Borders::ALL))
            .items(state.series_names.as_ref())
            .select(Some(state.selected_series))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC));

        if state.selection == Selection::Series {
            series_list = series_list.highlight_symbol(">");
        }

        series_list.render(frame, layout[0]);

        let season_nums = (1..=state.series.num_seasons)
            .map(|i| i.to_string())
            .collect::<SmallVec<[_; 4]>>();

        let mut season_list = SelectableList::default()
            .block(Block::default().title("Season").borders(Borders::ALL))
            .items(season_nums.as_ref())
            .select(Some(state.series.season.season_num))
            .style(Style::default().fg(Color::White))
            .highlight_style(Style::default().fg(Color::Green).modifier(Modifier::ITALIC));

        if state.selection == Selection::Season {
            season_list = season_list.highlight_symbol(">");
        }

        season_list.render(frame, layout[1]);
    }

    fn draw_info_panel(state: &UIState, layout: &[Rect], frame: &mut Frame<B>) {
        Block::default()
            .title("Info")
            .borders(Borders::ALL)
            .render(frame, layout[0]);

        let info_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Length(4),
                    Constraint::Percentage(70),
                    Constraint::Length(4),
                ]
                .as_ref(),
            )
            .margin(2)
            .split(layout[0]);

        let season = &state.series.season;
        let info = &season.series.info;
        let entry = &season.tracker.state;

        // Series title
        {
            let text_items = {
                let mut items = SmallVec::<[_; 2]>::new();

                items.push(Text::styled(
                    &info.title,
                    Style::default().modifier(Modifier::BOLD),
                ));

                if entry.needs_sync() {
                    items.push(Text::styled(
                        " [*]",
                        Style::default().modifier(Modifier::ITALIC),
                    ));
                }

                items
            };

            Paragraph::new(text_items.iter())
                .alignment(Alignment::Center)
                .render(frame, info_layout[0]);
        }

        // Items in panel
        let stat_layout = Layout::default()
            .direction(Direction::Horizontal)
            .constraints(
                [
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                    Constraint::Percentage(33),
                ]
                .as_ref(),
            )
            .split(info_layout[1]);

        {
            let left_items = create_stat_list!(
                "Watch Time" => season.value_cache.watch_time,
                "Time Left" => season.value_cache.watch_time_left,
                "Episode Length" => season.value_cache.episode_length
            );

            Paragraph::new(left_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[0]);
        }

        {
            let center_items = create_stat_list!(
                "Progress" => season.value_cache.progress,
                "Score" => season.value_cache.score,
                "Status" => entry.status()
            );

            Paragraph::new(center_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[1]);
        }

        {
            let right_items = create_stat_list!(
                "Start Date" => season.value_cache.start_date,
                "Finish Date" => season.value_cache.end_date,
                "Rewatched" => entry.times_rewatched()
            );

            Paragraph::new(right_items.iter())
                .alignment(Alignment::Center)
                .render(frame, stat_layout[2]);
        }

        // Watch time needed indicator at bottom
        match season.watch_state {
            WatchState::Idle => (),
            WatchState::Watching(_, progress_time, _) => {
                let watch_time = progress_time - Utc::now();
                let watch_secs = watch_time.num_seconds();

                if watch_secs > 0 {
                    let remaining_mins = watch_secs as f32 / 60.0;

                    let text_str = format!(
                        "{} Remaining Until Progression",
                        util::ms_from_mins(remaining_mins)
                    );

                    let text = Text::styled(text_str, Style::default().modifier(Modifier::BOLD));

                    Paragraph::new([text].iter())
                        .alignment(Alignment::Center)
                        .render(frame, info_layout[2]);
                }
            }
        }
    }

    fn draw_status_bar(log: &mut StatusLog, layout: Rect, frame: &mut Frame<B>) {
        log.prepare_to_draw(layout);

        Paragraph::new(log.draw_items().iter())
            .block(Block::default().title("Status").borders(Borders::ALL))
            .render(frame, layout);
    }
}

pub type TermionBackend = backend::TermionBackend<RawTerminal<io::Stdout>>;

impl<'a> UI<'a, TermionBackend> {
    pub fn init() -> Result<UI<'a, TermionBackend>> {
        let stdout = io::stdout().into_raw_mode().context(err::IO)?;
        let backend = TermionBackend::new(stdout);
        let mut terminal = Terminal::new(backend).context(err::IO)?;

        terminal.clear().context(err::IO)?;
        terminal.hide_cursor().context(err::IO)?;

        Ok(UI {
            terminal,
            status_log: StatusLog::new(),
        })
    }
}

pub enum Event {
    Input(Key),
    Tick,
}

pub struct Events(mpsc::Receiver<Event>);

impl Events {
    pub fn new(tick_rate: Duration) -> Events {
        let (tx, rx) = mpsc::channel();

        Events::spawn_key_handler(tx.clone());
        Events::spawn_tick_handler(tick_rate, tx);

        Events(rx)
    }

    fn spawn_key_handler(tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let stdin = io::stdin();

        thread::spawn(move || {
            for event in stdin.keys() {
                if let Ok(key) = event {
                    tx.send(Event::Input(key)).unwrap();
                }
            }
        })
    }

    fn spawn_tick_handler(tick_rate: Duration, tx: mpsc::Sender<Event>) -> thread::JoinHandle<()> {
        let tick_rate = tick_rate
            .to_std()
            .unwrap_or_else(|_| std::time::Duration::from_secs(1));

        thread::spawn(move || loop {
            thread::sleep(tick_rate);
            tx.send(Event::Tick).unwrap();
        })
    }

    pub fn next(&self) -> Result<Event> {
        self.0.recv().context(err::MPSCRecv)
    }
}

pub enum LogItemStatus {
    Ok,
    Pending,
    Failed(err::Error),
}

impl LogItemStatus {
    pub fn is_resolved(&self) -> bool {
        match self {
            LogItemStatus::Ok | LogItemStatus::Failed(_) => true,
            LogItemStatus::Pending => false,
        }
    }
}

impl fmt::Display for LogItemStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            LogItemStatus::Ok => write!(f, "ok"),
            LogItemStatus::Pending => write!(f, "pending"),
            LogItemStatus::Failed(_) => write!(f, "failed"),
        }
    }
}

pub struct LogItem {
    pub text: String,
    pub status: LogItemStatus,
}

impl LogItem {
    pub fn new<S>(text: S) -> LogItem
    where
        S: Into<String>,
    {
        LogItem {
            text: text.into(),
            status: LogItemStatus::Pending,
        }
    }
}

impl<T> From<T> for LogItem
where
    T: Into<String>,
{
    fn from(value: T) -> Self {
        LogItem::new(value)
    }
}

pub struct StatusLog<'a> {
    items: VecDeque<LogItem>,
    formatted: Vec<Text<'a>>,
    max_items: u16,
}

impl<'a> StatusLog<'a> {
    pub fn new() -> StatusLog<'a> {
        StatusLog {
            items: VecDeque::new(),
            formatted: Vec::new(),
            max_items: 0,
        }
    }

    fn rebuild(&mut self) {
        self.formatted.clear();

        for item in &self.items {
            let value_text = if item.status.is_resolved() {
                format!("{}... ", item.text)
            } else {
                format!("{}...\n", item.text)
            };

            self.formatted.push(Text::raw(value_text));

            if !item.status.is_resolved() {
                continue;
            }

            let status_color = match item.status {
                LogItemStatus::Ok => Color::Green,
                LogItemStatus::Pending => Color::Yellow,
                LogItemStatus::Failed(_) => Color::Red,
            };

            let status = Text::styled(
                format!("{}\n", item.status),
                Style::default().fg(status_color),
            );

            self.formatted.push(status);

            if let LogItemStatus::Failed(err) = &item.status {
                let err_text =
                    Text::styled(format!(".. {}\n", err), Style::default().fg(Color::Red));

                self.formatted.push(err_text);
            }
        }
    }

    pub fn push<I>(&mut self, item: I)
    where
        I: Into<LogItem>,
    {
        self.trim();
        self.items.push_back(item.into());
        self.rebuild();
    }

    pub fn set_status_on_last(&mut self, status: LogItemStatus) {
        let last = match self.items.get_mut(self.items.len() - 1) {
            Some(last) => last,
            None => return,
        };

        last.status = status;
        self.rebuild();
    }

    pub fn capture<S, F>(&mut self, text: S, f: F)
    where
        S: Into<String>,
        F: FnOnce() -> Result<()>,
    {
        self.push(text);

        let status = match f() {
            Ok(_) => LogItemStatus::Ok,
            Err(err) => LogItemStatus::Failed(err),
        };

        self.set_status_on_last(status);
    }

    pub fn prepare_to_draw(&mut self, size: Rect) {
        // We assume a border is around the list, so subtract 2 from the height
        self.max_items = size.height.saturating_sub(2);
        self.trim();
    }

    fn trim(&mut self) {
        while self.items.len() >= self.max_items as usize && !self.items.is_empty() {
            self.items.pop_front();
        }
    }

    /// Returns the log items in a ready-to-draw form.
    ///
    /// Note that this is intended by used with the Paragraph widget type.
    pub fn draw_items(&self) -> &Vec<Text<'a>> {
        &self.formatted
    }
}
<|endoftext|>


<commit_before>use cpu::arm64::asm::*;
use cpu::arm64::*;
use cpu::{Mem, Reg, trap};
use cpu::arm64::reg::*;
use baseline::buffer::*;
use baseline::codegen::CondCode;
use lexer::position::Position;
use mem::ptr_width;
use object::IntArray;
use ty::MachineMode;
use vtable::VTable;

pub fn prolog(buf: &mut Buffer, stacksize: i32) {
    buf.emit_u32(stp_pre(1, REG_FP, REG_LR, REG_SP, -2));
    buf.emit_u32(add_reg(1, REG_FP, REG_SP, REG_ZERO));

    if stacksize > 0 {
        let scratch = get_scratch();
        load_int_const(buf, scratch, stacksize);
        buf.emit_u32(sub_reg(1, REG_SP, REG_SP, scratch));
    }
}

pub fn epilog(buf: &mut Buffer, stacksize: i32) {
    if stacksize > 0 {
        let scratch = get_scratch();
        load_int_const(buf, scratch, stacksize);
        buf.emit_u32(add_reg(1, REG_SP, REG_SP, scratch));
    }

    buf.emit_u32(add_reg(1, REG_SP, REG_FP, REG_ZERO));
    buf.emit_u32(ldp_post(1, REG_FP, REG_LR, REG_SP, 2));
    buf.emit_u32(ret());
}

pub fn direct_call(buf: &mut Buffer, ptr: *const u8) {
    let disp = buf.add_addr(ptr);
    let pos = buf.pos() as i32;

    let scratch = get_scratch();

    load_constpool(buf, REG_RESULT, disp + pos);
    br(scratch);
}

pub fn indirect_call(buf: &mut Buffer, index: u32) {
    let obj = REG_PARAMS[0];

    // REG_RESULT = [obj] (load vtable)
    load_mem(buf, MachineMode::Ptr, REG_RESULT, Mem::Base(obj, 0));

    // calculate offset of VTable entry
    let disp = VTable::offset_of_method_table() + (index as i32) * ptr_width();

    // load vtable entry
    load_mem(buf, MachineMode::Ptr, REG_RESULT, Mem::Base(REG_RESULT, disp));

    // call *REG_RESULT
    buf.emit_u32(blr(REG_RESULT));
}

pub fn load_array_elem(buf: &mut Buffer, mode: MachineMode, dest: Reg, array: Reg, index: Reg) {
    assert!(mode == MachineMode::Int32);

    load_mem(buf, mode, dest, Mem::Index(array, index, mode.size(), IntArray::offset_of_data()));
}

pub fn store_array_elem(buf: &mut Buffer, mode: MachineMode, array: Reg, index: Reg, value: Reg) {
    assert!(mode == MachineMode::Int32);

    store_mem(buf, MachineMode::Int32,
              Mem::Index(array, index, 4, IntArray::offset_of_data()), value);
}

pub fn test_if_nil_bailout(buf: &mut Buffer, pos: Position, reg: Reg) {
    cmp_reg(buf, MachineMode::Ptr, reg, reg);

    let lbl = buf.create_label();
    jump_if(buf, CondCode::Zero, lbl);
    buf.emit_bailout(lbl, trap::NIL, pos);
}

pub fn test_if_nil(buf: &mut Buffer, reg: Reg) -> Label {
    cmp_reg(buf, MachineMode::Ptr, reg, reg);

    let lbl = buf.create_label();
    jump_if(buf, CondCode::Zero, lbl);

    lbl
}

pub fn set(buf: &mut Buffer, dest: Reg, op: CondCode) {
    buf.emit_u32(cmp_imm(0, dest, 0, 0));
    buf.emit_u32(cset(0, dest, CondCode::NotEqual.into()));
}

pub fn cmp_mem(buf: &mut Buffer, mode: MachineMode, mem: Mem, rhs: Reg) {
    unimplemented!();
}

pub fn cmp_mem_imm(buf: &mut Buffer, mode: MachineMode, mem: Mem, imm: i32) {
    unimplemented!();
}

pub fn cmp_reg(buf: &mut Buffer, mode: MachineMode, lhs: Reg, rhs: Reg) {
    buf.emit_u32(cmp_shreg(size_flag(mode), lhs, rhs, Shift::LSL, 0));
}

pub fn test_and_jump_if(buf: &mut Buffer, cond: CondCode, reg: Reg, lbl: Label) {
    assert!(cond == CondCode::Zero || cond == CondCode::NonZero);

    unimplemented!();
}

pub fn jump_if(buf: &mut Buffer, cond: CondCode, lbl: Label) {
    unimplemented!();
}

pub fn jump(buf: &mut Buffer, lbl: Label) {
    unimplemented!();
}

pub fn int_div(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(sdiv(0, dest, lhs, rhs));
}

pub fn int_mod(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    unimplemented!();
}

pub fn int_mul(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(mul(0, dest, lhs, rhs));
}

pub fn int_add(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(add_reg(0, dest, lhs, rhs));
}

pub fn int_sub(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(sub_reg(0, dest, lhs, rhs));
}

pub fn int_shl(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(lslv(0, dest, lhs, rhs));
}

pub fn int_or(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(orr_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
}

pub fn int_and(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(and_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
}

pub fn int_xor(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(eor_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
}

pub fn check_index_out_of_bounds(buf: &mut Buffer, pos: Position, array: Reg,
                                 index: Reg, temp: Reg) {
    load_mem(buf, MachineMode::Int32, temp,
             Mem::Base(array, IntArray::offset_of_length()));
    cmp_reg(buf, MachineMode::Int32, temp, index);

    let lbl = buf.create_label();
    jump_if(buf, CondCode::UnsignedGreaterEq, lbl);
    buf.emit_bailout(lbl, trap::INDEX_OUT_OF_BOUNDS, pos);
}

pub fn load_nil(buf: &mut Buffer, dest: Reg) {
    buf.emit_u32(add_imm(1, dest, REG_ZERO, 0, 0));
}

pub fn load_mem(buf: &mut Buffer, mode: MachineMode, dest: Reg, mem: Mem) {
    unimplemented!();
}

pub fn store_mem(buf: &mut Buffer, mode: MachineMode, mem: Mem, src: Reg) {
    unimplemented!();
}

pub fn copy_reg(buf: &mut Buffer, mode: MachineMode, dest: Reg, src: Reg) {
    buf.emit_u32(orr_shreg(0, dest, REG_ZERO, src, Shift::LSL, 0));
}

pub fn load_constpool(buf: &mut Buffer, dest: Reg, disp: i32) {
    unimplemented!();
}

pub fn call_reg(buf: &mut Buffer, reg: Reg) {
    buf.emit_u32(blr(reg));
}

pub fn debug(buf: &mut Buffer) {
    unimplemented!();
}

pub fn load_int_const(buf: &mut Buffer, dest: Reg, imm: i32) {
    let register_size = 32;
    let imm = imm as i64 as u64;

    if fits_movz(imm, register_size) {
        buf.emit_u32(movz(0, dest, imm as u32, 0));

    } else if fits_movn(imm, register_size) {
        buf.emit_u32(movn(0, dest, imm as u32, 0));

    } else {
        unimplemented!();
    }
}

pub fn load_true(buf: &mut Buffer, dest: Reg) {
    buf.emit_u32(movz(0, dest, 1, 0));
}

pub fn load_false(buf: &mut Buffer, dest: Reg) {
    buf.emit_u32(movz(0, dest, 0, 0));
}

pub fn int_neg(buf: &mut Buffer, dest: Reg, src: Reg) {
    buf.emit_u32(sub_reg(0, dest, REG_ZERO, src));
}

pub fn int_not(buf: &mut Buffer, dest: Reg, src: Reg) {
    buf.emit_u32(orn_shreg(0, dest, REG_ZERO, src, Shift::LSL, 0));
}

pub fn bool_not(buf: &mut Buffer, dest: Reg, src: Reg) {
    let scratch = get_scratch();

    buf.emit_u32(movz(0, scratch, 1, 0));
    buf.emit_u32(eor_shreg(0, dest, src, scratch, Shift::LSL, 0));
    buf.emit_u32(uxtb(dest, dest));
}

fn size_flag(mode: MachineMode) -> u32 {
    match mode {
        MachineMode::Int8 | MachineMode::Int32 => 0,
        MachineMode::Ptr => 1,
    }
}

fn get_scratch() -> Reg {
    SCRATCH[0]
}
<commit_msg>arm64: fix order for comparison<commit_after>use cpu::arm64::asm::*;
use cpu::arm64::*;
use cpu::{Mem, Reg, trap};
use cpu::arm64::reg::*;
use baseline::buffer::*;
use baseline::codegen::CondCode;
use lexer::position::Position;
use mem::ptr_width;
use object::IntArray;
use ty::MachineMode;
use vtable::VTable;

pub fn prolog(buf: &mut Buffer, stacksize: i32) {
    buf.emit_u32(stp_pre(1, REG_FP, REG_LR, REG_SP, -2));
    buf.emit_u32(add_reg(1, REG_FP, REG_SP, REG_ZERO));

    if stacksize > 0 {
        let scratch = get_scratch();
        load_int_const(buf, scratch, stacksize);
        buf.emit_u32(sub_reg(1, REG_SP, REG_SP, scratch));
    }
}

pub fn epilog(buf: &mut Buffer, stacksize: i32) {
    if stacksize > 0 {
        let scratch = get_scratch();
        load_int_const(buf, scratch, stacksize);
        buf.emit_u32(add_reg(1, REG_SP, REG_SP, scratch));
    }

    buf.emit_u32(add_reg(1, REG_SP, REG_FP, REG_ZERO));
    buf.emit_u32(ldp_post(1, REG_FP, REG_LR, REG_SP, 2));
    buf.emit_u32(ret());
}

pub fn direct_call(buf: &mut Buffer, ptr: *const u8) {
    let disp = buf.add_addr(ptr);
    let pos = buf.pos() as i32;

    let scratch = get_scratch();

    load_constpool(buf, REG_RESULT, disp + pos);
    br(scratch);
}

pub fn indirect_call(buf: &mut Buffer, index: u32) {
    let obj = REG_PARAMS[0];

    // REG_RESULT = [obj] (load vtable)
    load_mem(buf, MachineMode::Ptr, REG_RESULT, Mem::Base(obj, 0));

    // calculate offset of VTable entry
    let disp = VTable::offset_of_method_table() + (index as i32) * ptr_width();

    // load vtable entry
    load_mem(buf, MachineMode::Ptr, REG_RESULT, Mem::Base(REG_RESULT, disp));

    // call *REG_RESULT
    buf.emit_u32(blr(REG_RESULT));
}

pub fn load_array_elem(buf: &mut Buffer, mode: MachineMode, dest: Reg, array: Reg, index: Reg) {
    assert!(mode == MachineMode::Int32);

    load_mem(buf, mode, dest, Mem::Index(array, index, mode.size(), IntArray::offset_of_data()));
}

pub fn store_array_elem(buf: &mut Buffer, mode: MachineMode, array: Reg, index: Reg, value: Reg) {
    assert!(mode == MachineMode::Int32);

    store_mem(buf, MachineMode::Int32,
              Mem::Index(array, index, 4, IntArray::offset_of_data()), value);
}

pub fn test_if_nil_bailout(buf: &mut Buffer, pos: Position, reg: Reg) {
    cmp_reg(buf, MachineMode::Ptr, reg, reg);

    let lbl = buf.create_label();
    jump_if(buf, CondCode::Zero, lbl);
    buf.emit_bailout(lbl, trap::NIL, pos);
}

pub fn test_if_nil(buf: &mut Buffer, reg: Reg) -> Label {
    cmp_reg(buf, MachineMode::Ptr, reg, reg);

    let lbl = buf.create_label();
    jump_if(buf, CondCode::Zero, lbl);

    lbl
}

pub fn set(buf: &mut Buffer, dest: Reg, op: CondCode) {
    buf.emit_u32(cmp_imm(0, dest, 0, 0));
    buf.emit_u32(cset(0, dest, CondCode::NotEqual.into()));
}

pub fn cmp_mem(buf: &mut Buffer, mode: MachineMode, mem: Mem, rhs: Reg) {
    unimplemented!();
}

pub fn cmp_mem_imm(buf: &mut Buffer, mode: MachineMode, mem: Mem, imm: i32) {
    unimplemented!();
}

pub fn cmp_reg(buf: &mut Buffer, mode: MachineMode, lhs: Reg, rhs: Reg) {
    buf.emit_u32(cmp_shreg(size_flag(mode), lhs, rhs, Shift::LSL, 0));
}

pub fn test_and_jump_if(buf: &mut Buffer, cond: CondCode, reg: Reg, lbl: Label) {
    assert!(cond == CondCode::Zero || cond == CondCode::NonZero);

    unimplemented!();
}

pub fn jump_if(buf: &mut Buffer, cond: CondCode, lbl: Label) {
    unimplemented!();
}

pub fn jump(buf: &mut Buffer, lbl: Label) {
    unimplemented!();
}

pub fn int_div(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(sdiv(0, dest, lhs, rhs));
}

pub fn int_mod(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    unimplemented!();
}

pub fn int_mul(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(mul(0, dest, lhs, rhs));
}

pub fn int_add(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(add_reg(0, dest, lhs, rhs));
}

pub fn int_sub(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(sub_reg(0, dest, lhs, rhs));
}

pub fn int_shl(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(lslv(0, dest, lhs, rhs));
}

pub fn int_or(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(orr_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
}

pub fn int_and(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(and_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
}

pub fn int_xor(buf: &mut Buffer, dest: Reg, lhs: Reg, rhs: Reg) {
    buf.emit_u32(eor_shreg(0, dest, lhs, rhs, Shift::LSL, 0));
}

pub fn check_index_out_of_bounds(buf: &mut Buffer, pos: Position, array: Reg,
                                 index: Reg, temp: Reg) {
    load_mem(buf, MachineMode::Int32, temp,
             Mem::Base(array, IntArray::offset_of_length()));
    cmp_reg(buf, MachineMode::Int32, index, temp);

    let lbl = buf.create_label();
    jump_if(buf, CondCode::UnsignedGreaterEq, lbl);
    buf.emit_bailout(lbl, trap::INDEX_OUT_OF_BOUNDS, pos);
}

pub fn load_nil(buf: &mut Buffer, dest: Reg) {
    buf.emit_u32(add_imm(1, dest, REG_ZERO, 0, 0));
}

pub fn load_mem(buf: &mut Buffer, mode: MachineMode, dest: Reg, mem: Mem) {
    unimplemented!();
}

pub fn store_mem(buf: &mut Buffer, mode: MachineMode, mem: Mem, src: Reg) {
    unimplemented!();
}

pub fn copy_reg(buf: &mut Buffer, mode: MachineMode, dest: Reg, src: Reg) {
    buf.emit_u32(orr_shreg(0, dest, REG_ZERO, src, Shift::LSL, 0));
}

pub fn load_constpool(buf: &mut Buffer, dest: Reg, disp: i32) {
    unimplemented!();
}

pub fn call_reg(buf: &mut Buffer, reg: Reg) {
    buf.emit_u32(blr(reg));
}

pub fn debug(buf: &mut Buffer) {
    unimplemented!();
}

pub fn load_int_const(buf: &mut Buffer, dest: Reg, imm: i32) {
    let register_size = 32;
    let imm = imm as i64 as u64;

    if fits_movz(imm, register_size) {
        buf.emit_u32(movz(0, dest, imm as u32, 0));

    } else if fits_movn(imm, register_size) {
        buf.emit_u32(movn(0, dest, imm as u32, 0));

    } else {
        unimplemented!();
    }
}

pub fn load_true(buf: &mut Buffer, dest: Reg) {
    buf.emit_u32(movz(0, dest, 1, 0));
}

pub fn load_false(buf: &mut Buffer, dest: Reg) {
    buf.emit_u32(movz(0, dest, 0, 0));
}

pub fn int_neg(buf: &mut Buffer, dest: Reg, src: Reg) {
    buf.emit_u32(sub_reg(0, dest, REG_ZERO, src));
}

pub fn int_not(buf: &mut Buffer, dest: Reg, src: Reg) {
    buf.emit_u32(orn_shreg(0, dest, REG_ZERO, src, Shift::LSL, 0));
}

pub fn bool_not(buf: &mut Buffer, dest: Reg, src: Reg) {
    let scratch = get_scratch();

    buf.emit_u32(movz(0, scratch, 1, 0));
    buf.emit_u32(eor_shreg(0, dest, src, scratch, Shift::LSL, 0));
    buf.emit_u32(uxtb(dest, dest));
}

fn size_flag(mode: MachineMode) -> u32 {
    match mode {
        MachineMode::Int8 | MachineMode::Int32 => 0,
        MachineMode::Ptr => 1,
    }
}

fn get_scratch() -> Reg {
    SCRATCH[0]
}
<|endoftext|>