<commit_before>//! The parser is a bit more complex than it needs to be as it needs to be fully specialized to
//! avoid a recompilation every time a later part of the compiler is changed. Due to this the
//! string interner and therefore also garbage collector needs to compiled before the parser.

#[macro_use]
extern crate log;
#[macro_use]
extern crate quick_error;
extern crate gluon_base as base;
extern crate combine;
extern crate combine_language;

use std::error::Error as StdError;
use std::fmt;

use base::ast::{Expr, IdentEnv, SpannedExpr};
use base::error::Errors;
use base::pos::{self, BytePos, Spanned};
use base::symbol::Symbol;
use base::types::ArcType;

use combine::primitives::{StreamOnce, Error as CombineError};

use infix::{OpTable, Reparser};
use lexer::Lexer;

mod grammar;
mod infix;
mod lexer;

/// Shrink hidden spans to fit the visible expressions and flatten singleton blocks.
fn shrink_hidden_spans<Id>(mut expr: SpannedExpr<Id>) -> SpannedExpr<Id> {
    match expr.value {
        Expr::IfElse(_, _, ref last) |
        Expr::LetBindings(_, ref last) |
        Expr::TypeBindings(_, ref last) => {
            let end = last.span.end;
            drop(last);
            expr.span.end = end;
        }
        Expr::Lambda(ref lambda) => {
            let end = lambda.body.span.end;
            drop(lambda);
            expr.span.end = end;
        }
        Expr::Block(ref mut exprs) => {
            match exprs.len() {
                0 => (),
                1 => return exprs.pop().unwrap(),
                _ => {
                    let end = exprs.last().unwrap().span.end;
                    drop(exprs);
                    expr.span.end = end;
                }
            }
        }
        Expr::Match(_, ref alts) => {
            if let Some(last_alt) = alts.last() {
                let end = last_alt.expr.span.end;
                drop(last_alt);
                expr.span.end = end;
            }
        }
        Expr::App(_, _) |
        Expr::Ident(_) |
        Expr::Literal(_) |
        Expr::Projection(_, _, _) |
        Expr::Infix(_, _, _) |
        Expr::Array(_) |
        Expr::Record { .. } |
        Expr::Tuple(_) => (),
    }
    expr
}

#[derive(Debug, PartialEq)]
pub struct ParseError {
    pub errors: Vec<CombineError<String, String>>,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        CombineError::fmt_errors(&self.errors, f)
    }
}

impl StdError for ParseError {
    fn description(&self) -> &str {
        "Parse error"
    }
}

quick_error! {
    #[derive(Debug, PartialEq)]
    pub enum Error {
        Parser(err: Spanned<ParseError, BytePos>) {
            description(err.value.description())
            display("{}", err)
            from()
        }
        Infix(err: infix::ReparseError) {
            description(err.description())
            display("{}", err)
            from()
        }
    }
}

// Dummy type for ParseError which has the correct associated types
#[derive(Clone)]
pub struct StreamType(());

impl StreamOnce for StreamType {
    type Item = String;
    type Range = String;
    type Position = BytePos;

    fn uncons(&mut self) -> Result<String, CombineError<String, String>> {
        unimplemented!()
    }

    fn position(&self) -> Self::Position {
        unimplemented!()
    }
}

pub enum FieldPattern<Id> {
    Type(Id, Option<Id>),
    Value(Id, Option<Id>),
}

pub enum FieldExpr<Id> {
    Type(Id, Option<ArcType<Id>>),
    Value(Id, Option<SpannedExpr<Id>>),
}

// Hack around LALRPOP's limited type syntax
type MutIdentEnv<'env, Id> = &'env mut IdentEnv<Ident = Id>;

fn parse_expr_<Id>(symbols: &mut IdentEnv<Ident = Id>,
                   input: &str)
                   -> Result<SpannedExpr<Id>, Errors<Error>>
    where Id: Clone,
{
    let lexer = Lexer::new(input);

    match grammar::parse_TopExpr(input, symbols, lexer) {
        // TODO: handle errors
        Ok(mut expr) => {
            let mut reparser = Reparser::new(OpTable::default(), symbols);
            if let Err(errors) = reparser.reparse(&mut expr) {
                return Err(Errors {
                    errors: errors.errors.into_iter().map(Error::Infix).collect(),
                });
            }
            Ok(expr)
        }
        Err(err) => {
            let err =
                ParseError { errors: vec![CombineError::Message(format!("{:?}", err).into())] };
            Err(Errors {
                errors: vec![Error::Parser(pos::spanned2(BytePos::from(0), BytePos::from(0), err))],
            })
        }
    }
}

pub fn parse_expr(symbols: &mut IdentEnv<Ident = Symbol>,
                  input: &str)
                  -> Result<SpannedExpr<Symbol>, Errors<Error>> {
    parse_expr_(symbols, input)
}

#[cfg(feature = "test")]
pub fn parse_string<'env, 'input>
    (symbols: &'env mut IdentEnv<Ident = String>,
     input: &'input str)
     -> Result<SpannedExpr<String>, (Option<SpannedExpr<String>>, Errors<Error>)> {
    parse_expr_(symbols, input).map_err(|err| (None, err))
}
<commit_msg>Remove useless drops<commit_after>//! The parser is a bit more complex than it needs to be as it needs to be fully specialized to
//! avoid a recompilation every time a later part of the compiler is changed. Due to this the
//! string interner and therefore also garbage collector needs to compiled before the parser.

#[macro_use]
extern crate log;
#[macro_use]
extern crate quick_error;
extern crate gluon_base as base;
extern crate combine;
extern crate combine_language;

use std::error::Error as StdError;
use std::fmt;

use base::ast::{Expr, IdentEnv, SpannedExpr};
use base::error::Errors;
use base::pos::{self, BytePos, Spanned};
use base::symbol::Symbol;
use base::types::ArcType;

use combine::primitives::{StreamOnce, Error as CombineError};

use infix::{OpTable, Reparser};
use lexer::Lexer;

mod grammar;
mod infix;
mod lexer;

/// Shrink hidden spans to fit the visible expressions and flatten singleton blocks.
fn shrink_hidden_spans<Id>(mut expr: SpannedExpr<Id>) -> SpannedExpr<Id> {
    match expr.value {
        Expr::IfElse(_, _, ref last) |
        Expr::LetBindings(_, ref last) |
        Expr::TypeBindings(_, ref last) => expr.span.end = last.span.end,
        Expr::Lambda(ref lambda) => expr.span.end = lambda.body.span.end,
        Expr::Block(ref mut exprs) => {
            match exprs.len() {
                0 => (),
                1 => return exprs.pop().unwrap(),
                _ => expr.span.end = exprs.last().unwrap().span.end,
            }
        }
        Expr::Match(_, ref alts) => {
            if let Some(last_alt) = alts.last() {
                let end = last_alt.expr.span.end;
                expr.span.end = end;
            }
        }
        Expr::App(_, _) |
        Expr::Ident(_) |
        Expr::Literal(_) |
        Expr::Projection(_, _, _) |
        Expr::Infix(_, _, _) |
        Expr::Array(_) |
        Expr::Record { .. } |
        Expr::Tuple(_) => (),
    }
    expr
}

#[derive(Debug, PartialEq)]
pub struct ParseError {
    pub errors: Vec<CombineError<String, String>>,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        CombineError::fmt_errors(&self.errors, f)
    }
}

impl StdError for ParseError {
    fn description(&self) -> &str {
        "Parse error"
    }
}

quick_error! {
    #[derive(Debug, PartialEq)]
    pub enum Error {
        Parser(err: Spanned<ParseError, BytePos>) {
            description(err.value.description())
            display("{}", err)
            from()
        }
        Infix(err: infix::ReparseError) {
            description(err.description())
            display("{}", err)
            from()
        }
    }
}

// Dummy type for ParseError which has the correct associated types
#[derive(Clone)]
pub struct StreamType(());

impl StreamOnce for StreamType {
    type Item = String;
    type Range = String;
    type Position = BytePos;

    fn uncons(&mut self) -> Result<String, CombineError<String, String>> {
        unimplemented!()
    }

    fn position(&self) -> Self::Position {
        unimplemented!()
    }
}

pub enum FieldPattern<Id> {
    Type(Id, Option<Id>),
    Value(Id, Option<Id>),
}

pub enum FieldExpr<Id> {
    Type(Id, Option<ArcType<Id>>),
    Value(Id, Option<SpannedExpr<Id>>),
}

// Hack around LALRPOP's limited type syntax
type MutIdentEnv<'env, Id> = &'env mut IdentEnv<Ident = Id>;

