<commit_before>use super::scope::CowScope;
use super::CompileError;
use super::lexer::{Token, Symbol, Operator};
use super::function::Function;
use super::parser::{self, Parser, TokenStream};
use super::identifier::{IdMap, Identifier};
use super::expr::Expression;
use std::collections::VecMap;

/// Represents a function definition written in synthizer
#[derive(Debug)]
pub struct FunctionDef {
    statement: Option<Statement>,
    args: VecMap<Option<f32>>, // Default arguments
    ident: Option<Identifier>,
}

impl FunctionDef {
    pub fn new() -> FunctionDef {
        FunctionDef {
            statement: None,
            args: VecMap::new(),
            ident: None,
        }
    }

    pub fn ident(&self) -> Identifier {
        self.ident.expect("incomplete function definition! no identifier was attached")
    }

    pub fn set_ident(&mut self, ident: Identifier) {
        self.ident = Some(ident);
    }

    pub fn set_statement(&mut self, s: Statement) {
        self.statement = Some(s);
    }

    pub fn statement(&self) -> &Statement {
        self.statement.as_ref().expect("incomplete function definition! no statement was attached")
    }

    pub fn statement_mut(&mut self) -> &mut Statement {
        self.statement.as_mut().expect("incomplete function definition! no statement was attached")
    }

    pub fn args(&self) -> &VecMap<Option<f32>> {
        &self.args
    }

    pub fn args_mut(&mut self) -> &mut VecMap<Option<f32>> {
        &mut self.args
    }
}

impl<'a> Parser<'a> for FunctionDef {
    /// Parse a function definition from a token stream. Scope is used to find function definitions
    fn parse(tokens: TokenStream<'a>) -> Result<FunctionDef, CompileError> {
        let mut tokens = tokens;
        let mut def = FunctionDef::new();

        try!(expect!(tokens.next(), Token::Symbol(Symbol::LeftBracket), "expected `[`, got `{}`"));

        def.set_ident(try!(expect_value!(tokens.next(), Token::Ident, "expected function name, got `{}`")));

        loop {
            let mut args = def.args_mut();
            let next = tokens.next();
            if expect!(next, Token::Symbol(Symbol::RightBracket)).is_ok() {
                break;
            }
            let pos = next.map(|x| x.pos);
            let arg_ident = try!(expect_value!(next, Token::Ident, "expected argument name, got `{}`"));
            if args.contains_key(&arg_ident) {
                return Err(CompileError::new(format!("argument {} defined twice", arg_ident))
                           .with_pos(pos.unwrap()));
            }

            let next = tokens.next();
            let (pos, mut token) = (next.map(|x| x.pos), next.map(|x| x.token));
            if let Some(Token::Symbol(Symbol::Equals)) = token {
                let nexttoken = tokens.next();
                let value = if let Ok(_) = expect!(nexttoken, Token::Operator(Operator::Sub)) {
                    -try!(expect_value!(tokens.next(), Token::Const, "expected numerical constant, got `{}`"))
                } else {
                    try!(expect_value!(nexttoken, Token::Const, "expected numerical constant, got `{}`"))
                };
                args.insert(arg_ident, Some(value));
                token = tokens.next().map(|x| x.token);
            }
            match token {
                Some(Token::Symbol(Symbol::Comma)) => {
                    if !args.contains_key(&arg_ident) {
                        args.insert(arg_ident, None);
                    }
                }
                Some(Token::Symbol(Symbol::RightBracket)) => {
                    if !args.contains_key(&arg_ident) {
                        args.insert(arg_ident, None);
                    }
                    break;
                }
                Some(token) => return Err(CompileError::new(format!(
                            "expected `=`, `,` or `]`, got `{}`", token))
                            .with_pos(pos.unwrap())),
                None => return Err(CompileError::new_static("expected `=`, `,` or `]`, got EOF")
                                   .with_pos(tokens.end_source_pos())),
            }
        }

        def.set_statement(
            if !tokens.is_empty() {
                try!(Parser::parse(tokens))
            } else {
                return Err(CompileError::new_static("expected block, got EOF")
                           .with_pos(tokens.end_source_pos()));
            });

        Ok(def)
    }
}
impl Function for FunctionDef {
    fn call(&self, _: CowScope, _: &IdMap) -> Result<f32, CompileError> {
        unimplemented!();
    }
}

#[derive(Debug)]
pub enum Statement {
    Block(Vec<(Option<Expression>, Operator, Statement)>),
    Expr(Expression),
}

impl<'a> Parser<'a> for Statement {
    fn parse(tokens: TokenStream<'a>) -> Result<Statement, CompileError> {
        let mut tokens = tokens;
        let mut statements = Vec::new();
        match expect!(tokens.peek(0), Token::Symbol(Symbol::LeftBrace)) {
            // If it starts with `{` it's a block
            Ok(_) => {
                tokens.next(); // consume brace
                loop {
                    let operator = match tokens.next().map(|x| x.token) {
                        Some(Token::Operator(op)) => op,
                        Some(Token::Symbol(Symbol::Semicolon)) => continue,
                        Some(Token::Symbol(Symbol::RightBrace)) => break,
                        Some(x) =>
                            return Err(CompileError::new(format!("expected operator, `;` or `}}`, got {}", x))
                                       .with_pos(tokens.peek(0).map(|x| x.pos).unwrap())),
                        None =>
                            return Err(CompileError::new_static("expected operator, ';' or `}`, got EOF")
                                       .with_pos(tokens.end_source_pos()))
                    };
                    let mut condition = None;
                    let start_pos = tokens.pos();
                    let mut pos;
                    if let Some(Token::Symbol(Symbol::LeftBrace)) = tokens.peek(0).map(|x| x.token) {
                        tokens = try!(parser::match_paren(tokens,
                                                          Token::Symbol(Symbol::LeftBrace),
                                                          Token::Symbol(Symbol::RightBrace)));
                    }
                    loop {
                        pos = tokens.pos();
                        match tokens.next().map(|x| x.token) {
                            Some(Token::Symbol(Symbol::Semicolon)) => break,
                            Some(Token::Symbol(Symbol::QuestionMark)) => {
                                // parse condition
                                let start_pos = pos;
                                let mut pos;
                                loop {
                                    pos = tokens.pos();
                                    match tokens.next().map(|x| x.token) {
                                        Some(Token::Symbol(Symbol::Semicolon)) => break,
                                        Some(_) => { },
                                        None => return Err(CompileError::new_static(
                                                "expected `;` to end condition, got EOF")
                                            .with_pos(tokens.end_source_pos())),
                                    }
                                }
                                condition = Some(try!(Parser::parse(
                                            tokens.slice(start_pos+1, pos))));
                                break;
                            }
                            Some(_) => { }
                            None =>
                                return Err(CompileError::new_static(
                                        "expected `;` or `?` to end expression, got EOF")
                                    .with_pos(tokens.end_source_pos())),
                        }
                    }
                    let statement = try!(Parser::parse(tokens.slice(start_pos, pos)));
                    statements.push((condition, operator, statement));
                }
            }

            // otherwise it's an expression
            Err(_) => {
                return Ok(Statement::Expr(try!(Parser::parse(tokens))));
            }
        }
        Ok(Statement::Block(statements))
    }
}
impl Function for Statement {
    fn call(&self, scope: CowScope, idmap: &IdMap) -> Result<f32, CompileError> {
        match self {
            &Statement::Expr(ref x) =>
                x.call(scope, idmap),
            _ => unimplemented!(),
        }
    }
}
<commit_msg>Default arguments in function definitions can be expressions<commit_after>use super::scope::CowScope;
use super::CompileError;
use super::lexer::{Token, Symbol, Operator};
use super::function::Function;
use super::parser::{self, Parser, TokenStream};
use super::identifier::{IdMap, Identifier};
use super::expr::Expression;
use std::collections::VecMap;

/// Represents a function definition written in synthizer
#[derive(Debug)]
pub struct FunctionDef {
    statement: Option<Statement>,
    args: VecMap<Option<Expression>>, // Default arguments
    ident: Option<Identifier>,
}

impl FunctionDef {
    pub fn new() -> FunctionDef {
        FunctionDef {
            statement: None,
            args: VecMap::new(),
            ident: None,
        }
    }

    pub fn ident(&self) -> Identifier {
        self.ident.expect("incomplete function definition! no identifier was attached")
    }

    pub fn set_ident(&mut self, ident: Identifier) {
        self.ident = Some(ident);
    }

    pub fn set_statement(&mut self, s: Statement) {
        self.statement = Some(s);
    }

    pub fn statement(&self) -> &Statement {
        self.statement.as_ref().expect("incomplete function definition! no statement was attached")
    }

    pub fn statement_mut(&mut self) -> &mut Statement {
        self.statement.as_mut().expect("incomplete function definition! no statement was attached")
    }

    pub fn args(&self) -> &VecMap<Option<Expression>> {
        &self.args
    }

    pub fn args_mut(&mut self) -> &mut VecMap<Option<Expression>> {
        &mut self.args
    }
}

