<commit_before>use macroquad::prelude::*;

use colour::Colour;
use graph::Position;
use game::{Game, GameState};

mod colour;
mod graph;
mod grid;
mod game;

fn macroquad_colour(colour: Colour) -> Color {
    match colour {
        Colour::Red => color_u8!(255, 0, 0, 255),
        Colour::Yellow => color_u8!(255, 255, 0, 255),
        Colour::Green => color_u8!(0, 176, 0, 255),
        Colour::LightBrown => color_u8!(255, 204, 102, 255),
        Colour::Purple => color_u8!(128, 0, 128, 255),
        Colour::Cyan => color_u8!(0, 255, 255, 255),
        Colour::Blue => color_u8!(0, 0, 255, 255),
        Colour::Fuchsia => color_u8!(255, 0, 255, 255),
    }
}

fn draw_cell(grid_x: f32, grid_y: f32, cell_size: f32, position: &Position, colour: Colour) {
    let color = macroquad_colour(colour);
    draw_rectangle(
        grid_x + cell_size * position.column as f32,
        grid_y + cell_size * position.row as f32,
        cell_size,
        cell_size,
        color,
    );
}

struct Ui {
    /// Size of the grid in cells.
    size: u32,

    /// Size of the grid in pixels.  Assumes the grid is always square.
    grid_size: f32,

    /// Offset of the grid relative to the whole window along the horizontal axis.
    grid_x: f32,

    /// Offset of the grid relative to the whole window along the vertical axis.
    grid_y: f32,
}

impl Ui {
    fn create(size: u32) -> Ui {
        let mut ui = Ui {
            size,
            grid_size: 0.0,
            grid_x: 0.0,
            grid_y: 0.0,
        };

        ui.resize();

        ui
    }

    fn resize(&mut self) {
        let screen_height = screen_height();
        let screen_width = screen_width();

        let grid_size = screen_height.min(screen_width);
        self.grid_size = grid_size;
        self.grid_x = (screen_width - grid_size) / 2.0;
        self.grid_y = (screen_height - grid_size) / 2.0;
    }

    fn render(&self, game: &Game) {
        clear_background(BLACK);

        let grid_x = self.grid_x;
        let grid_y = self.grid_y;
        let cell_size = self.cell_size();

        let graph = &game.graph;
        for component in graph.neighbours.keys() {
            for position in &graph.components[component].cells {
                draw_cell(grid_x, grid_y, cell_size, position, graph.components[component].colour);
            }
        }
    }

    fn cell_size(&self) -> f32 {
        self.grid_size / self.size as f32
    }

    fn cell_position(&self, raw_position: (f32, f32)) -> Option<Position> {
        let (raw_x, raw_y) = raw_position;

        let cell_size = self.cell_size();
        let x = (raw_x - self.grid_x) / cell_size;
        let y = (raw_y - self.grid_y) / cell_size;

        let size = self.size as f32;
        if x < 0.0 || y < 0.0 || x > size || y > size {
            // Out of bounds, nothing to do
            None
        } else {
            Some(Position{column: x.floor() as usize, row: y.floor() as usize})
        }
    }

    fn handle_click(&self, game: &mut Game, mouse_position: (f32, f32)) {
        if let Some(position) = self.cell_position(mouse_position) {
            let clicked_component = game.graph.find_component(&position);
            let colour = clicked_component.colour;

            println!("Detected click at cell ({}, {}) with colour {:#?}", position.column, position.row, colour);

            game.fill_component_of_top_left_cell_with(colour);

            match game.state {
                GameState::Solving =>
                if game.graph.components.len() == 1 {
                    game.state = GameState::Solved;
                    println!("Done");
                }
                GameState::Solved => (),
            }
        }
    }
}

#[macroquad::main("Flood-It")]
async fn main() {
    let size = 8;
    let number_of_colours = 8;

    let mut game = Game::create(size, number_of_colours);
    let mut ui = Ui::create(size);

    loop {
        if let Some(KeyCode::Q) = get_last_key_pressed() {
            break;
        }

        if is_mouse_button_pressed(MouseButton::Left) {
            ui.handle_click(&mut game, mouse_position());
        }

        ui.resize();
        ui.render(&game);

        next_frame().await
    }
}
<commit_msg>Start with 4 colours by default<commit_after>use macroquad::prelude::*;

use colour::Colour;
use graph::Position;
use game::{Game, GameState};

mod colour;
mod graph;
mod grid;
mod game;

fn macroquad_colour(colour: Colour) -> Color {
    match colour {
        Colour::Red => color_u8!(255, 0, 0, 255),
        Colour::Yellow => color_u8!(255, 255, 0, 255),
        Colour::Green => color_u8!(0, 176, 0, 255),
        Colour::LightBrown => color_u8!(255, 204, 102, 255),
        Colour::Purple => color_u8!(128, 0, 128, 255),
        Colour::Cyan => color_u8!(0, 255, 255, 255),
        Colour::Blue => color_u8!(0, 0, 255, 255),
        Colour::Fuchsia => color_u8!(255, 0, 255, 255),
    }
}

fn draw_cell(grid_x: f32, grid_y: f32, cell_size: f32, position: &Position, colour: Colour) {
    let color = macroquad_colour(colour);
    draw_rectangle(
        grid_x + cell_size * position.column as f32,
        grid_y + cell_size * position.row as f32,
        cell_size,
        cell_size,
        color,
    );
}

struct Ui {
    /// Size of the grid in cells.
    size: u32,

    /// Size of the grid in pixels.  Assumes the grid is always square.
    grid_size: f32,

    /// Offset of the grid relative to the whole window along the horizontal axis.
    grid_x: f32,

    /// Offset of the grid relative to the whole window along the vertical axis.
    grid_y: f32,
}

impl Ui {
    fn create(size: u32) -> Ui {
        let mut ui = Ui {
            size,
            grid_size: 0.0,
            grid_x: 0.0,
            grid_y: 0.0,
        };

        ui.resize();

        ui
    }

    fn resize(&mut self) {
        let screen_height = screen_height();
        let screen_width = screen_width();

        let grid_size = screen_height.min(screen_width);
        self.grid_size = grid_size;
        self.grid_x = (screen_width - grid_size) / 2.0;
        self.grid_y = (screen_height - grid_size) / 2.0;
    }

    fn render(&self, game: &Game) {
        clear_background(BLACK);

        let grid_x = self.grid_x;
        let grid_y = self.grid_y;
        let cell_size = self.cell_size();

        let graph = &game.graph;
        for component in graph.neighbours.keys() {
            for position in &graph.components[component].cells {
                draw_cell(grid_x, grid_y, cell_size, position, graph.components[component].colour);
            }
        }
    }

    fn cell_size(&self) -> f32 {
        self.grid_size / self.size as f32
    }

    fn cell_position(&self, raw_position: (f32, f32)) -> Option<Position> {
        let (raw_x, raw_y) = raw_position;

        let cell_size = self.cell_size();
        let x = (raw_x - self.grid_x) / cell_size;
        let y = (raw_y - self.grid_y) / cell_size;

        let size = self.size as f32;
        if x < 0.0 || y < 0.0 || x > size || y > size {
            // Out of bounds, nothing to do
            None
        } else {
            Some(Position{column: x.floor() as usize, row: y.floor() as usize})
        }
    }

    fn handle_click(&self, game: &mut Game, mouse_position: (f32, f32)) {
        if let Some(position) = self.cell_position(mouse_position) {
            let clicked_component = game.graph.find_component(&position);
            let colour = clicked_component.colour;

            println!("Detected click at cell ({}, {}) with colour {:#?}", position.column, position.row, colour);

            game.fill_component_of_top_left_cell_with(colour);

            match game.state {
                GameState::Solving =>
                if game.graph.components.len() == 1 {
                    game.state = GameState::Solved;
                    println!("Done");
                }
                GameState::Solved => (),
            }
        }
    }
}

