<commit_before>use std::mem;

use orbclient::{Color, Event, EventOption, Window};
use orbclient::event;

const BLACK: Color = Color::rgb(0, 0, 0);
const RED: Color = Color::rgb(194, 54, 33);
const GREEN: Color = Color::rgb(37, 188, 36);
const YELLOW: Color = Color::rgb(173, 173, 39);
const BLUE: Color = Color::rgb(73, 46, 225);
const MAGENTA: Color = Color::rgb(211, 56, 211);
const CYAN: Color = Color::rgb(51, 187, 200);
const WHITE: Color = Color::rgb(203, 204, 205);

pub struct Console {
    pub window: Box<Window>,
    pub point_x: i32,
    pub point_y: i32,
    pub foreground: Color,
    pub background: Color,
    pub redraw: bool,
    pub command: String,
    pub escape: bool,
    pub escape_sequence: bool,
    pub sequence: Vec<String>,
}

impl Console {
    pub fn new() -> Console {
        let mut window = Window::new(-1, -1, 640, 480, "Terminal").unwrap();
        window.sync();
        Console {
            window: window,
            point_x: 0,
            point_y: 0,
            foreground: WHITE,
            background: BLACK,
            redraw: true,
            command: String::new(),
            escape: false,
            escape_sequence: false,
            sequence: Vec::new(),
        }
    }

    pub fn code(&mut self, c: char) {
        if self.escape_sequence {
            if c >= '0' && c <= '9' {
                // Add a number to the sequence list
                if let Some(mut value) = self.sequence.last_mut() {
                    value.push(c);
                }
            } else if c == ';' {
                // Split sequence into list
                self.sequence.push(String::new());
            } else if c == 'm' {
                // Display attributes
                for value in self.sequence.iter() {
                    if value == "0" {
                        // Reset all
                        self.foreground = WHITE;
                        self.background = BLACK;
                    } else if value == "30" {
                        self.foreground = BLACK;
                    } else if value == "31" {
                        self.foreground = RED;
                    } else if value == "32" {
                        self.foreground = GREEN;
                    } else if value == "33" {
                        self.foreground = YELLOW;
                    } else if value == "34" {
                        self.foreground = BLUE;
                    } else if value == "35" {
                        self.foreground = MAGENTA;
                    } else if value == "36" {
                        self.foreground = CYAN;
                    } else if value == "37" {
                        self.foreground = WHITE;
                    } else if value == "40" {
                        self.background = BLACK;
                    } else if value == "41" {
                        self.background = RED;
                    } else if value == "42" {
                        self.background = GREEN;
                    } else if value == "43" {
                        self.background = YELLOW;
                    } else if value == "44" {
                        self.background = BLUE;
                    } else if value == "45" {
                        self.background = MAGENTA;
                    } else if value == "46" {
                        self.background = CYAN;
                    } else if value == "47" {
                        self.background = WHITE;
                    }
                }

                self.escape_sequence = false;
            } else if c == 'H' || c == 'f' {
                self.window.rect(self.point_x, self.point_y, 8, 16, self.background);

                let row = self.sequence.get(0).map_or("", |p| &p).parse::<i32>().unwrap_or(0);
                self.point_y = row * 16;

                let col = self.sequence.get(1).map_or("", |p| &p).parse::<i32>().unwrap_or(0);
                self.point_x = col * 8;

                self.window.rect(self.point_x, self.point_y, 8, 16, self.foreground);
                self.redraw = true;

                self.escape_sequence = false;
            } else {
                self.escape_sequence = false;
            }

            if !self.escape_sequence {
                self.sequence.clear();
                self.escape = false;
            }
        } else if c == '[' {
            // Control sequence initiator

            self.escape_sequence = true;
            self.sequence.push(String::new());
        } else if c == 'c' {
            // Reset
            self.point_x = 0;
            self.point_y = 0;
            self.foreground = WHITE;
            self.background = BLACK;
            self.window.set(self.background);
            self.redraw = true;

            self.escape = false;
        } else {
            // Unknown escape character

            self.escape = false;
        }
    }

    pub fn scroll(&mut self, rows: usize) {
        if rows > 0 && rows < self.window.height() as usize {
            let offset = rows * self.window.width() as usize;
            let data = self.window.data_mut();
            for i in 0..data.len() - offset {
                let color = data[i + offset];
                data[i] = color;
            }
            for i in data.len() - offset..data.len() {
                data[i] = self.background;
            }
        }
    }

    pub fn character(&mut self, c: char) {
        self.window.rect(self.point_x, self.point_y, 8, 16, self.background);
        if c == '\x00' {
            // Ignore null character
        } else if c == '\x1B' {
            self.escape = true;
        } else if c == '\n' {
            self.point_x = 0;
            self.point_y += 16;
        } else if c == '\t' {
            self.point_x = ((self.point_x / 64) + 1) * 64;
        } else if c == '\x08' {
            self.point_x -= 8;
            if self.point_x < 0 {
                self.point_x = 0
            }
            self.window.rect(self.point_x, self.point_y, 8, 16, self.background);
        } else {
            self.window.char(self.point_x, self.point_y, c, self.foreground);
            self.point_x += 8;
        }
        if self.point_x >= self.window.width() as i32 {
            self.point_x = 0;
            self.point_y += 16;
        }
        while self.point_y + 16 > self.window.height() as i32 {
            self.scroll(16);
            self.point_y -= 16;
        }
        self.window.rect(self.point_x, self.point_y, 8, 16, self.foreground);
        self.redraw = true;
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    match key_event.scancode {
                        event::K_BKSP => if ! self.command.is_empty() {
                            self.write(&[8]);
                            self.command.pop();
                        },
                        _ => match key_event.character {
                            '\0' => (),
                            c => {
                                self.command.push(c);
                                self.write(&[c as u8]);

                                if c == '\n' {
                                    let mut command = String::new();
                                    mem::swap(&mut self.command, &mut command);
                                    return Some(command);
                                }
                            }
                        },
                    }
                }
            },
            _ => (),
        }

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        for byte in bytes.iter() {
            let c = *byte as char;

            if self.escape {
                self.code(c);
            } else {
                self.character(c);
            }
        }

        if self.redraw {
            self.redraw = false;
            self.window.sync();
        }
    }
}
<commit_msg>Carriage return<commit_after>use std::mem;

use orbclient::{Color, Event, EventOption, Window};
use orbclient::event;

const BLACK: Color = Color::rgb(0, 0, 0);
const RED: Color = Color::rgb(194, 54, 33);
const GREEN: Color = Color::rgb(37, 188, 36);
const YELLOW: Color = Color::rgb(173, 173, 39);
const BLUE: Color = Color::rgb(73, 46, 225);
const MAGENTA: Color = Color::rgb(211, 56, 211);
const CYAN: Color = Color::rgb(51, 187, 200);
const WHITE: Color = Color::rgb(203, 204, 205);

pub struct Console {
    pub window: Box<Window>,
    pub point_x: i32,
    pub point_y: i32,
    pub foreground: Color,
    pub background: Color,
    pub redraw: bool,
    pub command: String,
    pub escape: bool,
    pub escape_sequence: bool,
    pub sequence: Vec<String>,
}

impl Console {
    pub fn new() -> Console {
        let mut window = Window::new(-1, -1, 640, 480, "Terminal").unwrap();
        window.sync();
        Console {
            window: window,
            point_x: 0,
            point_y: 0,
            foreground: WHITE,
            background: BLACK,
            redraw: true,
            command: String::new(),
            escape: false,
            escape_sequence: false,
            sequence: Vec::new(),
        }
    }

    pub fn code(&mut self, c: char) {
        if self.escape_sequence {
            if c >= '0' && c <= '9' {
                // Add a number to the sequence list
                if let Some(mut value) = self.sequence.last_mut() {
                    value.push(c);
                }
            } else if c == ';' {
                // Split sequence into list
                self.sequence.push(String::new());
            } else if c == 'm' {
                // Display attributes
                for value in self.sequence.iter() {
                    if value == "0" {
                        // Reset all
                        self.foreground = WHITE;
                        self.background = BLACK;
                    } else if value == "30" {
                        self.foreground = BLACK;
                    } else if value == "31" {
                        self.foreground = RED;
                    } else if value == "32" {
                        self.foreground = GREEN;
                    } else if value == "33" {
                        self.foreground = YELLOW;
                    } else if value == "34" {
                        self.foreground = BLUE;
                    } else if value == "35" {
                        self.foreground = MAGENTA;
                    } else if value == "36" {
                        self.foreground = CYAN;
                    } else if value == "37" {
                        self.foreground = WHITE;
                    } else if value == "40" {
                        self.background = BLACK;
                    } else if value == "41" {
                        self.background = RED;
                    } else if value == "42" {
                        self.background = GREEN;
                    } else if value == "43" {
                        self.background = YELLOW;
                    } else if value == "44" {
                        self.background = BLUE;
                    } else if value == "45" {
                        self.background = MAGENTA;
                    } else if value == "46" {
                        self.background = CYAN;
                    } else if value == "47" {
                        self.background = WHITE;
                    }
                }

                self.escape_sequence = false;
            } else if c == 'H' || c == 'f' {
                self.window.rect(self.point_x, self.point_y, 8, 16, self.background);

                let row = self.sequence.get(0).map_or("", |p| &p).parse::<i32>().unwrap_or(0);
                self.point_y = row * 16;

                let col = self.sequence.get(1).map_or("", |p| &p).parse::<i32>().unwrap_or(0);
                self.point_x = col * 8;

                self.window.rect(self.point_x, self.point_y, 8, 16, self.foreground);
                self.redraw = true;

                self.escape_sequence = false;
            } else {
                self.escape_sequence = false;
            }

            if !self.escape_sequence {
                self.sequence.clear();
                self.escape = false;
            }
        } else if c == '[' {
            // Control sequence initiator

            self.escape_sequence = true;
            self.sequence.push(String::new());
        } else if c == 'c' {
            // Reset
            self.point_x = 0;
            self.point_y = 0;
            self.foreground = WHITE;
            self.background = BLACK;
            self.window.set(self.background);
            self.redraw = true;

            self.escape = false;
        } else {
            // Unknown escape character

            self.escape = false;
        }
    }