fn parse_expr_<Id>(symbols: &mut IdentEnv<Ident = Id>,
                   input: &str)
                   -> Result<SpannedExpr<Id>, Errors<Error>>
    where Id: Clone,
{
    let lexer = Lexer::new(input);

    match grammar::parse_TopExpr(input, symbols, lexer) {
        // TODO: handle errors
        Ok(mut expr) => {
            let mut reparser = Reparser::new(OpTable::default(), symbols);
            if let Err(errors) = reparser.reparse(&mut expr) {
                return Err(Errors {
                    errors: errors.errors.into_iter().map(Error::Infix).collect(),
                });
            }
            Ok(expr)
        }
        Err(err) => {
            let err =
                ParseError { errors: vec![CombineError::Message(format!("{:?}", err).into())] };
            Err(Errors {
                errors: vec![Error::Parser(pos::spanned2(BytePos::from(0), BytePos::from(0), err))],
            })
        }
    }
}

pub fn parse_expr(symbols: &mut IdentEnv<Ident = Symbol>,
                  input: &str)
                  -> Result<SpannedExpr<Symbol>, Errors<Error>> {
    parse_expr_(symbols, input)
}

#[cfg(feature = "test")]
pub fn parse_string<'env, 'input>
    (symbols: &'env mut IdentEnv<Ident = String>,
     input: &'input str)
     -> Result<SpannedExpr<String>, (Option<SpannedExpr<String>>, Errors<Error>)> {
    parse_expr_(symbols, input).map_err(|err| (None, err))
}
<|endoftext|>


<commit_before>use irc::client::prelude::*;

use std::time::Duration;
use std::sync::Mutex;

use time;
use chrono::NaiveDateTime;
use humantime::format_duration;

use plugin::*;

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

pub mod database;
use self::database::Database;

macro_rules! try_lock {
    ( $m:expr ) => {
        match $m.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }
}

#[derive(PluginName, Default)]
pub struct Tell<T: Database> {
    tells: Mutex<T>,
}

impl<T: Database> Tell<T> {
    pub fn new(db: T) -> Tell<T> {
        Tell {
            tells: Mutex::new(db),
        }
    }

    fn tell_command(
        &self,
        client: &IrcClient,
        command: PluginCommand,
    ) -> Result<String, TellError> {
        if command.tokens.len() < 2 {
            return Ok(self.invalid_command().to_owned());
        }

        let mut online = Vec::new();

        let receivers = command.tokens[0].split(',');
        let sender = command.source;

        for receiver in receivers {
            if receiver.eq_ignore_ascii_case(client.current_nickname())
                || receiver.eq_ignore_ascii_case(&sender)
            {
                online.push(receiver);
                continue;
            }

            let channels = client
                .list_channels()
                .expect("The irc crate should not be compiled with the \"nochanlists\" feature");

            if let Some(_) = channels
                .iter()
                .map(|c| client.list_users(&c))
                .map(|opt| {
                    opt.and_then(|us| {
                        us.into_iter()
                            .find(|u| u.get_nickname().eq_ignore_ascii_case(&receiver))
                    })
                })
                .find(|opt| opt.is_some())
            {
                online.push(receiver);
                continue;
            }

            let tm = time::now().to_timespec();
            let message = command.tokens[1..].join(" ");
            let tell = database::NewTellMessage {
                sender: &sender,
                receiver: &receiver.to_lowercase(),
                time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
                message: &message,
            };

            try_lock!(self.tells).insert_tell(&tell)?;
        }

        Ok(match online.len() {
            0 => format!("Got it!"),
            1 => format!("{} is currently online.", online[0]),
            _ => format!("{} are currently online.", online.join(", ")),
        })
    }

    fn on_namelist(&self, client: &IrcClient, channel: &str) -> Result<(), FrippyError> {
        let receivers = try_lock!(self.tells)
            .get_receivers()
            .context(FrippyErrorKind::Tell)?;

        if let Some(users) = client.list_users(channel) {
            debug!("Outstanding tells for {:?}", receivers);

            for receiver in users
                .iter()
                .map(|u| u.get_nickname())
                .filter(|u| receivers.iter().any(|r| r == &u.to_lowercase()))
            {
                self.send_tells(client, receiver)?;
            }

            Ok(())
        } else {
            Ok(())
        }
    }

    fn send_tells(&self, client: &IrcClient, receiver: &str) -> Result<(), FrippyError> {
        if client.current_nickname() == receiver {
            return Ok(());
        }

        let mut tells = try_lock!(self.tells);

        let tell_messages = match tells.get_tells(&receiver.to_lowercase()) {
            Ok(t) => t,
            Err(e) => {
                // This warning only occurs if frippy is built without a database
                #[allow(unreachable_patterns)]
                return match e.kind() {
                    ErrorKind::NotFound => Ok(()),
                    _ => Err(e.context(FrippyErrorKind::Tell))?,
                };
            }
        };

        for tell in tell_messages {
            let now = Duration::new(time::now().to_timespec().sec as u64, 0);
            let dur = now - Duration::new(tell.time.timestamp() as u64, 0);
            let human_dur = format_duration(dur);

            client
                .send_notice(
                    receiver,
                    &format!(
                        "Tell from {} {} ago: {}",
                        tell.sender, human_dur, tell.message
                    ),
                )
                .context(FrippyErrorKind::Connection)?;

            debug!(
                "Sent {:?} from {:?} to {:?}",
                tell.message, tell.sender, receiver
            );
        }

        tells
            .delete_tells(&receiver.to_lowercase())
            .context(FrippyErrorKind::Tell)?;

        Ok(())
    }

    fn invalid_command(&self) -> &str {
        "Incorrect Command. \
         Send \"tell help\" for help."
    }

    fn help(&self) -> &str {
        "usage: {} tell user message\r\n\
         example: {0} tell Foobar Hello!"
    }
}

impl<T: Database> Plugin for Tell<T> {
    fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
        let res = match message.command {
            Command::JOIN(_, _, _) => self.send_tells(client, message.source_nickname().unwrap()),
            Command::NICK(ref nick) => self.send_tells(client, nick),
            Command::Response(resp, ref chan_info, _) => {
                if resp == Response::RPL_NAMREPLY {
                    debug!("NAMREPLY info: {:?}", chan_info);

                    self.on_namelist(client, &chan_info[chan_info.len() - 1])
                } else {
                    Ok(())
                }
            }
            _ => Ok(()),
        };

        match res {
            Ok(_) => ExecutionStatus::Done,
            Err(e) => ExecutionStatus::Err(e),
        }
    }

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

    fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            return Ok(client
                .send_notice(&command.source, &self.invalid_command())
                .context(FrippyErrorKind::Connection)?);
        }

        let sender = command.source.to_owned();

        Ok(match command.tokens[0].as_ref() {
            "help" => client
                .send_notice(&command.source, &self.help())
                .context(FrippyErrorKind::Connection)
                .into(),
            _ => match self.tell_command(client, command) {
                Ok(msg) => client
                    .send_notice(&sender, &msg)
                    .context(FrippyErrorKind::Connection),
                Err(e) => client
                    .send_notice(&sender, &e.to_string())
                    .context(FrippyErrorKind::Connection)
                    .into(),
            },
        }?)
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from("This Plugin does not implement any commands."))
    }
}