#[macroquad::main("Flood-It")]
async fn main() {
    let size = 8;
    let number_of_colours = 4;

    let mut game = Game::create(size, number_of_colours);
    let mut ui = Ui::create(size);

    loop {
        if let Some(KeyCode::Q) = get_last_key_pressed() {
            break;
        }

        if is_mouse_button_pressed(MouseButton::Left) {
            ui.handle_click(&mut game, mouse_position());
        }

        ui.resize();
        ui.render(&game);

        next_frame().await
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

mod args;
mod macros;

use crate::args::AppArgs;
use monolith::html::{html_to_dom, stringify_document, walk_and_embed_assets};
use monolith::http::retrieve_asset;
use monolith::utils::is_valid_url;
use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use reqwest::Proxy;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{self, Error, Write};
use std::process;
use std::time::Duration;

enum Output {
    Stdout(io::Stdout),
    File(File),
}

impl Output {
    fn new(file_path: &str) -> Result<Output, Error> {
        if file_path.is_empty() {
            Ok(Output::Stdout(io::stdout()))
        } else {
            Ok(Output::File(File::create(file_path)?))
        }
    }

    fn writeln_str(&mut self, s: &str) -> Result<(), Error> {
        match self {
            Output::Stdout(stdout) => {
                writeln!(stdout, "{}", s)?;
                stdout.flush()
            }
            Output::File(f) => {
                writeln!(f, "{}", s)?;
                f.flush()
            }
        }
    }
}

fn create_http_client(args: &AppArgs) -> Result<Client, reqwest::Error> {
    let mut header_map = HeaderMap::new();
    header_map.insert(
        USER_AGENT,
        HeaderValue::from_str(&args.user_agent).expect("Invalid User-Agent header specified"),
    );

    let mut builder = Client::builder()
        .timeout(Duration::from_secs(10))
        .danger_accept_invalid_certs(args.insecure)
        .default_headers(header_map);

    if let Ok(var) = env::var("https_proxy").or_else(|_| env::var("HTTPS_PROXY")) {
        let proxy = Proxy::https(&var)
            .expect("Could not set HTTPS proxy. Please check $https_proxy env var");
        builder = builder.proxy(proxy);
    }

    if let Ok(var) = env::var("http_proxy").or_else(|_| env::var("HTTP_PROXY")) {
        let proxy =
            Proxy::http(&var).expect("Could not set HTTP proxy. Please check $http_proxy env var");
        builder = builder.proxy(proxy);
    }

    builder.build()
}

fn main() {
    let app_args = AppArgs::get();

    if !is_valid_url(app_args.url_target.as_str()) {
        eprintln!(
            "Only HTTP and HTTPS URLs are allowed but got: {}",
            &app_args.url_target
        );
        process::exit(1);
    }

    let mut output = Output::new(&app_args.output).expect("Could not prepare output");
    let mut cache = HashMap::new();
    let client = create_http_client(&app_args).expect("Failed to initialize HTTP client");

    // Retrieve root document
    let (data, final_url) = retrieve_asset(
        &mut cache,
        &client,
        app_args.url_target.as_str(),
        false,
        "",
        app_args.silent,
    )
    .expect("Could not retrieve assets in HTML");
    let dom = html_to_dom(&data);

    walk_and_embed_assets(
        &mut cache,
        &client,
        &final_url,
        &dom.document,
        app_args.no_css,
        app_args.no_js,
        app_args.no_images,
        app_args.silent,
        app_args.no_frames,
    );

    let html: String = stringify_document(
        &dom.document,
        app_args.no_css,
        app_args.no_frames,
        app_args.no_js,
        app_args.no_images,
        app_args.isolate,
    );

    output
        .writeln_str(&html)
        .expect("Could not write HTML output");
}
<commit_msg>fix crash at setting empty values to HTTP proxies<commit_after>#[macro_use]
extern crate clap;

mod args;
mod macros;

use crate::args::AppArgs;
use monolith::html::{html_to_dom, stringify_document, walk_and_embed_assets};
use monolith::http::retrieve_asset;
use monolith::utils::is_valid_url;
use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use reqwest::Proxy;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{self, Error, Write};
use std::process;
use std::time::Duration;

enum Output {
    Stdout(io::Stdout),
    File(File),
}

impl Output {
    fn new(file_path: &str) -> Result<Output, Error> {
        if file_path.is_empty() {
            Ok(Output::Stdout(io::stdout()))
        } else {
            Ok(Output::File(File::create(file_path)?))
        }
    }

    fn writeln_str(&mut self, s: &str) -> Result<(), Error> {
        match self {
            Output::Stdout(stdout) => {
                writeln!(stdout, "{}", s)?;
                stdout.flush()
            }
            Output::File(f) => {
                writeln!(f, "{}", s)?;
                f.flush()
            }
        }
    }
}

fn create_http_client(args: &AppArgs) -> Result<Client, reqwest::Error> {
    let mut header_map = HeaderMap::new();
    header_map.insert(
        USER_AGENT,
        HeaderValue::from_str(&args.user_agent).expect("Invalid User-Agent header specified"),
    );

    let mut builder = Client::builder()
        .timeout(Duration::from_secs(10))
        .danger_accept_invalid_certs(args.insecure)
        .default_headers(header_map);

    if let Ok(var) = env::var("https_proxy").or_else(|_| env::var("HTTPS_PROXY")) {
        if !var.is_empty() {
            let proxy = Proxy::https(&var)
                .expect("Could not set HTTPS proxy. Please check $https_proxy env var");
            builder = builder.proxy(proxy);
        }
    }

    if let Ok(var) = env::var("http_proxy").or_else(|_| env::var("HTTP_PROXY")) {
        if !var.is_empty() {
            let proxy = Proxy::http(&var)
                .expect("Could not set HTTP proxy. Please check $http_proxy env var");
            builder = builder.proxy(proxy);
        }
    }

    builder.build()
}

fn main() {
    let app_args = AppArgs::get();

    if !is_valid_url(app_args.url_target.as_str()) {
        eprintln!(
            "Only HTTP and HTTPS URLs are allowed but got: {}",
            &app_args.url_target
        );
        process::exit(1);
    }

    let mut output = Output::new(&app_args.output).expect("Could not prepare output");
    let mut cache = HashMap::new();
    let client = create_http_client(&app_args).expect("Failed to initialize HTTP client");

    // Retrieve root document
    let (data, final_url) = retrieve_asset(
        &mut cache,
        &client,
        app_args.url_target.as_str(),
        false,
        "",
        app_args.silent,
    )
    .expect("Could not retrieve assets in HTML");
    let dom = html_to_dom(&data);

    walk_and_embed_assets(
        &mut cache,
        &client,
        &final_url,
        &dom.document,
        app_args.no_css,
        app_args.no_js,
        app_args.no_images,
        app_args.silent,
        app_args.no_frames,
    );

    let html: String = stringify_document(
        &dom.document,
        app_args.no_css,
        app_args.no_frames,
        app_args.no_js,
        app_args.no_images,
        app_args.isolate,
    );

    output
        .writeln_str(&html)
        .expect("Could not write HTML output");
}
<|endoftext|>


<commit_before>//#![feature(convert)]

extern crate nix;

use nix::unistd::execvp;
use std::collections::BTreeSet;
use std::error::Error;
use std::option::Option;
use std::process::Command;
use std::str;
// use libc::{execvp, perror};
use std::env;
use std::ffi::CString;
use std::path::PathBuf;

const PROGRAM: &str = "tmux";

fn detached_session_number(line: &str) -> Option<usize> {
    if line.ends_with("(attached)") {
        None
    } else {
        line.split(':').next().and_then(|s| s.parse().ok())
    }
}

fn detached_sessions(output: &str) -> BTreeSet<usize> {
    output
        .lines()
        .filter_map(|line| detached_session_number(line))
        .collect()
}

// TODO: This should return a Result & let the caller decide to run a
// shell or something.
fn exec_program(program: &str, args: &[&str]) {
    // Each of these CStrings has to outlive pointers to it. Sadly,
    // the rust compiler doesn't scream when they don't (see
    // http://is.gd/nS339k), so we better make damn sure that the args
    // will stick around.
    let c_program_as_cstring = CString::new(program).unwrap();
    let args_as_cstring = args
        .iter()
        .map(|arg| CString::new(*arg).unwrap())
        .collect::<Vec<CString>>();

    if let Err(e) = execvp(&c_program_as_cstring, &args_as_cstring) {
        println!("execvp of {:?} failed: {:?}", program, e.description())
    }
}

fn prepare_environment() {
    if let Some(path) = env::var_os("PATH") {
        let mut paths = env::split_paths(&path).collect::<Vec<_>>();
        paths.push(PathBuf::from("/usr/local/bin"));
        let new_path = env::join_paths(paths.iter()).unwrap();
        env::set_var("PATH", &new_path);
    } else {
        let new_path = env::join_paths(
            ["/usr/bin", "/bin", "/usr/local/bin"]
                .iter()
                .map(PathBuf::from)
                .collect::<Vec<_>>(),
        )
        .unwrap();
        env::set_var("PATH", &new_path);
    }
}

fn start_server() {
    Command::new(PROGRAM)
        .arg("start-server")
        .status()
        .expect("Could not start tmux server: it exited with an error status.");
}

fn main() {
    prepare_environment();

    start_server();
    let session_output = Command::new(PROGRAM)
        .arg("list-sessions")
        .output()
        .expect("Running list-sessions command exited with an error status");

    let output = str::from_utf8(&session_output.stdout)
        .expect("Could not read the (expected) utf-8 from tmux");
    let sessions = detached_sessions(output);

    match sessions.iter().next() {
        Some(n) => {
            let my_str = n.to_string();
            let session = my_str.as_ref();
            let mut args: Vec<&str> = vec![PROGRAM, "attach-session", "-t"];
            args.push(session);
            exec_program(PROGRAM, &args);
        }
        _ => {
            exec_program(PROGRAM, [PROGRAM].as_ref());
        }
    }
}

#[test]
fn test_session_number_with_numbers() {
    match detached_session_number("11: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65]") {
        Some(11) => (),
        Some(n) => panic!(format!("Should have returned 11, got {}!", n)),
        None => panic!("Should have returned something, got nothing"),
    }
}

#[test]
fn test_session_number_with_strings() {
    match detached_session_number("oink: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65]") {
        Some(n) => panic!(format!("Should have returned None, got {}!", n)),
        None => (),
    }
}

#[test]
fn test_session_number_with_attached_session() {
    match detached_session_number(
        "1: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)",
    ) {
        Some(n) => panic!(format!("Should have returned None, got {}!", n)),
        None => (),
    }
}

#[test]
fn test_detached_sessions() {
    let set = detached_sessions("3: foo (attached)\n2: bar\n15: oink\n4: baz (attached)\nfoo: baz");
    // attached sessions:
    assert!(!set.contains(&3));
    assert!(!set.contains(&4));

    // detached sessions should be present:
    assert!(set.contains(&15));
    assert!(set.contains(&2));

    // missing session:
    assert!(!set.contains(&1));
}
<commit_msg>Add /run/current-system/sw/bin to PATH as well, for nix folks<commit_after>//#![feature(convert)]

extern crate nix;

use nix::unistd::execvp;
use std::collections::BTreeSet;
use std::error::Error;
use std::option::Option;
use std::process::Command;
use std::str;
// use libc::{execvp, perror};
use std::env;
use std::ffi::CString;
use std::path::PathBuf;

const PROGRAM: &str = "tmux";

fn detached_session_number(line: &str) -> Option<usize> {
    if line.ends_with("(attached)") {
        None
    } else {
        line.split(':').next().and_then(|s| s.parse().ok())
    }
}

fn detached_sessions(output: &str) -> BTreeSet<usize> {
    output
        .lines()
        .filter_map(|line| detached_session_number(line))
        .collect()
}

// TODO: This should return a Result & let the caller decide to run a
// shell or something.
fn exec_program(program: &str, args: &[&str]) {
    // Each of these CStrings has to outlive pointers to it. Sadly,
    // the rust compiler doesn't scream when they don't (see
    // http://is.gd/nS339k), so we better make damn sure that the args
    // will stick around.
    let c_program_as_cstring = CString::new(program).unwrap();
    let args_as_cstring = args
        .iter()
        .map(|arg| CString::new(*arg).unwrap())
        .collect::<Vec<CString>>();

    if let Err(e) = execvp(&c_program_as_cstring, &args_as_cstring) {
        println!("execvp of {:?} failed: {:?}", program, e.description())
    }
}

fn prepare_environment() {
    if let Some(path) = env::var_os("PATH") {
        let mut paths = env::split_paths(&path).collect::<Vec<_>>();
        paths.push(PathBuf::from("/usr/local/bin"));
        paths.push(PathBuf::from("/run/current-system/sw/bin"));
        let new_path = env::join_paths(paths.iter()).unwrap();
        env::set_var("PATH", &new_path);
    } else {
        let new_path = env::join_paths(
            ["/usr/bin", "/bin", "/usr/local/bin"]
                .iter()
                .map(PathBuf::from)
                .collect::<Vec<_>>(),
        )
        .unwrap();
        env::set_var("PATH", &new_path);
    }
}

fn start_server() {
    Command::new(PROGRAM)
        .arg("start-server")
        .status()
        .expect("Could not start tmux server: it exited with an error status.");
}

fn main() {
    prepare_environment();

    start_server();
    let session_output = Command::new(PROGRAM)
        .arg("list-sessions")
        .output()
        .expect("Running list-sessions command exited with an error status");

    let output = str::from_utf8(&session_output.stdout)
        .expect("Could not read the (expected) utf-8 from tmux");
    let sessions = detached_sessions(output);

    match sessions.iter().next() {
        Some(n) => {
            let my_str = n.to_string();
            let session = my_str.as_ref();
            let mut args: Vec<&str> = vec![PROGRAM, "attach-session", "-t"];
            args.push(session);
            exec_program(PROGRAM, &args);
        }
        _ => {
            exec_program(PROGRAM, [PROGRAM].as_ref());
        }
    }
}

#[test]
fn test_session_number_with_numbers() {
    match detached_session_number("11: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65]") {
        Some(11) => (),
        Some(n) => panic!(format!("Should have returned 11, got {}!", n)),
        None => panic!("Should have returned something, got nothing"),
    }
}

#[test]
fn test_session_number_with_strings() {
    match detached_session_number("oink: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65]") {
        Some(n) => panic!(format!("Should have returned None, got {}!", n)),
        None => (),
    }
}

#[test]
fn test_session_number_with_attached_session() {
    match detached_session_number(
        "1: 1 windows (created Sat Sep 14 17:11:29 2013) [130x65] (attached)",
    ) {
        Some(n) => panic!(format!("Should have returned None, got {}!", n)),
        None => (),
    }
}

#[test]
fn test_detached_sessions() {
    let set = detached_sessions("3: foo (attached)\n2: bar\n15: oink\n4: baz (attached)\nfoo: baz");
    // attached sessions:
    assert!(!set.contains(&3));
    assert!(!set.contains(&4));

    // detached sessions should be present:
    assert!(set.contains(&15));
    assert!(set.contains(&2));

    // missing session:
    assert!(!set.contains(&1));
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::env;
use std::fmt;
use std::io;
use std::io::Write;
use std::sync::Mutex;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
}

impl ConvertedType {
    fn new<T: Into<String>>(pre: T, post: String) -> Self {
        ConvertedType {
            prefix: pre.into(),
            postfix: post
        }
    }
}

impl Into<String> for ConvertedType {
    fn into(self) -> String {
        let mut str = String::from(self.prefix);
        str.push_str(&self.postfix);
        str
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        ConvertedType {
            prefix: str,
            postfix: String::new()
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> String {
    let l = p.segments[0].ident.to_string();
    let mut c = match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    };
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| map_ty(ty).into())
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c.push('<');
            c.push_str(&template_args);
            c.push('>');
        }
    }
    c
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => ConvertedType::from(map_path(p)),
        &Ty::Ptr(ref p) => ConvertedType::from(format!("{}*", map_ty(&p.ty))),
        &Ty::Rptr(_, ref mut_ty) => ConvertedType::from(format!("{}*", map_mut_ty(mut_ty))),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => ConvertedType::new(map_ty(&p), format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> String {
    match ret {
        &FunctionRetTy::Default => "void".to_string(),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty).into(),
    }
}

fn map_pat(pat: &Pat) -> String {
    match pat {
        &Pat::Ident(_, ref ident, _) => ident.to_string(),
        _ => format!("unknown {:?}", pat),
    }

}

fn map_arg(f: &FnArg) -> String {
    match f {
        &FnArg::Captured(ref pat, ref ty) => format!("{} {}", map_ty(ty), map_pat(pat)),
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field) -> String {
    let mut ret = String::from("  ");
    let converted = map_ty(&f.ty);
    ret.push_str(&converted.prefix);
    ret.push(' ');
    ret.push_str(&f.ident.as_ref().expect("Struct fields must have idents").to_string());
    ret.push_str(&converted.postfix);
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

/// A structure to store all the results of converting Rust stuff to C
/// stuff, so that we can pick and choose what we output.
struct ConversionResults {
    /// This holds the C function signatures of no_mangle rust functions,
    /// in the order that they were encountered in Rust.
    funcs: Vec<String>,
    /// This holds the C conversions of repr(C) structs and repr(u32) enums
    /// from Rust. The 'ds' in the name stands for 'data structures'. The
    /// key of the map is the name of the type, and the value is the C code.
    c_ds: HashMap<String, String>,
    /// This holds the dependency tree. If a struct contains another struct
    /// or an enum it will be in this map. The key is the dependent type,
    /// the list of values are all the things it depends on.
    _dep_tree: HashMap<String, Vec<String>>,
}

fn main() {
    let p = env::args().nth(1).unwrap();

    let results = Mutex::new(ConversionResults {
        funcs: Vec::new(),
        c_ds: HashMap::new(),
        _dep_tree: HashMap::new(),
    });

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        results.lock().unwrap().funcs.push(
                            format!("WR_INLINE {}\n{}({})\n{};\n",
                                    map_return_type(&decl.output),
                                    item.ident,
                                    decl.inputs
                                        .iter()
                                        .map(map_arg)
                                        .collect::<Vec<_>>()
                                        .join(", "),
                                    wr_func_body(&item.attrs)));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if let &VariantData::Struct(ref fields) = variant {
                            let mut c_struct = String::new();
                            if !generics.ty_params.is_empty() {
                                c_struct.push_str(
                                    &(format!("template<{}>",
                                         generics.ty_params
                                             .iter()
                                             .map(map_generic_param)
                                             .collect::<Vec<_>>()
                                             .join(", "))));
                            }
                            c_struct.push_str(
                                &(format!("struct {} {{\n{}}};\n",
                                     item.ident,
                                     fields
                                         .iter()
                                         .map(map_field)
                                         .collect::<String>())));
                            results.lock().unwrap().c_ds.insert(
                                item.ident.to_string(),
                                c_struct);
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        let c_enum = format!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                 item.ident,
                                 variants
                                     .iter()
                                     .fold((String::new(), -1), fold_enum_variants)
                                     .0);
                        results.lock().unwrap().c_ds.insert(
                            item.ident.to_string(),
                            c_enum);
                    }
                }
                _ => {}
            }
        }
    });
    for (_, c_stuff) in &results.lock().unwrap().c_ds {
        println!("{}", c_stuff);
    }
    for func in &results.lock().unwrap().funcs {
        println!("{}", func);
    }
}
<commit_msg>Clean up formatting a little bit. rustfmt generates gross results so this is done by hand<commit_after>use std::collections::HashMap;
use std::env;
use std::fmt;
use std::io;
use std::io::Write;
use std::sync::Mutex;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
}