    pub fn scroll(&mut self, rows: usize) {
        if rows > 0 && rows < self.window.height() as usize {
            let offset = rows * self.window.width() as usize;
            let data = self.window.data_mut();
            for i in 0..data.len() - offset {
                let color = data[i + offset];
                data[i] = color;
            }
            for i in data.len() - offset..data.len() {
                data[i] = self.background;
            }
        }
    }

    pub fn character(&mut self, c: char) {
        self.window.rect(self.point_x, self.point_y, 8, 16, self.background);
        if c == '\x00' {
            // Ignore null character
        } else if c == '\x1B' {
            self.escape = true;
        } else if c == '\n' {
            self.point_x = 0;
            self.point_y += 16;
        } else if c == '\r' {
            self.point_x = 0;
        } else if c == '\t' {
            self.point_x = ((self.point_x / 64) + 1) * 64;
        } else if c == '\x08' {
            self.point_x -= 8;
            if self.point_x < 0 {
                self.point_x = 0
            }
            self.window.rect(self.point_x, self.point_y, 8, 16, self.background);
        } else {
            self.window.char(self.point_x, self.point_y, c, self.foreground);
            self.point_x += 8;
        }
        if self.point_x >= self.window.width() as i32 {
            self.point_x = 0;
            self.point_y += 16;
        }
        while self.point_y + 16 > self.window.height() as i32 {
            self.scroll(16);
            self.point_y -= 16;
        }
        self.window.rect(self.point_x, self.point_y, 8, 16, self.foreground);
        self.redraw = true;
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    match key_event.scancode {
                        event::K_BKSP => if ! self.command.is_empty() {
                            self.write(&[8]);
                            self.command.pop();
                        },
                        _ => match key_event.character {
                            '\0' => (),
                            c => {
                                self.command.push(c);
                                self.write(&[c as u8]);

                                if c == '\n' {
                                    let mut command = String::new();
                                    mem::swap(&mut self.command, &mut command);
                                    return Some(command);
                                }
                            }
                        },
                    }
                }
            },
            _ => (),
        }

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        for byte in bytes.iter() {
            let c = *byte as char;

            if self.escape {
                self.code(c);
            } else {
                self.character(c);
            }
        }

        if self.redraw {
            self.redraw = false;
            self.window.sync();
        }
    }
}
<|endoftext|>


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

#[macro_use]
extern crate uucore;

use clap::{App, Arg, ArgMatches};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Result, Write};
use std::path::Path;
use std::str::FromStr;
use strum_macros::{AsRefStr, EnumString};

static ABOUT: &str = "Report or omit repeated lines.";
static VERSION: &str = env!("CARGO_PKG_VERSION");
pub mod options {
    pub static ALL_REPEATED: &str = "all-repeated";
    pub static CHECK_CHARS: &str = "check-chars";
    pub static COUNT: &str = "count";
    pub static IGNORE_CASE: &str = "ignore-case";
    pub static REPEATED: &str = "repeated";
    pub static SKIP_FIELDS: &str = "skip-fields";
    pub static SKIP_CHARS: &str = "skip-chars";
    pub static UNIQUE: &str = "unique";
    pub static ZERO_TERMINATED: &str = "zero-terminated";
    pub static GROUP: &str = "group";
}

static ARG_FILES: &str = "files";

#[derive(PartialEq, Clone, Copy, AsRefStr, EnumString)]
#[strum(serialize_all = "snake_case")]
enum Delimiters {
    Append,
    Prepend,
    Separate,
    Both,
    None,
}

struct Uniq {
    repeats_only: bool,
    uniques_only: bool,
    all_repeated: bool,
    delimiters: Delimiters,
    show_counts: bool,
    skip_fields: Option<usize>,
    slice_start: Option<usize>,
    slice_stop: Option<usize>,
    ignore_case: bool,
    zero_terminated: bool,
}

impl Uniq {
    pub fn print_uniq<R: Read, W: Write>(
        &self,
        reader: &mut BufReader<R>,
        writer: &mut BufWriter<W>,
    ) {
        let mut lines: Vec<String> = vec![];
        let mut first_line_printed = false;
        let delimiters = self.delimiters;
        let line_terminator = self.get_line_terminator();
        // Don't print any delimiting lines before, after or between groups if delimiting method is 'none'
        let no_delimiters = delimiters == Delimiters::None;
        // The 'prepend' and 'both' delimit methods will cause output to start with delimiter line
        let prepend_delimiter = delimiters == Delimiters::Prepend || delimiters == Delimiters::Both;
        // The 'append' and 'both' delimit methods will cause output to end with delimiter line
        let append_delimiter = delimiters == Delimiters::Append || delimiters == Delimiters::Both;

        for line in reader.split(line_terminator).map(get_line_string) {
            if !lines.is_empty() && self.cmp_keys(&lines[0], &line) {
                // Print delimiter if delimit method is not 'none' and any line has been output
                // before or if we need to start output with delimiter
                let print_delimiter = !no_delimiters && (prepend_delimiter || first_line_printed);
                first_line_printed |= self.print_lines(writer, &lines, print_delimiter);
                lines.truncate(0);
            }
            lines.push(line);
        }
        if !lines.is_empty() {
            // Print delimiter if delimit method is not 'none' and any line has been output
            // before or if we need to start output with delimiter
            let print_delimiter = !no_delimiters && (prepend_delimiter || first_line_printed);
            first_line_printed |= self.print_lines(writer, &lines, print_delimiter);
        }
        if append_delimiter && first_line_printed {
            crash_if_err!(1, writer.write_all(&[line_terminator]));
        }
    }

    fn skip_fields<'a>(&self, line: &'a str) -> &'a str {
        if let Some(skip_fields) = self.skip_fields {
            let mut i = 0;
            let mut char_indices = line.char_indices();
            for _ in 0..skip_fields {
                if char_indices.find(|(_, c)| !c.is_whitespace()) == None {
                    return "";
                }
                match char_indices.find(|(_, c)| c.is_whitespace()) {
                    None => return "",

                    Some((next_field_i, _)) => i = next_field_i,
                }
            }
            &line[i..]
        } else {
            line
        }
    }

    fn get_line_terminator(&self) -> u8 {
        if self.zero_terminated {
            0
        } else {
            b'\n'
        }
    }

    fn cmp_keys(&self, first: &str, second: &str) -> bool {
        self.cmp_key(first, |first_iter| {
            self.cmp_key(second, |second_iter| first_iter.ne(second_iter))
        })
    }

    fn cmp_key<F>(&self, line: &str, mut closure: F) -> bool
    where
        F: FnMut(&mut dyn Iterator<Item = char>) -> bool,
    {
        let fields_to_check = self.skip_fields(line);
        let len = fields_to_check.len();
        let slice_start = self.slice_start.unwrap_or(0);
        let slice_stop = self.slice_stop.unwrap_or(len);
        if len > 0 {
            // fast path: avoid doing any work if there is no need to skip or map to lower-case
            if !self.ignore_case && slice_start == 0 && slice_stop == len {
                return closure(&mut fields_to_check.chars());
            }

            // fast path: avoid skipping
            if self.ignore_case && slice_start == 0 && slice_stop == len {
                return closure(&mut fields_to_check.chars().flat_map(|c| c.to_uppercase()));
            }

            // fast path: we can avoid mapping chars to upper-case, if we don't want to ignore the case
            if !self.ignore_case {
                return closure(&mut fields_to_check.chars().skip(slice_start).take(slice_stop));
            }

            closure(
                &mut fields_to_check
                    .chars()
                    .skip(slice_start)
                    .take(slice_stop)
                    .flat_map(|c| c.to_uppercase()),
            )
        } else {
            closure(&mut fields_to_check.chars())
        }
    }

    fn print_lines<W: Write>(
        &self,
        writer: &mut BufWriter<W>,
        lines: &[String],
        print_delimiter: bool,
    ) -> bool {
        let mut first_line_printed = false;
        let mut count = if self.all_repeated { 1 } else { lines.len() };
        if lines.len() == 1 && !self.repeats_only || lines.len() > 1 && !self.uniques_only {
            self.print_line(writer, &lines[0], count, print_delimiter);
            first_line_printed = true;
            count += 1;
        }
        if self.all_repeated {
            for line in lines[1..].iter() {
                self.print_line(writer, line, count, print_delimiter && !first_line_printed);
                first_line_printed = true;
                count += 1;
            }
        }
        first_line_printed
    }

    fn print_line<W: Write>(
        &self,
        writer: &mut BufWriter<W>,
        line: &str,
        count: usize,
        print_delimiter: bool,
    ) {
        let line_terminator = self.get_line_terminator();

        if print_delimiter {
            crash_if_err!(1, writer.write_all(&[line_terminator]));
        }

        crash_if_err!(
            1,
            if self.show_counts {
                writer.write_all(format!("{:7} {}", count, line).as_bytes())
            } else {
                writer.write_all(line.as_bytes())
            }
        );
        crash_if_err!(1, writer.write_all(&[line_terminator]));
    }
}