use std::fmt;
impl<T: Database> fmt::Debug for Tell<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Tell {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "TellError"]
    pub enum ErrorKind {
        /// Not found command error
        #[fail(display = "Tell 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>Fix empty receivers getting accepted by Tell<commit_after>use irc::client::prelude::*;

use std::time::Duration;
use std::sync::Mutex;

use time;
use chrono::NaiveDateTime;
use humantime::format_duration;

use plugin::*;

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

pub mod database;
use self::database::Database;

macro_rules! try_lock {
    ( $m:expr ) => {
        match $m.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }
}

#[derive(PluginName, Default)]
pub struct Tell<T: Database> {
    tells: Mutex<T>,
}

impl<T: Database> Tell<T> {
    pub fn new(db: T) -> Tell<T> {
        Tell {
            tells: Mutex::new(db),
        }
    }

    fn tell_command(
        &self,
        client: &IrcClient,
        command: PluginCommand,
    ) -> Result<String, TellError> {
        if command.tokens.len() < 2 {
            return Ok(self.invalid_command().to_owned());
        }

        let mut online = Vec::new();

        let receivers = command.tokens[0].split(',').filter(|&s| !s.is_empty());
        let sender = command.source;

        let mut no_receiver = true;
        for receiver in receivers {
            if receiver.eq_ignore_ascii_case(client.current_nickname())
                || receiver.eq_ignore_ascii_case(&sender)
            {
                online.push(receiver);
                continue;
            }

            let channels = client
                .list_channels()
                .expect("The irc crate should not be compiled with the \"nochanlists\" feature");

            if let Some(_) = channels
                .iter()
                .map(|c| client.list_users(&c))
                .map(|opt| {
                    opt.and_then(|us| {
                        us.into_iter()
                            .find(|u| u.get_nickname().eq_ignore_ascii_case(&receiver))
                    })
                })
                .find(|opt| opt.is_some())
            {
                online.push(receiver);
                continue;
            }

            let tm = time::now().to_timespec();
            let message = command.tokens[1..].join(" ");
            let tell = database::NewTellMessage {
                sender: &sender,
                receiver: &receiver.to_lowercase(),
                time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
                message: &message,
            };

            debug!("Saving tell for {:?}", receiver);
            try_lock!(self.tells).insert_tell(&tell)?;
            no_receiver = false;
        }

        Ok(if no_receiver {
            format!("Invalid receiver.")
        } else {
            match online.len() {
                0 => format!("Got it!"),
                1 => format!("{} is currently online.", online[0]),
                _ => format!("{} are currently online.", online.join(", ")),
            }
        })
    }

    fn on_namelist(&self, client: &IrcClient, channel: &str) -> Result<(), FrippyError> {
        let receivers = try_lock!(self.tells)
            .get_receivers()
            .context(FrippyErrorKind::Tell)?;

        if let Some(users) = client.list_users(channel) {
            debug!("Outstanding tells for {:?}", receivers);

            for receiver in users
                .iter()
                .map(|u| u.get_nickname())
                .filter(|u| receivers.iter().any(|r| r == &u.to_lowercase()))
            {
                self.send_tells(client, receiver)?;
            }

            Ok(())
        } else {
            Ok(())
        }
    }

    fn send_tells(&self, client: &IrcClient, receiver: &str) -> Result<(), FrippyError> {
        if client.current_nickname() == receiver {
            return Ok(());
        }

        let mut tells = try_lock!(self.tells);

        let tell_messages = match tells.get_tells(&receiver.to_lowercase()) {
            Ok(t) => t,
            Err(e) => {
                // This warning only occurs if frippy is built without a database
                #[allow(unreachable_patterns)]
                return match e.kind() {
                    ErrorKind::NotFound => Ok(()),
                    _ => Err(e.context(FrippyErrorKind::Tell))?,
                };
            }
        };

        for tell in tell_messages {
            let now = Duration::new(time::now().to_timespec().sec as u64, 0);
            let dur = now - Duration::new(tell.time.timestamp() as u64, 0);
            let human_dur = format_duration(dur);

            client
                .send_notice(
                    receiver,
                    &format!(
                        "Tell from {} {} ago: {}",
                        tell.sender, human_dur, tell.message
                    ),
                )
                .context(FrippyErrorKind::Connection)?;

            debug!(
                "Sent {:?} from {:?} to {:?}",
                tell.message, tell.sender, receiver
            );
        }

        tells
            .delete_tells(&receiver.to_lowercase())
            .context(FrippyErrorKind::Tell)?;

        Ok(())
    }

    fn invalid_command(&self) -> &str {
        "Incorrect Command. \
         Send \"tell help\" for help."
    }

    fn help(&self) -> &str {
        "usage: {} tell user message\r\n\
         example: {0} tell Foobar Hello!"
    }
}

impl<T: Database> Plugin for Tell<T> {
    fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
        let res = match message.command {
            Command::JOIN(_, _, _) => self.send_tells(client, message.source_nickname().unwrap()),
            Command::NICK(ref nick) => self.send_tells(client, nick),
            Command::Response(resp, ref chan_info, _) => {
                if resp == Response::RPL_NAMREPLY {
                    debug!("NAMREPLY info: {:?}", chan_info);

                    self.on_namelist(client, &chan_info[chan_info.len() - 1])
                } else {
                    Ok(())
                }
            }
            _ => Ok(()),
        };

        match res {
            Ok(_) => ExecutionStatus::Done,
            Err(e) => ExecutionStatus::Err(e),
        }
    }

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

    fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            return Ok(client
                .send_notice(&command.source, &self.invalid_command())
                .context(FrippyErrorKind::Connection)?);
        }

        let sender = command.source.to_owned();

        Ok(match command.tokens[0].as_ref() {
            "help" => client
                .send_notice(&command.source, &self.help())
                .context(FrippyErrorKind::Connection)
                .into(),
            _ => match self.tell_command(client, command) {
                Ok(msg) => client
                    .send_notice(&sender, &msg)
                    .context(FrippyErrorKind::Connection),
                Err(e) => client
                    .send_notice(&sender, &e.to_string())
                    .context(FrippyErrorKind::Connection)
                    .into(),
            },
        }?)
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from("This Plugin does not implement any commands."))
    }
}

use std::fmt;
impl<T: Database> fmt::Debug for Tell<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Tell {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "TellError"]
    pub enum ErrorKind {
        /// Not found command error
        #[fail(display = "Tell 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|>


<commit_before>use error::{SassError, ErrorKind, Result};
use sass::op::Op;
use sass::number_value::NumberValue;

use std::borrow::Cow;
use std::fmt;

#[derive(Debug, Clone, PartialEq)]
pub struct ColorValue<'a> {
    pub red: i32,
    pub green: i32,
    pub blue: i32,
    pub original: Cow<'a, str>,
}

impl<'a, 'b> ColorValue<'a> {
    pub fn from_hex(hex: Cow<'a, str>) -> Result<ColorValue<'a>> {
        if hex.len() == 4 {
            Ok(ColorValue {
                red:   i32::from_str_radix(&hex[1..2], 16).unwrap() * 17,
                green: i32::from_str_radix(&hex[2..3], 16).unwrap() * 17,
                blue:  i32::from_str_radix(&hex[3..4], 16).unwrap() * 17,
                original: hex,
            })
        } else if hex.len() == 7 {
            Ok(ColorValue {
                red:   i32::from_str_radix(&hex[1..3], 16).unwrap(),
                green: i32::from_str_radix(&hex[3..5], 16).unwrap(),
                blue:  i32::from_str_radix(&hex[5..7], 16).unwrap(),
                original: hex,
            })
        } else {
            Err(SassError {
                kind: ErrorKind::InvalidColor,
                message: format!("Invalid hex color: {}", hex),
            })
        }
    }

    pub fn from_rgb(r: i32, g: i32, b: i32) -> ColorValue<'a> {
        ColorValue {
            red: r, green: g, blue: b, original: format!("#{:x}{:x}{:x}", r, g, b).into(),
        }
    }

    pub fn into_owned(self) -> ColorValue<'b> {
        ColorValue {
            red: self.red, green: self.green, blue: self.blue,
            original: self.original.into_owned().into(),
        }
    }

    pub fn apply_math(self, op: Op, nv: NumberValue<'a>) -> Result<ColorValue<'a>> {
        Ok(ColorValue::from_rgb(
            try!(op.math(self.red as f32, nv.scalar)) as i32,
            try!(op.math(self.green as f32, nv.scalar)) as i32,
            try!(op.math(self.blue as f32, nv.scalar)) as i32,
        ))
    }

    pub fn combine_colors(self, op: Op, c: ColorValue<'a>) -> Result<ColorValue<'a>> {
        Ok(ColorValue::from_rgb(
            try!(op.math(self.red as f32, c.red as f32)) as i32,
            try!(op.math(self.green as f32, c.green as f32)) as i32,
            try!(op.math(self.blue as f32, c.blue as f32)) as i32,
        ))
    }
}

impl<'a> fmt::Display for ColorValue<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let candidate = format!("#{:x}{:x}{:x}", self.red, self.green, self.blue);
        if candidate.len() < self.original.len() {
            write!(f, "{}", candidate)
        } else {
            write!(f, "{}", self.original)
        }
    }
}
<commit_msg>Cap RGB at 256<commit_after>use error::{SassError, ErrorKind, Result};
use sass::op::Op;
use sass::number_value::NumberValue;

use std::borrow::Cow;
use std::fmt;
use std::cmp;

#[derive(Debug, Clone, PartialEq)]
pub struct ColorValue<'a> {
    pub red: i32,
    pub green: i32,
    pub blue: i32,
    pub original: Cow<'a, str>,
}

impl<'a, 'b> ColorValue<'a> {
    pub fn from_hex(hex: Cow<'a, str>) -> Result<ColorValue<'a>> {
        if hex.len() == 4 {
            Ok(ColorValue {
                red:   i32::from_str_radix(&hex[1..2], 16).unwrap() * 17,
                green: i32::from_str_radix(&hex[2..3], 16).unwrap() * 17,
                blue:  i32::from_str_radix(&hex[3..4], 16).unwrap() * 17,
                original: hex,
            })
        } else if hex.len() == 7 {
            Ok(ColorValue {
                red:   i32::from_str_radix(&hex[1..3], 16).unwrap(),
                green: i32::from_str_radix(&hex[3..5], 16).unwrap(),
                blue:  i32::from_str_radix(&hex[5..7], 16).unwrap(),
                original: hex,
            })
        } else {
            Err(SassError {
                kind: ErrorKind::InvalidColor,
                message: format!("Invalid hex color: {}", hex),
            })
        }
    }

    pub fn from_rgb(r: i32, g: i32, b: i32) -> ColorValue<'a> {
        ColorValue {
            red: r, green: g, blue: b, original: format!("#{:x}{:x}{:x}", r, g, b).into(),
        }
    }

    pub fn into_owned(self) -> ColorValue<'b> {
        ColorValue {
            red: self.red, green: self.green, blue: self.blue,
            original: self.original.into_owned().into(),
        }
    }

    pub fn apply_math(self, op: Op, nv: NumberValue<'a>) -> Result<ColorValue<'a>> {
        Ok(ColorValue::from_rgb(
            cmp::min(try!(op.math(self.red as f32, nv.scalar)) as i32, 255),
            cmp::min(try!(op.math(self.green as f32, nv.scalar)) as i32, 255),
            cmp::min(try!(op.math(self.blue as f32, nv.scalar)) as i32, 255),
        ))
    }

    pub fn combine_colors(self, op: Op, c: ColorValue<'a>) -> Result<ColorValue<'a>> {
        Ok(ColorValue::from_rgb(
            try!(op.math(self.red as f32, c.red as f32)) as i32,
            try!(op.math(self.green as f32, c.green as f32)) as i32,
            try!(op.math(self.blue as f32, c.blue as f32)) as i32,
        ))
    }
}

impl<'a> fmt::Display for ColorValue<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let candidate = format!("#{:x}{:x}{:x}", self.red, self.green, self.blue);
        if candidate.len() < self.original.len() {
            write!(f, "{}", candidate)
        } else {
            write!(f, "{}", self.original)
        }
    }
}
<|endoftext|>