impl ConvertedType {
    fn new<T: Into<String>>(pre: T, post: String) -> Self {
        ConvertedType {
            prefix: pre.into(),
            postfix: post
        }
    }
}

impl Into<String> for ConvertedType {
    fn into(self) -> String {
        let mut str = String::from(self.prefix);
        str.push_str(&self.postfix);
        str
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        ConvertedType {
            prefix: str,
            postfix: String::new()
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> String {
    let l = p.segments[0].ident.to_string();
    let mut c = match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    };
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| map_ty(ty).into())
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c.push('<');
            c.push_str(&template_args);
            c.push('>');
        }
    }
    c
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => ConvertedType::from(map_path(p)),
        &Ty::Ptr(ref p) => ConvertedType::from(format!("{}*", map_ty(&p.ty))),
        &Ty::Rptr(_, ref mut_ty) => ConvertedType::from(format!("{}*", map_mut_ty(mut_ty))),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => ConvertedType::new(map_ty(&p), format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> String {
    match ret {
        &FunctionRetTy::Default => "void".to_string(),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty).into(),
    }
}

fn map_pat(pat: &Pat) -> String {
    match pat {
        &Pat::Ident(_, ref ident, _) => ident.to_string(),
        _ => format!("unknown {:?}", pat),
    }

}