fn get_line_string(io_line: Result<Vec<u8>>) -> String {
    let line_bytes = crash_if_err!(1, io_line);
    crash_if_err!(1, String::from_utf8(line_bytes))
}

fn opt_parsed<T: FromStr>(opt_name: &str, matches: &ArgMatches) -> Option<T> {
    matches.value_of(opt_name).map(|arg_str| {
        let opt_val: Option<T> = arg_str.parse().ok();
        opt_val.unwrap_or_else(|| crash!(1, "Invalid argument for {}: {}", opt_name, arg_str))
    })
}

fn get_usage() -> String {
    format!("{0} [OPTION]... [INPUT [OUTPUT]]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "Filter adjacent matching lines from INPUT (or standard input),\n\
        writing to OUTPUT (or standard output).
        Note: 'uniq' does not detect repeated lines unless they are adjacent.\n\
        You may want to sort the input first, or use 'sort -u' without 'uniq'.\n",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(options::ALL_REPEATED)
                .short("D")
                .long(options::ALL_REPEATED)
                .possible_values(&[
                    Delimiters::None.as_ref(), Delimiters::Prepend.as_ref(), Delimiters::Separate.as_ref()
                ])
                .help("print all duplicate lines. Delimiting is done with blank lines. [default: none]")
                .value_name("delimit-method")
                .min_values(0)
                .max_values(1),
        )
        .arg(
            Arg::with_name(options::GROUP)
                .long(options::GROUP)
                .possible_values(&[
                    Delimiters::Separate.as_ref(), Delimiters::Prepend.as_ref(),
                    Delimiters::Append.as_ref(), Delimiters::Both.as_ref()
                ])
                .help("show all items, separating groups with an empty line. [default: separate]")
                .value_name("group-method")
                .min_values(0)
                .max_values(1)
                .conflicts_with_all(&[
                    options::REPEATED,
                    options::ALL_REPEATED,
                    options::UNIQUE,
                ]),
        )
        .arg(
            Arg::with_name(options::CHECK_CHARS)
                .short("w")
                .long(options::CHECK_CHARS)
                .help("compare no more than N characters in lines")
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::COUNT)
                .short("c")
                .long(options::COUNT)
                .help("prefix lines by the number of occurrences"),
        )
        .arg(
            Arg::with_name(options::IGNORE_CASE)
                .short("i")
                .long(options::IGNORE_CASE)
                .help("ignore differences in case when comparing"),
        )
        .arg(
            Arg::with_name(options::REPEATED)
                .short("d")
                .long(options::REPEATED)
                .help("only print duplicate lines"),
        )
        .arg(
            Arg::with_name(options::SKIP_CHARS)
                .short("s")
                .long(options::SKIP_CHARS)
                .help("avoid comparing the first N characters")
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::SKIP_FIELDS)
                .short("f")
                .long(options::SKIP_FIELDS)
                .help("avoid comparing the first N fields")
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::UNIQUE)
                .short("u")
                .long(options::UNIQUE)
                .help("only print unique lines"),
        )
        .arg(
            Arg::with_name(options::ZERO_TERMINATED)
                .short("z")
                .long(options::ZERO_TERMINATED)
                .help("end lines with 0 byte, not newline"),
        )
        .arg(
            Arg::with_name(ARG_FILES)
                .multiple(true)
                .takes_value(true)
                .max_values(2),
        )
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let (in_file_name, out_file_name) = match files.len() {
        0 => ("-".to_owned(), "-".to_owned()),
        1 => (files[0].clone(), "-".to_owned()),
        2 => (files[0].clone(), files[1].clone()),
        _ => {
            // Cannot happen as clap will fail earlier
            crash!(1, "Extra operand: {}", files[2]);
        }
    };

    let uniq = Uniq {
        repeats_only: matches.is_present(options::REPEATED)
            || matches.is_present(options::ALL_REPEATED),
        uniques_only: matches.is_present(options::UNIQUE),
        all_repeated: matches.is_present(options::ALL_REPEATED)
            || matches.is_present(options::GROUP),
        delimiters: get_delimiter(&matches),
        show_counts: matches.is_present(options::COUNT),
        skip_fields: opt_parsed(options::SKIP_FIELDS, &matches),
        slice_start: opt_parsed(options::SKIP_CHARS, &matches),
        slice_stop: opt_parsed(options::CHECK_CHARS, &matches),
        ignore_case: matches.is_present(options::IGNORE_CASE),
        zero_terminated: matches.is_present(options::ZERO_TERMINATED),
    };
    uniq.print_uniq(
        &mut open_input_file(in_file_name),
        &mut open_output_file(out_file_name),
    );

    0
}

fn get_delimiter(matches: &ArgMatches) -> Delimiters {
    let value = matches
        .value_of(options::ALL_REPEATED)
        .or_else(|| matches.value_of(options::GROUP));
    if let Some(delimiter_arg) = value {
        crash_if_err!(1, Delimiters::from_str(delimiter_arg))
    } else if matches.is_present(options::GROUP) {
        Delimiters::Separate
    } else {
        Delimiters::None
    }
}

fn open_input_file(in_file_name: String) -> BufReader<Box<dyn Read + 'static>> {
    let in_file = if in_file_name == "-" {
        Box::new(stdin()) as Box<dyn Read>
    } else {
        let path = Path::new(&in_file_name[..]);
        let in_file = File::open(&path);
        let r = crash_if_err!(1, in_file);
        Box::new(r) as Box<dyn Read>
    };
    BufReader::new(in_file)
}

fn open_output_file(out_file_name: String) -> BufWriter<Box<dyn Write + 'static>> {
    let out_file = if out_file_name == "-" {
        Box::new(stdout()) as Box<dyn Write>
    } else {
        let path = Path::new(&out_file_name[..]);
        let in_file = File::create(&path);
        let w = crash_if_err!(1, in_file);
        Box::new(w) as Box<dyn Write>
    };
    BufWriter::new(out_file)
}
<commit_msg>uniq: avoid building list of duplicate lines<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Chirag B Jadwani <chirag.jadwani@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

#[macro_use]
extern crate uucore;

use clap::{App, Arg, ArgMatches};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Result, Write};
use std::path::Path;
use std::str::FromStr;
use strum_macros::{AsRefStr, EnumString};

static ABOUT: &str = "Report or omit repeated lines.";
static VERSION: &str = env!("CARGO_PKG_VERSION");
pub mod options {
    pub static ALL_REPEATED: &str = "all-repeated";
    pub static CHECK_CHARS: &str = "check-chars";
    pub static COUNT: &str = "count";
    pub static IGNORE_CASE: &str = "ignore-case";
    pub static REPEATED: &str = "repeated";
    pub static SKIP_FIELDS: &str = "skip-fields";
    pub static SKIP_CHARS: &str = "skip-chars";
    pub static UNIQUE: &str = "unique";
    pub static ZERO_TERMINATED: &str = "zero-terminated";
    pub static GROUP: &str = "group";
}

static ARG_FILES: &str = "files";

#[derive(PartialEq, Clone, Copy, AsRefStr, EnumString)]
#[strum(serialize_all = "snake_case")]
enum Delimiters {
    Append,
    Prepend,
    Separate,
    Both,
    None,
}

struct Uniq {
    repeats_only: bool,
    uniques_only: bool,
    all_repeated: bool,
    delimiters: Delimiters,
    show_counts: bool,
    skip_fields: Option<usize>,
    slice_start: Option<usize>,
    slice_stop: Option<usize>,
    ignore_case: bool,
    zero_terminated: bool,
}

impl Uniq {
    pub fn print_uniq<R: Read, W: Write>(
        &self,
        reader: &mut BufReader<R>,
        writer: &mut BufWriter<W>,
    ) {
        let mut first_line_printed = false;
        let mut group_count = 1;
        let line_terminator = self.get_line_terminator();
        let mut lines = reader.split(line_terminator).map(get_line_string);
        let mut line = match lines.next() {
            Some(l) => l,
            None => return,
        };

        // compare current `line` with consecutive lines (`next_line`) of the input
        // and if needed, print `line` based on the command line options provided
        for next_line in lines {
            if self.cmp_keys(&line, &next_line) {
                if (group_count == 1 && !self.repeats_only)
                    || (group_count > 1 && !self.uniques_only)
                {
                    self.print_line(writer, &line, group_count, first_line_printed);
                    first_line_printed = true;
                }
                line = next_line;
                group_count = 1;
            } else {
                if self.all_repeated {
                    self.print_line(writer, &line, group_count, first_line_printed);
                    first_line_printed = true;
                    line = next_line;
                }
                group_count += 1;
            }
        }
        if (group_count == 1 && !self.repeats_only) || (group_count > 1 && !self.uniques_only) {
            self.print_line(writer, &line, group_count, first_line_printed);
            first_line_printed = true;
        }
        if (self.delimiters == Delimiters::Append || self.delimiters == Delimiters::Both)
            && first_line_printed
        {
            crash_if_err!(1, writer.write_all(&[line_terminator]));
        }
    }