<commit_before>use raii::Raii;
use {ffi, Handle, Return};
use super::types::OdbcType;

/// Indicates that a type can be retrieved using `Cursor::get_data`
pub unsafe trait Output<'a>: Sized {
    fn get_data(
        stmt: &mut Raii<ffi::Stmt>,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>,
    ) -> Return<Option<Self>>;
}

unsafe impl<'a, T> Output<'a> for T
where
    T: OdbcType<'a>,
{
    fn get_data(
        stmt: &mut Raii<ffi::Stmt>,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>,
    ) -> Return<Option<Self>> {
        stmt.get_data(col_or_param_num, buffer)
    }
}

impl Raii<ffi::Stmt> {
    fn get_data<'a, T>(
        &mut self,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>
    ) -> Return<Option<T>>
    where
        T: OdbcType<'a>,
    {
        self.get_partial_data(col_or_param_num, buffer, 0)
    }

    fn get_partial_data<'a, T>(
        &mut self,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>,
        start_pos: usize
    ) -> Return<Option<T>>
    where
        T: OdbcType<'a>,
    {
        if buffer.len() - start_pos == 0 {
            panic!("buffer length may not be zero");
        }
        if buffer.len() - start_pos > ffi::SQLLEN::max_value() as usize {
            panic!("buffer is larger than {} bytes", ffi::SQLLEN::max_value());
        }
        let mut indicator: ffi::SQLLEN = 0;
        // Get buffer length...
        let result = unsafe { ffi::SQLGetData(
                self.handle(),
                col_or_param_num,
                T::c_data_type(),
                buffer.as_mut_ptr().offset(start_pos as isize) as ffi::SQLPOINTER,
                (buffer.len() - start_pos) as ffi::SQLLEN,
                &mut indicator as *mut ffi::SQLLEN,
            ) };
        match result {
            ffi::SQL_SUCCESS => {
                if indicator == ffi::SQL_NULL_DATA {
                    Return::Success(None)
                } else {
                    let slice = &buffer[..(start_pos + indicator as usize)];
                    Return::Success(Some(T::convert(slice)))
                }
            }
            ffi::SQL_SUCCESS_WITH_INFO => {
                let initial_len = buffer.len();
                if indicator == ffi::SQL_NO_TOTAL {
                    buffer.resize(initial_len * 2, 0);
                    return self.get_partial_data(col_or_param_num, buffer, initial_len - 1);
                } else {
                    // Check if string has been truncated.
                    if indicator >= initial_len as ffi::SQLLEN {
                        buffer.resize(indicator as usize + 1, 0);
                        return self.get_partial_data(col_or_param_num, buffer, initial_len - 1);
                    } else {
                        let slice = &buffer[..(start_pos + indicator as usize)];
                        // No truncation. Warning may be due to some other issue.
                        Return::SuccessWithInfo(Some(T::convert(slice)))
                    }
                }
            }
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NO_DATA => panic!("SQLGetData has already returned the colmun data"),
            r => panic!("unexpected return value from SQLGetData: {:?}", r),
        }
    }
}
<commit_msg>assert for indicator outside of buffer and workaround for broken driver<commit_after>use raii::Raii;
use {ffi, Handle, Return};
use super::types::OdbcType;

/// Indicates that a type can be retrieved using `Cursor::get_data`
pub unsafe trait Output<'a>: Sized {
    fn get_data(
        stmt: &mut Raii<ffi::Stmt>,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>,
    ) -> Return<Option<Self>>;
}

unsafe impl<'a, T> Output<'a> for T
where
    T: OdbcType<'a>,
{
    fn get_data(
        stmt: &mut Raii<ffi::Stmt>,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>,
    ) -> Return<Option<Self>> {
        stmt.get_data(col_or_param_num, buffer)
    }
}

impl Raii<ffi::Stmt> {
    fn get_data<'a, T>(
        &mut self,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>
    ) -> Return<Option<T>>
    where
        T: OdbcType<'a>,
    {
        self.get_partial_data(col_or_param_num, buffer, 0)
    }

    fn get_partial_data<'a, T>(
        &mut self,
        col_or_param_num: u16,
        buffer: &'a mut Vec<u8>,
        start_pos: usize
    ) -> Return<Option<T>>
    where
        T: OdbcType<'a>,
    {
        if buffer.len() - start_pos == 0 {
            panic!("buffer length may not be zero");
        }
        if buffer.len() - start_pos > ffi::SQLLEN::max_value() as usize {
            panic!("buffer is larger than {} bytes", ffi::SQLLEN::max_value());
        }
        let mut indicator: ffi::SQLLEN = 0;
        // Get buffer length...
        let result = unsafe { ffi::SQLGetData(
                self.handle(),
                col_or_param_num,
                T::c_data_type(),
                buffer.as_mut_ptr().offset(start_pos as isize) as ffi::SQLPOINTER,
                (buffer.len() - start_pos) as ffi::SQLLEN,
                &mut indicator as *mut ffi::SQLLEN,
            ) };
        match result {
            ffi::SQL_SUCCESS => {
                if indicator == ffi::SQL_NULL_DATA {
                    Return::Success(None)
                } else {
                    assert!(start_pos + indicator as usize <= buffer.len(), "no more data but indicatior outside of data buffer");
                    let slice = &buffer[..(start_pos + indicator as usize)];
                    Return::Success(Some(T::convert(slice)))
                }
            }
            ffi::SQL_SUCCESS_WITH_INFO => {
                let initial_len = buffer.len();
                // Workaround for drivers that don't include tailing null
                let null_offset = if *buffer.last().unwrap() == 0 { 1 } else { 0 };

                if indicator == ffi::SQL_NO_TOTAL {
                    buffer.resize(initial_len * 2, 0);
                    return self.get_partial_data(col_or_param_num, buffer, initial_len - null_offset);
                } else {
                    // Check if string has been truncated.
                    if indicator >= initial_len as ffi::SQLLEN {
                        buffer.resize(indicator as usize + 1, 0);
                        return self.get_partial_data(col_or_param_num, buffer, initial_len - null_offset);
                    } else {
                        let slice = &buffer[..(start_pos + indicator as usize)];
                        // No truncation. Warning may be due to some other issue.
                        Return::SuccessWithInfo(Some(T::convert(slice)))
                    }
                }
            }
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NO_DATA => panic!("SQLGetData has already returned the colmun data"),
            r => panic!("unexpected return value from SQLGetData: {:?}", r),
        }
    }
}
<|endoftext|>


<commit_before>extern crate orbfont;
extern crate ransid;

use self::orbfont::Font;

use std::mem;

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

pub struct Console {
    pub window: Window,
    pub inner: ransid::Console,
    pub font: Font,
    pub font_bold: Font,
    pub command: String,
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            window: window,
            inner: ransid::Console::new(width as usize/8, height as usize/16),
            font: Font::find(Some("Mono"), None, None).unwrap(),
            font_bold: Font::find(Some("Mono"), None, Some("Bold")).unwrap(),
            command: String::new(),
        }
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.inner.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x7F"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            return Some(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.inner.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.inner.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

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

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        self.inner.write(bytes);
        //TODO: if self.inner.redraw
        {
            self.inner.redraw = false;

            self.window.set(Color {
                data: self.inner.background.data
            });
            for y in 0..self.inner.h {
                for x in 0..self.inner.w {
                    let block = self.inner.display[y * self.inner.w + x];
                    self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color {
                        data: block.bg.data
                    });
                    if block.c != ' ' {
                        if block.bold {
                            self.font_bold.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                data: block.fg.data
                            });
                        } else {
                            self.font.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                data: block.fg.data
                            });
                        }
                    }
                    if block.underlined {
                        self.window.rect(x as i32 * 8, y as i32 * 16 + 15, 8, 1, Color {
                            data: block.fg.data
                        });
                    }
                }
            }
            if self.inner.cursor {
                self.window.rect(self.inner.x as i32 * 8, self.inner.y as i32 * 16, 8, 16, Color {
                    data: self.inner.foreground.data
                });
            }
            self.window.sync();
        }
    }
}
<commit_msg>Fix offset of underline<commit_after>extern crate orbfont;
extern crate ransid;