impl<'a> Parser<'a> for FunctionDef {
    /// Parse a function definition from a token stream. Scope is used to find function definitions
    fn parse(tokens: TokenStream<'a>) -> Result<FunctionDef, CompileError> {
        let mut tokens = tokens;
        let mut def = FunctionDef::new();

        try!(expect!(tokens.next(), Token::Symbol(Symbol::LeftBracket), "expected `[`, got `{}`"));

        def.set_ident(try!(expect_value!(tokens.next(), Token::Ident, "expected function name, got `{}`")));

        // Find arguments
        'outer: loop {
            let mut args = def.args_mut();
            let next = tokens.next();

            // If we hit a right bracket, the argument list is finished
            if expect!(next, Token::Symbol(Symbol::RightBracket)).is_ok() {
                break;
            }

            // Get argument name
            let arg_ident = try!(expect_value!(next, Token::Ident, "expected argument name, got `{}`"));
            if args.contains_key(&arg_ident) {
                return Err(CompileError::new(format!("argument {} defined twice", arg_ident))
                           .with_pos(next.unwrap().pos));
            }

            let next = tokens.next();
            match next.map(|x| x.token) {
                Some(Token::Symbol(Symbol::Comma)) => {
                    args.insert(arg_ident, None);
                },

                Some(Token::Symbol(Symbol::RightBracket)) => {
                    args.insert(arg_ident, None);
                    break 'outer;
                },

                Some(Token::Symbol(Symbol::Equals)) => {
                    let expr_start = tokens.pos();
                    loop {
                        let next = tokens.next();
                        match next.map(|x| x.token) {
                            Some(Token::Symbol(Symbol::LeftBracket)) => {
                                tokens.seek(-1);
                                tokens = try!(parser::match_paren(tokens,
                                                                  Token::Symbol(Symbol::LeftBracket),
                                                                  Token::Symbol(Symbol::RightBracket)));
                            }

                            Some(Token::Symbol(Symbol::Comma)) => {
                                args.insert(arg_ident,
                                        Some(try!(Parser::parse(
                                            tokens.slice(expr_start, tokens.pos()-1)))));
                                break;
                            },

                            Some(Token::Symbol(Symbol::RightBracket)) => {
                                args.insert(arg_ident,
                                        Some(try!(Parser::parse(
                                            tokens.slice(expr_start, tokens.pos()-1)))));
                                break 'outer;
                            },

                            None => return Err(CompileError::new_static(
                                    "expected expression, got EOF").with_pos(tokens.end_source_pos())),

                            Some(_) => { }
                        }
                    }
                }

                None => return Err(CompileError::new_static(
                        "expected `,`, `]`, or `=` following argument identifier, got EOF")
                    .with_pos(tokens.end_source_pos())),

                Some(x) => return Err(CompileError::new(format!(
                        "expected `,`, `]`, or `=` following argument identifier, got `{:?}`", x))
                    .with_pos(next.unwrap().pos)),
            }

        }

        def.set_statement(
            if !tokens.is_empty() {
                try!(Parser::parse(tokens))
            } else {
                return Err(CompileError::new_static("expected block, got EOF")
                           .with_pos(tokens.end_source_pos()));
            });

        Ok(def)
    }
}
impl Function for FunctionDef {
    fn call(&self, _: CowScope, _: &IdMap) -> Result<f32, CompileError> {
        unimplemented!();
    }
}

#[derive(Debug)]
pub enum Statement {
    Block(Vec<(Option<Expression>, Operator, Statement)>),
    Expr(Expression),
}

impl<'a> Parser<'a> for Statement {
    fn parse(tokens: TokenStream<'a>) -> Result<Statement, CompileError> {
        let mut tokens = tokens;
        let mut statements = Vec::new();
        match expect!(tokens.peek(0), Token::Symbol(Symbol::LeftBrace)) {
            // If it starts with `{` it's a block
            Ok(_) => {
                tokens.next(); // consume brace
                loop {
                    let operator = match tokens.next().map(|x| x.token) {
                        Some(Token::Operator(op)) => op,
                        Some(Token::Symbol(Symbol::Semicolon)) => continue,
                        Some(Token::Symbol(Symbol::RightBrace)) => break,
                        Some(x) =>
                            return Err(CompileError::new(format!("expected operator, `;` or `}}`, got {}", x))
                                       .with_pos(tokens.peek(0).map(|x| x.pos).unwrap())),
                        None =>
                            return Err(CompileError::new_static("expected operator, ';' or `}`, got EOF")
                                       .with_pos(tokens.end_source_pos()))
                    };
                    let mut condition = None;
                    let start_pos = tokens.pos();
                    let mut pos;
                    if let Some(Token::Symbol(Symbol::LeftBrace)) = tokens.peek(0).map(|x| x.token) {
                        tokens = try!(parser::match_paren(tokens,
                                                          Token::Symbol(Symbol::LeftBrace),
                                                          Token::Symbol(Symbol::RightBrace)));
                    }
                    loop {
                        pos = tokens.pos();
                        match tokens.next().map(|x| x.token) {
                            Some(Token::Symbol(Symbol::Semicolon)) => break,
                            Some(Token::Symbol(Symbol::QuestionMark)) => {
                                // parse condition
                                let start_pos = pos;
                                let mut pos;
                                loop {
                                    pos = tokens.pos();
                                    match tokens.next().map(|x| x.token) {
                                        Some(Token::Symbol(Symbol::Semicolon)) => break,
                                        Some(_) => { },
                                        None => return Err(CompileError::new_static(
                                                "expected `;` to end condition, got EOF")
                                            .with_pos(tokens.end_source_pos())),
                                    }
                                }
                                condition = Some(try!(Parser::parse(
                                            tokens.slice(start_pos+1, pos))));
                                break;
                            }
                            Some(_) => { }
                            None =>
                                return Err(CompileError::new_static(
                                        "expected `;` or `?` to end expression, got EOF")
                                    .with_pos(tokens.end_source_pos())),
                        }
                    }
                    let statement = try!(Parser::parse(tokens.slice(start_pos, pos)));
                    statements.push((condition, operator, statement));
                }
            }

            // otherwise it's an expression
            Err(_) => {
                return Ok(Statement::Expr(try!(Parser::parse(tokens))));
            }
        }
        Ok(Statement::Block(statements))
    }
}
impl Function for Statement {
    fn call(&self, scope: CowScope, idmap: &IdMap) -> Result<f32, CompileError> {
        match self {
            &Statement::Expr(ref x) =>
                x.call(scope, idmap),
            _ => unimplemented!(),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Base types that the block chain pipeline requires.

use std::io;

use secp;
use secp::pedersen::Commitment;

use grin_store as store;
use core::core::{Block, BlockHeader, Output};
use core::core::hash::{Hash, Hashed};
use core::core::target::Difficulty;
use core::ser;
use grin_store;

bitflags! {
/// Options for block validation
	pub flags Options: u32 {
		/// None flag
		const NONE = 0b00000001,
		/// Runs without checking the Proof of Work, mostly to make testing easier.
		const SKIP_POW = 0b00000010,
		/// Runs PoW verification with a lower cycle size.
		const EASY_POW = 0b00000100,
		/// Adds block while in syncing mode.
		const SYNC = 0b00001000,
	}
}

/// Errors
#[derive(Debug)]
pub enum Error {
	/// The block doesn't fit anywhere in our chain
	Unfit(String),
	/// Special case of orphan blocks
	Orphan,
	/// Difficulty is too low either compared to ours or the block PoW hash
	DifficultyTooLow,
	/// Addition of difficulties on all previous block is wrong
	WrongTotalDifficulty,
	/// The proof of work is invalid
	InvalidPow,
	/// The block doesn't sum correctly or a tx signature is invalid
	InvalidBlockProof(secp::Error),
	/// Block time is too old
	InvalidBlockTime,
	/// Block height is invalid (not previous + 1)
	InvalidBlockHeight,
	/// One of the root hashes in the block is invalid
	InvalidRoot,
	/// One of the inputs in the block has already been spent
	AlreadySpent,
	/// An output with that commitment already exists (should be unique)
	DuplicateCommitment(Commitment),
	/// A kernel with that excess commitment already exists (should be unique)
	DuplicateKernel(Commitment),
	/// coinbase can only be spent after it has matured (n blocks)
	ImmatureCoinbase,
	/// output not found
	OutputNotFound,
	/// output spent
	OutputSpent,
	/// Internal issue when trying to save or load data from store
	StoreErr(grin_store::Error),
	/// Error serializing or deserializing a type
	SerErr(ser::Error),
	/// Error while updating the sum trees
	SumTreeErr(String),
	/// No chain exists and genesis block is required
	GenesisBlockRequired,
	/// Anything else
	Other(String),
}

impl From<grin_store::Error> for Error {
	fn from(e: grin_store::Error) -> Error {
		Error::StoreErr(e)
	}
}
impl From<ser::Error> for Error {
	fn from(e: ser::Error) -> Error {
		Error::SerErr(e)
	}
}
impl From<io::Error> for Error {
	fn from(e: io::Error) -> Error {
		Error::SumTreeErr(e.to_string())
	}
}

/// The tip of a fork. A handle to the fork ancestry from its leaf in the
/// blockchain tree. References the max height and the latest and previous
/// blocks
/// for convenience and the total difficulty.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tip {
	/// Height of the tip (max height of the fork)
	pub height: u64,
	/// Last block pushed to the fork
	pub last_block_h: Hash,
	/// Block previous to last
	pub prev_block_h: Hash,
	/// Total difficulty accumulated on that fork
	pub total_difficulty: Difficulty,
}

impl Tip {
	/// Creates a new tip at height zero and the provided genesis hash.
	pub fn new(gbh: Hash) -> Tip {
		Tip {
			height: 0,
			last_block_h: gbh,
			prev_block_h: gbh,
			total_difficulty: Difficulty::one(),
		}
	}

	/// Append a new block to this tip, returning a new updated tip.
	pub fn from_block(bh: &BlockHeader) -> Tip {
		Tip {
			height: bh.height,
			last_block_h: bh.hash(),
			prev_block_h: bh.previous,
			total_difficulty: bh.total_difficulty.clone(),
		}
	}
}

/// Serialization of a tip, required to save to datastore.
impl ser::Writeable for Tip {
	fn write<W: ser::Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
		try!(writer.write_u64(self.height));
		try!(writer.write_fixed_bytes(&self.last_block_h));
		try!(writer.write_fixed_bytes(&self.prev_block_h));
		self.total_difficulty.write(writer)
	}
}

impl ser::Readable for Tip {
	fn read(reader: &mut ser::Reader) -> Result<Tip, ser::Error> {
		let height = try!(reader.read_u64());
		let last = try!(Hash::read(reader));
		let prev = try!(Hash::read(reader));
		let diff = try!(Difficulty::read(reader));
		Ok(Tip {
			height: height,
			last_block_h: last,
			prev_block_h: prev,
			total_difficulty: diff,
		})
	}
}

/// Trait the chain pipeline requires an implementor for in order to process
/// blocks.
pub trait ChainStore: Send + Sync {
	/// Get the tip that's also the head of the chain
	fn head(&self) -> Result<Tip, store::Error>;

