<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Tyler Steele <tyler.steele@protonmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (T0DO)

#[macro_use]
extern crate uucore;

#[cfg(test)]
mod dd_test;

mod parseargs;

mod conversion_tables;
use conversion_tables::*;

use std::convert::TryInto;
use std::error::Error;
use std::fs::{
    File, OpenOptions,
};
use getopts;
use std::io::{
    self, Read, Write,
    Seek,
};
use std::sync::mpsc;
use std::thread;

const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION";
const SUMMARY: &str = "convert, and optionally copy, a file";
const LONG_HELP: &str = "";

const DEFAULT_FILL_BYTE: u8 = 0xDD;

const RTN_SUCCESS: i32 = 0;
const RTN_FAILURE: i32 = 1;

// ----- Datatypes -----
enum SrcStat
{
    Read(usize),
    EOF,
}

/// Captures all Conv Flags that apply to the input
pub struct ConvFlagInput
{
    ctable: Option<&'static ConversionTable>,
    block: bool,
    unblock: bool,
    swab: bool,
    sync: bool,
    noerror: bool,
}

/// Captures all Conv Flags that apply to the output
#[derive(Debug, PartialEq)]
pub struct ConvFlagOutput
{
    sparse: bool,
    excl: bool,
    nocreat: bool,
    notrunc: bool,
    fdatasync: bool,
    fsync: bool,
}

/// The value of the status cl-option.
/// Controls printing of transfer stats
#[derive(PartialEq)]
pub enum StatusLevel
{
    Progress,
    Noxfer,
    None,
}

#[derive(Debug)]
enum InternalError
{
    WrongInputType,
    WrongOutputType,
}

impl std::fmt::Display for InternalError
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Internal dd error")
    }
}

impl Error for InternalError {}

struct Input<R: Read>
{
    src: R,
    ibs: usize,
    xfer_stats: StatusLevel,
    cf: ConvFlagInput,
}

impl<R: Read> Read for Input<R>
{
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize>
    {
        let len = self.src.read(&mut buf)?;

        if let Some(ct) = self.cf.ctable
        {
            for idx in 0..len
            {
                buf[idx] = ct[buf[idx] as usize];
            }
        }

        if self.cf.swab
        {
            let mut tmp = DEFAULT_FILL_BYTE;

            for base in (1..len).step_by(2)
            {
                tmp = buf[base];
                buf[base] = buf[base-1];
                buf[base-1] = tmp;
            }
        }

        Ok(len)
    }
}

impl Input<io::Stdin>
{
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let ibs = parseargs::parse_ibs(matches)?;
        let xfer_stats = parseargs::parse_status_level(matches)?;
        let cf = parseargs::parse_conv_flag_input(matches)?;

        Ok(
            Input {
                src: io::stdin(),
                ibs,
                xfer_stats,
                cf,
            }
        )

    }
}

impl Input<File>
{
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let ibs = parseargs::parse_ibs(matches)?;
        let xfer_stats = parseargs::parse_status_level(matches)?;
        let cf = parseargs::parse_conv_flag_input(matches)?;

        if let Some(fname) = matches.opt_str("if")
        {
            Ok(Input {
                src: File::open(fname)?,
                ibs,
                xfer_stats,
                cf,
            })
        }
        else
        {
            Err(Box::new(InternalError::WrongInputType))
        }

    }
}

impl<R: Read> Input<R>
{
    fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result<SrcStat, Box<dyn Error>>
    {
        let ibs = self.ibs;
        let mut bytes_read = 0;

        for n in 0..(obs/ibs) {
            // fill an ibs-len slice from src
            let this_read = self.read(&mut buf[n*ibs..(n+1)*ibs])?;

            if this_read != 0 {
                bytes_read += this_read;
            } else {
                break;
            }
        }

        if bytes_read != 0 {
            Ok(SrcStat::Read(bytes_read))
        } else {
            Ok(SrcStat::EOF)
        }
    }
}

struct Output<W: Write>
{
    dst: W,
    obs: usize,
    cf: ConvFlagOutput,
}

impl Output<io::Stdout> {
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let obs = parseargs::parse_obs(matches)?;
        let cf = parseargs::parse_conv_flag_output(matches)?;

        Ok(
            Output {
                dst: io::stdout(),
                obs,
                cf,
            }
        )
    }
}

impl Output<File> {
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let obs = parseargs::parse_obs(matches)?;
        let cf = parseargs::parse_conv_flag_output(matches)?;

        if let Some(fname) = matches.opt_str("of")
        {
            let dst = OpenOptions::new()
                .write(true)
                .create(!cf.nocreat)
                .truncate(!cf.notrunc)
                .open(fname)?;

            Ok(Output {
                dst,
                obs,
                cf,
            })
        }
        else
        {
            Err(Box::new(InternalError::WrongOutputType))
        }
    }
}

impl<W: Write> Seek for Output<W>
{
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
    {
        unimplemented!()
    }
}

// impl Seek for Output<io::Stdout>
// {
//     fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
//     {
//         // Default method. Called when output dst not backed by a traditional file and
//         // should not be seeked (eg. stdout)
//         Ok(0)
//     }
// }
//
// impl Seek for Output<File>
// {
//     fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
//     {
//         self.dst.seek(pos)
//     }
// }

impl<W: Write> Write for Output<W>
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize>
    {
            self.dst.write(buf)
    }

    fn flush(&mut self) -> io::Result<()>
    {
        self.dst.flush()
    }
}

fn is_sparse(buf: &[u8]) -> bool
{
    buf.iter().all(| &e | e == 0)
}

fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
{
    move || {

        // TODO: Replace ?? with accurate info
        print!("\rProgress ({}/??)", 0);

        loop
        {
            match rx.recv()
            {
                Ok(wr_total) => {
                    print!("\rProgress ({}/??)", wr_total);
                },
                Err(_) => {
                    println!("");
                    break
                },
            }
        }
    }
}

fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, usize), Box<dyn Error>>
{
    let prog_tx = if i.xfer_stats == StatusLevel::Progress
    {
        let (prog_tx, prog_rx) = mpsc::channel();

        thread::spawn(gen_prog_updater(prog_rx));

        Some(prog_tx)
    }
    else
    {
        None
    };

    let mut bytes_in  = 0;
    let mut bytes_out = 0;

    loop
    {
        let mut buf = vec![DEFAULT_FILL_BYTE; o.obs];

        // Read
        let r_len = match i.fill_n(&mut buf, o.obs)? {
            SrcStat::Read(len) =>
            {
                bytes_in += len;
                len
            },
            SrcStat::EOF =>
                break,
        };

        // Write
        let w_len = if o.cf.sparse && is_sparse(&buf)
        {
            let seek_amt: i64 = r_len.try_into()?;
            o.seek(io::SeekFrom::Current(seek_amt))?;
            r_len
        }
        else
        {
            o.write(&buf[..r_len])?
        };

        // Prog
        bytes_out += w_len;

        if let Some(prog_tx) = &prog_tx
        {
            prog_tx.send(bytes_out)?;
        }
    }

    // TODO: Also ensure file metadata is written when fsync option is specified. When _wouldn't_ this happen?
    // See fs::File::sync_all && fs::File::sync_data methods!
    if o.cf.fsync || o.cf.fdatasync
    {
        o.flush()?;
    }

    Ok((bytes_in, bytes_out))
}

#[macro_export]
macro_rules! build_app (
    () =>
    {
        app!(SYNTAX, SUMMARY, LONG_HELP)
            .optopt(
                "",
                "if",
                "The input file",
                "FILE"
            )
            .optopt(
                "",
                "ibs",
                "read up to BYTES bytes at a time (default: 512)",
                "BYTES"
            )
            .optopt(
                "",
                "of",
                "The output file",
                "FILE"
            )
            .optopt(
                "",
                "obs",
                "write BYTES bytes at a time (default: 512)",
                "BYTES"
            )
            .optopt(
                "",
                "conv",
                "One or more conversion options as a comma-serparated list",
                "OPT[,OPT]..."
            )
    }
);

fn append_dashes_if_not_present(mut acc: Vec<String>, s: &String) -> Vec<String>
{
    if Some("--") == s.get(0..=1) {
        acc
    } else {
        acc.push(format!("--{}", s));
        acc
    }
}