    fn skip_fields<'a>(&self, line: &'a str) -> &'a str {
        if let Some(skip_fields) = self.skip_fields {
            let mut i = 0;
            let mut char_indices = line.char_indices();
            for _ in 0..skip_fields {
                if char_indices.find(|(_, c)| !c.is_whitespace()) == None {
                    return "";
                }
                match char_indices.find(|(_, c)| c.is_whitespace()) {
                    None => return "",

                    Some((next_field_i, _)) => i = next_field_i,
                }
            }
            &line[i..]
        } else {
            line
        }
    }

    fn get_line_terminator(&self) -> u8 {
        if self.zero_terminated {
            0
        } else {
            b'\n'
        }
    }

    fn cmp_keys(&self, first: &str, second: &str) -> bool {
        self.cmp_key(first, |first_iter| {
            self.cmp_key(second, |second_iter| first_iter.ne(second_iter))
        })
    }

    fn cmp_key<F>(&self, line: &str, mut closure: F) -> bool
    where
        F: FnMut(&mut dyn Iterator<Item = char>) -> bool,
    {
        let fields_to_check = self.skip_fields(line);
        let len = fields_to_check.len();
        let slice_start = self.slice_start.unwrap_or(0);
        let slice_stop = self.slice_stop.unwrap_or(len);
        if len > 0 {
            // fast path: avoid doing any work if there is no need to skip or map to lower-case
            if !self.ignore_case && slice_start == 0 && slice_stop == len {
                return closure(&mut fields_to_check.chars());
            }

            // fast path: avoid skipping
            if self.ignore_case && slice_start == 0 && slice_stop == len {
                return closure(&mut fields_to_check.chars().flat_map(|c| c.to_uppercase()));
            }

            // fast path: we can avoid mapping chars to upper-case, if we don't want to ignore the case
            if !self.ignore_case {
                return closure(&mut fields_to_check.chars().skip(slice_start).take(slice_stop));
            }

            closure(
                &mut fields_to_check
                    .chars()
                    .skip(slice_start)
                    .take(slice_stop)
                    .flat_map(|c| c.to_uppercase()),
            )
        } else {
            closure(&mut fields_to_check.chars())
        }
    }

    fn should_print_delimiter(&self, group_count: usize, first_line_printed: bool) -> bool {
        // if no delimiter option is selected then no other checks needed
        self.delimiters != Delimiters::None
            // print delimiter only before the first line of a group, not between lines of a group
            && group_count == 1
            // if at least one line has been output before current group then print delimiter
            && (first_line_printed
                // or if we need to prepend delimiter then print it even at the start of the output
                || self.delimiters == Delimiters::Prepend
                // the 'both' delimit mode should prepend and append delimiters
                || self.delimiters == Delimiters::Both)
    }

    fn print_line<W: Write>(
        &self,
        writer: &mut BufWriter<W>,
        line: &str,
        count: usize,
        first_line_printed: bool,
    ) {
        let line_terminator = self.get_line_terminator();

        if self.should_print_delimiter(count, first_line_printed) {
            crash_if_err!(1, writer.write_all(&[line_terminator]));
        }

        crash_if_err!(
            1,
            if self.show_counts {
                writer.write_all(format!("{:7} {}", count, line).as_bytes())
            } else {
                writer.write_all(line.as_bytes())
            }
        );
        crash_if_err!(1, writer.write_all(&[line_terminator]));
    }
}

fn get_line_string(io_line: Result<Vec<u8>>) -> String {
    let line_bytes = crash_if_err!(1, io_line);
    crash_if_err!(1, String::from_utf8(line_bytes))
}

fn opt_parsed<T: FromStr>(opt_name: &str, matches: &ArgMatches) -> Option<T> {
    matches.value_of(opt_name).map(|arg_str| {
        let opt_val: Option<T> = arg_str.parse().ok();
        opt_val.unwrap_or_else(|| crash!(1, "Invalid argument for {}: {}", opt_name, arg_str))
    })
}

fn get_usage() -> String {
    format!("{0} [OPTION]... [INPUT [OUTPUT]]...", executable!())
}

fn get_long_usage() -> String {
    String::from(
        "Filter adjacent matching lines from INPUT (or standard input),\n\
        writing to OUTPUT (or standard output).
        Note: 'uniq' does not detect repeated lines unless they are adjacent.\n\
        You may want to sort the input first, or use 'sort -u' without 'uniq'.\n",
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let long_usage = get_long_usage();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(&long_usage[..])
        .arg(
            Arg::with_name(options::ALL_REPEATED)
                .short("D")
                .long(options::ALL_REPEATED)
                .possible_values(&[
                    Delimiters::None.as_ref(), Delimiters::Prepend.as_ref(), Delimiters::Separate.as_ref()
                ])
                .help("print all duplicate lines. Delimiting is done with blank lines. [default: none]")
                .value_name("delimit-method")
                .min_values(0)
                .max_values(1),
        )
        .arg(
            Arg::with_name(options::GROUP)
                .long(options::GROUP)
                .possible_values(&[
                    Delimiters::Separate.as_ref(), Delimiters::Prepend.as_ref(),
                    Delimiters::Append.as_ref(), Delimiters::Both.as_ref()
                ])
                .help("show all items, separating groups with an empty line. [default: separate]")
                .value_name("group-method")
                .min_values(0)
                .max_values(1)
                .conflicts_with_all(&[
                    options::REPEATED,
                    options::ALL_REPEATED,
                    options::UNIQUE,
                ]),
        )
        .arg(
            Arg::with_name(options::CHECK_CHARS)
                .short("w")
                .long(options::CHECK_CHARS)
                .help("compare no more than N characters in lines")
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::COUNT)
                .short("c")
                .long(options::COUNT)
                .help("prefix lines by the number of occurrences"),
        )
        .arg(
            Arg::with_name(options::IGNORE_CASE)
                .short("i")
                .long(options::IGNORE_CASE)
                .help("ignore differences in case when comparing"),
        )
        .arg(
            Arg::with_name(options::REPEATED)
                .short("d")
                .long(options::REPEATED)
                .help("only print duplicate lines"),
        )
        .arg(
            Arg::with_name(options::SKIP_CHARS)
                .short("s")
                .long(options::SKIP_CHARS)
                .help("avoid comparing the first N characters")
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::SKIP_FIELDS)
                .short("f")
                .long(options::SKIP_FIELDS)
                .help("avoid comparing the first N fields")
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::UNIQUE)
                .short("u")
                .long(options::UNIQUE)
                .help("only print unique lines"),
        )
        .arg(
            Arg::with_name(options::ZERO_TERMINATED)
                .short("z")
                .long(options::ZERO_TERMINATED)
                .help("end lines with 0 byte, not newline"),
        )
        .arg(
            Arg::with_name(ARG_FILES)
                .multiple(true)
                .takes_value(true)
                .max_values(2),
        )
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let (in_file_name, out_file_name) = match files.len() {
        0 => ("-".to_owned(), "-".to_owned()),
        1 => (files[0].clone(), "-".to_owned()),
        2 => (files[0].clone(), files[1].clone()),
        _ => {
            // Cannot happen as clap will fail earlier
            crash!(1, "Extra operand: {}", files[2]);
        }
    };

    let uniq = Uniq {
        repeats_only: matches.is_present(options::REPEATED)
            || matches.is_present(options::ALL_REPEATED),
        uniques_only: matches.is_present(options::UNIQUE),
        all_repeated: matches.is_present(options::ALL_REPEATED)
            || matches.is_present(options::GROUP),
        delimiters: get_delimiter(&matches),
        show_counts: matches.is_present(options::COUNT),
        skip_fields: opt_parsed(options::SKIP_FIELDS, &matches),
        slice_start: opt_parsed(options::SKIP_CHARS, &matches),
        slice_stop: opt_parsed(options::CHECK_CHARS, &matches),
        ignore_case: matches.is_present(options::IGNORE_CASE),
        zero_terminated: matches.is_present(options::ZERO_TERMINATED),
    };
    uniq.print_uniq(
        &mut open_input_file(in_file_name),
        &mut open_output_file(out_file_name),
    );

    0
}

fn get_delimiter(matches: &ArgMatches) -> Delimiters {
    let value = matches
        .value_of(options::ALL_REPEATED)
        .or_else(|| matches.value_of(options::GROUP));
    if let Some(delimiter_arg) = value {
        crash_if_err!(1, Delimiters::from_str(delimiter_arg))
    } else if matches.is_present(options::GROUP) {
        Delimiters::Separate
    } else {
        Delimiters::None
    }
}

fn open_input_file(in_file_name: String) -> BufReader<Box<dyn Read + 'static>> {
    let in_file = if in_file_name == "-" {
        Box::new(stdin()) as Box<dyn Read>
    } else {
        let path = Path::new(&in_file_name[..]);
        let in_file = File::open(&path);
        let r = crash_if_err!(1, in_file);
        Box::new(r) as Box<dyn Read>
    };
    BufReader::new(in_file)
}