	/// Block header for the chain head
	fn head_header(&self) -> Result<BlockHeader, store::Error>;

	/// Save the provided tip as the current head of our chain
	fn save_head(&self, t: &Tip) -> Result<(), store::Error>;

	/// Save the provided tip as the current head of the body chain, leaving the
	/// header chain alone.
	fn save_body_head(&self, t: &Tip) -> Result<(), store::Error>;

	/// Gets a block header by hash
	fn get_block(&self, h: &Hash) -> Result<Block, store::Error>;

	/// Gets a block header by hash
	fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, store::Error>;

	/// Checks whether a block has been been processed and saved
	fn check_block_exists(&self, h: &Hash) -> Result<bool, store::Error>;

	/// Save the provided block in store
	fn save_block(&self, b: &Block) -> Result<(), store::Error>;

	/// Save the provided block header in store
	fn save_block_header(&self, bh: &BlockHeader) -> Result<(), store::Error>;

	/// Get the tip of the header chain
	fn get_header_head(&self) -> Result<Tip, store::Error>;

	/// Save the provided tip as the current head of the block header chain
	fn save_header_head(&self, t: &Tip) -> Result<(), store::Error>;

	/// Gets the block header at the provided height
	fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, store::Error>;

	/// Gets an output by its commitment
	fn get_output_by_commit(&self, commit: &Commitment) -> Result<Output, store::Error>;

	/// Gets a block_header for the given input commit
	fn get_block_header_by_output_commit(
		&self,
		commit: &Commitment,
	) -> Result<BlockHeader, store::Error>;

	/// Saves the position of an output, represented by its commitment, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn save_output_pos(&self, commit: &Commitment, pos: u64) -> Result<(), store::Error>;

	/// Gets the position of an output, represented by its commitment, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn get_output_pos(&self, commit: &Commitment) -> Result<u64, store::Error>;

	/// Saves the position of a kernel, represented by its excess, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn save_kernel_pos(&self, commit: &Commitment, pos: u64) -> Result<(), store::Error>;

	/// Gets the position of a kernel, represented by its excess, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn get_kernel_pos(&self, commit: &Commitment) -> Result<u64, store::Error>;

	/// Saves the provided block header at the corresponding height. Also check
	/// the consistency of the height chain in store by assuring previous
	/// headers are also at their respective heights.
	fn setup_height(&self, bh: &BlockHeader) -> Result<(), store::Error>;
}

/// Bridge between the chain pipeline and the rest of the system. Handles
/// downstream processing of valid blocks by the rest of the system, most
/// importantly the broadcasting of blocks to our peers.
pub trait ChainAdapter {
	/// The blockchain pipeline has accepted this block as valid and added
	/// it to our chain.
	fn block_accepted(&self, b: &Block);
}

/// Dummy adapter used as a placeholder for real implementations
pub struct NoopAdapter {}
impl ChainAdapter for NoopAdapter {
	fn block_accepted(&self, _: &Block) {}
}
<commit_msg>Fixing chain error type<commit_after>// Copyright 2016 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Base types that the block chain pipeline requires.

use std::io;

use secp;
use secp::pedersen::Commitment;

use grin_store as store;
use core::core::{Block, BlockHeader, block, Output};
use core::core::hash::{Hash, Hashed};
use core::core::target::Difficulty;
use core::ser;
use grin_store;

bitflags! {
/// Options for block validation
	pub flags Options: u32 {
		/// None flag
		const NONE = 0b00000001,
		/// Runs without checking the Proof of Work, mostly to make testing easier.
		const SKIP_POW = 0b00000010,
		/// Runs PoW verification with a lower cycle size.
		const EASY_POW = 0b00000100,
		/// Adds block while in syncing mode.
		const SYNC = 0b00001000,
	}
}

/// Errors
#[derive(Debug)]
pub enum Error {
	/// The block doesn't fit anywhere in our chain
	Unfit(String),
	/// Special case of orphan blocks
	Orphan,
	/// Difficulty is too low either compared to ours or the block PoW hash
	DifficultyTooLow,
	/// Addition of difficulties on all previous block is wrong
	WrongTotalDifficulty,
	/// The proof of work is invalid
	InvalidPow,
	/// The block doesn't sum correctly or a tx signature is invalid
	InvalidBlockProof(block::Error),
	/// Block time is too old
	InvalidBlockTime,
	/// Block height is invalid (not previous + 1)
	InvalidBlockHeight,
	/// One of the root hashes in the block is invalid
	InvalidRoot,
	/// One of the inputs in the block has already been spent
	AlreadySpent,
	/// An output with that commitment already exists (should be unique)
	DuplicateCommitment(Commitment),
	/// A kernel with that excess commitment already exists (should be unique)
	DuplicateKernel(Commitment),
	/// coinbase can only be spent after it has matured (n blocks)
	ImmatureCoinbase,
	/// output not found
	OutputNotFound,
	/// output spent
	OutputSpent,
	/// Internal issue when trying to save or load data from store
	StoreErr(grin_store::Error),
	/// Error serializing or deserializing a type
	SerErr(ser::Error),
	/// Error while updating the sum trees
	SumTreeErr(String),
	/// No chain exists and genesis block is required
	GenesisBlockRequired,
	/// Anything else
	Other(String),
}

impl From<grin_store::Error> for Error {
	fn from(e: grin_store::Error) -> Error {
		Error::StoreErr(e)
	}
}
impl From<ser::Error> for Error {
	fn from(e: ser::Error) -> Error {
		Error::SerErr(e)
	}
}
impl From<io::Error> for Error {
	fn from(e: io::Error) -> Error {
		Error::SumTreeErr(e.to_string())
	}
}

/// The tip of a fork. A handle to the fork ancestry from its leaf in the
/// blockchain tree. References the max height and the latest and previous
/// blocks
/// for convenience and the total difficulty.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Tip {
	/// Height of the tip (max height of the fork)
	pub height: u64,
	/// Last block pushed to the fork
	pub last_block_h: Hash,
	/// Block previous to last
	pub prev_block_h: Hash,
	/// Total difficulty accumulated on that fork
	pub total_difficulty: Difficulty,
}

impl Tip {
	/// Creates a new tip at height zero and the provided genesis hash.
	pub fn new(gbh: Hash) -> Tip {
		Tip {
			height: 0,
			last_block_h: gbh,
			prev_block_h: gbh,
			total_difficulty: Difficulty::one(),
		}
	}

	/// Append a new block to this tip, returning a new updated tip.
	pub fn from_block(bh: &BlockHeader) -> Tip {
		Tip {
			height: bh.height,
			last_block_h: bh.hash(),
			prev_block_h: bh.previous,
			total_difficulty: bh.total_difficulty.clone(),
		}
	}
}

/// Serialization of a tip, required to save to datastore.
impl ser::Writeable for Tip {
	fn write<W: ser::Writer>(&self, writer: &mut W) -> Result<(), ser::Error> {
		try!(writer.write_u64(self.height));
		try!(writer.write_fixed_bytes(&self.last_block_h));
		try!(writer.write_fixed_bytes(&self.prev_block_h));
		self.total_difficulty.write(writer)
	}
}

impl ser::Readable for Tip {
	fn read(reader: &mut ser::Reader) -> Result<Tip, ser::Error> {
		let height = try!(reader.read_u64());
		let last = try!(Hash::read(reader));
		let prev = try!(Hash::read(reader));
		let diff = try!(Difficulty::read(reader));
		Ok(Tip {
			height: height,
			last_block_h: last,
			prev_block_h: prev,
			total_difficulty: diff,
		})
	}
}

/// Trait the chain pipeline requires an implementor for in order to process
/// blocks.
pub trait ChainStore: Send + Sync {
	/// Get the tip that's also the head of the chain
	fn head(&self) -> Result<Tip, store::Error>;

	/// Block header for the chain head
	fn head_header(&self) -> Result<BlockHeader, store::Error>;

	/// Save the provided tip as the current head of our chain
	fn save_head(&self, t: &Tip) -> Result<(), store::Error>;

	/// Save the provided tip as the current head of the body chain, leaving the
	/// header chain alone.
	fn save_body_head(&self, t: &Tip) -> Result<(), store::Error>;

	/// Gets a block header by hash
	fn get_block(&self, h: &Hash) -> Result<Block, store::Error>;

	/// Gets a block header by hash
	fn get_block_header(&self, h: &Hash) -> Result<BlockHeader, store::Error>;

	/// Checks whether a block has been been processed and saved
	fn check_block_exists(&self, h: &Hash) -> Result<bool, store::Error>;

	/// Save the provided block in store
	fn save_block(&self, b: &Block) -> Result<(), store::Error>;

	/// Save the provided block header in store
	fn save_block_header(&self, bh: &BlockHeader) -> Result<(), store::Error>;

	/// Get the tip of the header chain
	fn get_header_head(&self) -> Result<Tip, store::Error>;

	/// Save the provided tip as the current head of the block header chain
	fn save_header_head(&self, t: &Tip) -> Result<(), store::Error>;

	/// Gets the block header at the provided height
	fn get_header_by_height(&self, height: u64) -> Result<BlockHeader, store::Error>;

	/// Gets an output by its commitment
	fn get_output_by_commit(&self, commit: &Commitment) -> Result<Output, store::Error>;

	/// Gets a block_header for the given input commit
	fn get_block_header_by_output_commit(
		&self,
		commit: &Commitment,
	) -> Result<BlockHeader, store::Error>;

	/// Saves the position of an output, represented by its commitment, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn save_output_pos(&self, commit: &Commitment, pos: u64) -> Result<(), store::Error>;

	/// Gets the position of an output, represented by its commitment, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn get_output_pos(&self, commit: &Commitment) -> Result<u64, store::Error>;