pub fn uumain(args: impl uucore::Args) -> i32
{
    let dashed_args = args.collect_str()
                          .iter()
                          .fold(Vec::new(), append_dashes_if_not_present);

    let matches = build_app!().parse(dashed_args);

    let result = match (matches.opt_present("if"), matches.opt_present("of"))
    {
        (true, true) =>
        {
            let i = Input::<File>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<File>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
        (true, false) =>
        {
            let i = Input::<File>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<io::Stdout>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
        (false, true) =>
        {
            let i = Input::<io::Stdin>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<File>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
        (false, false) =>
        {
            let i = Input::<io::Stdin>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<io::Stdout>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
    };

    match result
    {
        Ok((b_in, b_out)) =>
        {
            // TODO: Print output stats, unless noxfer

            RTN_SUCCESS
        },
        Err(_) =>
            RTN_FAILURE,
    }
}

<commit_msg>Implements noerror (ignores read errors)<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Tyler Steele <tyler.steele@protonmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (T0DO)

#[macro_use]
extern crate uucore;

#[cfg(test)]
mod dd_test;

mod parseargs;

mod conversion_tables;
use conversion_tables::*;

use std::convert::TryInto;
use std::error::Error;
use std::fs::{
    File, OpenOptions,
};
use getopts;
use std::io::{
    self, Read, Write,
    Seek,
};
use std::sync::mpsc;
use std::thread;

const SYNTAX: &str = "dd [OPERAND]...\ndd OPTION";
const SUMMARY: &str = "convert, and optionally copy, a file";
const LONG_HELP: &str = "";

const DEFAULT_FILL_BYTE: u8 = 0xDD;

const RTN_SUCCESS: i32 = 0;
const RTN_FAILURE: i32 = 1;

// ----- Datatypes -----
enum SrcStat
{
    Read(usize),
    EOF,
}

/// Captures all Conv Flags that apply to the input
pub struct ConvFlagInput
{
    ctable: Option<&'static ConversionTable>,
    block: bool,
    unblock: bool,
    swab: bool,
    sync: bool,
    noerror: bool,
}

/// Captures all Conv Flags that apply to the output
#[derive(Debug, PartialEq)]
pub struct ConvFlagOutput
{
    sparse: bool,
    excl: bool,
    nocreat: bool,
    notrunc: bool,
    fdatasync: bool,
    fsync: bool,
}

/// The value of the status cl-option.
/// Controls printing of transfer stats
#[derive(PartialEq)]
pub enum StatusLevel
{
    Progress,
    Noxfer,
    None,
}

#[derive(Debug)]
enum InternalError
{
    WrongInputType,
    WrongOutputType,
}

impl std::fmt::Display for InternalError
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Internal dd error")
    }
}

impl Error for InternalError {}

struct Input<R: Read>
{
    src: R,
    ibs: usize,
    xfer_stats: StatusLevel,
    cf: ConvFlagInput,
}

impl<R: Read> Read for Input<R>
{
    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize>
    {
        let len = self.src.read(&mut buf)?;

        if let Some(ct) = self.cf.ctable
        {
            for idx in 0..len
            {
                buf[idx] = ct[buf[idx] as usize];
            }
        }

        if self.cf.swab
        {
            let mut tmp = DEFAULT_FILL_BYTE;

            for base in (1..len).step_by(2)
            {
                tmp = buf[base];
                buf[base] = buf[base-1];
                buf[base-1] = tmp;
            }
        }

        Ok(len)
    }
}

impl Input<io::Stdin>
{
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let ibs = parseargs::parse_ibs(matches)?;
        let xfer_stats = parseargs::parse_status_level(matches)?;
        let cf = parseargs::parse_conv_flag_input(matches)?;

        Ok(
            Input {
                src: io::stdin(),
                ibs,
                xfer_stats,
                cf,
            }
        )

    }
}

impl Input<File>
{
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let ibs = parseargs::parse_ibs(matches)?;
        let xfer_stats = parseargs::parse_status_level(matches)?;
        let cf = parseargs::parse_conv_flag_input(matches)?;

        if let Some(fname) = matches.opt_str("if")
        {
            Ok(Input {
                src: File::open(fname)?,
                ibs,
                xfer_stats,
                cf,
            })
        }
        else
        {
            Err(Box::new(InternalError::WrongInputType))
        }

    }
}

impl<R: Read> Input<R>
{
    fn fill_n(&mut self, buf: &mut [u8], obs: usize) -> Result<SrcStat, Box<dyn Error>>
    {
        let ibs = self.ibs;
        let mut bytes_read = 0;

        for n in 0..(obs/ibs) {
            // fill an ibs-len slice from src
            let this_read = self.read(&mut buf[n*ibs..(n+1)*ibs])?;

            if this_read != 0 {
                bytes_read += this_read;
            } else {
                break;
            }
        }

        if bytes_read != 0 {
            Ok(SrcStat::Read(bytes_read))
        } else {
            Ok(SrcStat::EOF)
        }
    }
}

struct Output<W: Write>
{
    dst: W,
    obs: usize,
    cf: ConvFlagOutput,
}

impl Output<io::Stdout> {
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let obs = parseargs::parse_obs(matches)?;
        let cf = parseargs::parse_conv_flag_output(matches)?;

        Ok(
            Output {
                dst: io::stdout(),
                obs,
                cf,
            }
        )
    }
}

impl Output<File> {
    fn new(matches: &getopts::Matches) -> Result<Self, Box<dyn Error>>
    {
        let obs = parseargs::parse_obs(matches)?;
        let cf = parseargs::parse_conv_flag_output(matches)?;

        if let Some(fname) = matches.opt_str("of")
        {
            let dst = OpenOptions::new()
                .write(true)
                .create(!cf.nocreat)
                .truncate(!cf.notrunc)
                .open(fname)?;

            Ok(Output {
                dst,
                obs,
                cf,
            })
        }
        else
        {
            Err(Box::new(InternalError::WrongOutputType))
        }
    }
}

impl<W: Write> Seek for Output<W>
{
    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
    {
        unimplemented!()
    }
}

// impl Seek for Output<io::Stdout>
// {
//     fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
//     {
//         // Default method. Called when output dst not backed by a traditional file and
//         // should not be seeked (eg. stdout)
//         Ok(0)
//     }
// }
//
// impl Seek for Output<File>
// {
//     fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64>
//     {
//         self.dst.seek(pos)
//     }
// }

impl<W: Write> Write for Output<W>
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize>
    {
            self.dst.write(buf)
    }

    fn flush(&mut self) -> io::Result<()>
    {
        self.dst.flush()
    }
}

fn is_sparse(buf: &[u8]) -> bool
{
    buf.iter().all(| &e | e == 0)
}

fn gen_prog_updater(rx: mpsc::Receiver<usize>) -> impl Fn() -> ()
{
    move || {

        // TODO: Replace ?? with accurate info
        print!("\rProgress ({}/??)", 0);

        loop
        {
            match rx.recv()
            {
                Ok(wr_total) => {
                    print!("\rProgress ({}/??)", wr_total);
                },
                Err(_) => {
                    println!("");
                    break
                },
            }
        }
    }
}

fn dd<R: Read, W: Write>(mut i: Input<R>, mut o: Output<W>) -> Result<(usize, usize), Box<dyn Error>>
{
    let prog_tx = if i.xfer_stats == StatusLevel::Progress
    {
        let (prog_tx, prog_rx) = mpsc::channel();

        thread::spawn(gen_prog_updater(prog_rx));

        Some(prog_tx)
    }
    else
    {
        None
    };

    let mut bytes_in  = 0;
    let mut bytes_out = 0;

    loop
    {
        let mut buf = vec![DEFAULT_FILL_BYTE; o.obs];

        // Read
        let r_len = match i.fill_n(&mut buf, o.obs) {
            Ok(SrcStat::Read(len)) =>
            {
                bytes_in += len;
                len
            },
            Ok(SrcStat::EOF) =>
                break,
            Err(e) =>
                if !i.cf.noerror {
                    return Err(e);
                } else {
                    continue
                },
        };

        // Write
        let w_len = if o.cf.sparse && is_sparse(&buf)
        {
            let seek_amt: i64 = r_len.try_into()?;
            o.seek(io::SeekFrom::Current(seek_amt))?;
            r_len
        }
        else
        {
            o.write(&buf[..r_len])?
        };

        // Prog
        bytes_out += w_len;

        if let Some(prog_tx) = &prog_tx
        {
            prog_tx.send(bytes_out)?;
        }
    }

    // TODO: Also ensure file metadata is written when fsync option is specified. When _wouldn't_ this happen?
    // See fs::File::sync_all && fs::File::sync_data methods!
    if o.cf.fsync || o.cf.fdatasync
    {
        o.flush()?;
    }

    Ok((bytes_in, bytes_out))
}

#[macro_export]
macro_rules! build_app (
    () =>
    {
        app!(SYNTAX, SUMMARY, LONG_HELP)
            .optopt(
                "",
                "if",
                "The input file",
                "FILE"
            )
            .optopt(
                "",
                "ibs",
                "read up to BYTES bytes at a time (default: 512)",
                "BYTES"
            )
            .optopt(
                "",
                "of",
                "The output file",
                "FILE"
            )
            .optopt(
                "",
                "obs",
                "write BYTES bytes at a time (default: 512)",
                "BYTES"
            )
            .optopt(
                "",
                "conv",
                "One or more conversion options as a comma-serparated list",
                "OPT[,OPT]..."
            )
    }
);

fn append_dashes_if_not_present(mut acc: Vec<String>, s: &String) -> Vec<String>
{
    if Some("--") == s.get(0..=1) {
        acc
    } else {
        acc.push(format!("--{}", s));
        acc
    }
}