fn open_output_file(out_file_name: String) -> BufWriter<Box<dyn Write + 'static>> {
    let out_file = if out_file_name == "-" {
        Box::new(stdout()) as Box<dyn Write>
    } else {
        let path = Path::new(&out_file_name[..]);
        let in_file = File::create(&path);
        let w = crash_if_err!(1, in_file);
        Box::new(w) as Box<dyn Write>
    };
    BufWriter::new(out_file)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::{DiagnosticsResult, PerfLogEvent, PerfLogger};
use fnv::FnvHashSet;
use graphql_ir::Program;
use interner::StringKey;
use relay_transforms::*;
use std::sync::Arc;

use super::log_program_stats::print_stats;

#[derive(Debug)]
pub struct Programs {
    pub source: Arc<Program>,
    pub reader: Arc<Program>,
    pub normalization: Arc<Program>,
    pub operation_text: Arc<Program>,
    pub typegen: Arc<Program>,
}

pub fn apply_transforms<TPerfLogger>(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<TPerfLogger>,
) -> DiagnosticsResult<Programs>
where
    TPerfLogger: PerfLogger + 'static,
{
    // The execution pipeline is as follows, where items at the same indentation
    // can be computed independently and therefore in parallel:
    // |- common
    //  |- reader
    //  |- operation
    //     |- normalization
    //     |- operation_text
    // |- typegen
    //
    // NOTE: try_join(f1, f2) prefers the errors from f1 over f2, so process the normalization
    // program first since it is likely to include more errors (both per-fragment and
    // whole-operation errors) than the reader_program which just includes per-fragment errors.
    let (((normalization_program, text_program), reader_program), typegen_program) = try_join(
        || {
            let common_program = apply_common_transforms(
                project_name,
                Arc::clone(&program),
                connection_interface,
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )?;

            try_join(
                || {
                    let operation_program = apply_operation_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        connection_interface,
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )?;

                    try_join(
                        || {
                            apply_normalization_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&feature_flags),
                                Arc::clone(&perf_logger),
                            )
                        },
                        || {
                            apply_operation_text_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&feature_flags),
                                Arc::clone(&perf_logger),
                            )
                        },
                    )
                },
                || {
                    apply_reader_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        Arc::clone(&feature_flags),
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )
                },
            )
        },
        || {
            apply_typegen_transforms(
                project_name,
                Arc::clone(&program),
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )
        },
    )?;

    Ok(Programs {
        source: program,
        reader: reader_program,
        normalization: normalization_program,
        operation_text: text_program,
        typegen: typegen_program,
    })
}

/// Applies transforms that apply to every output.
fn apply_common_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // * DisallowIdAsAlias (in validate)
    // + ConnectionTransform
    // * RelayDirectiveTransform (in validate)
    // + MaskTransform
    // + MatchTransform
    // + RefetchableFragmentTransform
    // + DeferStreamTransform
    let log_event = perf_logger.create_event("apply_common_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("transform_connections", || {
        transform_connections(&program, connection_interface)
    });
    program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_defer_stream", || {
        transform_defer_stream(&program)
    })?;
    program = log_event.time("transform_match", || transform_match(&program))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, false)
    })?;
    program = if feature_flags.enable_flight_transform {
        log_event.time("react_flight", || react_flight(&program))?
    } else {
        program
    };
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms only for generated reader code.
/// Corresponds to the "fragment transforms" in the JS compiler.
fn apply_reader_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + ClientExtensionsTransform
    // + FieldHandleTransform
    // + InlineDataFragmentTransform
    // + FlattenTransform, flattenAbstractTypes: true
    // + SkipRedundantNodesTransform
    let log_event = perf_logger.create_event("apply_reader_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;

    program = log_event.time("client_extensions", || client_extensions(&program));
    program = log_event.time("handle_field_transform", || {
        handle_field_transform(&program)
    });
    program = log_event.time("inline_data_fragment", || inline_data_fragment(&program))?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    log_event.time("flatten", || flatten(&mut program, true))?;
    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    program = log_event.time("generate_data_driven_dependency_metadata", || {
        generate_data_driven_dependency_metadata(&program)
    });

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms that apply to all operation artifacts.
/// Corresponds to the "query transforms" in the JS compiler.
fn apply_operation_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SplitModuleImportTransform
    // * ValidateUnusedVariablesTransform (Moved to common_transforms)
    // + ApplyFragmentArgumentTransform
    // + ValidateGlobalVariablesTransform
    // + GenerateIDFieldTransform
    // * TestOperationTransform - part of relay_codegen
    let log_event = perf_logger.create_event("apply_operation_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("split_module_import", || {
        split_module_import(&program, &base_fragment_names)
    });
    program = log_event.time("generate_id_field", || generate_id_field(&program));
    program = log_event.time("declarative_connection", || {
        transform_declarative_connection(&program, connection_interface)
    })?;

    // TODO(T67052528): execute FB-specific transforms only if config options is provided
    program = log_event.time("generate_subscription_name_metadata", || {
        generate_subscription_name_metadata(&program)
    })?;
    program = log_event.time("generate_live_query_metadata", || {
        generate_live_query_metadata(&program)
    })?;

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the generated normalization code.
///
/// Corresponds to the "codegen transforms" in the JS compiler
fn apply_normalization_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SkipUnreachableNodeTransform
    // + InlineFragmentsTransform
    // + ClientExtensionsTransform
    // + GenerateTypeNameTransform
    // + FlattenTransform, flattenAbstractTypes: true
    // + SkipRedundantNodesTransform
    let log_event = perf_logger.create_event("apply_normalization_transforms");
    log_event.string("project", project_name.to_string());
    print_stats("normalization start", &program);

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(&program, true, &feature_flags.no_inline)
    })?;
    print_stats("apply_fragment_arguments", &program);
    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    print_stats("relay_early_flush", &program);

    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    print_stats("skip_unreachable_node", &program);

    program = log_event.time("inline_fragments", || inline_fragments(&program));
    print_stats("inline_fragments", &program);

    program = log_event.time("client_extensions", || client_extensions(&program));
    print_stats("client_extensions", &program);

    program = log_event.time("generate_typename", || generate_typename(&program, true));
    print_stats("generate_typename", &program);

    log_event.time("flatten", || flatten(&mut program, true))?;
    print_stats("flatten", &program);

    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    print_stats("skip_redundant_nodes", &program);

    program = log_event.time("generate_test_operation_metadata", || {
        generate_test_operation_metadata(&program)
    });
    print_stats("generate_test_operation_metadata", &program);

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the printed operation text.
///
/// Corresponds to the "print transforms" in the JS compiler
fn apply_operation_text_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // + SkipSplitOperationTransform
    // * ClientExtensionsTransform (not necessary in rust)
    // + SkipClientExtensionsTransform
    // + SkipUnreachableNodeTransform
    // + GenerateTypeNameTransform
    // + FlattenTransform, flattenAbstractTypes: false
    // * SkipHandleFieldTransform (not necessary in rust)
    // + FilterDirectivesTransform
    // + SkipUnusedVariablesTransform
    // + ValidateRequiredArgumentsTransform
    let log_event = perf_logger.create_event("apply_operation_text_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(&program, false, &feature_flags.no_inline)
    })?;
    log_event.time("validate_global_variables", || {
        validate_global_variables(&program)
    })?;
    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    program = log_event.time("skip_split_operation", || skip_split_operation(&program));
    program = log_event.time("skip_client_extensions", || {
        skip_client_extensions(&program)
    });
    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    program = log_event.time("generate_typename", || generate_typename(&program, false));
    log_event.time("flatten", || flatten(&mut program, false))?;
    program = log_event.time("skip_unused_variables", || skip_unused_variables(&program));
    program = log_event.time("skip_client_directives", || {
        skip_client_directives(&program)
    });
    log_event.time("validate_required_arguments", || {
        validate_required_arguments(&program)
    })?;
    program = log_event.time("unwrap_custom_directive_selection", || {
        unwrap_custom_directive_selection(&program)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn apply_typegen_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    // JS compiler
    // * RelayDirectiveTransform
    // + MaskTransform
    // + MatchTransform
    // + FlattenTransform, flattenAbstractTypes: false
    // + RefetchableFragmentTransform,
    let log_event = perf_logger.create_event("apply_typegen_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_match", || transform_match(&program))?;
    program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;
    log_event.time("flatten", || flatten(&mut program, false))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, true)
    })?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn try_join<T1, F1, T2, F2, E>(f1: F1, f2: F2) -> Result<(T1, T2), Vec<E>>
where
    F1: FnOnce() -> Result<T1, Vec<E>> + Send,
    F2: FnOnce() -> Result<T2, Vec<E>> + Send,
    T1: Send,
    T2: Send,
    E: Send,
{
    let (v1, v2) = rayon::join(f1, f2);
    Ok((v1?, v2?))
}
<commit_msg>remove references to JS transforms from Rust compiler<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::{DiagnosticsResult, PerfLogEvent, PerfLogger};
use fnv::FnvHashSet;
use graphql_ir::Program;
use interner::StringKey;
use relay_transforms::*;
use std::sync::Arc;

use super::log_program_stats::print_stats;