	/// Saves the position of a kernel, represented by its excess, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn save_kernel_pos(&self, commit: &Commitment, pos: u64) -> Result<(), store::Error>;

	/// Gets the position of a kernel, represented by its excess, in the
	/// UTXO MMR. Used as an index for spending and pruning.
	fn get_kernel_pos(&self, commit: &Commitment) -> Result<u64, store::Error>;

	/// Saves the provided block header at the corresponding height. Also check
	/// the consistency of the height chain in store by assuring previous
	/// headers are also at their respective heights.
	fn setup_height(&self, bh: &BlockHeader) -> Result<(), store::Error>;
}

/// Bridge between the chain pipeline and the rest of the system. Handles
/// downstream processing of valid blocks by the rest of the system, most
/// importantly the broadcasting of blocks to our peers.
pub trait ChainAdapter {
	/// The blockchain pipeline has accepted this block as valid and added
	/// it to our chain.
	fn block_accepted(&self, b: &Block);
}

/// Dummy adapter used as a placeholder for real implementations
pub struct NoopAdapter {}
impl ChainAdapter for NoopAdapter {
	fn block_accepted(&self, _: &Block) {}
}
<|endoftext|>


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

pub struct SteadyTime {
    t: libc::LARGE_INTEGER,
}

impl SteadyTime {
    pub fn now() -> SteadyTime {
        let mut t = SteadyTime { t: 0 };
        unsafe { libc::QueryPerformanceCounter(&mut t.t); }
        t
    }

    pub fn ns(&self) -> u64 {
        self.t as u64 * 1_000_000_000 / frequency() as u64
    }
}

fn frequency() -> libc::LARGE_INTEGER {
    static mut FREQUENCY: libc::LARGE_INTEGER = 0;
    static ONCE: Once = ONCE_INIT;

    unsafe {
        ONCE.call_once(|| {
            libc::QueryPerformanceFrequency(&mut FREQUENCY);
        });
        FREQUENCY
    }
}

impl<'a> Sub for &'a SteadyTime {
    type Output = Duration;

    fn sub(self, other: &SteadyTime) -> Duration {
        let diff = self.t as i64 - other.t as i64;
        Duration::microseconds(diff * 1_000_000 / frequency() as i64)
    }
}
<commit_msg>Rollup merge of #22788 - vadimcn:fix-precise_time_ns, r=pnkfelix<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc;
use ops::Sub;
use time::Duration;
use sync::{Once, ONCE_INIT};

const NANOS_PER_SEC: i64 = 1_000_000_000;

pub struct SteadyTime {
    t: libc::LARGE_INTEGER,
}

impl SteadyTime {
    pub fn now() -> SteadyTime {
        let mut t = SteadyTime { t: 0 };
        unsafe { libc::QueryPerformanceCounter(&mut t.t); }
        t
    }

    pub fn ns(&self) -> u64 {
        mul_div_i64(self.t as i64, NANOS_PER_SEC, frequency() as i64) as u64
    }
}

fn frequency() -> libc::LARGE_INTEGER {
    static mut FREQUENCY: libc::LARGE_INTEGER = 0;
    static ONCE: Once = ONCE_INIT;

    unsafe {
        ONCE.call_once(|| {
            libc::QueryPerformanceFrequency(&mut FREQUENCY);
        });
        FREQUENCY
    }
}

impl<'a> Sub for &'a SteadyTime {
    type Output = Duration;

    fn sub(self, other: &SteadyTime) -> Duration {
        let diff = self.t as i64 - other.t as i64;
        Duration::nanoseconds(mul_div_i64(diff, NANOS_PER_SEC, frequency() as i64))
    }
}

// Computes (value*numer)/denom without overflow, as long as both
// (numer*denom) and the overall result fit into i64 (which is the case
// for our time conversions).
fn mul_div_i64(value: i64, numer: i64, denom: i64) -> i64 {
    let q = value / denom;
    let r = value % denom;
    // Decompose value as (value/denom*denom + value%denom),
    // substitute into (value*numer)/denom and simplify.
    // r < denom, so (denom*numer) is the upper bound of (r*numer)
    q * numer + r * numer / denom
}

#[test]
fn test_muldiv() {
    assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000, 1_000_000),  1_000_000_000_001_000);
    assert_eq!(mul_div_i64(-1_000_000_000_001, 1_000_000_000, 1_000_000), -1_000_000_000_001_000);
    assert_eq!(mul_div_i64(-1_000_000_000_001,-1_000_000_000, 1_000_000),  1_000_000_000_001_000);
    assert_eq!(mul_div_i64( 1_000_000_000_001, 1_000_000_000,-1_000_000), -1_000_000_000_001_000);
    assert_eq!(mul_div_i64( 1_000_000_000_001,-1_000_000_000,-1_000_000),  1_000_000_000_001_000);
}
<|endoftext|>


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

use memory::MemoryController;

mod idt;

macro_rules! save_scratch_registers {
    () => {
        asm!("push rax
              push rcx
              push rdx
              push rsi
              push rdi
              push r8
              push r9
              push r10
              push r11
        " :::: "intel", "volatile");
    }
}

macro_rules! restore_scratch_registers {
    () => {
        asm!("pop r11
              pop r10
              pop r9
              pop r8
              pop rdi
              pop rsi
              pop rdx
              pop rcx
              pop rax
            " :::: "intel", "volatile");
    }
}

macro_rules! handler {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                save_scratch_registers!();
                asm!("mov rdi, rsp
                      add rdi, 9*8 // calculate exception stack frame pointer
                      call $0"
                      :: "i"($name as extern "C" fn(
                          &ExceptionStackFrame))
                      : "rdi" : "intel");

                restore_scratch_registers!();
                asm!("iretq" :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

macro_rules! handler_with_error_code {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                save_scratch_registers!();
                asm!("mov rsi, [rsp + 9*8] // load error code into rsi
                      mov rdi, rsp
                      add rdi, 10*8 // calculate exception stack frame pointer
                      sub rsp, 8 // align the stack pointer
                      call $0
                      add rsp, 8 // undo stack pointer alignment
                      " :: "i"($name as extern "C" fn(
                          &ExceptionStackFrame, u64))
                      : "rdi","rsi" : "intel");
                restore_scratch_registers!();
                asm!("add rsp, 8 // pop error code
                      iretq" :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

lazy_static! {
    static ref IDT: idt::Idt = {
        let mut idt = idt::Idt::new();

        idt.set_handler(0, handler!(divide_by_zero_handler));
        idt.set_handler(3, handler!(breakpoint_handler));
        idt.set_handler(6, handler!(invalid_opcode_handler));
        idt.set_handler(8, handler_with_error_code!(double_fault_handler));
        idt.set_handler(14, handler_with_error_code!(page_fault_handler));

        idt
    };
}

pub fn init(memory_controller: &mut MemoryController) {
    let double_fault_stack = memory_controller.alloc_stack(1)
        .expect("could not allocate double fault stack");

    IDT.load();
}

#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrame {
    instruction_pointer: u64,
    code_segment: u64,
    cpu_flags: u64,
    stack_pointer: u64,
    stack_segment: u64,
}

extern "C" fn divide_by_zero_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame);
    loop {}
}

extern "C" fn breakpoint_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: BREAKPOINT at {:#x}\n{:#?}",
             stack_frame.instruction_pointer,
             stack_frame);
}

extern "C" fn invalid_opcode_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}",
             stack_frame.instruction_pointer,
             stack_frame);
    loop {}
}

bitflags! {
    flags PageFaultErrorCode: u64 {
        const PROTECTION_VIOLATION = 1 << 0,
        const CAUSED_BY_WRITE = 1 << 1,
        const USER_MODE = 1 << 2,
        const MALFORMED_TABLE = 1 << 3,
        const INSTRUCTION_FETCH = 1 << 4,
    }
}

extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame, error_code: u64) {
    use x86::shared::control_regs;
    println!("\nEXCEPTION: PAGE FAULT while accessing {:#x}\nerror code: \
                                  {:?}\n{:#?}",
             unsafe { control_regs::cr2() },
             PageFaultErrorCode::from_bits(error_code).unwrap(),
             stack_frame);
    loop {}
}

extern "C" fn double_fault_handler(stack_frame: &ExceptionStackFrame, _error_code: u64) {
    println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
    loop {}
}
<commit_msg>Create a new TaskStateSegment<commit_after>// Copyright 2016 Philipp Oppermann. See the README.md
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use memory::MemoryController;
use x86::bits64::task::TaskStateSegment;

mod idt;