pub fn uumain(args: impl uucore::Args) -> i32
{
    let dashed_args = args.collect_str()
                          .iter()
                          .fold(Vec::new(), append_dashes_if_not_present);

    let matches = build_app!().parse(dashed_args);

    let result = match (matches.opt_present("if"), matches.opt_present("of"))
    {
        (true, true) =>
        {
            let i = Input::<File>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<File>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
        (true, false) =>
        {
            let i = Input::<File>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<io::Stdout>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
        (false, true) =>
        {
            let i = Input::<io::Stdin>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<File>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
        (false, false) =>
        {
            let i = Input::<io::Stdin>::new(&matches)
                .expect("TODO: Return correct error code");
            let o = Output::<io::Stdout>::new(&matches)
                .expect("TODO: Return correct error code");

            dd(i,o)
        },
    };

    match result
    {
        Ok((b_in, b_out)) =>
        {
            // TODO: Print output stats, unless noxfer

            RTN_SUCCESS
        },
        Err(_) =>
            RTN_FAILURE,
    }
}

<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Gehring <mg@ebfe.org>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
//  * (c) Sergey "Shnatsel" Davidoff <shnatsel@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) allocs bset dflag cflag sflag tflag

#[macro_use]
extern crate uucore;

mod expand;

use bit_set::BitSet;
use clap::{App, Arg};
use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write};

use crate::expand::ExpandSet;

static NAME: &str = "tr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "translate or delete characters";
static LONG_HELP: &str = "Translate,  squeeze, and/or delete characters from standard input,
writing to standard output.";
const BUFFER_LEN: usize = 1024;

mod options {
    pub const COMPLEMENT: &str = "complement";
    pub const DELETE: &str = "delete";
    pub const SQUEEZE: &str = "squeeze-repeats";
    pub const TRUNCATE: &str = "truncate";
    pub const SETS: &str = "sets";
}

trait SymbolTranslator {
    fn translate(&self, c: char, prev_c: char) -> Option<char>;
}

struct DeleteOperation {
    bset: BitSet,
    complement: bool,
}

impl DeleteOperation {
    fn new(set: ExpandSet, complement: bool) -> DeleteOperation {
        DeleteOperation {
            bset: set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        let uc = c as usize;
        if self.complement == self.bset.contains(uc) {
            Some(c)
        } else {
            None
        }
    }
}

struct SqueezeOperation {
    squeeze_set: BitSet,
    complement: bool,
}

impl SqueezeOperation {
    fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation {
        SqueezeOperation {
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for SqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if prev_c == c && self.complement != self.squeeze_set.contains(c as usize) {
            None
        } else {
            Some(c)
        }
    }
}

struct DeleteAndSqueezeOperation {
    delete_set: BitSet,
    squeeze_set: BitSet,
    complement: bool,
}

impl DeleteAndSqueezeOperation {
    fn new(
        delete_set: ExpandSet,
        squeeze_set: ExpandSet,
        complement: bool,
    ) -> DeleteAndSqueezeOperation {
        DeleteAndSqueezeOperation {
            delete_set: delete_set.map(|c| c as usize).collect(),
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if self.complement != self.delete_set.contains(c as usize)
            || prev_c == c && self.squeeze_set.contains(c as usize)
        {
            None
        } else {
            Some(c)
        }
    }
}

struct TranslateOperation {
    translate_map: FnvHashMap<usize, char>,
}

impl TranslateOperation {
    fn new(set1: ExpandSet, set2: &mut ExpandSet, truncate: bool) -> TranslateOperation {
        let mut map = FnvHashMap::default();
        let mut s2_prev = '_';
        for i in set1 {
            let s2_next = set2.next();

            if s2_next.is_none() && truncate {
                map.insert(i as usize, i);
            } else {
                s2_prev = s2_next.unwrap_or(s2_prev);
                map.insert(i as usize, s2_prev);
            }
        }
        TranslateOperation { translate_map: map }
    }
}

impl SymbolTranslator for TranslateOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c))
    }
}

fn translate_input<T: SymbolTranslator>(
    input: &mut dyn BufRead,
    output: &mut dyn Write,
    translator: T,
) {
    let mut buf = String::with_capacity(BUFFER_LEN + 4);
    let mut output_buf = String::with_capacity(BUFFER_LEN + 4);

    while let Ok(length) = input.read_line(&mut buf) {
        let mut prev_c = 0 as char;
        if length == 0 {
            break;
        }
        {
            // isolation to make borrow checker happy
            let filtered = buf.chars().filter_map(|c| {
                let res = translator.translate(c, prev_c);
                if res.is_some() {
                    prev_c = c;
                }
                res
            });

            output_buf.extend(filtered);
            output.write_all(output_buf.as_bytes()).unwrap();
        }
        buf.clear();
        output_buf.clear();
    }
}

fn get_usage() -> String {
    format!("{} [OPTION]... SET1 [SET2]", executable!())
}

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

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::COMPLEMENT)
                .short("C")
                .short("c")
                .long(options::COMPLEMENT)
                .help("use the complement of SET1"),
        )
        .arg(
            Arg::with_name(options::DELETE)
                .short("d")
                .long(options::DELETE)
                .help("delete characters in SET1, do not translate"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .long(options::SQUEEZE)
                .short("s")
                .help(
                    "replace each sequence  of  a  repeated  character  that  is
            listed  in the last specified SET, with a single occurrence
            of that character",
                ),
        )
        .arg(
            Arg::with_name(options::TRUNCATE)
                .long(options::TRUNCATE)
                .short("t")
                .help("first truncate SET1 to length of SET2"),
        )
        .arg(Arg::with_name(options::SETS).multiple(true))
        .get_matches_from(args);

    let dflag = matches.is_present(options::DELETE);
    let cflag = matches.is_present(options::COMPLEMENT);
    let sflag = matches.is_present(options::SQUEEZE);
    let tflag = matches.is_present(options::TRUNCATE);

    let sets: Vec<String> = match matches.values_of(options::SETS) {
        Some(v) => v.map(|v| v.to_string()).collect(),
        None => vec![],
    };

    if sets.is_empty() {
        show_error!(
            "missing operand\nTry `{} --help` for more information.",
            NAME
        );
        return 1;
    }

    if !(dflag || sflag) && sets.len() < 2 {
        show_error!(
            "missing operand after ‘{}’\nTry `{} --help` for more information.",
            sets[0],
            NAME
        );
        return 1;
    }

    if cflag && !dflag && !sflag {
        show_error!("-c is only supported with -d or -s");
        return 1;
    }

    let stdin = stdin();
    let mut locked_stdin = stdin.lock();
    let stdout = stdout();
    let locked_stdout = stdout.lock();
    let mut buffered_stdout = BufWriter::new(locked_stdout);

    let set1 = ExpandSet::new(sets[0].as_ref());
    if dflag {
        if sflag {
            let set2 = ExpandSet::new(sets[1].as_ref());
            let op = DeleteAndSqueezeOperation::new(set1, set2, cflag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let op = DeleteOperation::new(set1, cflag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else if sflag {
        let op = SqueezeOperation::new(set1, cflag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op);
    } else {
        let mut set2 = ExpandSet::new(sets[1].as_ref());
        let op = TranslateOperation::new(set1, &mut set2, tflag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op)
    }

    0
}
<commit_msg>tr: more explicit flag names (#1966)<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Gehring <mg@ebfe.org>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
//  * (c) Sergey "Shnatsel" Davidoff <shnatsel@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) allocs bset dflag cflag sflag tflag

#[macro_use]
extern crate uucore;

mod expand;

use bit_set::BitSet;
use clap::{App, Arg};
use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write};

use crate::expand::ExpandSet;

static NAME: &str = "tr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "translate or delete characters";
static LONG_HELP: &str = "Translate,  squeeze, and/or delete characters from standard input,
writing to standard output.";
const BUFFER_LEN: usize = 1024;

mod options {
    pub const COMPLEMENT: &str = "complement";
    pub const DELETE: &str = "delete";
    pub const SQUEEZE: &str = "squeeze-repeats";
    pub const TRUNCATE: &str = "truncate";
    pub const SETS: &str = "sets";
}

trait SymbolTranslator {
    fn translate(&self, c: char, prev_c: char) -> Option<char>;
}

struct DeleteOperation {
    bset: BitSet,
    complement: bool,
}

impl DeleteOperation {
    fn new(set: ExpandSet, complement: bool) -> DeleteOperation {
        DeleteOperation {
            bset: set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        let uc = c as usize;
        if self.complement == self.bset.contains(uc) {
            Some(c)
        } else {
            None
        }
    }
}

struct SqueezeOperation {
    squeeze_set: BitSet,
    complement: bool,
}

impl SqueezeOperation {
    fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation {
        SqueezeOperation {
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for SqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if prev_c == c && self.complement != self.squeeze_set.contains(c as usize) {
            None
        } else {
            Some(c)
        }
    }
}

struct DeleteAndSqueezeOperation {
    delete_set: BitSet,
    squeeze_set: BitSet,
    complement: bool,
}

impl DeleteAndSqueezeOperation {
    fn new(
        delete_set: ExpandSet,
        squeeze_set: ExpandSet,
        complement: bool,
    ) -> DeleteAndSqueezeOperation {
        DeleteAndSqueezeOperation {
            delete_set: delete_set.map(|c| c as usize).collect(),
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if self.complement != self.delete_set.contains(c as usize)
            || prev_c == c && self.squeeze_set.contains(c as usize)
        {
            None
        } else {
            Some(c)
        }
    }
}

struct TranslateOperation {
    translate_map: FnvHashMap<usize, char>,
}

impl TranslateOperation {
    fn new(set1: ExpandSet, set2: &mut ExpandSet, truncate: bool) -> TranslateOperation {
        let mut map = FnvHashMap::default();
        let mut s2_prev = '_';
        for i in set1 {
            let s2_next = set2.next();

            if s2_next.is_none() && truncate {
                map.insert(i as usize, i);
            } else {
                s2_prev = s2_next.unwrap_or(s2_prev);
                map.insert(i as usize, s2_prev);
            }
        }
        TranslateOperation { translate_map: map }
    }
}

impl SymbolTranslator for TranslateOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c))
    }
}

fn translate_input<T: SymbolTranslator>(
    input: &mut dyn BufRead,
    output: &mut dyn Write,
    translator: T,
) {
    let mut buf = String::with_capacity(BUFFER_LEN + 4);
    let mut output_buf = String::with_capacity(BUFFER_LEN + 4);

    while let Ok(length) = input.read_line(&mut buf) {
        let mut prev_c = 0 as char;
        if length == 0 {
            break;
        }
        {
            // isolation to make borrow checker happy
            let filtered = buf.chars().filter_map(|c| {
                let res = translator.translate(c, prev_c);
                if res.is_some() {
                    prev_c = c;
                }
                res
            });

            output_buf.extend(filtered);
            output.write_all(output_buf.as_bytes()).unwrap();
        }
        buf.clear();
        output_buf.clear();
    }
}

fn get_usage() -> String {
    format!("{} [OPTION]... SET1 [SET2]", executable!())
}

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

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::COMPLEMENT)
                .short("C")
                .short("c")
                .long(options::COMPLEMENT)
                .help("use the complement of SET1"),
        )
        .arg(
            Arg::with_name(options::DELETE)
                .short("d")
                .long(options::DELETE)
                .help("delete characters in SET1, do not translate"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .long(options::SQUEEZE)
                .short("s")
                .help(
                    "replace each sequence  of  a  repeated  character  that  is
            listed  in the last specified SET, with a single occurrence
            of that character",
                ),
        )
        .arg(
            Arg::with_name(options::TRUNCATE)
                .long(options::TRUNCATE)
                .short("t")
                .help("first truncate SET1 to length of SET2"),
        )
        .arg(Arg::with_name(options::SETS).multiple(true))
        .get_matches_from(args);

    let delete_flag = matches.is_present(options::DELETE);
    let complement_flag = matches.is_present(options::COMPLEMENT);
    let squeeze_flag = matches.is_present(options::SQUEEZE);
    let truncate_flag = matches.is_present(options::TRUNCATE);

    let sets: Vec<String> = match matches.values_of(options::SETS) {
        Some(v) => v.map(|v| v.to_string()).collect(),
        None => vec![],
    };

    if sets.is_empty() {
        show_error!(
            "missing operand\nTry `{} --help` for more information.",
            NAME
        );
        return 1;
    }

    if !(delete_flag || squeeze_flag) && sets.len() < 2 {
        show_error!(
            "missing operand after ‘{}’\nTry `{} --help` for more information.",
            sets[0],
            NAME
        );
        return 1;
    }

    if complement_flag && !delete_flag && !squeeze_flag {
        show_error!("-c is only supported with -d or -s");
        return 1;
    }

    let stdin = stdin();
    let mut locked_stdin = stdin.lock();
    let stdout = stdout();
    let locked_stdout = stdout.lock();
    let mut buffered_stdout = BufWriter::new(locked_stdout);

    let set1 = ExpandSet::new(sets[0].as_ref());
    if delete_flag {
        if squeeze_flag {
            let set2 = ExpandSet::new(sets[1].as_ref());
            let op = DeleteAndSqueezeOperation::new(set1, set2, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let op = DeleteOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else if squeeze_flag {
        let op = SqueezeOperation::new(set1, complement_flag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op);
    } else {
        let mut set2 = ExpandSet::new(sets[1].as_ref());
        let op = TranslateOperation::new(set1, &mut set2, truncate_flag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op)
    }

    0
}
<|endoftext|>


<commit_before>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "9d78d1d02761b906038ba4d54c5f3427f920f5fb";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/closing-args-token.rs",
    "src/test/ui/const-generics/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/issues/auxiliary/issue-21146-inc.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Update test suite to nightly-2020-11-27<commit_after>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "72da5a9d85a522b11e80d0fdd1fd95247d442604";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO destructuring assignment (#933)
    "src/test/ui/destructuring-assignment/drop-order.rs",
    "src/test/ui/destructuring-assignment/nested_destructure.rs",
    "src/test/ui/destructuring-assignment/slice_destructure.rs",
    "src/test/ui/destructuring-assignment/struct_destructure.rs",
    "src/test/ui/destructuring-assignment/tuple_destructure.rs",
    "src/test/ui/destructuring-assignment/tuple_struct_destructure.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/closing-args-token.rs",
    "src/test/ui/const-generics/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/issues/auxiliary/issue-21146-inc.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, The Radare Project. All rights reserved.
// See the COPYING file at the top-level directory of this distribution.
// Licensed under the BSD 3-Clause License:
// <http://opensource.org/licenses/BSD-3-Clause>
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Implements the `GraphDot` trait for `SSAStorage`

use petgraph::graph;
use petgraph::visit::{IntoEdgeReferences, EdgeRef};
use petgraph::graph::NodeIndex;

use middle::ir::MOpcode;
use middle::dot::{DotAttrBlock, GraphDot};
use super::ssastorage::{EdgeData, NodeData, SSAStorage};
use super::ssa_traits::{SSA, SSAExtra};
use middle::ssa::cfg_traits::CFG;

///////////////////////////////////////////////////////////////////////////////
//// Implementation of GraphDot to emit Dot for SSAStorage.
///////////////////////////////////////////////////////////////////////////////

impl GraphDot for SSAStorage {
    type NodeIndex = graph::NodeIndex;
    type EdgeIndex = graph::EdgeIndex;

    fn configure(&self) -> String {
        "digraph cfg {\nsplines=\"ortho\";\ngraph [fontsize=12 fontname=\"Verdana\" compound=true \
         rankdir=TB;]\n"
            .to_owned()
    }

    fn nodes(&self) -> Vec<Self::NodeIndex> {
        self.g.node_indices().collect()
    }

    fn edges(&self) -> Vec<Self::EdgeIndex> {
        self.g.edge_references().map(|x| x.id()).collect()
    }

    fn node_count(&self) -> usize {
        self.g.node_count()
    }

    fn edge_count(&self) -> usize {
        self.g.edge_count()
    }

    fn node_index_new(i: usize) -> Self::NodeIndex {
        graph::NodeIndex::new(i)
    }

    fn edge_index_new(i: usize) -> Self::EdgeIndex {
        graph::EdgeIndex::new(i)
    }

    fn node_cluster(&self, i: &Self::NodeIndex) -> Option<usize> {
        let invalid_block = self.invalid_action().expect("Invalid Action is not defined");
        match self.g.node_weight(*i) {
            Some(&NodeData::BasicBlock(_, _)) | Some(&NodeData::DynamicAction) => Some(i.index()),
            _ => Some(self.block_for(*i).unwrap_or(invalid_block).index()),
        }
    }

    fn edge_source(&self, i: &Self::EdgeIndex) -> Self::NodeIndex {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return NodeIndex::end();
        };
        let edge = edge_opt.unwrap();
        match *edge.weight() {
            EdgeData::Data(_) => edge.target(),
            _ => edge.source(),
        }
    }

    fn edge_target(&self, i: &Self::EdgeIndex) -> Self::NodeIndex {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return NodeIndex::end();
        };
        let edge = edge_opt.unwrap();
         match *edge.weight() {
            EdgeData::Data(_) => edge.source(),
            _ => edge.target(),
        }
    }

    fn edge_skip(&self, i: &Self::EdgeIndex) -> bool {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return false;
        };
        let edge = edge_opt.unwrap();
        match *edge.weight() {
            EdgeData::ContainedInBB(_) | EdgeData::RegisterState => true,
            _ => false,
        }
    }

    // TODO: Ordering of clusters for ssa is kind of hacky and may not run top to
    // bottom in some
    // cases.
    fn edge_attrs(&self, i: &Self::EdgeIndex) -> DotAttrBlock {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return DotAttrBlock::Raw(String::new());
        };
        let edge = edge_opt.unwrap();
        let mut prefix = String::new();
        let src = edge.source();
        let target = edge.target();

        prefix.push_str(&format!("n{} -> n{}",
                                 src.index(),
                                 target.index()));
        let target_is_bb = if let NodeData::BasicBlock(_, _) = self.g[edge.target()] {
            true
        } else {
            false
        };
        let attr = match *edge.weight() {
            EdgeData::Control(_) if !target_is_bb => {
                vec![("color".to_string(), "red".to_string())]
            }
            EdgeData::Control(i) => {
                // Determine the source and destination clusters.
                let source_cluster = edge.source().index();
                let dst_cluster = edge.target().index();
                let (color, label) = match i {
                    0 => ("red", "F"),
                    1 => ("green", "T"),
                    2 => ("blue", "U"),
                    _ => unreachable!(),
                };
                vec![("color".to_string(), color.to_string()),
                     ("xlabel".to_string(), label.to_string()),
                     ("ltail".to_string(), format!("cluster_{}", source_cluster)),
                     ("lhead".to_string(), format!("cluster_{}", dst_cluster)),
                     ("minlen".to_string(), "9".to_owned())]
            }
            EdgeData::Data(i) => {
                vec![("dir".to_string(), "back".to_string()),
                     ("xlabel".to_string(), format!("{}", i))]
            }
            EdgeData::ContainedInBB(_) => {
                vec![("color".to_string(), "gray".to_string())]
            }
            EdgeData::Selector => {
                vec![("color".to_string(), "purple".to_string())]
            }
            EdgeData::ReplacedBy => {
                vec![("color".to_string(), "brown".to_string())]
            }
            EdgeData::RegisterInfo => {
                vec![("color".to_string(), "yellow".to_string())]
            }
            EdgeData::RegisterState => unreachable!(),
        };

        DotAttrBlock::Hybrid(prefix, attr)
    }

    fn node_attrs(&self, i: &Self::NodeIndex) -> DotAttrBlock {
        let node = &self.g[*i];
        let mut prefix = String::new();
        prefix.push_str(&format!("n{}", i.index()));

        let attr = match *node {
            NodeData::Op(ref opc, vi) => {
                let w = vi.width().get_width().unwrap_or(64);
                let mut attrs = Vec::new();
                let mut r = String::new();
                let addr = self.addr(i);
                if addr.is_some() {
                    r.push_str(&format!("<{}: ",
                                        addr.as_ref().unwrap()))
                }
                r.push_str(&format!("\"[i{}] {:?}\"", w, opc));
                if addr.is_some() {
                    r.push_str(">");
                }

                if let MOpcode::OpConst(_) = *opc {
                    attrs.push(("style".to_owned(), "filled".to_owned()));
                    attrs.push(("color".to_owned(), "black".to_owned()));
                    attrs.push(("fillcolor".to_owned(), "yellow".to_owned()));
                }
                if self.is_marked(i) {
                    attrs.push(("label".to_string(), r));
                    attrs.push(("style".to_owned(), "filled".to_owned()));
                    attrs.push(("color".to_owned(), "black".to_owned()));
                    attrs.push(("fillcolor".to_owned(), "green".to_owned()));
                } else {
                    attrs.push(("fillcolor".to_owned(), "white".to_owned()));
                }
                attrs
            }
            NodeData::BasicBlock(addr, _) => {
                let label_str = format!("<Basic Block \
                                         Information
Start Address: {}>",
                                        addr);
                let mut attrs = Vec::new();
                if let Some(e) = self.entry_node() {
                    if *i == e {
                        attrs.push(("rank".to_string(), "min".to_string()));
                    }
                };

                attrs.extend([("style".to_string(), "filled".to_string()),
                              ("fillcolor".to_string(), "white".to_string()),
                              ("label".to_string(), label_str),
                              ("shape".to_string(), "box".to_string()),
                              ("color".to_string(), "\"grey\"".to_string()),]
                                 .iter()
                                 .cloned());
                attrs
            }
            NodeData::Comment(_, ref msg) => {
                vec![("label".to_string(),
                      format!("\"{}\"", msg.replace("\"", "\\\""))),
                     ("shape".to_owned(), "box".to_owned()),
                     ("style".to_owned(), "filled".to_owned()),
                     ("color".to_owned(), "black".to_owned()),
                     ("fillcolor".to_owned(), "greenyellow".to_owned())]
            }
            NodeData::Phi(_, _) => {
                let mut attrs = Vec::new();
                let mut label = format!("{:?}", node);
                label = label.replace("\"", "\\\"");
                label = format!("\"{}\"", label);
                if let Some(addr) = self.addr(i) {
                    label = format!("<{}:  {}>", addr, label);
                }
                attrs.push(("label".to_string(), label));
                attrs.push(("style".to_owned(), "filled".to_owned()));
                attrs.push(("color".to_owned(), "black".to_owned()));
                if self.is_marked(i) {
                    attrs.push(("fillcolor".to_owned(), "green".to_owned()));
                } else {
                    attrs.push(("fillcolor".to_owned(), "orange".to_owned()));
                }
                attrs
            }
            _ => {
                let mut attrs = Vec::new();
                let mut label = format!("{:?}", node);
                label = label.replace("\"", "\\\"");
                label = format!("\"{}\"", label);
                if let Some(addr) = self.addr(i) {
                    label = format!("<{}:  {}>", addr, label);
                }
                attrs.push(("label".to_string(), label));
                attrs.push(("style".to_owned(), "filled".to_owned()));
                attrs.push(("color".to_owned(), "black".to_owned()));
                if self.is_marked(i) {
                    attrs.push(("fillcolor".to_owned(), "green".to_owned()));
                } else {
                    attrs.push(("fillcolor".to_owned(), "grey90".to_owned()));
                }
                attrs
            }
        };
        DotAttrBlock::Hybrid(prefix, attr)
    }
}
<commit_msg>Increase spacing among nodes<commit_after>// Copyright (c) 2015, The Radare Project. All rights reserved.
// See the COPYING file at the top-level directory of this distribution.
// Licensed under the BSD 3-Clause License:
// <http://opensource.org/licenses/BSD-3-Clause>
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Implements the `GraphDot` trait for `SSAStorage`