#[derive(Debug)]
pub struct Programs {
    pub source: Arc<Program>,
    pub reader: Arc<Program>,
    pub normalization: Arc<Program>,
    pub operation_text: Arc<Program>,
    pub typegen: Arc<Program>,
}

pub fn apply_transforms<TPerfLogger>(
    project_name: StringKey,
    program: Arc<Program>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<TPerfLogger>,
) -> DiagnosticsResult<Programs>
where
    TPerfLogger: PerfLogger + 'static,
{
    // The execution pipeline is as follows, where items at the same indentation
    // can be computed independently and therefore in parallel:
    // |- common
    //  |- reader
    //  |- operation
    //     |- normalization
    //     |- operation_text
    // |- typegen
    //
    // NOTE: try_join(f1, f2) prefers the errors from f1 over f2, so process the normalization
    // program first since it is likely to include more errors (both per-fragment and
    // whole-operation errors) than the reader_program which just includes per-fragment errors.
    let (((normalization_program, text_program), reader_program), typegen_program) = try_join(
        || {
            let common_program = apply_common_transforms(
                project_name,
                Arc::clone(&program),
                connection_interface,
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )?;

            try_join(
                || {
                    let operation_program = apply_operation_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        connection_interface,
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )?;

                    try_join(
                        || {
                            apply_normalization_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&feature_flags),
                                Arc::clone(&perf_logger),
                            )
                        },
                        || {
                            apply_operation_text_transforms(
                                project_name,
                                Arc::clone(&operation_program),
                                Arc::clone(&feature_flags),
                                Arc::clone(&perf_logger),
                            )
                        },
                    )
                },
                || {
                    apply_reader_transforms(
                        project_name,
                        Arc::clone(&common_program),
                        Arc::clone(&feature_flags),
                        Arc::clone(&base_fragment_names),
                        Arc::clone(&perf_logger),
                    )
                },
            )
        },
        || {
            apply_typegen_transforms(
                project_name,
                Arc::clone(&program),
                Arc::clone(&feature_flags),
                Arc::clone(&base_fragment_names),
                Arc::clone(&perf_logger),
            )
        },
    )?;

    Ok(Programs {
        source: program,
        reader: reader_program,
        normalization: normalization_program,
        operation_text: text_program,
        typegen: typegen_program,
    })
}

/// Applies transforms that apply to every output.
fn apply_common_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_common_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("transform_connections", || {
        transform_connections(&program, connection_interface)
    });
    program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_defer_stream", || {
        transform_defer_stream(&program)
    })?;
    program = log_event.time("transform_match", || transform_match(&program))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, false)
    })?;
    program = if feature_flags.enable_flight_transform {
        log_event.time("react_flight", || react_flight(&program))?
    } else {
        program
    };
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms only for generated reader code.
/// Corresponds to the "fragment transforms" in the JS compiler.
fn apply_reader_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_reader_transforms");
    log_event.string("project", project_name.to_string());
    let mut program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;

    program = log_event.time("client_extensions", || client_extensions(&program));
    program = log_event.time("handle_field_transform", || {
        handle_field_transform(&program)
    });
    program = log_event.time("inline_data_fragment", || inline_data_fragment(&program))?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    log_event.time("flatten", || flatten(&mut program, true))?;
    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    program = log_event.time("generate_data_driven_dependency_metadata", || {
        generate_data_driven_dependency_metadata(&program)
    });

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// Applies transforms that apply to all operation artifacts.
/// Corresponds to the "query transforms" in the JS compiler.
fn apply_operation_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    connection_interface: &ConnectionInterface,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_operation_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("split_module_import", || {
        split_module_import(&program, &base_fragment_names)
    });
    program = log_event.time("generate_id_field", || generate_id_field(&program));
    program = log_event.time("declarative_connection", || {
        transform_declarative_connection(&program, connection_interface)
    })?;

    // TODO(T67052528): execute FB-specific transforms only if config options is provided
    program = log_event.time("generate_subscription_name_metadata", || {
        generate_subscription_name_metadata(&program)
    })?;
    program = log_event.time("generate_live_query_metadata", || {
        generate_live_query_metadata(&program)
    })?;

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the generated normalization code.
///
/// Corresponds to the "codegen transforms" in the JS compiler
fn apply_normalization_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_normalization_transforms");
    log_event.string("project", project_name.to_string());
    print_stats("normalization start", &program);

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(&program, true, &feature_flags.no_inline)
    })?;
    print_stats("apply_fragment_arguments", &program);
    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    print_stats("relay_early_flush", &program);

    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    print_stats("skip_unreachable_node", &program);

    program = log_event.time("inline_fragments", || inline_fragments(&program));
    print_stats("inline_fragments", &program);

    program = log_event.time("client_extensions", || client_extensions(&program));
    print_stats("client_extensions", &program);

    program = log_event.time("generate_typename", || generate_typename(&program, true));
    print_stats("generate_typename", &program);

    log_event.time("flatten", || flatten(&mut program, true))?;
    print_stats("flatten", &program);

    program = log_event.time("skip_redundant_nodes", || skip_redundant_nodes(&program));
    print_stats("skip_redundant_nodes", &program);

    program = log_event.time("generate_test_operation_metadata", || {
        generate_test_operation_metadata(&program)
    });
    print_stats("generate_test_operation_metadata", &program);

    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