fn map_arg(f: &FnArg) -> String {
    match f {
        &FnArg::Captured(ref pat, ref ty) => format!("{} {}", map_ty(ty), map_pat(pat)),
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field) -> String {
    let mut ret = String::from("  ");
    let converted = map_ty(&f.ty);
    ret.push_str(&converted.prefix);
    ret.push(' ');
    ret.push_str(&f.ident.as_ref().expect("Struct fields must have idents").to_string());
    ret.push_str(&converted.postfix);
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

/// A structure to store all the results of converting Rust stuff to C
/// stuff, so that we can pick and choose what we output.
struct ConversionResults {
    /// This holds the C function signatures of no_mangle rust functions,
    /// in the order that they were encountered in Rust.
    funcs: Vec<String>,
    /// This holds the C conversions of repr(C) structs and repr(u32) enums
    /// from Rust. The 'ds' in the name stands for 'data structures'. The
    /// key of the map is the name of the type, and the value is the C code.
    c_ds: HashMap<String, String>,
    /// This holds the dependency tree. If a struct contains another struct
    /// or an enum it will be in this map. The key is the dependent type,
    /// the list of values are all the things it depends on.
    _dep_tree: HashMap<String, Vec<String>>,
}

fn main() {
    let p = env::args().nth(1).unwrap();

    let results = Mutex::new(ConversionResults {
        funcs: Vec::new(),
        c_ds: HashMap::new(),
        _dep_tree: HashMap::new(),
    });

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        results.lock().unwrap().funcs.push(
                            format!("WR_INLINE {}\n{}({})\n{};\n",
                                    map_return_type(&decl.output),
                                    item.ident,
                                    decl.inputs
                                        .iter()
                                        .map(map_arg)
                                        .collect::<Vec<_>>()
                                        .join(", "),
                                    wr_func_body(&item.attrs)));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if let &VariantData::Struct(ref fields) = variant {
                            let mut c_struct = String::new();
                            if !generics.ty_params.is_empty() {
                                c_struct.push_str(&(
                                    format!("template<{}>",
                                            generics.ty_params
                                                    .iter()
                                                    .map(map_generic_param)
                                                    .collect::<Vec<_>>()
                                                    .join(", "))));
                            }
                            c_struct.push_str(&(
                                format!("struct {} {{\n{}}};\n",
                                        item.ident,
                                        fields.iter()
                                              .map(map_field)
                                              .collect::<String>())));
                            results.lock().unwrap().c_ds.insert(
                                item.ident.to_string(),
                                c_struct);
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        let c_enum = format!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                             item.ident,
                                             variants.iter()
                                                     .fold((String::new(), -1), fold_enum_variants)
                                                     .0);
                        results.lock().unwrap().c_ds.insert(
                            item.ident.to_string(),
                            c_enum);
                    }
                }
                _ => {}
            }
        }
    });
    for (_, c_stuff) in &results.lock().unwrap().c_ds {
        println!("{}", c_stuff);
    }
    for func in &results.lock().unwrap().funcs {
        println!("{}", func);
    }
}
<|endoftext|>


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

struct MetricHolder {
    n: usize,
    sum: u32,
    history: LinkedList<u32>
}

impl MetricHolder {
    fn moving_average(&mut self, new_val: u32) -> f64 {
        self.history.push_back(new_val);
        self.sum += new_val;
        if self.history.len() > self.n {
            if let Some(val) = self.history.pop_front() {
                self.sum -= val;
            }
        }

        self.sum as f64 / self.history.len() as f64
    }

    fn new(n: usize) -> MetricHolder {
        MetricHolder {
            n: n,
            sum: 0,
            history: LinkedList::new()
        }
    }
}

fn main() {
    let mut metric_holder = MetricHolder::new(2);
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(2));
    println!("{}", metric_holder.moving_average(3));
}
<commit_msg>Add a module<commit_after>
mod metrics {
    use std::collections::LinkedList;

    pub struct MetricHolder {
        n: usize,
        sum: u32,
        history: LinkedList<u32>
    }

    impl MetricHolder {
        pub fn moving_average(&mut self, new_val: u32) -> f64 {
            self.history.push_back(new_val);
            self.sum += new_val;
            if self.history.len() > self.n {
                if let Some(val) = self.history.pop_front() {
                    self.sum -= val;
                }
            }

            self.sum as f64 / self.history.len() as f64
        }

        pub fn new(n: usize) -> MetricHolder {
            MetricHolder {
                n: n,
                sum: 0,
                history: LinkedList::new()
            }
        }
    }
}

fn main() {
    let mut metric_holder = metrics::MetricHolder::new(2);
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(2));
    println!("{}", metric_holder.moving_average(3));
}
<|endoftext|>


<commit_before>// Clippy settings, full list here:
// <https://rust-lang.github.io/rust-clippy/master/index.html>
#![allow(clippy::clippy::clippy::needless_return)]
//
// Fail build on Clippy warnings
#![deny(warnings)]

#[macro_use]
extern crate lazy_static;

use backtrace::Backtrace;
use constants::*;
use git_version::git_version;
use isatty::{stdin_isatty, stdout_isatty};
use regex::Regex;
use std::env;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::panic;
use std::process::exit;
use std::process::{Command, Stdio};
use std::str;

mod constants;
mod refiner;
mod token_collector;
mod tokenizer;

const HELP_TEXT: &str = r#"
Usage: diff ... | riff

Colors diff and highlights what parts of changed lines have changed.

Git integration:
    git config --global pager.diff riff
    git config --global pager.show riff
    git config --global interactive.diffFilter riff

Options:
    --help:    Print this text
    --version: Print version number
"#;

const HELP_TEXT_FOOTER: &str = r#"
Report issues at <https://github.com/walles/riff>.
"#;

const CRASH_FOOTER: &str = r#"
Please copy the above crash report and report it at one of:
* <https://github.com/walles/riff/issues> (preferred)
* <johan.walles@gmail.com>
"#;

const HUNK_HEADER: &str = "\x1b[36m"; // Cyan
const PAGER_FORKBOMB_STOP: &str = "_RIFF_IGNORE_PAGER";

const GIT_VERSION: &str = git_version!();