use petgraph::graph;
use petgraph::visit::{IntoEdgeReferences, EdgeRef};
use petgraph::graph::NodeIndex;

use middle::ir::MOpcode;
use middle::dot::{DotAttrBlock, GraphDot};
use super::ssastorage::{EdgeData, NodeData, SSAStorage};
use super::ssa_traits::{SSA, SSAExtra};
use middle::ssa::cfg_traits::CFG;

///////////////////////////////////////////////////////////////////////////////
//// Implementation of GraphDot to emit Dot for SSAStorage.
///////////////////////////////////////////////////////////////////////////////

impl GraphDot for SSAStorage {
    type NodeIndex = graph::NodeIndex;
    type EdgeIndex = graph::EdgeIndex;

    fn configure(&self) -> String {
        "digraph cfg {\nsplines=\"ortho\"\nranksep=1\nnodesep=1;\ngraph [fontsize=12 fontname=\"Verdana\" compound=true \
         rankdir=TB;]\n"
            .to_owned()
    }

    fn nodes(&self) -> Vec<Self::NodeIndex> {
        self.g.node_indices().collect()
    }

    fn edges(&self) -> Vec<Self::EdgeIndex> {
        self.g.edge_references().map(|x| x.id()).collect()
    }

    fn node_count(&self) -> usize {
        self.g.node_count()
    }