/// After the operation transforms, this applies further transforms that only
/// apply to the printed operation text.
///
/// Corresponds to the "print transforms" in the JS compiler
fn apply_operation_text_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_operation_text_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("apply_fragment_arguments", || {
        apply_fragment_arguments(&program, false, &feature_flags.no_inline)
    })?;
    log_event.time("validate_global_variables", || {
        validate_global_variables(&program)
    })?;
    program = log_event.time("relay_early_flush", || relay_early_flush(&program))?;
    program = log_event.time("skip_split_operation", || skip_split_operation(&program));
    program = log_event.time("skip_client_extensions", || {
        skip_client_extensions(&program)
    });
    program = log_event.time("skip_unreachable_node", || skip_unreachable_node(&program));
    program = log_event.time("generate_typename", || generate_typename(&program, false));
    log_event.time("flatten", || flatten(&mut program, false))?;
    program = log_event.time("skip_unused_variables", || skip_unused_variables(&program));
    program = log_event.time("skip_client_directives", || {
        skip_client_directives(&program)
    });
    log_event.time("validate_required_arguments", || {
        validate_required_arguments(&program)
    })?;
    program = log_event.time("unwrap_custom_directive_selection", || {
        unwrap_custom_directive_selection(&program)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn apply_typegen_transforms(
    project_name: StringKey,
    program: Arc<Program>,
    feature_flags: Arc<FeatureFlags>,
    base_fragment_names: Arc<FnvHashSet<StringKey>>,
    perf_logger: Arc<impl PerfLogger>,
) -> DiagnosticsResult<Arc<Program>> {
    let log_event = perf_logger.create_event("apply_typegen_transforms");
    log_event.string("project", project_name.to_string());

    let mut program = log_event.time("mask", || mask(&program));
    program = log_event.time("transform_match", || transform_match(&program))?;
    program = log_event.time("required_directive", || {
        required_directive(&program, &feature_flags)
    })?;
    log_event.time("flatten", || flatten(&mut program, false))?;
    program = log_event.time("transform_refetchable_fragment", || {
        transform_refetchable_fragment(&program, &base_fragment_names, true)
    })?;
    program = log_event.time("remove_base_fragments", || {
        remove_base_fragments(&program, base_fragment_names)
    });
    perf_logger.complete_event(log_event);

    Ok(Arc::new(program))
}

fn try_join<T1, F1, T2, F2, E>(f1: F1, f2: F2) -> Result<(T1, T2), Vec<E>>
where
    F1: FnOnce() -> Result<T1, Vec<E>> + Send,
    F2: FnOnce() -> Result<T2, Vec<E>> + Send,
    T1: Send,
    T2: Send,
    E: Send,
{
    let (v1, v2) = rayon::join(f1, f2);
    Ok((v1?, v2?))
}
<|endoftext|>


<commit_before>
#![feature(test)]

extern crate test;
use test::Bencher;
use std::io::Cursor;
use std::fs::File;

extern crate murmur3;

#[bench]
fn bench_32(b: &mut Bencher) {
    b.iter(|| {
        let string: &[u8] = "Lorem ipsum dolor sit amet, consectetur adipisicing elit".as_bytes();
        let mut tmp = Cursor::new(&string[0..string.len()]);
        murmur3::murmur3_32(&mut tmp, 0);
    });
}

#[bench]
fn bench_x64_128(b: &mut Bencher) {
    b.iter(|| {
        let mut out: [u8; 16] = [0; 16];
        let string: &[u8] = "Lorem ipsum dolor sit amet, consectetur adipisicing elit".as_bytes();
        let mut tmp = Cursor::new(&string[0..string.len()]);
        murmur3::murmur3_x64_128(&mut tmp, 0, &mut out);
    });
}

<commit_msg>Adding a change I left out in a previous commit.<commit_after>
#![feature(test)]

extern crate test;
use test::Bencher;
use std::io::Cursor;

extern crate murmur3;

#[bench]
fn bench_32(b: &mut Bencher) {
    b.iter(|| {
        let string: &[u8] = "Lorem ipsum dolor sit amet, consectetur adipisicing elit".as_bytes();
        let mut tmp = Cursor::new(&string[0..string.len()]);
        murmur3::murmur3_32(&mut tmp, 0);
    });
}

#[bench]
fn bench_x64_128(b: &mut Bencher) {
    b.iter(|| {
        let mut out: [u8; 16] = [0; 16];
        let string: &[u8] = "Lorem ipsum dolor sit amet, consectetur adipisicing elit".as_bytes();
        let mut tmp = Cursor::new(&string[0..string.len()]);
        murmur3::murmur3_x64_128(&mut tmp, 0, &mut out);
    });
}

<|endoftext|>


<commit_before>extern crate dsfmt;
extern crate test;

use test::Bencher;

use std::rand::{Rng, SeedableRng, Isaac64Rng, XorShiftRng};
use dsfmt::DSFMTRng;

#[bench]
fn bench_mt19937_1_000_000_rands(b: &mut Bencher){
    let mut rng: DSFMTRng = SeedableRng::from_seed(1);

    b.iter(||{
        for _ in range(1u, 1_000_000){
            rng.next_f64();
        }
    });
}

#[bench]
fn bench_isaac64_1_000_000_rands(b: &mut Bencher){
    let mut rng: Isaac64Rng = SeedableRng::from_seed([1u64].as_slice());

    b.iter(||{
        for _ in range(1u, 1_000_000){
            rng.next_f64();
        }
    });
}

#[bench]
fn bench_xor_1_000_000_rands(b: &mut Bencher){
    let mut rng: XorShiftRng = SeedableRng::from_seed([1u32, 1u32, 1u32, 1u32]);

    b.iter(||{
        for _ in range(1u, 1_000_000){
            rng.next_f64();
        }
    });
}
<commit_msg>Add `black_box` calls to avoid f64 generation disappearing.<commit_after>extern crate dsfmt;
extern crate test;

use test::Bencher;

use std::rand::{Rng, SeedableRng, Isaac64Rng, XorShiftRng};
use dsfmt::DSFMTRng;

#[bench]
fn bench_mt19937_1_000_000_rands(b: &mut Bencher){
    let mut rng: DSFMTRng = SeedableRng::from_seed(1);

    b.iter(||{
        for _ in range(1u, 1_000_000){
            test::black_box(rng.next_f64());
        }
    });
}

#[bench]
fn bench_isaac64_1_000_000_rands(b: &mut Bencher){
    let mut rng: Isaac64Rng = SeedableRng::from_seed([1u64].as_slice());

    b.iter(||{
        for _ in range(1u, 1_000_000){
            test::black_box(rng.next_f64());
        }
    });
}

#[bench]
fn bench_xor_1_000_000_rands(b: &mut Bencher){
    let mut rng: XorShiftRng = SeedableRng::from_seed([1u32, 1u32, 1u32, 1u32]);

    b.iter(||{
        for _ in range(1u, 1_000_000){
            test::black_box(rng.next_f64());
        }
    });
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate gimli;
extern crate test;

use gimli::{DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugLineOffset, DebugPubNames,
            DebugPubTypes, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into()));
    path.push("./fixtures/self/");
    path.push(section);

    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[bench]
fn bench_parsing_debug_abbrev(b: &mut test::Bencher) {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);
    let unit = debug_info.units()
        .next()
        .expect("Should have at least one compilation unit")
        .expect("And it should parse OK");

    let debug_abbrev = read_section("debug_abbrev");

    b.iter(|| {
        let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);
        test::black_box(unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations"));
    });
}

#[bench]
fn bench_parsing_debug_info(b: &mut test::Bencher) {
    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_info = read_section("debug_info");

    b.iter(|| {
        let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

        let mut iter = debug_info.units();
        while let Some(unit) = iter.next().expect("Should parse compilation unit") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Should parse abbreviations");

            let mut cursor = unit.entries(&abbrevs);
            while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") {
                let mut attrs = entry.attrs();
                while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
                    test::black_box(&attr);
                }
            }
        }
    });
}

#[bench]
fn bench_parsing_debug_aranges(b: &mut test::Bencher) {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    b.iter(|| {
        let mut aranges = debug_aranges.items();
        while let Some(arange) = aranges.next().expect("Should parse arange OK") {
            test::black_box(arange);
        }
    });
}

#[bench]
fn bench_parsing_debug_pubnames(b: &mut test::Bencher) {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    b.iter(|| {
        let mut pubnames = debug_pubnames.items();
        while let Some(pubname) = pubnames.next().expect("Should parse pubname OK") {
            test::black_box(pubname);
        }
    });
}

#[bench]
fn bench_parsing_debug_types(b: &mut test::Bencher) {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    b.iter(|| {
        let mut pubtypes = debug_pubtypes.items();
        while let Some(pubtype) = pubtypes.next().expect("Should parse pubtype OK") {
            test::black_box(pubtype);
        }
    });
}

// We happen to know that there is a line number program and header at
// offset 0 and that address size is 8 bytes. No need to parse DIEs to grab
// this info off of the compilation units.
const OFFSET: DebugLineOffset = DebugLineOffset(0);
const ADDRESS_SIZE: u8 = 8;

#[bench]
fn bench_parsing_line_number_program_opcodes(b: &mut test::Bencher) {
    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    b.iter(|| {
        let header = debug_line.header(OFFSET, ADDRESS_SIZE, None, None)
            .expect("Should parse line number program header");

        let mut opcodes = header.opcodes();
        while let Some(opcode) = opcodes.next_opcode(&header).expect("Should parse opcode") {
            test::black_box(opcode);
        }
    });
}

#[bench]
fn bench_executing_line_number_programs(b: &mut test::Bencher) {
    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    b.iter(|| {
        let header = debug_line.header(OFFSET, ADDRESS_SIZE, None, None)
            .expect("Should parse line number program header");

        let mut rows = header.rows();
        while let Some(row) = rows.next_row()
            .expect("Should parse and execute all rows in the line number program") {
            test::black_box(row);
        }
    });
}
<commit_msg>Add #[bench]s for CFI things<commit_after>#![feature(test)]

extern crate gimli;
extern crate test;

use gimli::{DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugLineOffset, DebugPubNames,
            DebugPubTypes, LittleEndian};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

pub fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into()));
    path.push("./fixtures/self/");
    path.push(section);

    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[bench]
fn bench_parsing_debug_abbrev(b: &mut test::Bencher) {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);
    let unit = debug_info.units()
        .next()
        .expect("Should have at least one compilation unit")
        .expect("And it should parse OK");

    let debug_abbrev = read_section("debug_abbrev");

    b.iter(|| {
        let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);
        test::black_box(unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations"));
    });
}

#[bench]
fn bench_parsing_debug_info(b: &mut test::Bencher) {
    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_info = read_section("debug_info");

    b.iter(|| {
        let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

        let mut iter = debug_info.units();
        while let Some(unit) = iter.next().expect("Should parse compilation unit") {
            let abbrevs = unit.abbreviations(debug_abbrev)
                .expect("Should parse abbreviations");

            let mut cursor = unit.entries(&abbrevs);
            while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") {
                let mut attrs = entry.attrs();
                while let Some(attr) = attrs.next().expect("Should parse entry's attribute") {
                    test::black_box(&attr);
                }
            }
        }
    });
}

#[bench]
fn bench_parsing_debug_aranges(b: &mut test::Bencher) {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    b.iter(|| {
        let mut aranges = debug_aranges.items();
        while let Some(arange) = aranges.next().expect("Should parse arange OK") {
            test::black_box(arange);
        }
    });
}

#[bench]
fn bench_parsing_debug_pubnames(b: &mut test::Bencher) {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    b.iter(|| {
        let mut pubnames = debug_pubnames.items();
        while let Some(pubname) = pubnames.next().expect("Should parse pubname OK") {
            test::black_box(pubname);
        }
    });
}

#[bench]
fn bench_parsing_debug_types(b: &mut test::Bencher) {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    b.iter(|| {
        let mut pubtypes = debug_pubtypes.items();
        while let Some(pubtype) = pubtypes.next().expect("Should parse pubtype OK") {
            test::black_box(pubtype);
        }
    });
}

// We happen to know that there is a line number program and header at
// offset 0 and that address size is 8 bytes. No need to parse DIEs to grab
// this info off of the compilation units.
const OFFSET: DebugLineOffset = DebugLineOffset(0);
const ADDRESS_SIZE: u8 = 8;

#[bench]
fn bench_parsing_line_number_program_opcodes(b: &mut test::Bencher) {
    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    b.iter(|| {
        let header = debug_line.header(OFFSET, ADDRESS_SIZE, None, None)
            .expect("Should parse line number program header");

        let mut opcodes = header.opcodes();
        while let Some(opcode) = opcodes.next_opcode(&header).expect("Should parse opcode") {
            test::black_box(opcode);
        }
    });
}

#[bench]
fn bench_executing_line_number_programs(b: &mut test::Bencher) {
    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    b.iter(|| {
        let header = debug_line.header(OFFSET, ADDRESS_SIZE, None, None)
            .expect("Should parse line number program header");

        let mut rows = header.rows();
        while let Some(row) = rows.next_row()
            .expect("Should parse and execute all rows in the line number program") {
            test::black_box(row);
        }
    });
}