use self::orbfont::Font;

use std::mem;

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

pub struct Console {
    pub window: Window,
    pub inner: ransid::Console,
    pub font: Font,
    pub font_bold: Font,
    pub command: String,
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            window: window,
            inner: ransid::Console::new(width as usize/8, height as usize/16),
            font: Font::find(Some("Mono"), None, None).unwrap(),
            font_bold: Font::find(Some("Mono"), None, Some("Bold")).unwrap(),
            command: String::new(),
        }
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.inner.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x7F"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            return Some(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.inner.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.inner.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

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

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        self.inner.write(bytes);
        //TODO: if self.inner.redraw
        {
            self.inner.redraw = false;

            self.window.set(Color {
                data: self.inner.background.data
            });
            for y in 0..self.inner.h {
                for x in 0..self.inner.w {
                    let block = self.inner.display[y * self.inner.w + x];
                    self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color {
                        data: block.bg.data
                    });
                    if block.c != ' ' {
                        if block.bold {
                            self.font_bold.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                data: block.fg.data
                            });
                        } else {
                            self.font.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                data: block.fg.data
                            });
                        }
                    }
                    if block.underlined {
                        self.window.rect(x as i32 * 8, y as i32 * 16 + 14, 8, 1, Color {
                            data: block.fg.data
                        });
                    }
                }
            }
            if self.inner.cursor {
                self.window.rect(self.inner.x as i32 * 8, self.inner.y as i32 * 16, 8, 16, Color {
                    data: self.inner.foreground.data
                });
            }
            self.window.sync();
        }
    }
}
<|endoftext|>


<commit_before>mod reader;
mod writer;
mod message;

use std::net::SocketAddr;
use std::{io, fmt, mem, time, cmp};
use std::net::TcpStream;

use chrono::{DateTime, Utc};

pub use self::message::Message;
use self::reader::Reader;
use self::writer::Writer;
use socket::Socket;
use torrent::{Torrent, Bitfield};
use throttle::Throttle;
use control::cio;
use rpc::{self, resource};
use tracker;
use util;
use {DHT_EXT, CONFIG};

error_chain! {
    errors {
        ProtocolError(r: &'static str) {
            description("Peer did not conform to the bittorrent protocol")
                display("Peer protocol error: {:?}", r)
        }
    }
}

const INIT_MAX_QUEUE: u16 = 15;
const MAX_QUEUE_CAP: u16 = 400;

/// Peer connection and associated metadata.
pub struct Peer<T: cio::CIO> {
    id: usize,
    cio: T,
    pieces: Bitfield,
    remote_status: Status,
    local_status: Status,
    /// Current number of queued requests
    queued: u16,
    /// Maximum number of requests that can be queued
    /// at a time.
    max_queue: u16,
    tid: usize,
    downloaded: u32,
    uploaded: u32,
    downloaded_bytes: u64,
    uploaded_bytes: u64,
    last_flush: DateTime<Utc>,
    addr: SocketAddr,
    t_hash: [u8; 20],
    cid: Option<[u8; 20]>,
    rsv: Option<[u8; 8]>,
}

#[derive(Debug)]
pub struct Status {
    pub choked: bool,
    pub interested: bool,
}

pub struct PeerConn {
    last_action: time::Instant,
    sock: Socket,
    reader: Reader,
    writer: Writer,
}

impl PeerConn {
    pub fn new(sock: Socket) -> PeerConn {
        let writer = Writer::new();
        let reader = Reader::new();
        PeerConn {
            sock,
            writer,
            reader,
            last_action: time::Instant::now(),
        }
    }

    #[cfg(test)]
    pub fn test() -> PeerConn {
        let writer = Writer::new();
        let reader = Reader::new();
        PeerConn {
            last_action: time::Instant::now(),
            sock: Socket::empty(),
            writer,
            reader,
        }
    }

    pub fn sock(&self) -> &Socket {
        &self.sock
    }

    pub fn sock_mut(&mut self) -> &mut Socket {
        &mut self.sock
    }

    pub fn last_action(&self) -> &time::Instant {
        &self.last_action
    }

    /// Creates a new "outgoing" peer, which acts as a client.
    /// Once created, set_torrent should be called.
    pub fn new_outgoing(ip: &SocketAddr) -> io::Result<PeerConn> {
        Ok(PeerConn::new(Socket::new(ip)?))
    }

    /// Creates a peer where we are acting as the server.
    /// Once the handshake is received, set_torrent should be called.
    pub fn new_incoming(sock: TcpStream) -> io::Result<PeerConn> {
        Ok(PeerConn::new(Socket::from_stream(sock)?))
    }

    pub fn writable(&mut self) -> io::Result<()> {
        self.last_action = time::Instant::now();
        self.writer.writable(&mut self.sock)
    }

    pub fn readable(&mut self) -> io::Result<Option<Message>> {
        self.last_action = time::Instant::now();
        self.reader.readable(&mut self.sock)
    }

    pub fn write_message(&mut self, msg: Message) -> io::Result<()> {
        self.writer.write_message(msg, &mut self.sock)
    }

    pub fn set_throttle(&mut self, throt: Throttle) {
        self.sock.throttle = Some(throt);
    }
}

impl Status {
    fn new() -> Status {
        Status {
            choked: true,
            interested: false,
        }
    }
}

#[cfg(test)]
impl Peer<cio::test::TCIO> {
    pub fn test(
        id: usize,
        uploaded: u32,
        downloaded: u32,
        queued: u16,
        pieces: Bitfield,
    ) -> Peer<cio::test::TCIO> {
        Peer {
            id,
            remote_status: Status::new(),
            local_status: Status::new(),
            uploaded,
            downloaded,
            uploaded_bytes: 0,
            downloaded_bytes: 0,
            addr: "127.0.0.1:0".parse().unwrap(),
            cio: cio::test::TCIO::new(),
            queued,
            max_queue: queued,
            pieces,
            tid: 0,
            t_hash: [0u8; 20],
            rsv: None,
            cid: None,
            last_flush: Utc::now(),
        }
    }

    pub fn test_from_pieces(id: usize, pieces: Bitfield) -> Peer<cio::test::TCIO> {
        Peer::test(id, 0, 0, 0, pieces)
    }

    pub fn test_from_stats(id: usize, ul: u32, dl: u32) -> Peer<cio::test::TCIO> {
        Peer::test(id, ul, dl, 0, Bitfield::new(4))
    }

    pub fn test_with_tcio(mut cio: cio::test::TCIO) -> Peer<cio::test::TCIO> {
        use control::cio::CIO;

        let conn = PeerConn::test();
        let id = cio.add_peer(conn).unwrap();
        let mut peer = Peer::test(id, 0, 0, 0, Bitfield::new(4));
        peer.cio = cio;
        peer
    }
}

impl<T: cio::CIO> Peer<T> {
    pub fn new(
        mut conn: PeerConn,
        t: &mut Torrent<T>,
        cid: Option<[u8; 20]>,
        rsv: Option<[u8; 8]>,
    ) -> cio::Result<Peer<T>> {
        let addr = conn.sock().addr();
        conn.set_throttle(t.get_throttle(0));
        let id = t.cio.add_peer(conn)?;
        let mut p = Peer {
            id,
            addr,
            remote_status: Status::new(),
            local_status: Status::new(),
            uploaded: 0,
            downloaded: 0,
            uploaded_bytes: 0,
            downloaded_bytes: 0,
            cio: t.cio.new_handle(),
            queued: 0,
            max_queue: INIT_MAX_QUEUE,
            pieces: Bitfield::new(t.info.hashes.len() as u64),
            tid: t.id,
            t_hash: t.info.hash,
            rsv,
            cid,
            last_flush: Utc::now(),
        };
        p.send_message(Message::handshake(&t.info));
        p.send_message(Message::Bitfield(t.pieces.clone()));
        p.send_rpc_info();
        Ok(p)
    }

    /// Returns whether or not the peer has received a handshake
    pub fn ready(&self) -> bool {
        self.cid.is_some()
    }

    pub fn addr(&self) -> SocketAddr {
        self.addr
    }

    pub fn pieces(&self) -> &Bitfield {
        &self.pieces
    }

    #[cfg(test)]
    pub fn pieces_mut(&mut self) -> &mut Bitfield {
        &mut self.pieces
    }

    pub fn id(&self) -> usize {
        self.id
    }

    pub fn flush(&mut self) -> (u32, u32) {
        (
            mem::replace(&mut self.uploaded, 0),
            mem::replace(&mut self.downloaded, 0),
        )
    }

    pub fn remote_status(&self) -> &Status {
        &self.remote_status
    }