    fn edge_count(&self) -> usize {
        self.g.edge_count()
    }

    fn node_index_new(i: usize) -> Self::NodeIndex {
        graph::NodeIndex::new(i)
    }

    fn edge_index_new(i: usize) -> Self::EdgeIndex {
        graph::EdgeIndex::new(i)
    }

    fn node_cluster(&self, i: &Self::NodeIndex) -> Option<usize> {
        let invalid_block = self.invalid_action().expect("Invalid Action is not defined");
        match self.g.node_weight(*i) {
            Some(&NodeData::BasicBlock(_, _)) | Some(&NodeData::DynamicAction) => Some(i.index()),
            _ => Some(self.block_for(*i).unwrap_or(invalid_block).index()),
        }
    }

    fn edge_source(&self, i: &Self::EdgeIndex) -> Self::NodeIndex {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return NodeIndex::end();
        };
        let edge = edge_opt.unwrap();
        match *edge.weight() {
            EdgeData::Data(_) => edge.target(),
            _ => edge.source(),
        }
    }

    fn edge_target(&self, i: &Self::EdgeIndex) -> Self::NodeIndex {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return NodeIndex::end();
        };
        let edge = edge_opt.unwrap();
         match *edge.weight() {
            EdgeData::Data(_) => edge.source(),
            _ => edge.target(),
        }
    }

    fn edge_skip(&self, i: &Self::EdgeIndex) -> bool {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return false;
        };
        let edge = edge_opt.unwrap();
        match *edge.weight() {
            EdgeData::ContainedInBB(_) | EdgeData::RegisterState => true,
            _ => false,
        }
    }

    // TODO: Ordering of clusters for ssa is kind of hacky and may not run top to
    // bottom in some
    // cases.
    fn edge_attrs(&self, i: &Self::EdgeIndex) -> DotAttrBlock {
        let edge_opt = &self.g.edge_references().find(|x| x.id() == *i);
        if edge_opt.is_none() {
            radeco_err!("Invalid edge index");
            return DotAttrBlock::Raw(String::new());
        };
        let edge = edge_opt.unwrap();
        let mut prefix = String::new();
        let src = edge.source();
        let target = edge.target();

        prefix.push_str(&format!("n{} -> n{}",
                                 src.index(),
                                 target.index()));
        let target_is_bb = if let NodeData::BasicBlock(_, _) = self.g[edge.target()] {
            true
        } else {
            false
        };
        let attr = match *edge.weight() {
            EdgeData::Control(_) if !target_is_bb => {
                vec![("color".to_string(), "red".to_string())]
            }
            EdgeData::Control(i) => {
                // Determine the source and destination clusters.
                let source_cluster = edge.source().index();
                let dst_cluster = edge.target().index();
                let (color, label) = match i {
                    0 => ("red", "F"),
                    1 => ("green", "T"),
                    2 => ("blue", "U"),
                    _ => unreachable!(),
                };
                vec![("color".to_string(), color.to_string()),
                     ("xlabel".to_string(), label.to_string()),
                     ("ltail".to_string(), format!("cluster_{}", source_cluster)),
                     ("lhead".to_string(), format!("cluster_{}", dst_cluster)),
                     ("minlen".to_string(), "9".to_owned())]
            }
            EdgeData::Data(i) => {
                vec![("dir".to_string(), "back".to_string()),
                     ("xlabel".to_string(), format!("{}", i))]
            }
            EdgeData::ContainedInBB(_) => {
                vec![("color".to_string(), "gray".to_string())]
            }
            EdgeData::Selector => {
                vec![("color".to_string(), "purple".to_string())]
            }
            EdgeData::ReplacedBy => {
                vec![("color".to_string(), "brown".to_string())]
            }
            EdgeData::RegisterInfo => {
                vec![("color".to_string(), "yellow".to_string())]
            }
            EdgeData::RegisterState => unreachable!(),
        };

        DotAttrBlock::Hybrid(prefix, attr)
    }

    fn node_attrs(&self, i: &Self::NodeIndex) -> DotAttrBlock {
        let node = &self.g[*i];
        let mut prefix = String::new();
        prefix.push_str(&format!("n{}", i.index()));

        let attr = match *node {
            NodeData::Op(ref opc, vi) => {
                let w = vi.width().get_width().unwrap_or(64);
                let mut attrs = Vec::new();
                let mut r = String::new();
                let addr = self.addr(i);
                if addr.is_some() {
                    r.push_str(&format!("<{}: ",
                                        addr.as_ref().unwrap()))
                }
                r.push_str(&format!("\"[i{}] {:?}\"", w, opc));
                if addr.is_some() {
                    r.push_str(">");
                }

                if let MOpcode::OpConst(_) = *opc {
                    attrs.push(("style".to_owned(), "filled".to_owned()));
                    attrs.push(("color".to_owned(), "black".to_owned()));
                    attrs.push(("fillcolor".to_owned(), "yellow".to_owned()));
                }
                if self.is_marked(i) {
                    attrs.push(("label".to_string(), r));
                    attrs.push(("style".to_owned(), "filled".to_owned()));
                    attrs.push(("color".to_owned(), "black".to_owned()));
                    attrs.push(("fillcolor".to_owned(), "green".to_owned()));
                } else {
                    attrs.push(("fillcolor".to_owned(), "white".to_owned()));
                }
                attrs
            }
            NodeData::BasicBlock(addr, _) => {
                let label_str = format!("<Basic Block \
                                         Information
Start Address: {}>",
                                        addr);
                let mut attrs = Vec::new();
                if let Some(e) = self.entry_node() {
                    if *i == e {
                        attrs.push(("rank".to_string(), "min".to_string()));
                    }
                };

                attrs.extend([("style".to_string(), "filled".to_string()),
                              ("fillcolor".to_string(), "white".to_string()),
                              ("label".to_string(), label_str),
                              ("shape".to_string(), "box".to_string()),
                              ("color".to_string(), "\"grey\"".to_string()),]
                                 .iter()
                                 .cloned());
                attrs
            }
            NodeData::Comment(_, ref msg) => {
                vec![("label".to_string(),
                      format!("\"{}\"", msg.replace("\"", "\\\""))),
                     ("shape".to_owned(), "box".to_owned()),
                     ("style".to_owned(), "filled".to_owned()),
                     ("color".to_owned(), "black".to_owned()),
                     ("fillcolor".to_owned(), "greenyellow".to_owned())]
            }
            NodeData::Phi(_, _) => {
                let mut attrs = Vec::new();
                let mut label = format!("{:?}", node);
                label = label.replace("\"", "\\\"");
                label = format!("\"{}\"", label);
                if let Some(addr) = self.addr(i) {
                    label = format!("<{}:  {}>", addr, label);
                }
                attrs.push(("label".to_string(), label));
                attrs.push(("style".to_owned(), "filled".to_owned()));
                attrs.push(("color".to_owned(), "black".to_owned()));
                if self.is_marked(i) {
                    attrs.push(("fillcolor".to_owned(), "green".to_owned()));
                } else {
                    attrs.push(("fillcolor".to_owned(), "orange".to_owned()));
                }
                attrs
            }
            _ => {
                let mut attrs = Vec::new();
                let mut label = format!("{:?}", node);
                label = label.replace("\"", "\\\"");
                label = format!("\"{}\"", label);
                if let Some(addr) = self.addr(i) {
                    label = format!("<{}:  {}>", addr, label);
                }
                attrs.push(("label".to_string(), label));
                attrs.push(("style".to_owned(), "filled".to_owned()));
                attrs.push(("color".to_owned(), "black".to_owned()));
                if self.is_marked(i) {
                    attrs.push(("fillcolor".to_owned(), "green".to_owned()));
                } else {
                    attrs.push(("fillcolor".to_owned(), "grey90".to_owned()));
                }
                attrs
            }
        };
        DotAttrBlock::Hybrid(prefix, attr)
    }
}
<|endoftext|>