// See comment above `test_parse_self_eh_frame`.
#[cfg(target_pointer_width="64")]
mod cfi {
    extern crate fallible_iterator;
    extern crate gimli;
    extern crate test;

    use super::*;
    use self::fallible_iterator::FallibleIterator;

    use gimli::{BaseAddresses, CieOrFde, EhFrame, FrameDescriptionEntry, LittleEndian,
                UninitializedUnwindContext, UnwindSection, UnwindTable};

    #[bench]
    fn iterate_entries_and_do_not_parse_any_fde(b: &mut test::Bencher) {
        let eh_frame = read_section("eh_frame");
        let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

        let bases = BaseAddresses::default()
            .set_cfi(0)
            .set_data(0)
            .set_text(0);

        b.iter(|| {
            let mut entries = eh_frame.entries(&bases);
            while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
                test::black_box(entry);
            }
        });
    }

    #[bench]
    fn iterate_entries_and_parse_every_fde(b: &mut test::Bencher) {
        let eh_frame = read_section("eh_frame");
        let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

        let bases = BaseAddresses::default()
            .set_cfi(0)
            .set_data(0)
            .set_text(0);

        b.iter(|| {
            let mut entries = eh_frame.entries(&bases);
            while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
                match entry {
                    CieOrFde::Cie(cie) => {
                        test::black_box(cie);
                    }
                    CieOrFde::Fde(partial) => {
                        let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                            .expect("Should be able to get CIE for FED");
                        test::black_box(fde);
                    }
                };
            }
        });
    }

    #[bench]
    fn iterate_entries_and_parse_every_fde_and_instructions(b: &mut test::Bencher) {
        let eh_frame = read_section("eh_frame");
        let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

        let bases = BaseAddresses::default()
            .set_cfi(0)
            .set_data(0)
            .set_text(0);

        b.iter(|| {
            let mut entries = eh_frame.entries(&bases);
            while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
                match entry {
                    CieOrFde::Cie(cie) => {
                        let mut instrs = cie.instructions();
                        while let Some(i) = instrs.next()
                            .expect("Can parse next CFI instruction OK") {
                            test::black_box(i);
                        }
                    }
                    CieOrFde::Fde(partial) => {
                        let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                            .expect("Should be able to get CIE for FED");
                        let mut instrs = fde.instructions();
                        while let Some(i) = instrs.next()
                            .expect("Can parse next CFI instruction OK") {
                            test::black_box(i);
                        }
                    }
                };
            }
        });
    }

    #[bench]
    fn iterate_entries_evaluate_every_fde(b: &mut test::Bencher) {
        let eh_frame = read_section("eh_frame");
        let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);

        let bases = BaseAddresses::default()
            .set_cfi(0)
            .set_data(0)
            .set_text(0);

        let mut ctx = Some(UninitializedUnwindContext::new());

        b.iter(|| {
            let mut entries = eh_frame.entries(&bases);
            while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
                match entry {
                    CieOrFde::Cie(_) => {}
                    CieOrFde::Fde(partial) => {
                        let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                            .expect("Should be able to get CIE for FED");

                        let mut context = ctx.take()
                            .unwrap()
                            .initialize(fde.cie())
                            .expect("Should be able to initialize ctx");

                        {
                            let mut table = UnwindTable::new(&mut context, &fde);
                            while let Some(row) = table.next_row()
                                .expect("Should get next unwind table row") {
                                test::black_box(row);
                            }
                        }

                        ctx = Some(context.reset());
                    }
                };
            }
        });
    }

    fn instrs_len<'input>(fde: &FrameDescriptionEntry<'input,
                                                      LittleEndian,
                                                      EhFrame<'input, LittleEndian>>)
                          -> usize {
        fde.instructions().fold(0, |count, _| count + 1).expect("fold over instructions OK")
    }

    fn get_fde_with_longest_cfi_instructions<'input>
        (eh_frame: EhFrame<'input, LittleEndian>)
         -> FrameDescriptionEntry<'input, LittleEndian, EhFrame<'input, LittleEndian>> {
        let bases = BaseAddresses::default()
            .set_cfi(0)
            .set_data(0)
            .set_text(0);

        let mut longest: Option<(usize, FrameDescriptionEntry<_, _>)> = None;

        let mut entries = eh_frame.entries(&bases);
        while let Some(entry) = entries.next().expect("Should parse CFI entry OK") {
            match entry {
                CieOrFde::Cie(_) => {}
                CieOrFde::Fde(partial) => {
                    let fde = partial.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
                        .expect("Should be able to get CIE for FED");

                    let this_len = instrs_len(&fde);

                    let found_new_longest = match longest {
                        None => true,
                        Some((longest_len, ref _fde)) => this_len > longest_len,
                    };

                    if found_new_longest {
                        longest = Some((this_len, fde));
                    }
                }
            };
        }

        longest.expect("At least one FDE in .eh_frame").1
    }

    #[bench]
    fn parse_longest_fde_instructions(b: &mut test::Bencher) {
        let eh_frame = read_section("eh_frame");
        let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);
        let fde = get_fde_with_longest_cfi_instructions(eh_frame);

        b.iter(|| {
            let mut instrs = fde.instructions();
            while let Some(i) = instrs.next().expect("Should parse instruction OK") {
                test::black_box(i);
            }
        });
    }

    #[bench]
    fn eval_longest_fde_instructions_new_ctx_everytime(b: &mut test::Bencher) {
        let eh_frame = read_section("eh_frame");
        let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);
        let fde = get_fde_with_longest_cfi_instructions(eh_frame);

        b.iter(|| {
            let mut ctx = UninitializedUnwindContext::new()
                .initialize(fde.cie())
                .expect("Should initialize the ctx OK");

            let mut table = UnwindTable::new(&mut ctx, &fde);
            while let Some(row) = table.next_row().expect("Should get next unwind table row") {
                test::black_box(row);
            }
        });
    }

    #[bench]
    fn eval_longest_fde_instructions_same_ctx(b: &mut test::Bencher) {
        let eh_frame = read_section("eh_frame");
        let eh_frame = EhFrame::<LittleEndian>::new(&eh_frame);
        let fde = get_fde_with_longest_cfi_instructions(eh_frame);

        let mut ctx = Some(UninitializedUnwindContext::new());

        b.iter(|| {
            let mut context = ctx.take()
                .unwrap()
                .initialize(fde.cie())
                .expect("Should be able to initialize ctx");

            {
                let mut table = UnwindTable::new(&mut context, &fde);
                while let Some(row) = table.next_row().expect("Should get next unwind table row") {
                    test::black_box(row);
                }
            }

            ctx = Some(context.reset());
        });
    }
}
<|endoftext|>


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

use backtrace::Backtrace;
use logger::prelude::*;
use serde::Serialize;
use std::{
    panic::{self, PanicInfo},
    process, thread, time,
};

#[derive(Debug, Serialize)]
pub struct CrashInfo {
    details: String,
    backtrace: String,
}

pub fn setup_panic_handler() {
    // If RUST_BACKTRACE variable isn't present or RUST_BACKTRACE=0, we setup panic handler
    let is_backtrace_set = std::env::var_os("RUST_BACKTRACE")
        .map(|x| &x != "0")
        .unwrap_or(false);

    if is_backtrace_set {
        info!("Skip panic handler setup because RUST_BACKTRACE is set");
    } else {
        panic::set_hook(Box::new(move |pi: &PanicInfo<'_>| {
            handle_panic(pi);
        }));
    }
}

// formats and logs panic information
fn handle_panic(panic_info: &PanicInfo<'_>) {
    // The Display formatter for a PanicInfo contains the message, payload and location.
    let details = format!("{}", panic_info);
    let backtrace = format!("{:#?}", Backtrace::new());

    let info = CrashInfo { details, backtrace };
    crit!("{}", toml::to_string_pretty(&info).unwrap());

    // allow to save on disk
    thread::sleep(time::Duration::from_millis(100));

    // kill the process
    process::exit(12);
}
<commit_msg>[general] setup_panic_handler ensures all thread panics lead to process exit<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use backtrace::Backtrace;
use logger::prelude::*;
use serde::Serialize;
use std::{
    panic::{self, PanicInfo},
    process, thread, time,
};

#[derive(Debug, Serialize)]
pub struct CrashInfo {
    details: String,
    backtrace: String,
}

/// Invoke to ensure process exits on a thread panic.
///
/// Tokio's default behavior is to catch panics and ignore them.  Invoking this function will
/// ensure that all subsequent thread panics (even Tokio threads) will report the
/// details/backtrace and then exit.
pub fn setup_panic_handler() {
    panic::set_hook(Box::new(move |pi: &PanicInfo<'_>| {
        handle_panic(pi);
    }));
}

// Formats and logs panic information
fn handle_panic(panic_info: &PanicInfo<'_>) {
    // The Display formatter for a PanicInfo contains the message, payload and location.
    let details = format!("{}", panic_info);
    let backtrace = format!("{:#?}", Backtrace::new());

    let info = CrashInfo { details, backtrace };
    crit!("{}", toml::to_string_pretty(&info).unwrap());

    // Provide some time to save the log to disk
    thread::sleep(time::Duration::from_millis(100));

    // Kill the process
    process::exit(12);
}
<|endoftext|>