    pub fn get_tx_rates(&mut self) -> (u64, u64) {
        let dur = Utc::now()
            .signed_duration_since(self.last_flush)
            .num_milliseconds() as u64;
        let ub = mem::replace(&mut self.uploaded_bytes, 0);
        let db = mem::replace(&mut self.downloaded_bytes, 0);
        let ul = (1000 * ub) / dur;
        let dl = (1000 * db) / dur;
        self.last_flush = Utc::now();

        let rate = (dl / 1024) as u16;
        // Taken from rtorrent's pipeline calculation
        let nmq = if rate < 20 { rate + 2 } else { rate / 5 + 18 };
        // Clamp between -15 / +30 for queue len changes
        self.max_queue = cmp::min(
            cmp::max(nmq, self.max_queue.saturating_sub(15)),
            self.max_queue + 50,
        );
        // Keep it under the max cap
        self.max_queue = cmp::min(self.max_queue, MAX_QUEUE_CAP);
        (ul, dl)
    }

    pub fn queue_reqs(&mut self) -> Option<u16> {
        if self.remote_status.choked || self.queued > self.max_queue / 2 {
            None
        } else {
            Some(cmp::max(self.max_queue.saturating_sub(self.queued), 1))
        }
    }

    pub fn handle_msg(&mut self, msg: &mut Message) -> Result<()> {
        match *msg {
            Message::Handshake { rsv, id, .. } => {
                if (rsv[DHT_EXT.0] & DHT_EXT.1) != 0 {
                    self.send_message(Message::Port(CONFIG.dht.port));
                }
                self.rsv = Some(rsv);
                self.cid = Some(id);
                self.send_rpc_info();
            }
            Message::Piece { length, .. } |
            Message::SharedPiece { length, .. } => {
                self.downloaded_bytes += length as u64;
                self.downloaded += 1;
                self.queued -= 1;
            }
            Message::Request { .. } => {
                if self.local_status.choked {
                    return Err(
                        ErrorKind::ProtocolError("Peer requested while choked!").into(),
                    );
                }
            }
            Message::Choke => {
                self.remote_status.choked = true;
            }
            Message::Unchoke => {
                self.remote_status.choked = false;
            }
            Message::Interested => {
                self.remote_status.interested = true;
            }
            Message::Uninterested => {
                self.remote_status.interested = false;
            }
            Message::Have(idx) => {
                if idx >= self.pieces.len() as u32 {
                    return Err(
                        ErrorKind::ProtocolError("Invalid piece provided in HAVE!").into(),
                    );
                }
                self.pieces.set_bit(idx as u64);
            }
            Message::Bitfield(ref mut pieces) => {
                // Set the correct length, then swap the pieces
                pieces.cap(self.pieces.len());
                mem::swap(pieces, &mut self.pieces);
            }
            Message::KeepAlive => {
                self.send_message(Message::KeepAlive);
            }
            Message::Cancel { index, begin, .. } => {
                self.cio.get_peer(self.id, |conn| {
                    conn.writer.write_queue.retain(|m| {
                        if let Message::Piece { index: i, begin: b, .. } = *m {
                            return !(i == index && b == begin);
                        }
                        true
                    });
                });
            }
            Message::Port(p) => {
                let mut s = self.addr();
                s.set_port(p);
                self.cio.msg_trk(tracker::Request::AddNode(s));
            }
        }
        Ok(())
    }

    pub fn request_piece(&mut self, idx: u32, offset: u32, len: u32) {
        let m = Message::request(idx, offset, len);
        self.queued += 1;
        self.send_message(m);
    }

    pub fn choke(&mut self) {
        if !self.local_status.choked {
            self.local_status.choked = true;
            self.send_message(Message::Choke);
        }
    }

    pub fn unchoke(&mut self) {
        if self.local_status.choked {
            self.local_status.choked = false;
            self.send_message(Message::Unchoke);
        }
    }

    pub fn interested(&mut self) {
        if !self.local_status.interested {
            self.local_status.interested = true;
            self.send_message(Message::Interested);
        }
    }

    pub fn uninterested(&mut self) {
        if self.local_status.interested {
            self.local_status.interested = false;
            self.send_message(Message::Uninterested);
        }
    }

    pub fn send_message(&mut self, msg: Message) {
        match msg {
            Message::SharedPiece { length, .. } |
            Message::Piece { length, .. } => {
                self.uploaded += 1;
                self.uploaded_bytes += length as u64;
            }
            _ => {}
        }
        self.cio.msg_peer(self.id, msg);
    }

    fn send_rpc_info(&mut self) {
        if let Some(cid) = self.cid {
            let id = util::peer_rpc_id(&self.t_hash, self.id as u64);
            self.cio.msg_rpc(rpc::CtlMessage::Extant(vec![
                resource::Resource::Peer(resource::Peer {
                    id,
                    torrent_id: util::hash_to_id(&self.t_hash[..]),
                    client_id: util::hash_to_id(&cid[..]),
                    ip: self.addr.to_string(),
                    rate_up: 0,
                    rate_down: 0,
                    availability: 0.,
                }),
            ]));
        }
    }

    pub fn send_rpc_removal(&mut self) {
        if self.ready() {
            self.cio.msg_rpc(rpc::CtlMessage::Removed(
                vec![util::peer_rpc_id(&self.t_hash, self.id as u64)],
            ));
        }
    }
}

impl<T: cio::CIO> Drop for Peer<T> {
    fn drop(&mut self) {
        self.send_rpc_removal();
    }
}

impl<T: cio::CIO> fmt::Debug for Peer<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Peer {{ id: {}, tid: {}, local_status: {:?}, remote_status: {:?} }}",
            self.id,
            self.tid,
            self.local_status,
            self.remote_status
        )
    }
}

#[cfg(test)]
mod tests {
    use super::Peer;
    use control::cio::{CIO, test};
    use torrent::Message;

    #[test]
    fn test_cancel() {
        let mut tcio = test::TCIO::new();
        let mut peer = Peer::test_with_tcio(tcio.new_handle());
        let p1 = Message::Piece {
            index: 0,
            begin: 0,
            data: Box::new([0u8; 16_384]),
            length: 16_384,
        };
        let p2 = Message::Piece {
            index: 1,
            begin: 1,
            data: Box::new([0u8; 16_384]),
            length: 16_384,
        };
        let p3 = Message::Piece {
            index: 2,
            begin: 2,
            data: Box::new([0u8; 16_384]),
            length: 16_384,
        };
        peer.send_message(Message::KeepAlive);
        peer.send_message(p1.clone());
        peer.send_message(p2.clone());
        peer.send_message(p3.clone());

        let mut c = Message::Cancel {
            index: 1,
            begin: 1,
            length: 16_384,
        };
        peer.handle_msg(&mut c).unwrap();
        let wq = tcio.get_peer(peer.id, |p| p.writer.write_queue.clone())
            .unwrap();
        assert_eq!(wq.len(), 2);
        assert_eq!(wq[0], p1);
        assert_eq!(wq[1], p3);
    }
}
<commit_msg>Update comment to reflect queue len changes<commit_after>mod reader;
mod writer;
mod message;

use std::net::SocketAddr;
use std::{io, fmt, mem, time, cmp};
use std::net::TcpStream;

use chrono::{DateTime, Utc};

pub use self::message::Message;
use self::reader::Reader;
use self::writer::Writer;
use socket::Socket;
use torrent::{Torrent, Bitfield};
use throttle::Throttle;
use control::cio;
use rpc::{self, resource};
use tracker;
use util;
use {DHT_EXT, CONFIG};

error_chain! {
    errors {
        ProtocolError(r: &'static str) {
            description("Peer did not conform to the bittorrent protocol")
                display("Peer protocol error: {:?}", r)
        }
    }
}

const INIT_MAX_QUEUE: u16 = 15;
const MAX_QUEUE_CAP: u16 = 400;

/// Peer connection and associated metadata.
pub struct Peer<T: cio::CIO> {
    id: usize,
    cio: T,
    pieces: Bitfield,
    remote_status: Status,
    local_status: Status,
    /// Current number of queued requests
    queued: u16,
    /// Maximum number of requests that can be queued
    /// at a time.
    max_queue: u16,
    tid: usize,
    downloaded: u32,
    uploaded: u32,
    downloaded_bytes: u64,
    uploaded_bytes: u64,
    last_flush: DateTime<Utc>,
    addr: SocketAddr,
    t_hash: [u8; 20],
    cid: Option<[u8; 20]>,
    rsv: Option<[u8; 8]>,
}

#[derive(Debug)]
pub struct Status {
    pub choked: bool,
    pub interested: bool,
}

pub struct PeerConn {
    last_action: time::Instant,
    sock: Socket,
    reader: Reader,
    writer: Writer,
}

impl PeerConn {
    pub fn new(sock: Socket) -> PeerConn {
        let writer = Writer::new();
        let reader = Reader::new();
        PeerConn {
            sock,
            writer,
            reader,
            last_action: time::Instant::now(),
        }
    }