macro_rules! save_scratch_registers {
    () => {
        asm!("push rax
              push rcx
              push rdx
              push rsi
              push rdi
              push r8
              push r9
              push r10
              push r11
        " :::: "intel", "volatile");
    }
}

macro_rules! restore_scratch_registers {
    () => {
        asm!("pop r11
              pop r10
              pop r9
              pop r8
              pop rdi
              pop rsi
              pop rdx
              pop rcx
              pop rax
            " :::: "intel", "volatile");
    }
}

macro_rules! handler {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                save_scratch_registers!();
                asm!("mov rdi, rsp
                      add rdi, 9*8 // calculate exception stack frame pointer
                      call $0"
                      :: "i"($name as extern "C" fn(
                          &ExceptionStackFrame))
                      : "rdi" : "intel");

                restore_scratch_registers!();
                asm!("iretq" :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

macro_rules! handler_with_error_code {
    ($name: ident) => {{
        #[naked]
        extern "C" fn wrapper() -> ! {
            unsafe {
                save_scratch_registers!();
                asm!("mov rsi, [rsp + 9*8] // load error code into rsi
                      mov rdi, rsp
                      add rdi, 10*8 // calculate exception stack frame pointer
                      sub rsp, 8 // align the stack pointer
                      call $0
                      add rsp, 8 // undo stack pointer alignment
                      " :: "i"($name as extern "C" fn(
                          &ExceptionStackFrame, u64))
                      : "rdi","rsi" : "intel");
                restore_scratch_registers!();
                asm!("add rsp, 8 // pop error code
                      iretq" :::: "intel", "volatile");
                ::core::intrinsics::unreachable();
            }
        }
        wrapper
    }}
}

lazy_static! {
    static ref IDT: idt::Idt = {
        let mut idt = idt::Idt::new();

        idt.set_handler(0, handler!(divide_by_zero_handler));
        idt.set_handler(3, handler!(breakpoint_handler));
        idt.set_handler(6, handler!(invalid_opcode_handler));
        idt.set_handler(8, handler_with_error_code!(double_fault_handler));
        idt.set_handler(14, handler_with_error_code!(page_fault_handler));

        idt
    };
}

const DOUBLE_FAULT_IST_INDEX: usize = 0;

pub fn init(memory_controller: &mut MemoryController) {
    let double_fault_stack = memory_controller.alloc_stack(1)
        .expect("could not allocate double fault stack");

    let mut tss = TaskStateSegment::new();
    tss.ist[DOUBLE_FAULT_IST_INDEX] = double_fault_stack.top() as u64;

    IDT.load();
}

#[derive(Debug)]
#[repr(C)]
struct ExceptionStackFrame {
    instruction_pointer: u64,
    code_segment: u64,
    cpu_flags: u64,
    stack_pointer: u64,
    stack_segment: u64,
}

extern "C" fn divide_by_zero_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: DIVIDE BY ZERO\n{:#?}", stack_frame);
    loop {}
}

extern "C" fn breakpoint_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: BREAKPOINT at {:#x}\n{:#?}",
             stack_frame.instruction_pointer,
             stack_frame);
}

extern "C" fn invalid_opcode_handler(stack_frame: &ExceptionStackFrame) {
    println!("\nEXCEPTION: INVALID OPCODE at {:#x}\n{:#?}",
             stack_frame.instruction_pointer,
             stack_frame);
    loop {}
}

bitflags! {
    flags PageFaultErrorCode: u64 {
        const PROTECTION_VIOLATION = 1 << 0,
        const CAUSED_BY_WRITE = 1 << 1,
        const USER_MODE = 1 << 2,
        const MALFORMED_TABLE = 1 << 3,
        const INSTRUCTION_FETCH = 1 << 4,
    }
}

extern "C" fn page_fault_handler(stack_frame: &ExceptionStackFrame, error_code: u64) {
    use x86::shared::control_regs;
    println!("\nEXCEPTION: PAGE FAULT while accessing {:#x}\nerror code: \
                                  {:?}\n{:#?}",
             unsafe { control_regs::cr2() },
             PageFaultErrorCode::from_bits(error_code).unwrap(),
             stack_frame);
    loop {}
}

extern "C" fn double_fault_handler(stack_frame: &ExceptionStackFrame, _error_code: u64) {
    println!("\nEXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
    loop {}
}
<|endoftext|>


<commit_before>use std::io::{self, Read, Write};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::f64;
use std::env;

use tabwriter::TabWriter;

use comment::Comment;
use config::{Config, Utf8Rule};
use count::Count;
use error::{CliError, CliResult};
use fsutil;
use fmt::{self, Format};
use language::Language;
use gitignore;

pub struct Counts<'c> {
    cfg: &'c Config<'c>,
    counts: Vec<Count>,
    tot: usize,
    tot_lines: u64,
    tot_comments: u64,
    tot_blanks: u64,
    tot_code: u64,
    tot_usafe: u64,
}

impl<'c> Counts<'c> {
    pub fn new(cfg: &'c Config) -> Self {
        Counts {
            cfg: cfg,
            counts: vec![],
            tot: 0,
            tot_lines: 0,
            tot_comments: 0,
            tot_blanks: 0,
            tot_code: 0,
            tot_usafe: 0,
        }
    }

    pub fn fill_from(&mut self) {
        debugln!("executing; fill_from; cfg={:?}", self.cfg);
        let cd;
        let gitignore = if self.cfg.all {
            None
        } else {
            cd = env::current_dir().unwrap().join(".gitignore");
            gitignore::File::new(&cd).ok()
        };
        for path in &self.cfg.to_count {
            debugln!("iter; path={:?};", path);
            let mut files = vec![];
            fsutil::get_all_files(&mut files,
                                  &path,
                                  &self.cfg.exclude,
                                  self.cfg.follow_links,
                                  &gitignore);

            for file in files {
                debugln!("iter; file={:?};", file);
                let extension = match Path::new(&file).extension() {
                    Some(result) => {
                        if let Some(ref exts) = self.cfg.exts {
                            if !exts.contains(&result.to_str().unwrap_or("")) {
                                continue;
                            }
                        }
                        result.to_str().unwrap()
                    }
                    None => continue,
                };

                debugln!("found extension: {:?}", extension);
                if let Some(pos_lang) = Language::from_ext(extension) {
                    debugln!("Extension is valid");
                    let mut found = false;
                    debugln!("Searching for previous entries of that type");
                    for l in self.counts.iter_mut() {
                        if l.lang == pos_lang {
                            debugln!("Found");
                            found = true;
                            l.add_file(PathBuf::from(&file));
                            break;
                        }
                    }
                    if !found {
                        debugln!("Not found, creating new");
                        let mut c = Count::new(pos_lang, self.cfg.thousands);
                        c.add_file(PathBuf::from(&file));
                        self.counts.push(c);
                    }
                } else {
                    debugln!("extension wasn't valid");
                }
            }
        }
    }

    #[cfg_attr(feature = "lints", allow(cyclomatic_complexity, trivial_regex))]
    pub fn count(&mut self) -> CliResult<()> {
        for count in self.counts.iter_mut() {
            debugln!("iter; count={:?};", count);
            let re = if let Some(kw) = count.lang.unsafe_keyword() {
                regex!(&*format!("(.*?)([:^word:]{}[:^word:])(.*)", kw))
            } else {
                regex!("")
            };
            for file in count.files.iter() {
                debugln!("iter; file={:?};", file);
                let mut buffer = String::new();

                let mut file_ref = cli_try!(File::open(&file));

                match self.cfg.utf8_rule {
                    Utf8Rule::Ignore => {
                        if let Err(..) = file_ref.read_to_string(&mut buffer) {
                            continue;
                        }
                    }
                    Utf8Rule::Lossy => {
                        let mut vec_buf = vec![];
                        cli_try!(file_ref.read_to_end(&mut vec_buf));
                        buffer = String::from_utf8_lossy(&vec_buf).into_owned();
                    }
                    Utf8Rule::Strict => {
                        cli_try!(file_ref.read_to_string(&mut buffer));
                    }
                }
                let mut is_in_comments = false;
                let mut is_in_unsafe = false;
                let mut bracket_count: i64 = 0;

                'new_line: for line in buffer.lines() {
                    let line = line.trim();
                    debugln!("iter; line={:?};", line);
                    count.lines += 1;

                    if is_in_comments {
                        debugln!("still in comments");
                        if line.contains(count.multi_end().unwrap()) {
                            debugln!("line contained ending comment, stopping comments");
                            is_in_comments = false;
                        }
                        count.comments += 1;
                        continue;
                    }
                    debugln!("not in comments");

                    if line.trim().is_empty() {
                        debugln!("line was empty");
                        count.blanks += 1;
                        continue;
                    }
                    debugln!("Line isn't empty");

                    if let Some(ms) = count.multi_start() {
                        debugln!("This file type has a multi start of: {:?}", ms);
                        if line.starts_with(ms) {
                            debugln!("line starts with multi comment");
                            count.comments += 1;
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            continue;
                        } else if line.contains(ms) {
                            debugln!("line contains a multi start");
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            if is_in_comments {
                                continue;
                            }
                        }
                    } else {
                        debugln!("No multi line comments for this type");
                    }
                    debugln!("No multi line comments for this line");

                    if let Some(single_comments) = count.single() {
                        debugln!("This type has single line comments: {:?}", single_comments);
                        for single in single_comments {
                            if line.starts_with(single) {
                                debugln!("Line started with a comment");
                                count.comments += 1;
                                continue 'new_line;
                            } else {
                                debugln!("Line dind't start with a comment");
                            }
                        }
                    } else {
                        debugln!("No single line comments for this type");
                    }

                    if self.cfg.usafe && count.lang.is_unsafe() {
                        debugln!("Calculating --unsafe-statistics");
                        debugln!("The language is not safe");
                        if let Some(..) = count.lang.unsafe_keyword() {
                            debugln!("There is a keyword");
                            debugln!("line={:?}", line);
                            if is_in_unsafe {
                                debugln!("It didn't contain the keyword, but we are still in \
                                          unsafe");
                                count.usafe += 1;
                                bracket_count = Counts::count_brackets(line, Some(bracket_count));
                                is_in_unsafe = bracket_count > 0;
                                debugln!("after counting brackets; is_in_unsafe={:?}; \
                                          bracket_count={:?}",
                                         is_in_unsafe,
                                         bracket_count);
                            } else if let Some(caps) = re.captures(line) {
                                let mut should_count = true;
                                if let Some(before) = caps.at(1) {
                                    if let Some(single_v) = count.lang.single() {
                                        for s in single_v {
                                            if before.contains(s) {
                                                should_count = false;
                                                break;
                                            }
                                        }
                                    }
                                    if let Some(multi) = count.lang.multi_start() {
                                        if before.contains(multi) &&
                                           !before.contains(count.lang.multi_end().unwrap()) {
                                            should_count = false;
                                        }
                                    }
                                }
                                if should_count {
                                    debugln!("It contained the keyword; usafe_line={:?}", line);
                                    count.usafe += 1;
                                    if let Some(after) = caps.at(3) {
                                        debugln!("after_usafe={:?}", after);
                                        bracket_count = Counts::count_brackets(after, None);
                                        is_in_unsafe = bracket_count > 0;
                                        debugln!("after counting brackets; is_in_unsafe={:?}; \
                                                  bracket_count={:?}",
                                                 is_in_unsafe,
                                                 bracket_count);
                                    }
                                }
                            } else {
                                debugln!("It didn't contain the keyword, and we are not in unsafe");
                            }

                            if bracket_count < 0 {
                                debugln!("bracket_count < 0; resetting");
                                bracket_count = 0
                            }
                        } else {
                            debugln!("Language is unsafe, incing the count");
                            count.usafe += 1;
                        }
                    }
                    count.code += 1;
                }
            }
            self.tot += count.files.len();
            self.tot_lines += count.lines;
            self.tot_comments += count.comments;
            self.tot_blanks += count.blanks;
            self.tot_code += count.code;
            self.tot_usafe += count.usafe;
        }

        Ok(())
    }

    pub fn write_results(&mut self) -> CliResult<()> {
        let mut w = TabWriter::new(vec![]);
        cli_try!(write!(w,
                        "\tLanguage\tFiles\tLines\tBlanks\tComments\tCode{}\n",
                        if self.cfg.usafe {
                            "\tUnsafe (%)"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        for count in &self.counts {
            if self.cfg.usafe {
                let usafe_per = (count.usafe as f64 / count.code as f64) * 100.00f64;
                cli_try!(write!(w,
                                "\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
                                count.lang.name(),
                                count.total_files(),
                                count.lines(),
                                count.blanks(),
                                count.comments(),
                                count.code(),
                                if (usafe_per - 00f64).abs() < f64::EPSILON {
                                    "".to_owned()
                                } else {
                                    format!("{} ({:.2}%)", count.usafe(), usafe_per)
                                }));
            } else {
                cli_try!(write!(w, "\t{}\n", count));
            }
        }
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "{}\t\t{}\t{}\t{}\t{}\t{}{}\n",
                        "Totals:",
                        fmt::format_number(self.tot as u64, self.cfg.thousands),
                        fmt::format_number(self.tot_lines, self.cfg.thousands),
                        fmt::format_number(self.tot_blanks, self.cfg.thousands),
                        fmt::format_number(self.tot_comments, self.cfg.thousands),
                        fmt::format_number(self.tot_code, self.cfg.thousands),
                        if self.cfg.usafe {
                            format!("\t{} ({:.2}%)",
                                    fmt::format_number(self.tot_usafe, self.cfg.thousands),
                                    (self.tot_usafe as f64 / self.tot_code as f64) * 100.00f64)
                        } else {
                            "".to_owned()
                        }));

        cli_try!(w.flush());

        verboseln!(self.cfg,
                   "{} {}",
                   Format::Good("Displaying"),
                   "the results:");
        if self.tot > 0 {
            write!(io::stdout(),
                   "{}",
                   String::from_utf8(w.unwrap()).ok().expect("failed to get valid UTF-8 String"))
                .expect("failed to write output");
        } else {
            println!("\n\tNo source files were found matching the specified criteria");
        }
        Ok(())
    }

    fn count_brackets(line: &str, count: Option<i64>) -> i64 {
        let mut b: i64 = count.unwrap_or(0);
        for c in line.chars() {
            match c {
                '{' => b += 1,
                '}' => b -= 1,
                _ => (),
            }
        }
        b
    }
}
<commit_msg>Auto merge of #26 - 0x1997:patch-1, r=kbknapp<commit_after>use std::io::{self, Read, Write};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::f64;
use std::env;

use tabwriter::TabWriter;

use comment::Comment;
use config::{Config, Utf8Rule};
use count::Count;
use error::{CliError, CliResult};
use fsutil;
use fmt::{self, Format};
use language::Language;
use gitignore;

pub struct Counts<'c> {
    cfg: &'c Config<'c>,
    counts: Vec<Count>,
    tot: usize,
    tot_lines: u64,
    tot_comments: u64,
    tot_blanks: u64,
    tot_code: u64,
    tot_usafe: u64,
}

impl<'c> Counts<'c> {
    pub fn new(cfg: &'c Config) -> Self {
        Counts {
            cfg: cfg,
            counts: vec![],
            tot: 0,
            tot_lines: 0,
            tot_comments: 0,
            tot_blanks: 0,
            tot_code: 0,
            tot_usafe: 0,
        }
    }

    pub fn fill_from(&mut self) {
        debugln!("executing; fill_from; cfg={:?}", self.cfg);
        let cd;
        let gitignore = if self.cfg.all {
            None
        } else {
            cd = env::current_dir().unwrap().join(".gitignore");
            gitignore::File::new(&cd).ok()
        };
        for path in &self.cfg.to_count {
            debugln!("iter; path={:?};", path);
            let mut files = vec![];
            fsutil::get_all_files(&mut files,
                                  &path,
                                  &self.cfg.exclude,
                                  self.cfg.follow_links,
                                  &gitignore);

            for file in files {
                debugln!("iter; file={:?};", file);
                let extension = match Path::new(&file).extension() {
                    Some(result) => {
                        if let Some(ref exts) = self.cfg.exts {
                            if !exts.contains(&result.to_str().unwrap_or("")) {
                                continue;
                            }
                        }
                        result.to_str().unwrap()
                    }
                    None => continue,
                };

                debugln!("found extension: {:?}", extension);
                if let Some(pos_lang) = Language::from_ext(extension) {
                    debugln!("Extension is valid");
                    let mut found = false;
                    debugln!("Searching for previous entries of that type");
                    for l in self.counts.iter_mut() {
                        if l.lang == pos_lang {
                            debugln!("Found");
                            found = true;
                            l.add_file(PathBuf::from(&file));
                            break;
                        }
                    }
                    if !found {
                        debugln!("Not found, creating new");
                        let mut c = Count::new(pos_lang, self.cfg.thousands);
                        c.add_file(PathBuf::from(&file));
                        self.counts.push(c);
                    }
                } else {
                    debugln!("extension wasn't valid");
                }
            }
        }
    }

    #[cfg_attr(feature = "lints", allow(cyclomatic_complexity, trivial_regex))]
    pub fn count(&mut self) -> CliResult<()> {
        for count in self.counts.iter_mut() {
            debugln!("iter; count={:?};", count);
            let re = if let Some(kw) = count.lang.unsafe_keyword() {
                regex!(&*format!("(.*?)([:^word:]{}[:^word:])(.*)", kw))
            } else {
                regex!("")
            };
            for file in count.files.iter() {
                debugln!("iter; file={:?};", file);
                let mut buffer = String::new();

                let mut file_ref = cli_try!(File::open(&file));

                match self.cfg.utf8_rule {
                    Utf8Rule::Ignore => {
                        if let Err(..) = file_ref.read_to_string(&mut buffer) {
                            continue;
                        }
                    }
                    Utf8Rule::Lossy => {
                        let mut vec_buf = vec![];
                        cli_try!(file_ref.read_to_end(&mut vec_buf));
                        buffer = String::from_utf8_lossy(&vec_buf).into_owned();
                    }
                    Utf8Rule::Strict => {
                        cli_try!(file_ref.read_to_string(&mut buffer));
                    }
                }
                let mut is_in_comments = false;
                let mut is_in_unsafe = false;
                let mut bracket_count: i64 = 0;

                'new_line: for line in buffer.lines() {
                    let line = line.trim();
                    debugln!("iter; line={:?};", line);
                    count.lines += 1;

                    if is_in_comments {
                        debugln!("still in comments");
                        if line.contains(count.multi_end().unwrap()) {
                            debugln!("line contained ending comment, stopping comments");
                            is_in_comments = false;
                        }
                        count.comments += 1;
                        continue;
                    }
                    debugln!("not in comments");

                    if line.trim().is_empty() {
                        debugln!("line was empty");
                        count.blanks += 1;
                        continue;
                    }
                    debugln!("Line isn't empty");

                    if let Some(ms) = count.multi_start() {
                        debugln!("This file type has a multi start of: {:?}", ms);
                        if line.starts_with(ms) {
                            debugln!("line starts with multi comment");
                            count.comments += 1;
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            continue;
                        } else if line.contains(ms) {
                            debugln!("line contains a multi start");
                            is_in_comments = !line.contains(count.multi_end().unwrap());
                            debugln!("line also contained a multi end: {:?}", is_in_comments);
                            if is_in_comments {
                                continue;
                            }
                        }
                    } else {
                        debugln!("No multi line comments for this type");
                    }
                    debugln!("No multi line comments for this line");

                    if let Some(single_comments) = count.single() {
                        debugln!("This type has single line comments: {:?}", single_comments);
                        for single in single_comments {
                            if line.starts_with(single) {
                                debugln!("Line started with a comment");
                                count.comments += 1;
                                continue 'new_line;
                            } else {
                                debugln!("Line dind't start with a comment");
                            }
                        }
                    } else {
                        debugln!("No single line comments for this type");
                    }

                    if self.cfg.usafe && count.lang.is_unsafe() {
                        debugln!("Calculating --unsafe-statistics");
                        debugln!("The language is not safe");
                        if let Some(..) = count.lang.unsafe_keyword() {
                            debugln!("There is a keyword");
                            debugln!("line={:?}", line);
                            if is_in_unsafe {
                                debugln!("It didn't contain the keyword, but we are still in \
                                          unsafe");
                                count.usafe += 1;
                                bracket_count = Counts::count_brackets(line, Some(bracket_count));
                                is_in_unsafe = bracket_count > 0;
                                debugln!("after counting brackets; is_in_unsafe={:?}; \
                                          bracket_count={:?}",
                                         is_in_unsafe,
                                         bracket_count);
                            } else if let Some(caps) = re.captures(line) {
                                let mut should_count = true;
                                if let Some(before) = caps.at(1) {
                                    if let Some(single_v) = count.lang.single() {
                                        for s in single_v {
                                            if before.contains(s) {
                                                should_count = false;
                                                break;
                                            }
                                        }
                                    }
                                    if let Some(multi) = count.lang.multi_start() {
                                        if before.contains(multi) &&
                                           !before.contains(count.lang.multi_end().unwrap()) {
                                            should_count = false;
                                        }
                                    }
                                }
                                if should_count {
                                    debugln!("It contained the keyword; usafe_line={:?}", line);
                                    count.usafe += 1;
                                    if let Some(after) = caps.at(3) {
                                        debugln!("after_usafe={:?}", after);
                                        bracket_count = Counts::count_brackets(after, None);
                                        is_in_unsafe = bracket_count > 0;
                                        debugln!("after counting brackets; is_in_unsafe={:?}; \
                                                  bracket_count={:?}",
                                                 is_in_unsafe,
                                                 bracket_count);
                                    }
                                }
                            } else {
                                debugln!("It didn't contain the keyword, and we are not in unsafe");
                            }

                            if bracket_count < 0 {
                                debugln!("bracket_count < 0; resetting");
                                bracket_count = 0
                            }
                        } else {
                            debugln!("Language is unsafe, incing the count");
                            count.usafe += 1;
                        }
                    }
                    count.code += 1;
                }
            }
            self.tot += count.files.len();
            self.tot_lines += count.lines;
            self.tot_comments += count.comments;
            self.tot_blanks += count.blanks;
            self.tot_code += count.code;
            self.tot_usafe += count.usafe;
        }

        Ok(())
    }

    pub fn write_results(&mut self) -> CliResult<()> {
        let mut w = TabWriter::new(vec![]);
        cli_try!(write!(w,
                        "\tLanguage\tFiles\tLines\tBlanks\tComments\tCode{}\n",
                        if self.cfg.usafe {
                            "\tUnsafe (%)"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        for count in &self.counts {
            if self.cfg.usafe {
                let usafe_per = if count.code != 0 {
                    (count.usafe as f64 / count.code as f64) * 100.00f64
                } else {
                    0f64
                };
                cli_try!(write!(w,
                                "\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
                                count.lang.name(),
                                count.total_files(),
                                count.lines(),
                                count.blanks(),
                                count.comments(),
                                count.code(),
                                if (usafe_per - 00f64).abs() < f64::EPSILON {
                                    "".to_owned()
                                } else {
                                    format!("{} ({:.2}%)", count.usafe(), usafe_per)
                                }));
            } else {
                cli_try!(write!(w, "\t{}\n", count));
            }
        }
        cli_try!(write!(w,
                        "\t--------\t-----\t-----\t------\t--------\t----{}\n",
                        if self.cfg.usafe {
                            "\t----------"
                        } else {
                            ""
                        }));
        cli_try!(write!(w,
                        "{}\t\t{}\t{}\t{}\t{}\t{}{}\n",
                        "Totals:",
                        fmt::format_number(self.tot as u64, self.cfg.thousands),
                        fmt::format_number(self.tot_lines, self.cfg.thousands),
                        fmt::format_number(self.tot_blanks, self.cfg.thousands),
                        fmt::format_number(self.tot_comments, self.cfg.thousands),
                        fmt::format_number(self.tot_code, self.cfg.thousands),
                        if self.cfg.usafe {
                            format!("\t{} ({:.2}%)",
                                    fmt::format_number(self.tot_usafe, self.cfg.thousands),
                                    (self.tot_usafe as f64 / self.tot_code as f64) * 100.00f64)
                        } else {
                            "".to_owned()
                        }));

        cli_try!(w.flush());

        verboseln!(self.cfg,
                   "{} {}",
                   Format::Good("Displaying"),
                   "the results:");
        if self.tot > 0 {
            write!(io::stdout(),
                   "{}",
                   String::from_utf8(w.unwrap()).ok().expect("failed to get valid UTF-8 String"))
                .expect("failed to write output");
        } else {
            println!("\n\tNo source files were found matching the specified criteria");
        }
        Ok(())
    }

    fn count_brackets(line: &str, count: Option<i64>) -> i64 {
        let mut b: i64 = count.unwrap_or(0);
        for c in line.chars() {
            match c {
                '{' => b += 1,
                '}' => b -= 1,
                _ => (),
            }
        }
        b
    }
}
<|endoftext|>


<commit_before>//! Generic message handling.
//!
//! This module provides the ability to read SBD messages from and write SBD messages to streams.

use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};

use {Error, Result};
use information_element::InformationElement;

/// An SBD message.
#[derive(Debug, Default)]
pub struct Message {
    protocol_revision_number: u8,
    overall_message_length: u16,
    information_elements: HashMap<u8, InformationElement>,
}

impl Message {

    /// Reads in a message from a file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Message> {
        let file = try!(File::open(path));
        Message::read_from(file)
    }

    /// Reads in a message from an object that implements `Read`.
    ///
    /// Per the specification, oversized and undersized messages will result in an error.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use sbd::message::Message;
    /// let mut file = File::open("data/0-mo.sbd").unwrap();
    /// let message = Message::read_from(file).unwrap();
    pub fn read_from<R: Read>(mut readable: R) -> Result<Message> {
        let mut message: Message = Default::default();
        message.protocol_revision_number = try!(readable.read_u8());
        if message.protocol_revision_number != 1 {
            return Err(Error::InvalidProtocolRevisionNumber(message.protocol_revision_number));
        }
        message.overall_message_length = try!(readable.read_u16::<BigEndian>());
        let mut bytes_read = 0u16;
        loop {
            let ie = match InformationElement::read_from(&mut readable) {
                Ok(ie) => ie,
                Err(e) => return Err(e),
            };
            bytes_read += ie.len();
            message.information_elements.insert(ie.id(), ie);
            if bytes_read >= message.overall_message_length {
                break
            }
        }
        if try!(readable.take(1).read_to_end(&mut Vec::new())) != 0 {
            return Err(Error::Oversized);
        }
        Ok(message)
    }

    /// Returns the overall message length of the SBD message.
    ///
    /// This value *includes* the initial three bytes, whereas the `overall_message_length`
    /// value in the SBD header does *not* include those bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert_eq!(59, message.len());
    /// ```
    pub fn len(&self) -> usize {
        self.overall_message_length as usize + 3
    }

    /// Returns true if this message is mobile originated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(message.is_mobile_originated());
    /// ```
    pub fn is_mobile_originated(&self) -> bool {
        true
    }

    /// Returns true if this message is mobile terminated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(!message.is_mobile_terminated());
    /// ```
    pub fn is_mobile_terminated(&self) -> bool {
        false
    }

    /// Returns the mobile originated header information element.
    pub fn mobile_originated_header(&self) -> Option<&InformationElement> {
        self.information_elements.get(&1)
    }
}

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

    use std::fs::File;
    use std::io::{Cursor, Read};

    #[test]
    fn from_path() {
        Message::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn from_read() {
        let file = File::open("data/0-mo.sbd").unwrap();
        Message::read_from(file).unwrap();
    }

    #[test]
    fn from_path_that_doesnt_exist() {
        assert!(Message::from_path("notafile.sbd").is_err());
    }

    #[test]
    fn from_path_that_is_not_an_sbd_message() {
        assert!(Message::from_path("data/1-invalid.sbd").is_err());
    }

    #[test]
    fn len() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(59, message.len());
    }

    #[test]
    fn is_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert!(message.is_mobile_originated());
        assert!(!message.is_mobile_terminated());
        // TODO try to get a mobile terminated message to test the other way
    }

    #[test]
    fn undersized() {
        let file = File::open("data/0-mo.sbd").unwrap();
        let readable = file.take(58);
        assert!(Message::read_from(readable).is_err());
    }

    #[test]
    fn oversized() {
        let file = File::open("data/0-mo.sbd").unwrap();
        let readable = file.chain(Cursor::new(vec![0]));
        assert!(Message::read_from(readable).is_err());
    }
}
<commit_msg>Remove magic number from message<commit_after>//! Generic message handling.
//!
//! This module provides the ability to read SBD messages from and write SBD messages to streams.