lazy_static! {
    static ref STATIC_HEADER_PREFIXES: Vec<(&'static str, &'static str)> = vec![
        ("diff ", BOLD),
        ("index ", BOLD),
        ("--- ", BOLD),
        ("+++ ", BOLD),
        ("@@ ", HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

enum LastLineKind {
    Initial,
    Old,
    New,
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header_prefix in STATIC_HEADER_PREFIXES.iter() {
        let prefix = static_header_prefix.0;
        if line.starts_with(prefix) {
            return static_header_prefix.1;
        }
    }

    return "";
}

fn print(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    stream.write_all(text.as_bytes()).unwrap();
}

fn println(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    print(stream, &text);
    print(stream, "\n");
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut old_text = String::new();
    let mut new_text = String::new();
    let input = BufReader::new(input);
    let output = &mut BufWriter::new(output);
    let mut last_line_kind = LastLineKind::Initial;
    for line in input.lines() {
        let line = line.unwrap();

        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in refiner::format(&old_text, &new_text) {
                println(output, &line);
            }
            old_text.clear();
            new_text.clear();

            print(output, fixed_highlight);
            print(output, &line);
            println(output, NORMAL);
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.starts_with('+') {
            new_text.push_str(&line[1..]);
            new_text.push('\n');
            last_line_kind = LastLineKind::New;
            continue;
        } else if !line.is_empty() && line.starts_with('-') {
            old_text.push_str(&line[1..]);
            old_text.push('\n');
            last_line_kind = LastLineKind::Old;
            continue;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            match last_line_kind {
                LastLineKind::Old => {
                    assert!(old_text.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::New => {
                    assert!(new_text.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Initial => {
                    // This block intentionally left blank
                }
            }
        }

        last_line_kind = LastLineKind::Initial;

        // Drain outstanding adds / removes
        for line in refiner::format(&old_text, &new_text) {
            println(output, &line);
        }
        old_text.clear();
        new_text.clear();

        // Print current line
        if line == NO_EOF_NEWLINE_MARKER {
            print(output, NO_EOF_NEWLINE_COLOR);
            print(output, &line);
            println(output, NORMAL);
        } else {
            println(output, &line);
        }
    }
    for line in refiner::format(&old_text, &new_text) {
        println(output, &line);
    }
}

/// Try paging using the named pager (`$PATH` will be searched).
///
/// Returns `true` if the pager was found, `false` otherwise.
#[must_use]
fn try_pager(input: &mut dyn io::Read, pager_name: &str) -> bool {
    let mut command = Command::new(pager_name);

    if env::var(PAGER_FORKBOMB_STOP).is_ok() {
        // Try preventing fork bombing if $PAGER is set to riff
        return false;
    }
    command.env(PAGER_FORKBOMB_STOP, "1");

    if env::var("LESS").is_err() {
        // Set by git when paging
        command.env("LESS", "FRX");
    }

    if env::var("LV").is_err() {
        // Set by git when paging
        command.env("LV", "-c");
    }

    command.stdin(Stdio::piped());

    match command.spawn() {
        Ok(mut pager) => {
            let pager_stdin = pager.stdin.as_mut().unwrap();
            highlight_diff(input, pager_stdin);

            // FIXME: Report pager exit status if non-zero, together with
            // contents of pager stderr as well if possible.
            pager.wait().expect("Waiting for pager failed");

            return true;
        }
        Err(_) => {
            return false;
        }
    }
}

/// If `option` is found in `argv`, all instances of `option` will be removed
/// from `argv`.
///
/// Returns `true` if `option` was found and consumed, false otherwise.
#[must_use]
fn consume(option: &str, argv: &mut Vec<String>) -> bool {
    if !argv.contains(&option.to_string()) {
        // Not found
        return false;
    }

    argv.retain(|x| x != option);
    return true;
}

fn print_help(output: &mut dyn io::Write) {
    output.write_all(HELP_TEXT.trim().as_bytes()).unwrap();
    output.write_all(b"\n").unwrap();
    output.write_all(b"\n").unwrap();

    output
        .write_all(b"Installing riff in the $PATH:\n")
        .unwrap();
    output
        .write_all(b"    sudo cp riff /usr/local/bin\n")
        .unwrap();
    output.write_all(b"\n").unwrap();

    output
        .write_all(HELP_TEXT_FOOTER.trim().as_bytes())
        .unwrap();
    output.write_all(b"\n").unwrap();
}

fn panic_handler(panic_info: &panic::PanicInfo) {
    let stderr: &mut dyn Write = &mut io::stderr();
    let stderr = &mut BufWriter::new(stderr);
    println(
        stderr,
        "\n\n-v-v-v----------- RIFF CRASHED ---------------v-v-v-\n",
    );

    // Panic message
    if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
        println(stderr, &format!("Panic message: <{:?}>", s));
        println(stderr, "");
    }

    // Backtrace
    println(stderr, &format!("{:?}", Backtrace::new()));

    println(stderr, &format!("Riff version: {}", GIT_VERSION));

    println(stderr, "Command line arguments:");
    for argument in env::args() {
        println(stderr, &format!("* <{}>", argument));
    }

    println(
        stderr,
        "\n-^-^-^------- END OF RIFF CRASH REPORT -------^-^-^-\n",
    );

    println(stderr, CRASH_FOOTER);
}

fn highlight_stream(input: &mut dyn io::Read) {
    if !stdout_isatty() {
        // We're being piped, just do stdin -> stdout
        highlight_diff(input, &mut io::stdout());
        return;
    }

    if let Ok(pager_value) = env::var("PAGER") {
        if try_pager(input, &pager_value) {
            return;
        }

        // FIXME: Print warning at the end if $PAGER was set to something that
        // doesn't exist.
    }

    if try_pager(input, "moar") {
        return;
    }

    if try_pager(input, "less") {
        return;
    }

    // No pager found, wth?
    highlight_diff(input, &mut io::stdout());
}

fn exec_diff_highlight(file1: &str, file2: &str) {
    // FIXME: Check that either:
    // * Both file1 and file2 are files
    // * Both file1 and file2 are directories

    // Run "diff -ur file1 file2"
    let command: &mut Command = &mut Command::new("diff");
    let command = command
        .arg("-ur") // "-u = unified diff, -r = recurse subdirectories"
        .arg(file1)
        .arg(file2)
        .stdout(Stdio::piped());

    match command.spawn() {
        Ok(mut diff) => {
            let diff_stdout = diff.stdout.as_mut().unwrap();

            highlight_stream(diff_stdout);

            let _diff_exit_code = diff.wait();
            // FIXME: Do we care what happened to diff? How to handle an error here?
        }
        Err(_) => {
            panic!("FIXME: Better error reporting here");
        }
    }
}

fn main() {
    panic::set_hook(Box::new(|panic_info: &panic::PanicInfo| {
        panic_handler(panic_info);
    }));

    let mut args: Vec<String> = env::args().collect();
    if consume("--help", &mut args) || consume("-h", &mut args) {
        print_help(&mut io::stdout());
        return;
    }

    if consume("--version", &mut args) {
        println!("riff {}", GIT_VERSION);
        println!();
        println!("Source code available at <https://github.com/walles/riff>.");
        return;
    }

    if consume("--please-panic", &mut args) {
        panic!("Panicking on purpose");
    }

    if args.len() == 3 {
        // "riff file1 file2"
        exec_diff_highlight(args.get(1).unwrap(), args.get(2).unwrap());
        return;
    }

    if stdin_isatty() {
        eprintln!("ERROR: Expected input from a pipe");
        eprintln!("");
        print_help(&mut io::stderr());
        exit(1);
    }

    highlight_stream(&mut io::stdin().lock());
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    fn old(text: &str) -> String {
        return format!("{}{}{}", OLD, text, NORMAL);
    }

    fn new(text: &str) -> String {
        return format!("{}{}{}", NEW, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            old(&format!("-hej{}⏎", INVERSE_VIDEO)),
            new("+hej"),
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut actual: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut actual);
        assert_eq!(std::str::from_utf8(&actual).unwrap(), expected);
    }

    #[test]
    fn test_trailing_newline_context() {
        let mut input = "+bepa\n apa\n\\ No newline at end of file\n".as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            new("+bepa"),
            " apa",
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }

    #[test]
    fn test_testdata_examples() {
        // Example value: `/Users/johan/src/riff/target/debug/deps/riff-7a8916c06b0d3d6c`
        let exe_path = std::env::current_exe().unwrap();

        // Example value: `/Users/johan/src/riff`
        let project_path = exe_path
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap();

        // Example value: `/Users/johan/src/riff/testdata`
        let testdata_path = &project_path.join("testdata");
        assert!(testdata_path.is_dir());

        // Iterate all files in there
        for diff in fs::read_dir(testdata_path).unwrap() {
            let diff = diff.unwrap();
            let diff = diff.path();
            let diff = diff.as_path();
            if !diff.is_file() {
                continue;
            }

            if diff.extension().unwrap() != "diff" {
                continue;
            }

            println!("Evaluating example file <{}>...", diff.to_str().unwrap());

            // Run highlighting on the file into a memory buffer
            let mut actual_result: Vec<u8> = Vec::new();
            highlight_diff(&mut fs::File::open(diff).unwrap(), &mut actual_result);
            let actual_result = str::from_utf8(&actual_result).unwrap();

            // Load the corresponding .riff-output file into a string
            let basename = diff.file_stem().unwrap().to_str().unwrap();
            let expected_path = format!(
                "{}/{}.riff-output",
                testdata_path.to_str().unwrap(),
                basename
            );
            let expected_result = fs::read_to_string(expected_path).unwrap();

            // Assert that the highlighting output matches the contents of .riff-output
            assert_eq!(actual_result, expected_result);
        }
    }
}
<commit_msg>Verify path types before spawning diff<commit_after>// Clippy settings, full list here:
// <https://rust-lang.github.io/rust-clippy/master/index.html>
#![allow(clippy::clippy::clippy::needless_return)]
//
// Fail build on Clippy warnings
#![deny(warnings)]

#[macro_use]
extern crate lazy_static;

use backtrace::Backtrace;
use constants::*;
use git_version::git_version;
use isatty::{stdin_isatty, stdout_isatty};
use regex::Regex;
use std::env;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::panic;
use std::path;
use std::process::exit;
use std::process::{Command, Stdio};
use std::str;

mod constants;
mod refiner;
mod token_collector;
mod tokenizer;

const HELP_TEXT: &str = r#"
Usage:
  diff ... | riff
  riff <file1> <file2>
  riff <directory1> <directory2>

Colors diff output, highlighting the changed parts of every line.

Git integration:
    git config --global pager.diff riff
    git config --global pager.show riff
    git config --global interactive.diffFilter riff

Options:
    --help:    Print this text
    --version: Print version number
"#;

const HELP_TEXT_FOOTER: &str = r#"
Report issues at <https://github.com/walles/riff>.
"#;

const CRASH_FOOTER: &str = r#"
Please copy the above crash report and report it at one of:
* <https://github.com/walles/riff/issues> (preferred)
* <johan.walles@gmail.com>
"#;

const HUNK_HEADER: &str = "\x1b[36m"; // Cyan
const PAGER_FORKBOMB_STOP: &str = "_RIFF_IGNORE_PAGER";

const GIT_VERSION: &str = git_version!();

lazy_static! {
    static ref STATIC_HEADER_PREFIXES: Vec<(&'static str, &'static str)> = vec![
        ("diff ", BOLD),
        ("index ", BOLD),
        ("--- ", BOLD),
        ("+++ ", BOLD),
        ("@@ ", HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

enum LastLineKind {
    Initial,
    Old,
    New,
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header_prefix in STATIC_HEADER_PREFIXES.iter() {
        let prefix = static_header_prefix.0;
        if line.starts_with(prefix) {
            return static_header_prefix.1;
        }
    }

    return "";
}

fn print(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    stream.write_all(text.as_bytes()).unwrap();
}

fn println(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    print(stream, &text);
    print(stream, "\n");
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut old_text = String::new();
    let mut new_text = String::new();
    let input = BufReader::new(input);
    let output = &mut BufWriter::new(output);
    let mut last_line_kind = LastLineKind::Initial;
    for line in input.lines() {
        let line = line.unwrap();

        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in refiner::format(&old_text, &new_text) {
                println(output, &line);
            }
            old_text.clear();
            new_text.clear();

            print(output, fixed_highlight);
            print(output, &line);
            println(output, NORMAL);
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.starts_with('+') {
            new_text.push_str(&line[1..]);
            new_text.push('\n');
            last_line_kind = LastLineKind::New;
            continue;
        } else if !line.is_empty() && line.starts_with('-') {
            old_text.push_str(&line[1..]);
            old_text.push('\n');
            last_line_kind = LastLineKind::Old;
            continue;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            match last_line_kind {
                LastLineKind::Old => {
                    assert!(old_text.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::New => {
                    assert!(new_text.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Initial => {
                    // This block intentionally left blank
                }
            }
        }

        last_line_kind = LastLineKind::Initial;

        // Drain outstanding adds / removes
        for line in refiner::format(&old_text, &new_text) {
            println(output, &line);
        }
        old_text.clear();
        new_text.clear();

        // Print current line
        if line == NO_EOF_NEWLINE_MARKER {
            print(output, NO_EOF_NEWLINE_COLOR);
            print(output, &line);
            println(output, NORMAL);
        } else {
            println(output, &line);
        }
    }
    for line in refiner::format(&old_text, &new_text) {
        println(output, &line);
    }
}

/// Try paging using the named pager (`$PATH` will be searched).
///
/// Returns `true` if the pager was found, `false` otherwise.
#[must_use]
fn try_pager(input: &mut dyn io::Read, pager_name: &str) -> bool {
    let mut command = Command::new(pager_name);

    if env::var(PAGER_FORKBOMB_STOP).is_ok() {
        // Try preventing fork bombing if $PAGER is set to riff
        return false;
    }
    command.env(PAGER_FORKBOMB_STOP, "1");

    if env::var("LESS").is_err() {
        // Set by git when paging
        command.env("LESS", "FRX");
    }

    if env::var("LV").is_err() {
        // Set by git when paging
        command.env("LV", "-c");
    }

    command.stdin(Stdio::piped());

    match command.spawn() {
        Ok(mut pager) => {
            let pager_stdin = pager.stdin.as_mut().unwrap();
            highlight_diff(input, pager_stdin);

            // FIXME: Report pager exit status if non-zero, together with
            // contents of pager stderr as well if possible.
            pager.wait().expect("Waiting for pager failed");

            return true;
        }
        Err(_) => {
            return false;
        }
    }
}

/// If `option` is found in `argv`, all instances of `option` will be removed
/// from `argv`.
///
/// Returns `true` if `option` was found and consumed, false otherwise.
#[must_use]
fn consume(option: &str, argv: &mut Vec<String>) -> bool {
    if !argv.contains(&option.to_string()) {
        // Not found
        return false;
    }

    argv.retain(|x| x != option);
    return true;
}

fn print_help(output: &mut dyn io::Write) {
    output.write_all(HELP_TEXT.trim().as_bytes()).unwrap();
    output.write_all(b"\n").unwrap();
    output.write_all(b"\n").unwrap();

    output
        .write_all(b"Installing riff in the $PATH:\n")
        .unwrap();
    output
        .write_all(b"    sudo cp riff /usr/local/bin\n")
        .unwrap();
    output.write_all(b"\n").unwrap();

    output
        .write_all(HELP_TEXT_FOOTER.trim().as_bytes())
        .unwrap();
    output.write_all(b"\n").unwrap();
}

fn panic_handler(panic_info: &panic::PanicInfo) {
    let stderr: &mut dyn Write = &mut io::stderr();
    let stderr = &mut BufWriter::new(stderr);
    println(
        stderr,
        "\n\n-v-v-v----------- RIFF CRASHED ---------------v-v-v-\n",
    );

    // Panic message
    if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
        println(stderr, &format!("Panic message: <{:?}>", s));
        println(stderr, "");
    }

    // Backtrace
    println(stderr, &format!("{:?}", Backtrace::new()));

    println(stderr, &format!("Riff version: {}", GIT_VERSION));

    println(stderr, "Command line arguments:");
    for argument in env::args() {
        println(stderr, &format!("* <{}>", argument));
    }

    println(
        stderr,
        "\n-^-^-^------- END OF RIFF CRASH REPORT -------^-^-^-\n",
    );

    println(stderr, CRASH_FOOTER);
}

fn highlight_stream(input: &mut dyn io::Read) {
    if !stdout_isatty() {
        // We're being piped, just do stdin -> stdout
        highlight_diff(input, &mut io::stdout());
        return;
    }

    if let Ok(pager_value) = env::var("PAGER") {
        if try_pager(input, &pager_value) {
            return;
        }

        // FIXME: Print warning at the end if $PAGER was set to something that
        // doesn't exist.
    }

    if try_pager(input, "moar") {
        return;
    }

    if try_pager(input, "less") {
        return;
    }

    // No pager found, wth?
    highlight_diff(input, &mut io::stdout());
}

pub fn type_string(path: &path::Path) -> &str {
    if !path.exists() {
        return "Not found";
    }
    if path.is_file() {
        return "File";
    }
    if path.is_dir() {
        return "Directory";
    }

    panic!("Not sure what this is: {}", path.to_string_lossy());
}

fn exec_diff_highlight(path1: &str, path2: &str) {
    let path1 = path::Path::new(path1);
    let path2 = path::Path::new(path2);
    let both_paths_are_files = path1.is_file() && path2.is_file();
    let both_paths_are_dirs = path1.is_dir() && path2.is_dir();

    if !(both_paths_are_files || both_paths_are_dirs) {
        let stderr: &mut dyn Write = &mut io::stderr();
        let stderr = &mut BufWriter::new(stderr);
        println(
            stderr,
            "Can only compare file to file or directory to directory, not like this:",
        );
        println(
            stderr,
            &format!("  {:<9}: {}", type_string(path1), path1.to_string_lossy()),
        );
        println(
            stderr,
            &format!("  {:<9}: {}", type_string(path2), path2.to_string_lossy()),
        );
        let _flush_result = stderr.flush();
        exit(1);
    }

    // Run "diff -ur file1 file2"
    let command: &mut Command = &mut Command::new("diff");
    let command = command
        .arg("-ur") // "-u = unified diff, -r = recurse subdirectories"
        .arg(path1)
        .arg(path2)
        .stdout(Stdio::piped());

    match command.spawn() {
        Ok(mut diff) => {
            let diff_stdout = diff.stdout.as_mut().unwrap();

            highlight_stream(diff_stdout);

            let _diff_exit_code = diff.wait();
            // FIXME: Do we care what happened to diff? How to handle an error here?
        }
        Err(_) => {
            panic!("FIXME: Better error reporting here");
        }
    }
}

fn main() {
    panic::set_hook(Box::new(|panic_info: &panic::PanicInfo| {
        panic_handler(panic_info);
    }));

    let mut args: Vec<String> = env::args().collect();
    if consume("--help", &mut args) || consume("-h", &mut args) {
        print_help(&mut io::stdout());
        return;
    }

    if consume("--version", &mut args) {
        println!("riff {}", GIT_VERSION);
        println!();
        println!("Source code available at <https://github.com/walles/riff>.");
        return;
    }

    if consume("--please-panic", &mut args) {
        panic!("Panicking on purpose");
    }

    if args.len() == 3 {
        // "riff file1 file2"
        exec_diff_highlight(args.get(1).unwrap(), args.get(2).unwrap());
        return;
    }

    if stdin_isatty() {
        eprintln!("ERROR: Expected input from a pipe");
        eprintln!("");
        print_help(&mut io::stderr());
        exit(1);
    }

    highlight_stream(&mut io::stdin().lock());
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    fn old(text: &str) -> String {
        return format!("{}{}{}", OLD, text, NORMAL);
    }

    fn new(text: &str) -> String {
        return format!("{}{}{}", NEW, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            old(&format!("-hej{}⏎", INVERSE_VIDEO)),
            new("+hej"),
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut actual: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut actual);
        assert_eq!(std::str::from_utf8(&actual).unwrap(), expected);
    }

    #[test]
    fn test_trailing_newline_context() {
        let mut input = "+bepa\n apa\n\\ No newline at end of file\n".as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            new("+bepa"),
            " apa",
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }

    #[test]
    fn test_testdata_examples() {
        // Example value: `/Users/johan/src/riff/target/debug/deps/riff-7a8916c06b0d3d6c`
        let exe_path = std::env::current_exe().unwrap();

        // Example value: `/Users/johan/src/riff`
        let project_path = exe_path
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap()
            .parent()
            .unwrap();

        // Example value: `/Users/johan/src/riff/testdata`
        let testdata_path = &project_path.join("testdata");
        assert!(testdata_path.is_dir());

        // Iterate all files in there
        for diff in fs::read_dir(testdata_path).unwrap() {
            let diff = diff.unwrap();
            let diff = diff.path();
            let diff = diff.as_path();
            if !diff.is_file() {
                continue;
            }

            if diff.extension().unwrap() != "diff" {
                continue;
            }

            println!("Evaluating example file <{}>...", diff.to_str().unwrap());

            // Run highlighting on the file into a memory buffer
            let mut actual_result: Vec<u8> = Vec::new();
            highlight_diff(&mut fs::File::open(diff).unwrap(), &mut actual_result);
            let actual_result = str::from_utf8(&actual_result).unwrap();

            // Load the corresponding .riff-output file into a string
            let basename = diff.file_stem().unwrap().to_str().unwrap();
            let expected_path = format!(
                "{}/{}.riff-output",
                testdata_path.to_str().unwrap(),
                basename
            );
            let expected_result = fs::read_to_string(expected_path).unwrap();

            // Assert that the highlighting output matches the contents of .riff-output
            assert_eq!(actual_result, expected_result);
        }
    }
}
<|endoftext|>


<commit_before>extern crate mmap;
extern crate time;
extern crate timely;
extern crate getopts;

extern crate graph_map;
extern crate graph_layout;
extern crate radix_sort;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::nested::Summary::Local;
use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::dataflow::channels::pact::{Pipeline, Exchange};
use timely::drain::DrainExt;

use graph_layout::layout::*;
use graph_layout::compression::*;

use graph_map::graph_map::GraphMMap;

use radix_sort::*;

fn main () {

    /*
        The plan here is to partition the rows of the matrix into #processes parts, and the columns
        into #threads parts. This could be generally #row_parts and #col_parts, but that requires
        more thinking about things.
    */

    let threads: u32 = ::std::env::args().nth(1).unwrap().parse().unwrap();
    let filename = ::std::env::args().nth(2).unwrap();
    let nodes: u32 = ::std::env::args().nth(3).unwrap().parse().unwrap();

    timely::execute_from_args(::std::env::args().skip(4), move |root| {

        let index = root.index() as u32;
        let peers = root.peers() as u32;
        let processes = (peers / threads) as u32;
        let dst_index = index % threads;

        let start = time::precise_time_s();
        let mut going = start;

        let mut counter = 0;
        let mut sorter = RadixSorter::new();
        let hilbert = Hilbert::new();
        let mut decoder = BytewiseCached::new();
        let mut compressed = vec![];

        // let nodes: u32 = 3_563_602_788 + 1;

        let local_srcs = (nodes / processes) + 1;
        let local_dsts = (nodes / threads) + 1;

        // degree is ignored at the moment.
        // mostly just a pain to put together.
        let deg = vec![1.0f32; local_srcs as usize];
        let mut src = vec![1.0f32; local_srcs as usize];
        let mut dst = vec![0.0f32; local_dsts as usize];

        let mut input = root.scoped(|builder| {

            let (input, edges) = builder.new_input::<(u32, u32)>();
            let (cycle, ranks) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

            let ranks = edges.binary_notify(&ranks,
                                Exchange::new(move |x: &(u32,u32)| ((x.0 % processes) * threads + (x.1 % threads)) as u64),
                                Pipeline,   // data are partitioned as part of the broadcast step.
                                "hilbrank",
                                vec![RootTimestamp::new(0)],
                                move |input1, input2, output, notificator| {

                // receive incoming edges (should only be iter 0)
                while let Some((_index, data)) = input1.next() {
                    counter += data.len();
                    for &(src,dst) in data.iter() {
                        let shrunk = (src / processes, dst / threads);
                        sorter.push(hilbert.entangle(shrunk), &|&x| x);
                    }

                    // should be 1GB per worker
                    if counter > (1 << 27) {
                        let mut results = sorter.finish(&|&x| x);
                        let mut compressor = Compressor::with_capacity(counter);
                        for vec in results.iter_mut() {
                            for elem in vec.drain_temp() {
                                compressor.push(elem);
                            }
                        }
                        sorter.recycle(results);
                        compressed.push(compressor.done());
                        counter = 0;
                    }
                }

                if counter > 0 {
                    // round out any remaining edges
                    let mut results = sorter.finish(&|&x| x);
                    let mut compressor = Compressor::with_capacity(counter);
                    for vec in results.iter_mut() {
                        for elem in vec.drain_temp() {
                            compressor.push(elem);
                        }
                    }
                    compressed.push(compressor.done());
                    counter = 0;
                }

                // all inputs received for iter, commence multiplication
                while let Some((iter, _)) = notificator.next() {

                    let now = time::now();

                    if index == 0 { println!("{}:{}:{}.{} starting iteration {}", now.tm_hour, now.tm_min, now.tm_sec, now.tm_nsec, iter.inner); }

                    // if the very first iteration, prepare some stuff.
                    if iter.inner == 0 {
                        // now we would merge the compressed representations, if we want to.
                        // let's measure both doing this and not doing it. :)

                    }

                    // record some timings in order to estimate per-iteration times
                    // if iter.inner == 0  && index == 0 { println!("src: {}, dst: {}, edges: {}", src.len(), rev.len(), trn.len()); }
                    if iter.inner == 10 && index == 0 { going = time::precise_time_s(); }
                    if iter.inner == 20 && index == 0 { println!("average: {}", (time::precise_time_s() - going) / 10.0 ); }

                    // prepare src for transmitting to destinations
                    for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }
                    for d in 0..dst.len() { dst[d] = 0.0; }

                    let test = time::precise_time_s();

                    // do the multiplication for each compressed sequence
                    for sequence in &compressed {
                        for element in sequence.decompress() {
                            let (s,d) = decoder.detangle(element);
                            dst[d as usize] += src[s as usize];
                        }
                    }

                    if index == 0 { println!("mult: {}", time::precise_time_s() - test); }

                    for s in 0..src.len() { src[s] = 0.0; }

                    // now we need to send some stuff to other threads.
                    // TODO : This could be done as blocks, as they mostly go to the same place.
                    let mut session = output.session(&iter);
                    for (index, &value) in dst.iter().enumerate() {
                        if value != 0.0 {
                            session.give((index as u32 * threads + dst_index, value));
                        }
                    }
                }

                // receive data from threads, accumulate in src
                while let Some((iter, data)) = input2.next() {
                    notificator.notify_at(&iter);
                    for &(node, rank) in data.iter() {
                        src[(node / processes) as usize] += rank;
                    }
                }
            });

            let mut aggregates = vec![0.0f32; (nodes / peers) as usize];
            // want to do aggregation so that aggregates are local to the folks who will need them.
            // that is, (node, rank) goes to some worker on machine#: name % processes
            let aggregated = ranks.unary_notify(
                Exchange::new(move |&(n,_): &(u32,f32)| (((n % processes) * threads) + ((n / processes) % threads)) as u64),
                "aggregation",
                vec![],
                move |input, output, notificator | {

                while let Some((time, data)) = input.next() {
                    notificator.notify_at(time);
                    for &(node, rank) in data.iter() {
                        aggregates[(node / peers) as usize] += rank;
                    }
                }

                while let Some((time, _)) = notificator.next() {
                    let test = time::precise_time_s();
                    let mut session = output.session(&time);
                    for (node, &rank) in aggregates.iter().enumerate() {
                        if rank != 0.0 {
                            session.give((node as u32 * peers, rank));
                        }
                    }
                    if index == 0 { println!("aggr: {}", time::precise_time_s() - test); }
                }
            });

            // data are now aggregated, and only need to be broadcast out to the intended recipients
            // ideally, none of this actually ends up hitting the network. also, it seems like it
            // shouldn't even require looking at `node`, on account of already putting all the data
            // on the right processes. We should be able to use (index / threads) * threads, I think.
            let mut exchanged = aggregated.exchange(move |&(node, _)| ((node % processes) * threads) as u64);
            for i in 1..threads {
                exchanged = aggregated.exchange(move |&(node, _)| (((node % processes) * threads) + i) as u64)
                                      .concat(&exchanged);
            }

            exchanged.connect_loop(cycle);

            input
        });

        // introduce edges into the computation;
        // allow mmaped file to drop
        {
            let graph = GraphMMap::new(&filename);
            for node in 0..graph.nodes() {
                if node as u32 % peers == index {
                    for dst in graph.edges(node) {
                        input.send((node as u32, *dst as u32));
                    }
                }
            }
        }
        input.close();
        while root.step() { }

        if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
    });

}
<commit_msg>measurements<commit_after>extern crate mmap;
extern crate time;
extern crate timely;
extern crate getopts;

extern crate graph_map;
extern crate graph_layout;
extern crate radix_sort;

use timely::progress::timestamp::RootTimestamp;
use timely::progress::nested::Summary::Local;
use timely::dataflow::*;
use timely::dataflow::operators::*;
use timely::dataflow::channels::pact::{Pipeline, Exchange};
use timely::drain::DrainExt;

use graph_layout::layout::*;
use graph_layout::compression::*;

use graph_map::graph_map::GraphMMap;

use radix_sort::*;

fn main () {

    /*
        The plan here is to partition the rows of the matrix into #processes parts, and the columns
        into #threads parts. This could be generally #row_parts and #col_parts, but that requires
        more thinking about things.
    */

    let threads: u32 = ::std::env::args().nth(1).unwrap().parse().unwrap();
    let filename = ::std::env::args().nth(2).unwrap();
    let nodes: u32 = ::std::env::args().nth(3).unwrap().parse().unwrap();

    timely::execute_from_args(::std::env::args().skip(4), move |root| {

        let index = root.index() as u32;
        let peers = root.peers() as u32;
        let processes = (peers / threads) as u32;
        let dst_index = index % threads;

        let start = time::precise_time_s();
        let mut going = start;

        let mut counter = 0;
        let mut sorter = RadixSorter::new();
        let hilbert = Hilbert::new();
        let mut decoder = BytewiseCached::new();
        let mut compressed = vec![];

        // let nodes: u32 = 3_563_602_788 + 1;

        let local_srcs = (nodes / processes) + 1;
        let local_dsts = (nodes / threads) + 1;

        // degree is ignored at the moment.
        // mostly just a pain to put together.
        let deg = vec![1.0f32; local_srcs as usize];
        let mut src = vec![1.0f32; local_srcs as usize];
        let mut dst = vec![0.0f32; local_dsts as usize];

        let mut input = root.scoped(|builder| {

            let (input, edges) = builder.new_input::<(u32, u32)>();
            let (cycle, ranks) = builder.loop_variable::<(u32, f32)>(RootTimestamp::new(20), Local(1));

            let ranks = edges.binary_notify(&ranks,
                                Exchange::new(move |x: &(u32,u32)| ((x.0 % processes) * threads + (x.1 % threads)) as u64),
                                Pipeline,   // data are partitioned as part of the broadcast step.
                                "hilbrank",
                                vec![RootTimestamp::new(0)],
                                move |input1, input2, output, notificator| {

                // receive incoming edges (should only be iter 0)
                while let Some((_index, data)) = input1.next() {
                    counter += data.len();
                    for &(src,dst) in data.iter() {
                        let shrunk = (src / processes, dst / threads);
                        sorter.push(hilbert.entangle(shrunk), &|&x| x);
                    }

                    // should be 1GB per worker
                    if counter > (1 << 27) {
                        let mut results = sorter.finish(&|&x| x);
                        let mut compressor = Compressor::with_capacity(counter);
                        for vec in results.iter_mut() {
                            for elem in vec.drain_temp() {
                                compressor.push(elem);
                            }
                        }
                        sorter.recycle(results);
                        compressed.push(compressor.done());
                        counter = 0;
                    }
                }

                if counter > 0 {
                    // round out any remaining edges
                    let mut results = sorter.finish(&|&x| x);
                    let mut compressor = Compressor::with_capacity(counter);
                    for vec in results.iter_mut() {
                        for elem in vec.drain_temp() {
                            compressor.push(elem);
                        }
                    }
                    compressed.push(compressor.done());
                    counter = 0;
                }

                // all inputs received for iter, commence multiplication
                while let Some((iter, _)) = notificator.next() {

                    let now = time::now();

                    if index == 0 { println!("{}:{}:{}.{} starting iteration {}", now.tm_hour, now.tm_min, now.tm_sec, now.tm_nsec, iter.inner); }

                    // if the very first iteration, prepare some stuff.
                    if iter.inner == 0 {
                        // now we would merge the compressed representations, if we want to.
                        // let's measure both doing this and not doing it. :)

                    }

                    // record some timings in order to estimate per-iteration times
                    // if iter.inner == 0  && index == 0 { println!("src: {}, dst: {}, edges: {}", src.len(), rev.len(), trn.len()); }
                    if iter.inner == 10 && index == 0 { going = time::precise_time_s(); }
                    if iter.inner == 20 && index == 0 { println!("average: {}", (time::precise_time_s() - going) / 10.0 ); }

                    // prepare src for transmitting to destinations
                    for s in 0..src.len() { src[s] = 0.15 + 0.85 * src[s] / deg[s] as f32; }
                    for d in 0..dst.len() { dst[d] = 0.0; }

                    if index == 0 { println!("mult (start): {}", time::precise_time_s() - start); }

                    // do the multiplication for each compressed sequence
                    for sequence in &compressed {
                        for element in sequence.decompress() {
                            let (s,d) = decoder.detangle(element);
                            dst[d as usize] += src[s as usize];
                        }
                    }

                    if index == 0 { println!("mult (end): {}", time::precise_time_s() - start); }

                    for s in 0..src.len() { src[s] = 0.0; }

                    // now we need to send some stuff to other threads.
                    // TODO : This could be done as blocks, as they mostly go to the same place.
                    let mut session = output.session(&iter);
                    for (index, &value) in dst.iter().enumerate() {
                        if value != 0.0 {
                            session.give((index as u32 * threads + dst_index, value));
                        }
                    }
                }

                // receive data from threads, accumulate in src
                while let Some((iter, data)) = input2.next() {
                    notificator.notify_at(&iter);
                    for &(node, rank) in data.iter() {
                        src[(node / processes) as usize] += rank;
                    }
                }
            });

            let mut aggregates = vec![0.0f32; (nodes / peers) as usize];
            // want to do aggregation so that aggregates are local to the folks who will need them.
            // that is, (node, rank) goes to some worker on machine#: name % processes
            let aggregated = ranks.unary_notify(
                Exchange::new(move |&(n,_): &(u32,f32)| (((n % processes) * threads) + ((n / processes) % threads)) as u64),
                "aggregation",
                vec![],
                move |input, output, notificator | {

                while let Some((time, data)) = input.next() {
                    notificator.notify_at(time);
                    for &(node, rank) in data.iter() {
                        aggregates[(node / peers) as usize] += rank;
                    }
                }

                while let Some((time, _)) = notificator.next() {
                    if index == 0 { println!("aggr (start): {}", time::precise_time_s() - start); }
                    let mut session = output.session(&time);
                    for (node, &rank) in aggregates.iter().enumerate() {
                        if rank != 0.0 {
                            session.give((node as u32 * peers, rank));
                        }
                    }
                    if index == 0 { println!("aggr (end): {}", time::precise_time_s() - start); }
                }
            });

            // data are now aggregated, and only need to be broadcast out to the intended recipients
            // ideally, none of this actually ends up hitting the network. also, it seems like it
            // shouldn't even require looking at `node`, on account of already putting all the data
            // on the right processes. We should be able to use (index / threads) * threads, I think.
            let mut exchanged = aggregated.exchange(move |&(node, _)| ((node % processes) * threads) as u64);
            for i in 1..threads {
                exchanged = aggregated.exchange(move |&(node, _)| (((node % processes) * threads) + i) as u64)
                                      .concat(&exchanged);
            }

            exchanged.connect_loop(cycle);

            input
        });

        // introduce edges into the computation;
        // allow mmaped file to drop
        {
            let graph = GraphMMap::new(&filename);
            for node in 0..graph.nodes() {
                if node as u32 % peers == index {
                    for dst in graph.edges(node) {
                        input.send((node as u32, *dst as u32));
                    }
                }
            }
        }
        input.close();
        while root.step() { }

        if index == 0 { println!("elapsed: {}", time::precise_time_s() - start); }
    });

}
<|endoftext|>