    #[cfg(test)]
    pub fn test() -> PeerConn {
        let writer = Writer::new();
        let reader = Reader::new();
        PeerConn {
            last_action: time::Instant::now(),
            sock: Socket::empty(),
            writer,
            reader,
        }
    }

    pub fn sock(&self) -> &Socket {
        &self.sock
    }

    pub fn sock_mut(&mut self) -> &mut Socket {
        &mut self.sock
    }

    pub fn last_action(&self) -> &time::Instant {
        &self.last_action
    }

    /// Creates a new "outgoing" peer, which acts as a client.
    /// Once created, set_torrent should be called.
    pub fn new_outgoing(ip: &SocketAddr) -> io::Result<PeerConn> {
        Ok(PeerConn::new(Socket::new(ip)?))
    }

    /// Creates a peer where we are acting as the server.
    /// Once the handshake is received, set_torrent should be called.
    pub fn new_incoming(sock: TcpStream) -> io::Result<PeerConn> {
        Ok(PeerConn::new(Socket::from_stream(sock)?))
    }

    pub fn writable(&mut self) -> io::Result<()> {
        self.last_action = time::Instant::now();
        self.writer.writable(&mut self.sock)
    }

    pub fn readable(&mut self) -> io::Result<Option<Message>> {
        self.last_action = time::Instant::now();
        self.reader.readable(&mut self.sock)
    }

    pub fn write_message(&mut self, msg: Message) -> io::Result<()> {
        self.writer.write_message(msg, &mut self.sock)
    }

    pub fn set_throttle(&mut self, throt: Throttle) {
        self.sock.throttle = Some(throt);
    }
}

impl Status {
    fn new() -> Status {
        Status {
            choked: true,
            interested: false,
        }
    }
}

#[cfg(test)]
impl Peer<cio::test::TCIO> {
    pub fn test(
        id: usize,
        uploaded: u32,
        downloaded: u32,
        queued: u16,
        pieces: Bitfield,
    ) -> Peer<cio::test::TCIO> {
        Peer {
            id,
            remote_status: Status::new(),
            local_status: Status::new(),
            uploaded,
            downloaded,
            uploaded_bytes: 0,
            downloaded_bytes: 0,
            addr: "127.0.0.1:0".parse().unwrap(),
            cio: cio::test::TCIO::new(),
            queued,
            max_queue: queued,
            pieces,
            tid: 0,
            t_hash: [0u8; 20],
            rsv: None,
            cid: None,
            last_flush: Utc::now(),
        }
    }

    pub fn test_from_pieces(id: usize, pieces: Bitfield) -> Peer<cio::test::TCIO> {
        Peer::test(id, 0, 0, 0, pieces)
    }

    pub fn test_from_stats(id: usize, ul: u32, dl: u32) -> Peer<cio::test::TCIO> {
        Peer::test(id, ul, dl, 0, Bitfield::new(4))
    }

    pub fn test_with_tcio(mut cio: cio::test::TCIO) -> Peer<cio::test::TCIO> {
        use control::cio::CIO;

        let conn = PeerConn::test();
        let id = cio.add_peer(conn).unwrap();
        let mut peer = Peer::test(id, 0, 0, 0, Bitfield::new(4));
        peer.cio = cio;
        peer
    }
}

impl<T: cio::CIO> Peer<T> {
    pub fn new(
        mut conn: PeerConn,
        t: &mut Torrent<T>,
        cid: Option<[u8; 20]>,
        rsv: Option<[u8; 8]>,
    ) -> cio::Result<Peer<T>> {
        let addr = conn.sock().addr();
        conn.set_throttle(t.get_throttle(0));
        let id = t.cio.add_peer(conn)?;
        let mut p = Peer {
            id,
            addr,
            remote_status: Status::new(),
            local_status: Status::new(),
            uploaded: 0,
            downloaded: 0,
            uploaded_bytes: 0,
            downloaded_bytes: 0,
            cio: t.cio.new_handle(),
            queued: 0,
            max_queue: INIT_MAX_QUEUE,
            pieces: Bitfield::new(t.info.hashes.len() as u64),
            tid: t.id,
            t_hash: t.info.hash,
            rsv,
            cid,
            last_flush: Utc::now(),
        };
        p.send_message(Message::handshake(&t.info));
        p.send_message(Message::Bitfield(t.pieces.clone()));
        p.send_rpc_info();
        Ok(p)
    }

    /// Returns whether or not the peer has received a handshake
    pub fn ready(&self) -> bool {
        self.cid.is_some()
    }

    pub fn addr(&self) -> SocketAddr {
        self.addr
    }

    pub fn pieces(&self) -> &Bitfield {
        &self.pieces
    }

    #[cfg(test)]
    pub fn pieces_mut(&mut self) -> &mut Bitfield {
        &mut self.pieces
    }

    pub fn id(&self) -> usize {
        self.id
    }

    pub fn flush(&mut self) -> (u32, u32) {
        (
            mem::replace(&mut self.uploaded, 0),
            mem::replace(&mut self.downloaded, 0),
        )
    }

    pub fn remote_status(&self) -> &Status {
        &self.remote_status
    }

    pub fn get_tx_rates(&mut self) -> (u64, u64) {
        let dur = Utc::now()
            .signed_duration_since(self.last_flush)
            .num_milliseconds() as u64;
        let ub = mem::replace(&mut self.uploaded_bytes, 0);
        let db = mem::replace(&mut self.downloaded_bytes, 0);
        let ul = (1000 * ub) / dur;
        let dl = (1000 * db) / dur;
        self.last_flush = Utc::now();

        let rate = (dl / 1024) as u16;
        // Taken from rtorrent's pipeline calculation
        let nmq = if rate < 20 { rate + 2 } else { rate / 5 + 18 };
        // Clamp between -15 / +50 for queue len changes
        self.max_queue = cmp::min(
            cmp::max(nmq, self.max_queue.saturating_sub(15)),
            self.max_queue + 50,
        );
        // Keep it under the max cap
        self.max_queue = cmp::min(self.max_queue, MAX_QUEUE_CAP);
        (ul, dl)
    }

    pub fn queue_reqs(&mut self) -> Option<u16> {
        if self.remote_status.choked || self.queued > self.max_queue / 2 {
            None
        } else {
            Some(cmp::max(self.max_queue.saturating_sub(self.queued), 1))
        }
    }

    pub fn handle_msg(&mut self, msg: &mut Message) -> Result<()> {
        match *msg {
            Message::Handshake { rsv, id, .. } => {
                if (rsv[DHT_EXT.0] & DHT_EXT.1) != 0 {
                    self.send_message(Message::Port(CONFIG.dht.port));
                }
                self.rsv = Some(rsv);
                self.cid = Some(id);
                self.send_rpc_info();
            }
            Message::Piece { length, .. } |
            Message::SharedPiece { length, .. } => {
                self.downloaded_bytes += length as u64;
                self.downloaded += 1;
                self.queued -= 1;
            }
            Message::Request { .. } => {
                if self.local_status.choked {
                    return Err(
                        ErrorKind::ProtocolError("Peer requested while choked!").into(),
                    );
                }
            }
            Message::Choke => {
                self.remote_status.choked = true;
            }
            Message::Unchoke => {
                self.remote_status.choked = false;
            }
            Message::Interested => {
                self.remote_status.interested = true;
            }
            Message::Uninterested => {
                self.remote_status.interested = false;
            }
            Message::Have(idx) => {
                if idx >= self.pieces.len() as u32 {
                    return Err(
                        ErrorKind::ProtocolError("Invalid piece provided in HAVE!").into(),
                    );
                }
                self.pieces.set_bit(idx as u64);
            }
            Message::Bitfield(ref mut pieces) => {
                // Set the correct length, then swap the pieces
                pieces.cap(self.pieces.len());
                mem::swap(pieces, &mut self.pieces);
            }
            Message::KeepAlive => {
                self.send_message(Message::KeepAlive);
            }
            Message::Cancel { index, begin, .. } => {
                self.cio.get_peer(self.id, |conn| {
                    conn.writer.write_queue.retain(|m| {
                        if let Message::Piece { index: i, begin: b, .. } = *m {
                            return !(i == index && b == begin);
                        }
                        true
                    });
                });
            }
            Message::Port(p) => {
                let mut s = self.addr();
                s.set_port(p);
                self.cio.msg_trk(tracker::Request::AddNode(s));
            }
        }
        Ok(())
    }

    pub fn request_piece(&mut self, idx: u32, offset: u32, len: u32) {
        let m = Message::request(idx, offset, len);
        self.queued += 1;
        self.send_message(m);
    }

    pub fn choke(&mut self) {
        if !self.local_status.choked {
            self.local_status.choked = true;
            self.send_message(Message::Choke);
        }
    }

    pub fn unchoke(&mut self) {
        if self.local_status.choked {
            self.local_status.choked = false;
            self.send_message(Message::Unchoke);
        }
    }