use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};

use {Error, Result};
use information_element::InformationElement;

const MESSAGE_HEADER_LENGTH: usize = 3;

/// An SBD message.
#[derive(Debug, Default)]
pub struct Message {
    protocol_revision_number: u8,
    overall_message_length: u16,
    information_elements: HashMap<u8, InformationElement>,
}

impl Message {

    /// Reads in a message from a file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Message> {
        let file = try!(File::open(path));
        Message::read_from(file)
    }

    /// Reads in a message from an object that implements `Read`.
    ///
    /// Per the specification, oversized and undersized messages will result in an error.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use sbd::message::Message;
    /// let mut file = File::open("data/0-mo.sbd").unwrap();
    /// let message = Message::read_from(file).unwrap();
    pub fn read_from<R: Read>(mut readable: R) -> Result<Message> {
        let mut message: Message = Default::default();
        message.protocol_revision_number = try!(readable.read_u8());
        if message.protocol_revision_number != 1 {
            return Err(Error::InvalidProtocolRevisionNumber(message.protocol_revision_number));
        }
        message.overall_message_length = try!(readable.read_u16::<BigEndian>());
        let mut bytes_read = 0u16;
        loop {
            let ie = match InformationElement::read_from(&mut readable) {
                Ok(ie) => ie,
                Err(e) => return Err(e),
            };
            bytes_read += ie.len();
            message.information_elements.insert(ie.id(), ie);
            if bytes_read >= message.overall_message_length {
                break
            }
        }
        if try!(readable.take(1).read_to_end(&mut Vec::new())) != 0 {
            return Err(Error::Oversized);
        }
        Ok(message)
    }

    /// Returns the overall message length of the SBD message.
    ///
    /// This value *includes* the initial three bytes, whereas the `overall_message_length`
    /// value in the SBD header does *not* include those bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert_eq!(59, message.len());
    /// ```
    pub fn len(&self) -> usize {
        self.overall_message_length as usize + MESSAGE_HEADER_LENGTH
    }

    /// Returns true if this message is mobile originated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(message.is_mobile_originated());
    /// ```
    pub fn is_mobile_originated(&self) -> bool {
        true
    }

    /// Returns true if this message is mobile terminated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(!message.is_mobile_terminated());
    /// ```
    pub fn is_mobile_terminated(&self) -> bool {
        false
    }

    /// Returns the mobile originated header information element.
    pub fn mobile_originated_header(&self) -> Option<&InformationElement> {
        self.information_elements.get(&1)
    }
}

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

    use std::fs::File;
    use std::io::{Cursor, Read};

    #[test]
    fn from_path() {
        Message::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn from_read() {
        let file = File::open("data/0-mo.sbd").unwrap();
        Message::read_from(file).unwrap();
    }

    #[test]
    fn from_path_that_doesnt_exist() {
        assert!(Message::from_path("notafile.sbd").is_err());
    }

    #[test]
    fn from_path_that_is_not_an_sbd_message() {
        assert!(Message::from_path("data/1-invalid.sbd").is_err());
    }

    #[test]
    fn len() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(59, message.len());
    }

    #[test]
    fn is_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert!(message.is_mobile_originated());
        assert!(!message.is_mobile_terminated());
        // TODO try to get a mobile terminated message to test the other way
    }

    #[test]
    fn undersized() {
        let file = File::open("data/0-mo.sbd").unwrap();
        let readable = file.take(58);
        assert!(Message::read_from(readable).is_err());
    }

    #[test]
    fn oversized() {
        let file = File::open("data/0-mo.sbd").unwrap();
        let readable = file.chain(Cursor::new(vec![0]));
        assert!(Message::read_from(readable).is_err());
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;

#[derive(Debug, PartialEq)]
pub enum NumericReply {
    RPL_WELCOME = 1,
    RPL_YOURHOST,
    RPL_CREATED,
    RPL_MYINFO,
    RPL_ISUPPORT = 5,
    RPL_WHOISUSER = 311,
    RPL_WHOISSERVER = 312,
    RPL_ENDOFWHOIS = 318,
    RPL_MOTDSTART = 375,
    RPL_MOTD = 372,
    RPL_ENDOFMOTD = 376,
    ERR_NOSUCHNICK = 401,
    PING = 65533,
    PRIVMSG = 65534,
    NOTICE = 65535,
    CAP = 65536,
    NONE = -1,
}

impl FromStr for NumericReply {
    type Err = ();

    fn from_str(s: &str) -> Result<NumericReply, ()> {
        match s {
            "PRIVMSG" => Ok(NumericReply::PRIVMSG),
            "NOTICE" => Ok(NumericReply::NOTICE),
            "001" => Ok(NumericReply::RPL_WELCOME),
            "002" => Ok(NumericReply::RPL_YOURHOST),
            "003" => Ok(NumericReply::RPL_CREATED),
            "004" => Ok(NumericReply::RPL_MYINFO),
            "005" => Ok(NumericReply::RPL_ISUPPORT),
            "372" => Ok(NumericReply::RPL_MOTD),
            "311" => Ok(NumericReply::RPL_WHOISUSER),
            "312" => Ok(NumericReply::RPL_WHOISSERVER),
            "318" => Ok(NumericReply::RPL_ENDOFWHOIS),
            "PING" => Ok(NumericReply::PING),
            "CAP" => Ok(NumericReply::CAP),
            _ => Ok(NumericReply::NONE),
        }
    }
}

trait Substring {
    fn substr(&self, start: u32, length: u32) -> &str;
}

impl Substring for str {
    fn substr(&self, start: u32, length: u32) -> &str {
        return &self[start as usize..start as usize + length as usize];
    }
}

#[derive(Debug)]
pub struct IrcMessage {
    pub raw: String,
    pub prefix: String,
    pub command: NumericReply,
    pub params: Vec<String>,
}

impl PartialEq for IrcMessage {
    fn eq(&self, other: &IrcMessage) -> bool {
        self.raw == other.raw
    }
}

// Thanks SirCmpwn for his awesome and simple way to parse IRC messages!
// https://github.com/SirCmpwn/ChatSharp/blob/master/ChatSharp/IrcMessage.cs
impl FromStr for IrcMessage {
    type Err = String;
    fn from_str(s: &str) -> Result<IrcMessage, String> {
        let mut msg = s.trim_right_matches(|c: char| c == '\r' || c == '\n');
        let raw_message_clone = String::from(msg);

        let mut prefix = String::new();
        let mut command: NumericReply = NumericReply::NONE;
        let mut params: Vec<String> = Vec::new();

        if msg.starts_with(":") {
            let whitespace: u32 = match msg.find(' ') {
                Some(x) => x as u32,
                None => 0u32,
            };
            prefix = match String::from_str(msg.substr(1, whitespace - 1)) {
                Ok(x) => x,
                Err(_) => return Err(String::from("error parsing message prefix")),
            };
            msg = msg.substr(whitespace + 1, msg.len() as u32 - (whitespace + 1));
        }

        if msg.contains(' ') {
            let idx = match msg.find(' ') {
                Some(x) => x as u32,
                None => 0u32,
            };
            command = match NumericReply::from_str(msg.substr(0, idx)) {
                Ok(x) => x,
                Err(_) => return Err(format!("error parsing irc message {}", msg)),
            };
            msg = msg.substr(idx + 1, msg.len() as u32 - (idx + 1));

            // Parse parameters
            while msg != "" {
                if msg.starts_with(":") {
                    params.push(String::from(msg.substr(1, msg.len() as u32 - 1)));
                    break;
                }

                if !msg.contains(' ') {
                    params.push(String::from(msg));
                    msg = "";
                    break;
                }

                let idx = match msg.find(' ') {
                    Some(x) => x as u32,
                    None => 0u32,
                };
                params.push(String::from(msg.substr(0, idx)));
                msg = msg.substr(idx + 1, msg.len() as u32 - (idx + 1));
            }
        }

        Ok(IrcMessage {
            raw: raw_message_clone,
            prefix: prefix,
            command: command,
            params: params,
        })
    }
}
<commit_msg>Get rid of numbers in replies enum<commit_after>use std::str::FromStr;

#[derive(Debug, PartialEq)]
pub enum NumericReply {
    RPL_WELCOME,
    RPL_YOURHOST,
    RPL_CREATED,
    RPL_MYINFO,
    RPL_ISUPPORT,
    RPL_WHOISUSER,
    RPL_WHOISSERVER,
    RPL_ENDOFWHOIS,
    RPL_MOTDSTART,
    RPL_MOTD,
    RPL_ENDOFMOTD,
    ERR_NOSUCHNICK,
    PING,
    PRIVMSG,
    NOTICE,
    CAP,
    NONE = -1,
}

impl FromStr for NumericReply {
    type Err = ();

    fn from_str(s: &str) -> Result<NumericReply, ()> {
        match s {
            "PRIVMSG" => Ok(NumericReply::PRIVMSG),
            "NOTICE" => Ok(NumericReply::NOTICE),
            "001" => Ok(NumericReply::RPL_WELCOME),
            "002" => Ok(NumericReply::RPL_YOURHOST),
            "003" => Ok(NumericReply::RPL_CREATED),
            "004" => Ok(NumericReply::RPL_MYINFO),
            "005" => Ok(NumericReply::RPL_ISUPPORT),
            "372" => Ok(NumericReply::RPL_MOTD),
            "311" => Ok(NumericReply::RPL_WHOISUSER),
            "312" => Ok(NumericReply::RPL_WHOISSERVER),
            "318" => Ok(NumericReply::RPL_ENDOFWHOIS),
            "PING" => Ok(NumericReply::PING),
            "CAP" => Ok(NumericReply::CAP),
            _ => Ok(NumericReply::NONE),
        }
    }
}

trait Substring {
    fn substr(&self, start: u32, length: u32) -> &str;
}

impl Substring for str {
    fn substr(&self, start: u32, length: u32) -> &str {
        return &self[start as usize..start as usize + length as usize];
    }
}

#[derive(Debug)]
pub struct IrcMessage {
    pub raw: String,
    pub prefix: String,
    pub command: NumericReply,
    pub params: Vec<String>,
}

impl PartialEq for IrcMessage {
    fn eq(&self, other: &IrcMessage) -> bool {
        self.raw == other.raw
    }
}

// Thanks SirCmpwn for his awesome and simple way to parse IRC messages!
// https://github.com/SirCmpwn/ChatSharp/blob/master/ChatSharp/IrcMessage.cs
impl FromStr for IrcMessage {
    type Err = String;
    fn from_str(s: &str) -> Result<IrcMessage, String> {
        let mut msg = s.trim_right_matches(|c: char| c == '\r' || c == '\n');
        let raw_message_clone = String::from(msg);

        let mut prefix = String::new();
        let mut command: NumericReply = NumericReply::NONE;
        let mut params: Vec<String> = Vec::new();

        if msg.starts_with(":") {
            let whitespace: u32 = match msg.find(' ') {
                Some(x) => x as u32,
                None => 0u32,
            };
            prefix = match String::from_str(msg.substr(1, whitespace - 1)) {
                Ok(x) => x,
                Err(_) => return Err(String::from("error parsing message prefix")),
            };
            msg = msg.substr(whitespace + 1, msg.len() as u32 - (whitespace + 1));
        }

        if msg.contains(' ') {
            let idx = match msg.find(' ') {
                Some(x) => x as u32,
                None => 0u32,
            };
            command = match NumericReply::from_str(msg.substr(0, idx)) {
                Ok(x) => x,
                Err(_) => return Err(format!("error parsing irc message {}", msg)),
            };
            msg = msg.substr(idx + 1, msg.len() as u32 - (idx + 1));

            // Parse parameters
            while msg != "" {
                if msg.starts_with(":") {
                    params.push(String::from(msg.substr(1, msg.len() as u32 - 1)));
                    break;
                }

                if !msg.contains(' ') {
                    params.push(String::from(msg));
                    msg = "";
                    break;
                }

                let idx = match msg.find(' ') {
                    Some(x) => x as u32,
                    None => 0u32,
                };
                params.push(String::from(msg.substr(0, idx)));
                msg = msg.substr(idx + 1, msg.len() as u32 - (idx + 1));
            }
        }

        Ok(IrcMessage {
            raw: raw_message_clone,
            prefix: prefix,
            command: command,
            params: params,
        })
    }
}
<|endoftext|>