<commit_before>//! Internal traits and functions used to implement parallel
//! iteration. These should be considered highly unstable: users of
//! parallel iterators should not need to interact with them directly.
//! See `README.md` for a high-level overview.

use join;
use super::IndexedParallelIterator;
use super::len::*;
use thread_pool::get_registry;

pub trait ProducerCallback<ITEM> {
    type Output;
    fn callback<P>(self, producer: P) -> Self::Output
        where P: Producer<Item=ITEM>;
}

/// A producer which will produce a fixed number of items N. This is
/// not queryable through the API; the consumer is expected to track
/// it.
pub trait Producer: IntoIterator + Send + Sized {
    /// Reports whether the producer has explicit weights.
    fn weighted(&self) -> bool { false }

    /// Cost to produce `len` items, where `len` must be `N`.
    fn cost(&mut self, len: usize) -> f64;

    /// Split into two producers; one produces items `0..index`, the
    /// other `index..N`. Index must be less than `N`.
    fn split_at(self, index: usize) -> (Self, Self);
}

/// A consumer which consumes items that are fed to it.
pub trait Consumer<Item>: Send + Sized {
    type Folder: Folder<Item, Result=Self::Result>;
    type Reducer: Reducer<Self::Result>;
    type Result: Send;

    /// Reports whether the consumer has explicit weights.
    fn weighted(&self) -> bool { false }

    /// If it costs `producer_cost` to produce the items we will
    /// consume, returns cost adjusted to account for consuming them.
    fn cost(&mut self, producer_cost: f64) -> f64;

    /// Divide the consumer into two consumers, one processing items
    /// `0..index` and one processing items from `index..`. Also
    /// produces a reducer that can be used to reduce the results at
    /// the end.
    fn split_at(self, index: usize) -> (Self, Self, Self::Reducer);

    /// Convert the consumer into a folder that can consume items
    /// sequentially, eventually producing a final result.
    fn into_folder(self) -> Self::Folder;

}

pub trait Folder<Item> {
    type Result;

    /// Consume next item and return new sequential state.
    fn consume(self, item: Item) -> Self;

    /// Finish consuming items, produce final result.
    fn complete(self) -> Self::Result;
}

pub trait Reducer<Result> {
    /// Reduce two final results into one; this is executed after a
    /// split.
    fn reduce(self, left: Result, right: Result) -> Result;
}

/// A stateless consumer can be freely copied.
pub trait UnindexedConsumer<ITEM>: Consumer<ITEM> {
    fn split_off(&self) -> Self;
    fn to_reducer(&self) -> Self::Reducer;
}

/// A splitter controls the policy for splitting into smaller work items.
trait Splitter: Send + Sized {
    fn needs_split(&self) -> bool;
    fn split_off(self) -> (Self, Self);
}

/// Classic cost-splitting uses weights to split until below a threshold.
#[derive(Clone, Copy)]
struct CostSplitter(f64);

impl Splitter for CostSplitter {
    #[inline]
    fn needs_split(&self) -> bool {
        self.0 > THRESHOLD
    }

    #[inline]
    fn split_off(mut self) -> (Self, Self) {
        self.0 /= 2.0;
        (self, self)
    }
}

/// Thief-splitting is an adaptive policy that starts by splitting into enough
/// jobs for every worker thread, and then resets itself whenever a job is
/// actually stolen into a different thread.
#[derive(Clone, Copy)]
struct ThiefSplitter {
    origin: usize,
    splits: usize,
}

impl ThiefSplitter {
    #[inline]
    fn id() -> usize {
        // The actual `ID` value is irrelevant.  We're just using its TLS
        // address as a unique thread key, faster than a real thread-id call.
        thread_local!{ static ID: bool = false; }
        ID.with(|id| id as *const bool as usize )
    }

    #[inline]
    fn new() -> ThiefSplitter {
        ThiefSplitter {
            origin: ThiefSplitter::id(),
            splits: get_registry().num_threads(),
        }
    }
}

impl Splitter for ThiefSplitter {
    #[inline]
    fn needs_split(&self) -> bool {
        self.splits > 0 || self.origin != ThiefSplitter::id()
    }

    #[inline]
    fn split_off(mut self) -> (Self, Self) {
        let id = ThiefSplitter::id();
        if self.origin != id {
            self.origin = id;
            self.splits = get_registry().num_threads();
        } else {
            self.splits /= 2;
        }
        (self, self)
    }
}

pub fn bridge<PAR_ITER,C>(mut par_iter: PAR_ITER,
                             consumer: C)
                             -> C::Result
    where PAR_ITER: IndexedParallelIterator, C: Consumer<PAR_ITER::Item>
{
    let len = par_iter.len();
    return par_iter.with_producer(Callback { len: len,
                                             consumer: consumer, });

    struct Callback<C> {
        len: usize,
        consumer: C,
    }

    impl<C, ITEM> ProducerCallback<ITEM> for Callback<C>
        where C: Consumer<ITEM>
    {
        type Output = C::Result;
        fn callback<P>(mut self, mut producer: P) -> C::Result
            where P: Producer<Item=ITEM>
        {
            if producer.weighted() || self.consumer.weighted() {
                let producer_cost = producer.cost(self.len);
                let cost = self.consumer.cost(producer_cost);
                let splitter = CostSplitter(cost);
                bridge_producer_consumer(self.len, splitter, producer, self.consumer)
            } else {
                let splitter = ThiefSplitter::new();
                bridge_producer_consumer(self.len, splitter, producer, self.consumer)
            }
        }
    }
}

fn bridge_producer_consumer<S,P,C>(len: usize,
                                   splitter: S,
                                   producer: P,
                                   consumer: C)
                                   -> C::Result
    where S: Splitter, P: Producer, C: Consumer<P::Item>
{
    if len > 1 && splitter.needs_split() {
        let mid = len / 2;
        let (left_splitter, right_splitter) = splitter.split_off();
        let (left_producer, right_producer) = producer.split_at(mid);
        let (left_consumer, right_consumer, reducer) = consumer.split_at(mid);
        let (left_result, right_result) =
            join(|| bridge_producer_consumer(mid, left_splitter,
                                             left_producer, left_consumer),
                 || bridge_producer_consumer(len - mid, right_splitter,
                                             right_producer, right_consumer));
        reducer.reduce(left_result, right_result)
    } else {
        let mut folder = consumer.into_folder();
        for item in producer {
            folder = folder.consume(item);
        }
        folder.complete()
    }
}

/// Utility type for consumers that don't need a "reduce" step. Just
/// reduces unit to unit.
pub struct NoopReducer;

impl Reducer<()> for NoopReducer {
    fn reduce(self, _left: (), _right: ()) { }
}
<commit_msg>Transform the Splitter trait into an enum<commit_after>//! Internal traits and functions used to implement parallel
//! iteration. These should be considered highly unstable: users of
//! parallel iterators should not need to interact with them directly.
//! See `README.md` for a high-level overview.