    pub fn interested(&mut self) {
        if !self.local_status.interested {
            self.local_status.interested = true;
            self.send_message(Message::Interested);
        }
    }

    pub fn uninterested(&mut self) {
        if self.local_status.interested {
            self.local_status.interested = false;
            self.send_message(Message::Uninterested);
        }
    }

    pub fn send_message(&mut self, msg: Message) {
        match msg {
            Message::SharedPiece { length, .. } |
            Message::Piece { length, .. } => {
                self.uploaded += 1;
                self.uploaded_bytes += length as u64;
            }
            _ => {}
        }
        self.cio.msg_peer(self.id, msg);
    }

    fn send_rpc_info(&mut self) {
        if let Some(cid) = self.cid {
            let id = util::peer_rpc_id(&self.t_hash, self.id as u64);
            self.cio.msg_rpc(rpc::CtlMessage::Extant(vec![
                resource::Resource::Peer(resource::Peer {
                    id,
                    torrent_id: util::hash_to_id(&self.t_hash[..]),
                    client_id: util::hash_to_id(&cid[..]),
                    ip: self.addr.to_string(),
                    rate_up: 0,
                    rate_down: 0,
                    availability: 0.,
                }),
            ]));
        }
    }

    pub fn send_rpc_removal(&mut self) {
        if self.ready() {
            self.cio.msg_rpc(rpc::CtlMessage::Removed(
                vec![util::peer_rpc_id(&self.t_hash, self.id as u64)],
            ));
        }
    }
}

impl<T: cio::CIO> Drop for Peer<T> {
    fn drop(&mut self) {
        self.send_rpc_removal();
    }
}

impl<T: cio::CIO> fmt::Debug for Peer<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Peer {{ id: {}, tid: {}, local_status: {:?}, remote_status: {:?} }}",
            self.id,
            self.tid,
            self.local_status,
            self.remote_status
        )
    }
}

#[cfg(test)]
mod tests {
    use super::Peer;
    use control::cio::{CIO, test};
    use torrent::Message;

    #[test]
    fn test_cancel() {
        let mut tcio = test::TCIO::new();
        let mut peer = Peer::test_with_tcio(tcio.new_handle());
        let p1 = Message::Piece {
            index: 0,
            begin: 0,
            data: Box::new([0u8; 16_384]),
            length: 16_384,
        };
        let p2 = Message::Piece {
            index: 1,
            begin: 1,
            data: Box::new([0u8; 16_384]),
            length: 16_384,
        };
        let p3 = Message::Piece {
            index: 2,
            begin: 2,
            data: Box::new([0u8; 16_384]),
            length: 16_384,
        };
        peer.send_message(Message::KeepAlive);
        peer.send_message(p1.clone());
        peer.send_message(p2.clone());
        peer.send_message(p3.clone());

        let mut c = Message::Cancel {
            index: 1,
            begin: 1,
            length: 16_384,
        };
        peer.handle_msg(&mut c).unwrap();
        let wq = tcio.get_peer(peer.id, |p| p.writer.write_queue.clone())
            .unwrap();
        assert_eq!(wq.len(), 2);
        assert_eq!(wq[0], p1);
        assert_eq!(wq[1], p3);
    }
}
<|endoftext|>


<commit_before>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};
use rustc_span::def_id::DefId;

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let def_id = source.def_id();
        let param_env = tcx.param_env(def_id);
        TypeChecker { when: &self.when, def_id, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    def_id: DefId,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.def_id,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<commit_msg>validator: print MIR instance on failure<commit_after>//! Validates the MIR to ensure that invariants are upheld.

use super::{MirPass, MirSource};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::{
    mir::{
        BasicBlock, Body, Location, Operand, Rvalue, Statement, StatementKind, Terminator,
        TerminatorKind,
    },
    ty::{self, ParamEnv, TyCtxt},
};

#[derive(Copy, Clone, Debug)]
enum EdgeKind {
    Unwind,
    Normal,
}

pub struct Validator {
    /// Describes at which point in the pipeline this validation is happening.
    pub when: String,
}

impl<'tcx> MirPass<'tcx> for Validator {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
        let param_env = tcx.param_env(source.def_id());
        TypeChecker { when: &self.when, source, body, tcx, param_env }.visit_body(body);
    }
}

struct TypeChecker<'a, 'tcx> {
    when: &'a str,
    source: MirSource<'tcx>,
    body: &'a Body<'tcx>,
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
}

impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
    fn fail(&self, location: Location, msg: impl AsRef<str>) {
        let span = self.body.source_info(location).span;
        // We use `delay_span_bug` as we might see broken MIR when other errors have already
        // occurred.
        self.tcx.sess.diagnostic().delay_span_bug(
            span,
            &format!(
                "broken MIR in {:?} ({}) at {:?}:\n{}",
                self.source.instance,
                self.when,
                location,
                msg.as_ref()
            ),
        );
    }

    fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
        if let Some(bb) = self.body.basic_blocks().get(bb) {
            let src = self.body.basic_blocks().get(location.block).unwrap();
            match (src.is_cleanup, bb.is_cleanup, edge_kind) {
                // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
                (false, false, EdgeKind::Normal)
                // Non-cleanup blocks can jump to cleanup blocks along unwind edges
                | (false, true, EdgeKind::Unwind)
                // Cleanup blocks can jump to cleanup blocks along non-unwind edges
                | (true, true, EdgeKind::Normal) => {}
                // All other jumps are invalid
                _ => {
                    self.fail(
                        location,
                        format!(
                            "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
                            edge_kind,
                            bb,
                            src.is_cleanup,
                            bb.is_cleanup,
                        )
                    )
                }
            }
        } else {
            self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
    fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
        // `Operand::Copy` is only supposed to be used with `Copy` types.
        if let Operand::Copy(place) = operand {
            let ty = place.ty(&self.body.local_decls, self.tcx).ty;
            let span = self.body.source_info(location).span;

            if !ty.is_copy_modulo_regions(self.tcx, self.param_env, span) {
                self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
            }
        }

        self.super_operand(operand, location);
    }

    fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
        // The sides of an assignment must not alias. Currently this just checks whether the places
        // are identical.
        if let StatementKind::Assign(box (dest, rvalue)) = &statement.kind {
            match rvalue {
                Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => {
                    if dest == src {
                        self.fail(
                            location,
                            "encountered `Assign` statement with overlapping memory",
                        );
                    }
                }
                _ => {}
            }
        }
    }

    fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
        match &terminator.kind {
            TerminatorKind::Goto { target } => {
                self.check_edge(location, *target, EdgeKind::Normal);
            }
            TerminatorKind::SwitchInt { targets, values, .. } => {
                if targets.len() != values.len() + 1 {
                    self.fail(
                        location,
                        format!(
                            "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)",
                            values.len(),
                            targets.len(),
                        ),
                    );
                }
                for target in targets {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
            }
            TerminatorKind::Drop { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::DropAndReplace { target, unwind, .. } => {
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Call { func, destination, cleanup, .. } => {
                let func_ty = func.ty(&self.body.local_decls, self.tcx);
                match func_ty.kind {
                    ty::FnPtr(..) | ty::FnDef(..) => {}
                    _ => self.fail(
                        location,
                        format!("encountered non-callable type {} in `Call` terminator", func_ty),
                    ),
                }
                if let Some((_, target)) = destination {
                    self.check_edge(location, *target, EdgeKind::Normal);
                }
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Assert { cond, target, cleanup, .. } => {
                let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
                if cond_ty != self.tcx.types.bool {
                    self.fail(
                        location,
                        format!(
                            "encountered non-boolean condition of type {} in `Assert` terminator",
                            cond_ty
                        ),
                    );
                }
                self.check_edge(location, *target, EdgeKind::Normal);
                if let Some(cleanup) = cleanup {
                    self.check_edge(location, *cleanup, EdgeKind::Unwind);
                }
            }
            TerminatorKind::Yield { resume, drop, .. } => {
                self.check_edge(location, *resume, EdgeKind::Normal);
                if let Some(drop) = drop {
                    self.check_edge(location, *drop, EdgeKind::Normal);
                }
            }
            TerminatorKind::FalseEdge { real_target, imaginary_target } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                self.check_edge(location, *imaginary_target, EdgeKind::Normal);
            }
            TerminatorKind::FalseUnwind { real_target, unwind } => {
                self.check_edge(location, *real_target, EdgeKind::Normal);
                if let Some(unwind) = unwind {
                    self.check_edge(location, *unwind, EdgeKind::Unwind);
                }
            }
            TerminatorKind::InlineAsm { destination, .. } => {
                if let Some(destination) = destination {
                    self.check_edge(location, *destination, EdgeKind::Normal);
                }
            }
            // Nothing to validate for these.
            TerminatorKind::Resume
            | TerminatorKind::Abort
            | TerminatorKind::Return
            | TerminatorKind::Unreachable
            | TerminatorKind::GeneratorDrop => {}
        }
    }
}
<|endoftext|>