use join;
use super::IndexedParallelIterator;
use super::len::*;
use thread_pool::get_registry;

pub trait ProducerCallback<ITEM> {
    type Output;
    fn callback<P>(self, producer: P) -> Self::Output
        where P: Producer<Item=ITEM>;
}

/// A producer which will produce a fixed number of items N. This is
/// not queryable through the API; the consumer is expected to track
/// it.
pub trait Producer: IntoIterator + Send + Sized {
    /// Reports whether the producer has explicit weights.
    fn weighted(&self) -> bool { false }

    /// Cost to produce `len` items, where `len` must be `N`.
    fn cost(&mut self, len: usize) -> f64;

    /// Split into two producers; one produces items `0..index`, the
    /// other `index..N`. Index must be less than `N`.
    fn split_at(self, index: usize) -> (Self, Self);
}

/// A consumer which consumes items that are fed to it.
pub trait Consumer<Item>: Send + Sized {
    type Folder: Folder<Item, Result=Self::Result>;
    type Reducer: Reducer<Self::Result>;
    type Result: Send;

    /// Reports whether the consumer has explicit weights.
    fn weighted(&self) -> bool { false }

    /// If it costs `producer_cost` to produce the items we will
    /// consume, returns cost adjusted to account for consuming them.
    fn cost(&mut self, producer_cost: f64) -> f64;

    /// Divide the consumer into two consumers, one processing items
    /// `0..index` and one processing items from `index..`. Also
    /// produces a reducer that can be used to reduce the results at
    /// the end.
    fn split_at(self, index: usize) -> (Self, Self, Self::Reducer);

    /// Convert the consumer into a folder that can consume items
    /// sequentially, eventually producing a final result.
    fn into_folder(self) -> Self::Folder;

}

pub trait Folder<Item> {
    type Result;

    /// Consume next item and return new sequential state.
    fn consume(self, item: Item) -> Self;

    /// Finish consuming items, produce final result.
    fn complete(self) -> Self::Result;
}

pub trait Reducer<Result> {
    /// Reduce two final results into one; this is executed after a
    /// split.
    fn reduce(self, left: Result, right: Result) -> Result;
}

/// A stateless consumer can be freely copied.
pub trait UnindexedConsumer<ITEM>: Consumer<ITEM> {
    fn split_off(&self) -> Self;
    fn to_reducer(&self) -> Self::Reducer;
}

/// A splitter controls the policy for splitting into smaller work items.
#[derive(Clone, Copy)]
enum Splitter {
    /// Classic cost-splitting uses weights to split until below a threshold.
    Cost(f64),

    /// Thief-splitting is an adaptive policy that starts by splitting into
    /// enough jobs for every worker thread, and then resets itself whenever a
    /// job is actually stolen into a different thread.
    Thief(usize, usize),
}

impl Splitter {
    #[inline]
    fn thief_id() -> usize {
        // The actual `ID` value is irrelevant.  We're just using its TLS
        // address as a unique thread key, faster than a real thread-id call.
        thread_local!{ static ID: bool = false; }
        ID.with(|id| id as *const bool as usize )
    }

    #[inline]
    fn new_thief() -> Splitter {
        Splitter::Thief(Splitter::thief_id(), get_registry().num_threads())
    }

    #[inline]
    fn try(&mut self) -> bool {
        match *self {
            Splitter::Cost(ref mut cost) => {
                if *cost > THRESHOLD {
                    *cost /= 2.0;
                    true
                } else { false }
            },

            Splitter::Thief(ref mut origin, ref mut splits) => {
                let id = Splitter::thief_id();
                if *origin != id {
                    *origin = id;
                    *splits = get_registry().num_threads();
                    true
                } else if *splits > 0 {
                    *splits /= 2;
                    true
                } else { false }
            }
        }
    }
}

pub fn bridge<PAR_ITER,C>(mut par_iter: PAR_ITER,
                             consumer: C)
                             -> C::Result
    where PAR_ITER: IndexedParallelIterator, C: Consumer<PAR_ITER::Item>
{
    let len = par_iter.len();
    return par_iter.with_producer(Callback { len: len,
                                             consumer: consumer, });

    struct Callback<C> {
        len: usize,
        consumer: C,
    }

    impl<C, ITEM> ProducerCallback<ITEM> for Callback<C>
        where C: Consumer<ITEM>
    {
        type Output = C::Result;
        fn callback<P>(mut self, mut producer: P) -> C::Result
            where P: Producer<Item=ITEM>
        {
            let splitter = if producer.weighted() || self.consumer.weighted() {
                let producer_cost = producer.cost(self.len);
                let cost = self.consumer.cost(producer_cost);
                Splitter::Cost(cost)
            } else {
                Splitter::new_thief()
            };
            bridge_producer_consumer(self.len, splitter, producer, self.consumer)
        }
    }
}

fn bridge_producer_consumer<P,C>(len: usize,
                                 mut splitter: Splitter,
                                 producer: P,
                                 consumer: C)
                                 -> C::Result
    where P: Producer, C: Consumer<P::Item>
{
    if len > 1 && splitter.try() {
        let mid = len / 2;
        let (left_producer, right_producer) = producer.split_at(mid);
        let (left_consumer, right_consumer, reducer) = consumer.split_at(mid);
        let (left_result, right_result) =
            join(move || bridge_producer_consumer(mid, splitter,
                                                  left_producer, left_consumer),
                 move || bridge_producer_consumer(len - mid, splitter,
                                                  right_producer, right_consumer));
        reducer.reduce(left_result, right_result)
    } else {
        let mut folder = consumer.into_folder();
        for item in producer {
            folder = folder.consume(item);
        }
        folder.complete()
    }
}

/// Utility type for consumers that don't need a "reduce" step. Just
/// reduces unit to unit.
pub struct NoopReducer;

impl Reducer<()> for NoopReducer {
    fn reduce(self, _left: (), _right: ()) { }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;

use antidote::RwLock;
use chrono::NaiveDateTime;
use irc::client::prelude::*;
use rand::{thread_rng, Rng};
use time;

use plugin::*;
use FrippyClient;
pub mod database;
use self::database::Database;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::ResultExt;

enum QuoteResponse {
    Public(String),
    Private(String),
}

#[derive(PluginName)]
pub struct Quote<T: Database, C: Client> {
    quotes: RwLock<T>,
    phantom: PhantomData<C>,
}

impl<T: Database, C: Client> Quote<T, C> {
    pub fn new(db: T) -> Self {
        Quote {
            quotes: RwLock::new(db),
            phantom: PhantomData,
        }
    }

    fn create_quote(
        &self,
        quotee: &str,
        channel: &str,
        content: &str,
        author: &str,
    ) -> Result<&str, QuoteError> {
        let count = self.quotes.read().count_quotes(quotee, channel)?;
        let tm = time::now().to_timespec();

        let quote = database::NewQuote {
            quotee,
            channel,
            idx: count + 1,
            content,
            author,
            created: NaiveDateTime::from_timestamp(tm.sec, 0u32),
        };

        Ok(self
            .quotes
            .write()
            .insert_quote(&quote)
            .map(|()| "Successfully added!")?)
    }

    fn add(&self, command: &mut PluginCommand) -> Result<&str, QuoteError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        if command.target == command.source {
            Err(ErrorKind::PrivateMessageNotAllowed)?;
        }

        let quotee = command.tokens.remove(0);
        let channel = &command.target;
        let content = command.tokens.join(" ");

        Ok(self.create_quote(&quotee, channel, &content, &command.source)?)
    }

    fn get(&self, command: &PluginCommand) -> Result<String, QuoteError> {
        if command.tokens.is_empty() {
            Err(ErrorKind::InvalidCommand)?;
        }

        let quotee = &command.tokens[0];
        let channel = &command.target;
        let count = self.quotes.read().count_quotes(quotee, channel)?;

        if count < 1 {
            Err(ErrorKind::NotFound)?;
        }

        let idx = match command.tokens.len() {
            1 | _ if command.tokens[1].is_empty() => thread_rng().gen_range(1, count + 1),
            _ => {
                let idx_string = &command.tokens[1];

                let idx = match i32::from_str(idx_string) {
                    Ok(i) => i,
                    Err(_) => Err(ErrorKind::InvalidIndex)?,
                };

                if idx < 0 {
                    count + idx + 1
                } else {
                    idx
                }
            }
        };

        let quote = self
            .quotes
            .read()
            .get_quote(quotee, channel, idx)
            .context(ErrorKind::NotFound)?;

        Ok(format!(
            "\"{}\" - {}[{}/{}]",
            quote.content, quote.quotee, idx, count
        ))
    }

    fn info(&self, command: &PluginCommand) -> Result<String, QuoteError> {
        match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let quotee = &command.tokens[0];
                let channel = &command.target;
                let count = self.quotes.read().count_quotes(quotee, channel)?;

                Ok(match count {
                    0 => Err(ErrorKind::NotFound)?,
                    1 => format!("{} has 1 quote", quotee),
                    _ => format!("{} has {} quotes", quotee, count),
                })
            }
            _ => {
                let quotee = &command.tokens[0];
                let channel = &command.target;
                let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?;
                let quote = self.quotes.read().get_quote(quotee, channel, idx)?;

                Ok(format!(
                    "{}'s quote was added by {} at {} UTC",
                    quotee, quote.author, quote.created
                ))
            }
        }
    }

    fn help(&self) -> &str {
        "usage: quotes <subcommand>\r\n\
         subcommands: add, get, info, help"
    }
}

impl<T: Database, C: FrippyClient> Plugin for Quote<T, C> {
    type Client = C;
    fn execute(&self, _: &Self::Client, _: &Message) -> ExecutionStatus {
        ExecutionStatus::Done
    }

    fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> {
        panic!("Quotes should not use threading")
    }

    fn command(
        &self,
        client: &Self::Client,
        mut command: PluginCommand,
    ) -> Result<(), FrippyError> {
        use self::QuoteResponse::{Private, Public};

        if command.tokens.is_empty() {
            client
                .send_notice(&command.source, &ErrorKind::InvalidCommand.to_string())
                .context(FrippyErrorKind::Connection)?;

            return Ok(());
        }

        let target = command.target.clone();
        let source = command.source.clone();

        let sub_command = command.tokens.remove(0);
        let result = match sub_command.as_ref() {
            "add" => self.add(&mut command).map(|s| Private(s.to_owned())),
            "get" => self.get(&command).map(Public),
            "info" => self.info(&command).map(Public),
            "help" => Ok(Private(self.help().to_owned())),
            _ => Err(ErrorKind::InvalidCommand.into()),
        };

        match result {
            Ok(v) => match v {
                Public(m) => client
                    .send_privmsg(&target, &m)
                    .context(FrippyErrorKind::Connection)?,
                Private(m) => client
                    .send_notice(&source, &m)
                    .context(FrippyErrorKind::Connection)?,
            },
            Err(e) => {
                let message = e.to_string();
                client
                    .send_notice(&source, &message)
                    .context(FrippyErrorKind::Connection)?;
                Err(e).context(FrippyErrorKind::Quote)?
            }
        }

        Ok(())
    }

    fn evaluate(&self, _: &Self::Client, _: PluginCommand) -> Result<String, String> {
        Err(String::from(
            "Evaluation of commands is not implemented for Quote at this time",
        ))
    }
}

impl<T: Database, C: FrippyClient> fmt::Debug for Quote<T, C> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Quote {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "QuoteError"]
    pub enum ErrorKind {
        /// Invalid command error
        #[fail(display = "Incorrect command. Send \"quote help\" for help")]
        InvalidCommand,

        /// Invalid index error
        #[fail(display = "Invalid index")]
        InvalidIndex,

        /// Private message error
        #[fail(display = "You can only add quotes in channel messages")]
        PrivateMessageNotAllowed,

        /// Download  error
        #[fail(display = "Download failed")]
        Download,

        /// Duplicate error
        #[fail(display = "Entry already exists")]
        Duplicate,

        /// Not found error
        #[fail(display = "Quote was not found")]
        NotFound,

        /// MySQL error
        #[cfg(feature = "mysql")]
        #[fail(display = "Failed to execute MySQL Query")]
        MysqlError,

        /// No connection error
        #[cfg(feature = "mysql")]
        #[fail(display = "No connection to the database")]
        NoConnection,
    }
}
<commit_msg>Quote: Fix get command when no index was specified<commit_after>use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;

use antidote::RwLock;
use chrono::NaiveDateTime;
use irc::client::prelude::*;
use rand::{thread_rng, Rng};
use time;

use plugin::*;
use FrippyClient;
pub mod database;
use self::database::Database;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::ResultExt;

enum QuoteResponse {
    Public(String),
    Private(String),
}

#[derive(PluginName)]
pub struct Quote<T: Database, C: Client> {
    quotes: RwLock<T>,
    phantom: PhantomData<C>,
}

impl<T: Database, C: Client> Quote<T, C> {
    pub fn new(db: T) -> Self {
        Quote {
            quotes: RwLock::new(db),
            phantom: PhantomData,
        }
    }

    fn create_quote(
        &self,
        quotee: &str,
        channel: &str,
        content: &str,
        author: &str,
    ) -> Result<&str, QuoteError> {
        let count = self.quotes.read().count_quotes(quotee, channel)?;
        let tm = time::now().to_timespec();

        let quote = database::NewQuote {
            quotee,
            channel,
            idx: count + 1,
            content,
            author,
            created: NaiveDateTime::from_timestamp(tm.sec, 0u32),
        };

        Ok(self
            .quotes
            .write()
            .insert_quote(&quote)
            .map(|()| "Successfully added!")?)
    }

    fn add(&self, command: &mut PluginCommand) -> Result<&str, QuoteError> {
        if command.tokens.len() < 2 {
            Err(ErrorKind::InvalidCommand)?;
        }

        if command.target == command.source {
            Err(ErrorKind::PrivateMessageNotAllowed)?;
        }

        let quotee = command.tokens.remove(0);
        let channel = &command.target;
        let content = command.tokens.join(" ");

        Ok(self.create_quote(&quotee, channel, &content, &command.source)?)
    }

    fn get(&self, command: &PluginCommand) -> Result<String, QuoteError> {
        if command.tokens.is_empty() {
            Err(ErrorKind::InvalidCommand)?;
        }

        let quotee = &command.tokens[0];
        let channel = &command.target;
        let count = self.quotes.read().count_quotes(quotee, channel)?;

        if count < 1 {
            Err(ErrorKind::NotFound)?;
        }

        let len = command.tokens.len();
        let idx = if len < 2 || command.tokens[1].is_empty() {
            thread_rng().gen_range(1, count + 1)
        } else {
            let idx_string = &command.tokens[1];
            let idx = match i32::from_str(idx_string) {
                Ok(i) => i,
                Err(_) => Err(ErrorKind::InvalidIndex)?,
            };

            if idx < 0 {
                count + idx + 1
            } else {
                idx
            }
        };

        let quote = self
            .quotes
            .read()
            .get_quote(quotee, channel, idx)
            .context(ErrorKind::NotFound)?;

        Ok(format!(
            "\"{}\" - {}[{}/{}]",
            quote.content, quote.quotee, idx, count
        ))
    }

    fn info(&self, command: &PluginCommand) -> Result<String, QuoteError> {
        match command.tokens.len() {
            0 => Err(ErrorKind::InvalidCommand)?,
            1 => {
                let quotee = &command.tokens[0];
                let channel = &command.target;
                let count = self.quotes.read().count_quotes(quotee, channel)?;

                Ok(match count {
                    0 => Err(ErrorKind::NotFound)?,
                    1 => format!("{} has 1 quote", quotee),
                    _ => format!("{} has {} quotes", quotee, count),
                })
            }
            _ => {
                let quotee = &command.tokens[0];
                let channel = &command.target;
                let idx = i32::from_str(&command.tokens[1]).context(ErrorKind::InvalidIndex)?;
                let quote = self.quotes.read().get_quote(quotee, channel, idx)?;

                Ok(format!(
                    "{}'s quote was added by {} at {} UTC",
                    quotee, quote.author, quote.created
                ))
            }
        }
    }

    fn help(&self) -> &str {
        "usage: quotes <subcommand>\r\n\
         subcommands: add, get, info, help"
    }
}

impl<T: Database, C: FrippyClient> Plugin for Quote<T, C> {
    type Client = C;
    fn execute(&self, _: &Self::Client, _: &Message) -> ExecutionStatus {
        ExecutionStatus::Done
    }

    fn execute_threaded(&self, _: &Self::Client, _: &Message) -> Result<(), FrippyError> {
        panic!("Quotes should not use threading")
    }

    fn command(
        &self,
        client: &Self::Client,
        mut command: PluginCommand,
    ) -> Result<(), FrippyError> {
        use self::QuoteResponse::{Private, Public};

        if command.tokens.is_empty() {
            client
                .send_notice(&command.source, &ErrorKind::InvalidCommand.to_string())
                .context(FrippyErrorKind::Connection)?;

            return Ok(());
        }

        let target = command.target.clone();
        let source = command.source.clone();

        let sub_command = command.tokens.remove(0);
        let result = match sub_command.as_ref() {
            "add" => self.add(&mut command).map(|s| Private(s.to_owned())),
            "get" => self.get(&command).map(Public),
            "info" => self.info(&command).map(Public),
            "help" => Ok(Private(self.help().to_owned())),
            _ => Err(ErrorKind::InvalidCommand.into()),
        };

        match result {
            Ok(v) => match v {
                Public(m) => client
                    .send_privmsg(&target, &m)
                    .context(FrippyErrorKind::Connection)?,
                Private(m) => client
                    .send_notice(&source, &m)
                    .context(FrippyErrorKind::Connection)?,
            },
            Err(e) => {
                let message = e.to_string();
                client
                    .send_notice(&source, &message)
                    .context(FrippyErrorKind::Connection)?;
                Err(e).context(FrippyErrorKind::Quote)?
            }
        }

        Ok(())
    }

    fn evaluate(&self, _: &Self::Client, _: PluginCommand) -> Result<String, String> {
        Err(String::from(
            "Evaluation of commands is not implemented for Quote at this time",
        ))
    }
}

impl<T: Database, C: FrippyClient> fmt::Debug for Quote<T, C> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Quote {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "QuoteError"]
    pub enum ErrorKind {
        /// Invalid command error
        #[fail(display = "Incorrect command. Send \"quote help\" for help")]
        InvalidCommand,

        /// Invalid index error
        #[fail(display = "Invalid index")]
        InvalidIndex,

        /// Private message error
        #[fail(display = "You can only add quotes in channel messages")]
        PrivateMessageNotAllowed,

        /// Download  error
        #[fail(display = "Download failed")]
        Download,

        /// Duplicate error
        #[fail(display = "Entry already exists")]
        Duplicate,

        /// Not found error
        #[fail(display = "Quote was not found")]
        NotFound,

        /// MySQL error
        #[cfg(feature = "mysql")]
        #[fail(display = "Failed to execute MySQL Query")]
        MysqlError,

        /// No connection error
        #[cfg(feature = "mysql")]
        #[fail(display = "No connection to the database")]
        NoConnection,
    }
}
<|endoftext|>