<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use model::ttable::{EvalType, TTable};
use model::{Board, Move, Outcome};

const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;

pub struct AI {
    status: Status,
    ttable: Arc<Mutex<TTable>>,
}

enum Status {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> Self {
        Self {
            status: Status::Idle,
            ttable: Arc::new(Mutex::new(TTable::new())),
        }
    }

    pub fn is_idle(&self) -> bool {
        match self.status {
            Status::Idle => true,
            Status::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let Status::Thinking {
            ref stop_signal, ..
        } = self.status
        {
            stop_signal.store(true, Ordering::Relaxed);
        }
        // Unconditionally assign because without NLL, we can't put this in the if let block above
        self.status = Status::Idle;
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        let result;
        match self.status {
            Status::Idle => result = None,
            Status::Thinking { ref move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => result = Some(mv),
                Err(Empty) => result = None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }

        // We can't set status in the Ok(mv) arm above without NLL
        if result.is_some() {
            self.status = Status::Idle;
        }
        result
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_status = mem::replace(&mut self.status, Status::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let ttable_mutex = self.ttable.clone();

        let handle = thread::spawn(move || {
            if let Status::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_status
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // If the previous AI thread was sent the stop signal, but hasn't received it yet, we
            // will block here until it finishes. We won't have joined on its handle above because
            // stop throws away its Status enum.
            let mut ttable = match ttable_mutex.lock() {
                Ok(table) => table,
                Err(_) => panic!("Transposition table mutex is poisoned"),
            };
            ttable.inc_age();

            // Only take positions after the last irreversible move
            let mut board_list: Vec<_> = board_list
                .into_iter()
                .rev()
                .take_while(|b| b.vitals == board.vitals)
                .collect();
            board_list.reverse();

            let mut moves: Vec<(Move, i16)> = board
                .generate_moves()
                .map(|mv| (mv, NEG_INFINITY))
                .collect();

            if moves.is_empty() {
                panic!("AI has no moves");
            }

            let mut pv = None;
            for depth in 0..depth {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }
                let mut max_score = NEG_INFINITY;
                // TODO: use the scores here are a kind of aspiration window?
                for pair in &mut moves {
                    let mut new_board = board;
                    new_board.apply_move(&pair.0);

                    let mut new_pv = vec![];

                    let score = -alphabeta_negamax(
                        &new_board,
                        &mut board_list,
                        &mut new_pv,
                        NEG_INFINITY,
                        -max_score,
                        depth,
                        &mut ttable,
                    );

                    if score > max_score {
                        max_score = score;
                        pv = Some(new_pv);
                    }
                    pair.1 = score;
                }
                moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
                println!();
                println!("Depth {}: {:>6}", depth, moves[0].1);
                if let Some(ref mut pv) = pv {
                    pv.push(moves[0].0);
                    for mv in pv.iter().rev() {
                        println!("    {}", mv);
                    }
                }
            }
            println!();
            println!("---------------------");
            move_sender
                .send(moves[0].0)
                .expect("AI failed to send Move");
        });

        self.status = Status::Thinking {
            move_recv,
            stop_signal,
            handle,
        }
    }
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    pv: &mut Vec<Move>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let mut set_pv = move |score, new_pv| {
        if score > alpha && score < beta {
            *pv = new_pv;
        }
    };
    let set_ttable = |ttable: &mut TTable, eval_type, score| {
        ttable.set(board.zobrist, eval_type, depth, score);
    };

    use self::Outcome::*;
    match board.outcome() {
        DrawStalemate | DrawInsufficientMaterial => {
            // This is safe to do because Board does not detect draws by threefold repetition
            set_ttable(ttable, EvalType::Exact, DRAW);
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Win(color) => {
            assert_ne!(color, board.turn);
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - i16::from(depth);
            set_ttable(ttable, EvalType::Exact, score);
            set_pv(score, vec![]);
            return score;
        }
        InProgress => {}
        DrawThreefoldRepetition => unreachable!(),
    }

    if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
        set_pv(DRAW, vec![]);
        return DRAW;
    }

    if depth == 0 {
        let score = quiescence_search(board, ttable, alpha, beta);
        set_pv(score, vec![]);
        return score;
    }

    {
        let entry = ttable.get(board.zobrist);
        if entry.zobrist == board.zobrist && entry.depth == depth {
            match entry.eval_type {
                EvalType::Exact => {
                    // This will cut the PV short
                    // TODO: Store the best move in the table and get the PV from that?
                    set_pv(entry.score, vec![]);
                    return entry.score;
                }
                EvalType::Beta => {
                    if entry.score >= beta {
                        return entry.score;
                    }
                    beta = entry.score;
                }
            }
        }
    }

    let mut best_score = NEG_INFINITY;
    let mut best_move = None;

    let mut new_pv = vec![];
    let moves = board.generate_moves();
    for mv in moves {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        board_list.push(*board);
        let score = -alphabeta_negamax(
            &new_board,
            &mut board_list,
            &mut new_pv,
            -beta,
            -alpha,
            depth - 1,
            ttable,
        );
        board_list.pop();

        best_score = cmp::max(score, best_score);

        if score >= beta {
            set_ttable(ttable, EvalType::Beta, score);
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, EvalType::Exact, best_score);
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(board: &Board, ttable: &mut TTable, mut alpha: i16, mut beta: i16) -> i16 {
    {
        let entry = ttable.get(board.zobrist);
        // All quiescence entries in the TTable are marked with a depth of 0
        if entry.zobrist == board.zobrist && entry.depth == 0 {
            match entry.eval_type {
                EvalType::Exact => {
                    return entry.score;
                }
                EvalType::Beta => {
                    if entry.score >= beta {
                        return entry.score;
                    }
                    beta = entry.score;
                }
            }
        }
    }

    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        ttable.set(board.zobrist, EvalType::Beta, 0, stand_pat);
        return beta;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    for mv in board.generate_captures() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        let score = -quiescence_search(&new_board, ttable, -beta, -alpha);

        if score >= beta {
            ttable.set(board.zobrist, EvalType::Beta, 0, score);
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    alpha
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<commit_msg>Don't check TTable in quiescence search<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::cmp;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};

use model::ttable::{EvalType, TTable};
use model::{Board, Move, Outcome};

const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
// Small contempt factor to discourage draws
const DRAW: i16 = 1;

pub struct AI {
    status: Status,
    ttable: Arc<Mutex<TTable>>,
}

enum Status {
    Idle,
    // Either the AI thread is running, or there is a move waiting to be received
    Thinking {
        move_recv: Receiver<Move>,
        // We store and load this atomic with Ordering::Relaxed. It *should* be fine because it
        // doesn't interact with any other atomics--all we want to do is tell the AI thread to stop
        // searching for a move
        stop_signal: Arc<AtomicBool>,
        handle: JoinHandle<()>,
    },
}

impl AI {
    pub fn new() -> Self {
        Self {
            status: Status::Idle,
            ttable: Arc::new(Mutex::new(TTable::new())),
        }
    }

    pub fn is_idle(&self) -> bool {
        match self.status {
            Status::Idle => true,
            Status::Thinking { .. } => false,
        }
    }

    pub fn stop(&mut self) {
        if let Status::Thinking {
            ref stop_signal, ..
        } = self.status
        {
            stop_signal.store(true, Ordering::Relaxed);
        }
        // Unconditionally assign because without NLL, we can't put this in the if let block above
        self.status = Status::Idle;
    }

    pub fn try_recv(&mut self) -> Option<Move> {
        use self::TryRecvError::*;

        let result;
        match self.status {
            Status::Idle => result = None,
            Status::Thinking { ref move_recv, .. } => match move_recv.try_recv() {
                Ok(mv) => result = Some(mv),
                Err(Empty) => result = None,
                Err(Disconnected) => panic!("Tried to receive move from disconnected sender"),
            },
        }

        // We can't set status in the Ok(mv) arm above without NLL
        if result.is_some() {
            self.status = Status::Idle;
        }
        result
    }

    pub fn think(&mut self, board: Board, board_list: Vec<Board>, depth: u8) {
        assert_ne!(depth, 0);

        let prev_status = mem::replace(&mut self.status, Status::Idle);

        let (move_sender, move_recv) = mpsc::channel();
        let stop_signal = Arc::new(AtomicBool::new(false));
        let stop_signal_clone = stop_signal.clone();

        let ttable_mutex = self.ttable.clone();

        let handle = thread::spawn(move || {
            if let Status::Thinking {
                stop_signal,
                handle,
                ..
            } = prev_status
            {
                stop_signal.store(true, Ordering::Relaxed);
                handle
                    .join()
                    .expect("Old AI thread panicked when new AI thread joined on it");
            }

            // If the previous AI thread was sent the stop signal, but hasn't received it yet, we
            // will block here until it finishes. We won't have joined on its handle above because
            // stop throws away its Status enum.
            let mut ttable = match ttable_mutex.lock() {
                Ok(table) => table,
                Err(_) => panic!("Transposition table mutex is poisoned"),
            };
            ttable.inc_age();

            // Only take positions after the last irreversible move
            let mut board_list: Vec<_> = board_list
                .into_iter()
                .rev()
                .take_while(|b| b.vitals == board.vitals)
                .collect();
            board_list.reverse();

            let mut moves: Vec<(Move, i16)> = board
                .generate_moves()
                .map(|mv| (mv, NEG_INFINITY))
                .collect();

            if moves.is_empty() {
                panic!("AI has no moves");
            }

            let mut pv = None;
            for depth in 0..depth {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }
                let mut max_score = NEG_INFINITY;
                // TODO: use the scores here are a kind of aspiration window?
                for pair in &mut moves {
                    let mut new_board = board;
                    new_board.apply_move(&pair.0);

                    let mut new_pv = vec![];

                    let score = -alphabeta_negamax(
                        &new_board,
                        &mut board_list,
                        &mut new_pv,
                        NEG_INFINITY,
                        -max_score,
                        depth,
                        &mut ttable,
                    );

                    if score > max_score {
                        max_score = score;
                        pv = Some(new_pv);
                    }
                    pair.1 = score;
                }
                moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
                println!();
                println!("Depth {}: {:>6}", depth, moves[0].1);
                if let Some(ref mut pv) = pv {
                    pv.push(moves[0].0);
                    for mv in pv.iter().rev() {
                        println!("    {}", mv);
                    }
                }
            }
            println!();
            println!("---------------------");
            move_sender
                .send(moves[0].0)
                .expect("AI failed to send Move");
        });

        self.status = Status::Thinking {
            move_recv,
            stop_signal,
            handle,
        }
    }
}

fn alphabeta_negamax(
    board: &Board,
    // This list does not include the current board
    mut board_list: &mut Vec<Board>,
    pv: &mut Vec<Move>,
    mut alpha: i16,
    mut beta: i16,
    depth: u8,
    ttable: &mut TTable,
) -> i16 {
    let mut set_pv = move |score, new_pv| {
        if score > alpha && score < beta {
            *pv = new_pv;
        }
    };
    let set_ttable = |ttable: &mut TTable, eval_type, score| {
        ttable.set(board.zobrist, eval_type, depth, score);
    };

    use self::Outcome::*;
    match board.outcome() {
        DrawStalemate | DrawInsufficientMaterial => {
            // This is safe to do because Board does not detect draws by threefold repetition
            set_ttable(ttable, EvalType::Exact, DRAW);
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Win(color) => {
            assert_ne!(color, board.turn);
            // Weight score by depth to encourage shorter wins. The shorter the win, the greater
            // `depth` will be, and so the larger the score will be. This also encourages the AI to
            // prolong a loss.
            let score = LOSE - i16::from(depth);
            set_ttable(ttable, EvalType::Exact, score);
            set_pv(score, vec![]);
            return score;
        }
        InProgress => {}
        DrawThreefoldRepetition => unreachable!(),
    }

    if board_list.len() >= 8 && board_list.iter().filter(|&&b| b == *board).count() >= 2 {
        set_pv(DRAW, vec![]);
        return DRAW;
    }

    if depth == 0 {
        let score = quiescence_search(board, alpha, beta);
        set_pv(score, vec![]);
        return score;
    }

    {
        let entry = ttable.get(board.zobrist);
        if entry.zobrist == board.zobrist && entry.depth == depth {
            match entry.eval_type {
                EvalType::Exact => {
                    // This will cut the PV short
                    // TODO: Store the best move in the table and get the PV from that?
                    set_pv(entry.score, vec![]);
                    return entry.score;
                }
                EvalType::Beta => {
                    if entry.score >= beta {
                        return entry.score;
                    }
                    beta = entry.score;
                }
            }
        }
    }

    let mut best_score = NEG_INFINITY;
    let mut best_move = None;

    let mut new_pv = vec![];
    let moves = board.generate_moves();
    for mv in moves {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        board_list.push(*board);
        let score = -alphabeta_negamax(
            &new_board,
            &mut board_list,
            &mut new_pv,
            -beta,
            -alpha,
            depth - 1,
            ttable,
        );
        board_list.pop();

        best_score = cmp::max(score, best_score);

        if score >= beta {
            set_ttable(ttable, EvalType::Beta, score);
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, EvalType::Exact, best_score);
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(board: &Board, mut alpha: i16, beta: i16) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    for mv in board.generate_captures() {
        let mut new_board = *board;
        new_board.apply_move(&mv);

        let score = -quiescence_search(&new_board, -beta, -alpha);

        if score >= beta {
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    alpha
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 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.

#![allow(dead_code)]

use std::mem;

enum Tag<A> {
    Tag2(A)
}

struct Rec {
    c8: u8,
    t: Tag<u64>
}

fn mk_rec() -> Rec {
    return Rec { c8:0, t:Tag::Tag2(0) };
}

fn is_u64_aligned(u: &Tag<u64>) -> bool {
    let p: usize = unsafe { mem::transmute(u) };
    let u64_align = std::mem::align_of::<u64>();
    return (p & (u64_align + 1)) == 0;
}

pub fn main() {
    let x = mk_rec();
    assert!(is_u64_aligned(&x.t));
}
<commit_msg>fix non-deterministic test<commit_after>// Copyright 2012-2014 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.

#![allow(dead_code)]

use std::mem;

enum Tag<A> {
    Tag2(A)
}

struct Rec {
    c8: u8,
    t: Tag<u64>
}

fn mk_rec() -> Rec {
    return Rec { c8:0, t:Tag::Tag2(0) };
}

fn is_u64_aligned(u: &Tag<u64>) -> bool {
    let p: usize = unsafe { mem::transmute(u) };
    let u64_align = std::mem::align_of::<u64>();
    return (p & (u64_align + 1)) == 0;
}

pub fn main() {
    let x = mk_rec();
    is_u64_aligned(&x.t); // the result of this is non-deterministic
}
<|endoftext|>


<commit_before>use std::{
    collections::BTreeMap,
    env,
    io::Write as _,
    path::Path,
    time::{Instant, SystemTime, UNIX_EPOCH},
};

use anyhow::{bail, format_err, Result};
use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf};

use crate::flags;

type Unit = String;

impl flags::Metrics {
    pub(crate) fn run(self) -> Result<()> {
        let mut metrics = Metrics::new()?;
        if !self.dry_run {
            rm_rf("./target/release")?;
        }
        if !Path::new("./target/rustc-perf").exists() {
            mkdir_p("./target/rustc-perf")?;
            cmd!("git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")
                .run()?;
        }
        {
            let _d = pushd("./target/rustc-perf")?;
            let revision = &metrics.perf_revision;
            cmd!("git reset --hard {revision}").run()?;
        }

        let _env = pushenv("RA_METRICS", "1");

        metrics.measure_build()?;
        metrics.measure_analysis_stats_self()?;
        metrics.measure_analysis_stats("ripgrep")?;
        metrics.measure_analysis_stats("webrender")?;
        metrics.measure_analysis_stats("diesel/diesel")?;

        if !self.dry_run {
            let _d = pushd("target")?;
            let metrics_token = env::var("METRICS_TOKEN").unwrap();
            cmd!(
                "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git"
            )
            .run()?;
            let _d = pushd("metrics")?;

            let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?;
            writeln!(file, "{}", metrics.json())?;
            cmd!("git add .").run()?;
            cmd!("git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈")
                .run()?;
            cmd!("git push origin master").run()?;
        }
        eprintln!("{:#?}", metrics);
        Ok(())
    }
}

impl Metrics {
    fn measure_build(&mut self) -> Result<()> {
        eprintln!("\nMeasuring build");
        cmd!("cargo fetch").run()?;

        let time = Instant::now();
        cmd!("cargo build --release --package rust-analyzer --bin rust-analyzer").run()?;
        let time = time.elapsed();
        self.report("build", time.as_millis() as u64, "ms".into());
        Ok(())
    }
    fn measure_analysis_stats_self(&mut self) -> Result<()> {
        self.measure_analysis_stats_path("self", &".")
    }
    fn measure_analysis_stats(&mut self, bench: &str) -> Result<()> {
        self.measure_analysis_stats_path(
            bench,
            &format!("./target/rustc-perf/collector/benchmarks/{}", bench),
        )
    }
    fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> {
        eprintln!("\nMeasuring analysis-stats/{}", name);
        let output =
            cmd!("./target/release/rust-analyzer --quiet analysis-stats --memory-usage {path}")
                .read()?;
        for (metric, value, unit) in parse_metrics(&output) {
            self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into());
        }
        Ok(())
    }
}

fn parse_metrics(output: &str) -> Vec<(&str, u64, &str)> {
    output
        .lines()
        .filter_map(|it| {
            let entry = it.split(':').collect::<Vec<_>>();
            match entry.as_slice() {
                ["METRIC", name, value, unit] => Some((*name, value.parse().unwrap(), *unit)),
                _ => None,
            }
        })
        .collect()
}

#[derive(Debug)]
struct Metrics {
    host: Host,
    timestamp: SystemTime,
    revision: String,
    perf_revision: String,
    metrics: BTreeMap<String, (u64, Unit)>,
}

#[derive(Debug)]
struct Host {
    os: String,
    cpu: String,
    mem: String,
}

impl Metrics {
    fn new() -> Result<Metrics> {
        let host = Host::new()?;
        let timestamp = SystemTime::now();
        let revision = cmd!("git rev-parse HEAD").read()?;
        let perf_revision = "c52ee623e231e7690a93be88d943016968c1036b".into();
        Ok(Metrics { host, timestamp, revision, perf_revision, metrics: BTreeMap::new() })
    }

    fn report(&mut self, name: &str, value: u64, unit: Unit) {
        self.metrics.insert(name.into(), (value, unit));
    }

    fn json(&self) -> String {
        let mut buf = String::new();
        self.to_json(write_json::object(&mut buf));
        buf
    }

    fn to_json(&self, mut obj: write_json::Object<'_>) {
        self.host.to_json(obj.object("host"));
        let timestamp = self.timestamp.duration_since(UNIX_EPOCH).unwrap();
        obj.number("timestamp", timestamp.as_secs() as f64);
        obj.string("revision", &self.revision);
        obj.string("perf_revision", &self.perf_revision);
        let mut metrics = obj.object("metrics");
        for (k, (value, unit)) in &self.metrics {
            metrics.array(k).number(*value as f64).string(unit);
        }
    }
}

impl Host {
    fn new() -> Result<Host> {
        if cfg!(not(target_os = "linux")) {
            bail!("can only collect metrics on Linux ");
        }

        let os = read_field("/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_string();

        let cpu =
            read_field("/proc/cpuinfo", "model name")?.trim_start_matches(':').trim().to_string();

        let mem = read_field("/proc/meminfo", "MemTotal:")?;

        return Ok(Host { os, cpu, mem });

        fn read_field<'a>(path: &str, field: &str) -> Result<String> {
            let text = read_file(path)?;

            let line = text
                .lines()
                .find(|it| it.starts_with(field))
                .ok_or_else(|| format_err!("can't parse {}", path))?;
            Ok(line[field.len()..].trim().to_string())
        }
    }
    fn to_json(&self, mut obj: write_json::Object<'_>) {
        obj.string("os", &self.os).string("cpu", &self.cpu).string("mem", &self.mem);
    }
}
<commit_msg>xtask: remove redundant lifetime (clippy::extra_unused_lifetimes)<commit_after>use std::{
    collections::BTreeMap,
    env,
    io::Write as _,
    path::Path,
    time::{Instant, SystemTime, UNIX_EPOCH},
};

use anyhow::{bail, format_err, Result};
use xshell::{cmd, mkdir_p, pushd, pushenv, read_file, rm_rf};

use crate::flags;

type Unit = String;

impl flags::Metrics {
    pub(crate) fn run(self) -> Result<()> {
        let mut metrics = Metrics::new()?;
        if !self.dry_run {
            rm_rf("./target/release")?;
        }
        if !Path::new("./target/rustc-perf").exists() {
            mkdir_p("./target/rustc-perf")?;
            cmd!("git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf")
                .run()?;
        }
        {
            let _d = pushd("./target/rustc-perf")?;
            let revision = &metrics.perf_revision;
            cmd!("git reset --hard {revision}").run()?;
        }

        let _env = pushenv("RA_METRICS", "1");

        metrics.measure_build()?;
        metrics.measure_analysis_stats_self()?;
        metrics.measure_analysis_stats("ripgrep")?;
        metrics.measure_analysis_stats("webrender")?;
        metrics.measure_analysis_stats("diesel/diesel")?;

        if !self.dry_run {
            let _d = pushd("target")?;
            let metrics_token = env::var("METRICS_TOKEN").unwrap();
            cmd!(
                "git clone --depth 1 https://{metrics_token}@github.com/rust-analyzer/metrics.git"
            )
            .run()?;
            let _d = pushd("metrics")?;

            let mut file = std::fs::OpenOptions::new().append(true).open("metrics.json")?;
            writeln!(file, "{}", metrics.json())?;
            cmd!("git add .").run()?;
            cmd!("git -c user.name=Bot -c user.email=dummy@example.com commit --message 📈")
                .run()?;
            cmd!("git push origin master").run()?;
        }
        eprintln!("{:#?}", metrics);
        Ok(())
    }
}

impl Metrics {
    fn measure_build(&mut self) -> Result<()> {
        eprintln!("\nMeasuring build");
        cmd!("cargo fetch").run()?;

        let time = Instant::now();
        cmd!("cargo build --release --package rust-analyzer --bin rust-analyzer").run()?;
        let time = time.elapsed();
        self.report("build", time.as_millis() as u64, "ms".into());
        Ok(())
    }
    fn measure_analysis_stats_self(&mut self) -> Result<()> {
        self.measure_analysis_stats_path("self", &".")
    }
    fn measure_analysis_stats(&mut self, bench: &str) -> Result<()> {
        self.measure_analysis_stats_path(
            bench,
            &format!("./target/rustc-perf/collector/benchmarks/{}", bench),
        )
    }
    fn measure_analysis_stats_path(&mut self, name: &str, path: &str) -> Result<()> {
        eprintln!("\nMeasuring analysis-stats/{}", name);
        let output =
            cmd!("./target/release/rust-analyzer --quiet analysis-stats --memory-usage {path}")
                .read()?;
        for (metric, value, unit) in parse_metrics(&output) {
            self.report(&format!("analysis-stats/{}/{}", name, metric), value, unit.into());
        }
        Ok(())
    }
}

fn parse_metrics(output: &str) -> Vec<(&str, u64, &str)> {
    output
        .lines()
        .filter_map(|it| {
            let entry = it.split(':').collect::<Vec<_>>();
            match entry.as_slice() {
                ["METRIC", name, value, unit] => Some((*name, value.parse().unwrap(), *unit)),
                _ => None,
            }
        })
        .collect()
}

#[derive(Debug)]
struct Metrics {
    host: Host,
    timestamp: SystemTime,
    revision: String,
    perf_revision: String,
    metrics: BTreeMap<String, (u64, Unit)>,
}

#[derive(Debug)]
struct Host {
    os: String,
    cpu: String,
    mem: String,
}

impl Metrics {
    fn new() -> Result<Metrics> {
        let host = Host::new()?;
        let timestamp = SystemTime::now();
        let revision = cmd!("git rev-parse HEAD").read()?;
        let perf_revision = "c52ee623e231e7690a93be88d943016968c1036b".into();
        Ok(Metrics { host, timestamp, revision, perf_revision, metrics: BTreeMap::new() })
    }

    fn report(&mut self, name: &str, value: u64, unit: Unit) {
        self.metrics.insert(name.into(), (value, unit));
    }

    fn json(&self) -> String {
        let mut buf = String::new();
        self.to_json(write_json::object(&mut buf));
        buf
    }

    fn to_json(&self, mut obj: write_json::Object<'_>) {
        self.host.to_json(obj.object("host"));
        let timestamp = self.timestamp.duration_since(UNIX_EPOCH).unwrap();
        obj.number("timestamp", timestamp.as_secs() as f64);
        obj.string("revision", &self.revision);
        obj.string("perf_revision", &self.perf_revision);
        let mut metrics = obj.object("metrics");
        for (k, (value, unit)) in &self.metrics {
            metrics.array(k).number(*value as f64).string(unit);
        }
    }
}

impl Host {
    fn new() -> Result<Host> {
        if cfg!(not(target_os = "linux")) {
            bail!("can only collect metrics on Linux ");
        }

        let os = read_field("/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_string();

        let cpu =
            read_field("/proc/cpuinfo", "model name")?.trim_start_matches(':').trim().to_string();

        let mem = read_field("/proc/meminfo", "MemTotal:")?;

        return Ok(Host { os, cpu, mem });

        fn read_field(path: &str, field: &str) -> Result<String> {
            let text = read_file(path)?;

            let line = text
                .lines()
                .find(|it| it.starts_with(field))
                .ok_or_else(|| format_err!("can't parse {}", path))?;
            Ok(line[field.len()..].trim().to_string())
        }
    }
    fn to_json(&self, mut obj: write_json::Object<'_>) {
        obj.string("os", &self.os).string("cpu", &self.cpu).string("mem", &self.mem);
    }
}
<|endoftext|>


<commit_before>use sodiumoxide::crypto::sign;

use crate::crypto::PublicKey;

pub mod constants;
pub mod builder;
pub mod error;
pub mod op;

use self::constants::*;
use self::error::*;
use self::op::*;

pub struct ScriptEngine {
    byte_code: Vec<u8>,
    pos: usize,
    stack: Vec<OpFrame>
}

impl ScriptEngine {

    pub fn new(byte_code: Vec<u8>) -> Option<Self> {
        if byte_code.len() > MAX_BYTE_SIZE { return None }
        Some(Self {
            byte_code,
            pos: 0,
            stack: Vec::with_capacity(MAX_FRAME_STACK)
        })
    }

    pub fn eval(&mut self) -> Result<bool, EvalErr> {
        self.pos = 0;
        let mut if_marker = 0;
        let mut ignore_else = false;
        loop {
            let op = match self.consume_op()? {
                Some(op) => op,
                None => break
            };
            match op {
                // Push value
                OpFrame::False => {
                    self.insert(OpFrame::False)?;
                },
                OpFrame::True => {
                    self.insert(OpFrame::True)?;
                },
                OpFrame::PubKey(key) => {
                    self.insert(OpFrame::PubKey(key))?;
                },
                // Control
                OpFrame::OpIf => {
                    if_marker += 1;
                    ignore_else = self.pop_bool()?;
                    if ignore_else { continue; }
                    let req_if_marker = if_marker;
                    self.consume_op_until(|op| {
                        if op == OpFrame::OpIf {
                            if_marker += 1;
                            false
                        } else if op == OpFrame::OpElse {
                            if_marker == req_if_marker
                        } else if op == OpFrame::OpEndIf {
                            let do_break = if_marker == req_if_marker;
                            if_marker -= 1;
                            do_break
                        } else {
                            false
                        }
                    })?;
                },
                OpFrame::OpElse => {
                    if !ignore_else { continue; }
                    let req_if_marker = if_marker;
                    self.consume_op_until(|op| {
                        if op == OpFrame::OpIf {
                            if_marker += 1;
                            false
                        } else if op == OpFrame::OpElse {
                            if_marker == req_if_marker
                        } else if op == OpFrame::OpEndIf {
                            let do_break = if_marker == req_if_marker;
                            if_marker -= 1;
                            do_break
                        } else {
                            false
                        }
                    })?;
                },
                OpFrame::OpEndIf => {
                    if_marker -= 1;
                }
            }
        }

        if if_marker > 0 {
            return Err(self.new_err(EvalErrType::UnexpectedEOF))
        }

        // Scripts must return true or false
        Ok(self.pop_bool()?)
    }

    fn consume_op_until<F>(&mut self, mut filter: F) -> Result<(), EvalErr>
            where F: FnMut(OpFrame) -> bool {
        loop {
            match self.consume_op()? {
                Some(op) => {
                    if filter(op) { break; }
                },
                None => {
                    return Err(self.new_err(EvalErrType::UnexpectedEOF))
                }
            }
        }

        Ok(())
    }

    fn consume_op(&mut self) -> Result<Option<OpFrame>, EvalErr> {
        if self.pos == self.byte_code.len() { return Ok(None) }
        let byte = self.byte_code[self.pos];
        self.pos += 1;

        match byte {
            o if o == Operand::PushFalse as u8 => Ok(Some(OpFrame::False)),
            o if o == Operand::PushTrue as u8 => Ok(Some(OpFrame::True)),
            o if o == Operand::PushPubKey as u8 => {
                let slice = self.pos .. self.pos + sign::PUBLICKEYBYTES;
                let key = match self.byte_code.get(slice) {
                    Some(slice) => {
                        self.pos += sign::PUBLICKEYBYTES;
                        PublicKey::from_bytes(slice).unwrap()
                    },
                    None => return Err(self.new_err(EvalErrType::UnexpectedEOF))
                };
                Ok(Some(OpFrame::PubKey(key)))
            },
            o if o == Operand::OpIf as u8 => Ok(Some(OpFrame::OpIf)),
            o if o == Operand::OpElse as u8 => Ok(Some(OpFrame::OpElse)),
            o if o == Operand::OpEndIf as u8 => Ok(Some(OpFrame::OpEndIf)),
            _ => Err(self.new_err(EvalErrType::UnknownOp))
        }
    }

    fn insert(&mut self, op: OpFrame) -> Result<(), EvalErr> {
        if self.stack.len() + 1 <= MAX_FRAME_STACK {
            self.stack.push(op);
            Ok(())
        } else {
            Err(self.new_err(EvalErrType::StackOverflow))
        }
    }

    fn pop_bool(&mut self) -> Result<bool, EvalErr> {
        let frame = self.pop()?;
        match frame {
            OpFrame::False => Ok(false),
            OpFrame::True => Ok(true),
            _ => Err(self.new_err(EvalErrType::InvalidCmp))
        }
    }

    fn pop(&mut self) -> Result<OpFrame, EvalErr> {
        self.stack.pop().ok_or_else(|| {
            self.new_err(EvalErrType::StackUnderflow)
        })
    }

    fn new_err(&mut self, err: EvalErrType) -> EvalErr {
        EvalErr::new(self.pos, err)
    }
}

#[cfg(test)]
mod tests {
    use crate::crypto::KeyPair;
    use super::builder::*;
    use super::*;

    #[test]
    fn true_only_script() {
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::True));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn false_only_script() {
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::False));
        assert!(!engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn if_script() {
        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::False)
                                .push(OpFrame::OpEndIf));
        assert!(!engine.eval().unwrap());
        assert!(engine.stack.is_empty());

        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::True)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn branched_if() {
        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::True)
                                .push(OpFrame::OpElse)
                                .push(OpFrame::False)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());

        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::False)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::False)
                                .push(OpFrame::OpElse)
                                .push(OpFrame::True)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn fail_invalid_stack_on_return() {
        let key = KeyPair::gen_keypair().0;
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::PubKey(key)));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::InvalidCmp);
    }

    #[test]
    fn fail_invalid_if_cmp() {
        let key = KeyPair::gen_keypair().0;
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::PubKey(key))
                                        .push(OpFrame::OpIf));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::InvalidCmp);
    }

    #[test]
    fn fail_unended_if() {
        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::UnexpectedEOF);

        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::False)
                                .push(OpFrame::OpIf));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::UnexpectedEOF);
    }
}
<commit_msg>Add nested-if branch testing<commit_after>use sodiumoxide::crypto::sign;

use crate::crypto::PublicKey;

pub mod constants;
pub mod builder;
pub mod error;
pub mod op;

use self::constants::*;
use self::error::*;
use self::op::*;

pub struct ScriptEngine {
    byte_code: Vec<u8>,
    pos: usize,
    stack: Vec<OpFrame>
}

impl ScriptEngine {

    pub fn new(byte_code: Vec<u8>) -> Option<Self> {
        if byte_code.len() > MAX_BYTE_SIZE { return None }
        Some(Self {
            byte_code,
            pos: 0,
            stack: Vec::with_capacity(MAX_FRAME_STACK)
        })
    }

    pub fn eval(&mut self) -> Result<bool, EvalErr> {
        self.pos = 0;
        let mut if_marker = 0;
        let mut ignore_else = false;
        loop {
            let op = match self.consume_op()? {
                Some(op) => op,
                None => break
            };
            match op {
                // Push value
                OpFrame::False => {
                    self.insert(OpFrame::False)?;
                },
                OpFrame::True => {
                    self.insert(OpFrame::True)?;
                },
                OpFrame::PubKey(key) => {
                    self.insert(OpFrame::PubKey(key))?;
                },
                // Control
                OpFrame::OpIf => {
                    if_marker += 1;
                    ignore_else = self.pop_bool()?;
                    if ignore_else { continue; }
                    let req_if_marker = if_marker;
                    self.consume_op_until(|op| {
                        if op == OpFrame::OpIf {
                            if_marker += 1;
                            false
                        } else if op == OpFrame::OpElse {
                            if_marker == req_if_marker
                        } else if op == OpFrame::OpEndIf {
                            let do_break = if_marker == req_if_marker;
                            if_marker -= 1;
                            do_break
                        } else {
                            false
                        }
                    })?;
                },
                OpFrame::OpElse => {
                    if !ignore_else { continue; }
                    let req_if_marker = if_marker;
                    self.consume_op_until(|op| {
                        if op == OpFrame::OpIf {
                            if_marker += 1;
                            false
                        } else if op == OpFrame::OpElse {
                            if_marker == req_if_marker
                        } else if op == OpFrame::OpEndIf {
                            let do_break = if_marker == req_if_marker;
                            if_marker -= 1;
                            do_break
                        } else {
                            false
                        }
                    })?;
                },
                OpFrame::OpEndIf => {
                    if_marker -= 1;
                }
            }
        }

        if if_marker > 0 {
            return Err(self.new_err(EvalErrType::UnexpectedEOF))
        }

        // Scripts must return true or false
        Ok(self.pop_bool()?)
    }

    fn consume_op_until<F>(&mut self, mut filter: F) -> Result<(), EvalErr>
            where F: FnMut(OpFrame) -> bool {
        loop {
            match self.consume_op()? {
                Some(op) => {
                    if filter(op) { break; }
                },
                None => {
                    return Err(self.new_err(EvalErrType::UnexpectedEOF))
                }
            }
        }

        Ok(())
    }

    fn consume_op(&mut self) -> Result<Option<OpFrame>, EvalErr> {
        if self.pos == self.byte_code.len() { return Ok(None) }
        let byte = self.byte_code[self.pos];
        self.pos += 1;

        match byte {
            o if o == Operand::PushFalse as u8 => Ok(Some(OpFrame::False)),
            o if o == Operand::PushTrue as u8 => Ok(Some(OpFrame::True)),
            o if o == Operand::PushPubKey as u8 => {
                let slice = self.pos .. self.pos + sign::PUBLICKEYBYTES;
                let key = match self.byte_code.get(slice) {
                    Some(slice) => {
                        self.pos += sign::PUBLICKEYBYTES;
                        PublicKey::from_bytes(slice).unwrap()
                    },
                    None => return Err(self.new_err(EvalErrType::UnexpectedEOF))
                };
                Ok(Some(OpFrame::PubKey(key)))
            },
            o if o == Operand::OpIf as u8 => Ok(Some(OpFrame::OpIf)),
            o if o == Operand::OpElse as u8 => Ok(Some(OpFrame::OpElse)),
            o if o == Operand::OpEndIf as u8 => Ok(Some(OpFrame::OpEndIf)),
            _ => Err(self.new_err(EvalErrType::UnknownOp))
        }
    }

    fn insert(&mut self, op: OpFrame) -> Result<(), EvalErr> {
        if self.stack.len() + 1 <= MAX_FRAME_STACK {
            self.stack.push(op);
            Ok(())
        } else {
            Err(self.new_err(EvalErrType::StackOverflow))
        }
    }

    fn pop_bool(&mut self) -> Result<bool, EvalErr> {
        let frame = self.pop()?;
        match frame {
            OpFrame::False => Ok(false),
            OpFrame::True => Ok(true),
            _ => Err(self.new_err(EvalErrType::InvalidCmp))
        }
    }

    fn pop(&mut self) -> Result<OpFrame, EvalErr> {
        self.stack.pop().ok_or_else(|| {
            self.new_err(EvalErrType::StackUnderflow)
        })
    }

    fn new_err(&mut self, err: EvalErrType) -> EvalErr {
        EvalErr::new(self.pos, err)
    }
}

#[cfg(test)]
mod tests {
    use crate::crypto::KeyPair;
    use super::builder::*;
    use super::*;

    #[test]
    fn true_only_script() {
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::True));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn false_only_script() {
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::False));
        assert!(!engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn if_script() {
        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::False)
                                .push(OpFrame::OpEndIf));
        assert!(!engine.eval().unwrap());
        assert!(engine.stack.is_empty());

        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::True)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn branch_if() {
        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::True)
                                .push(OpFrame::OpElse)
                                .push(OpFrame::False)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());

        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::False)
                                .push(OpFrame::OpIf)
                                .push(OpFrame::False)
                                .push(OpFrame::OpElse)
                                .push(OpFrame::True)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn nested_branch_if() {
        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf)
                                    .push(OpFrame::True)
                                    .push(OpFrame::OpIf)
                                        .push(OpFrame::True)
                                    .push(OpFrame::OpEndIf)
                                .push(OpFrame::OpElse)
                                    .push(OpFrame::False)
                                    .push(OpFrame::OpIf)
                                        .push(OpFrame::False)
                                    .push(OpFrame::OpEndIf)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());

        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::False)
                                .push(OpFrame::OpIf)
                                    .push(OpFrame::True)
                                    .push(OpFrame::OpIf)
                                        .push(OpFrame::False)
                                    .push(OpFrame::OpEndIf)
                                .push(OpFrame::OpElse)
                                    .push(OpFrame::True)
                                    .push(OpFrame::OpIf)
                                        .push(OpFrame::True)
                                    .push(OpFrame::OpEndIf)
                                .push(OpFrame::OpEndIf));
        assert!(engine.eval().unwrap());
        assert!(engine.stack.is_empty());
    }

    #[test]
    fn fail_invalid_stack_on_return() {
        let key = KeyPair::gen_keypair().0;
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::PubKey(key)));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::InvalidCmp);
    }

    #[test]
    fn fail_invalid_if_cmp() {
        let key = KeyPair::gen_keypair().0;
        let mut engine = ScriptEngine::from(Builder::new()
                                        .push(OpFrame::PubKey(key))
                                        .push(OpFrame::OpIf));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::InvalidCmp);
    }

    #[test]
    fn fail_unended_if() {
        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::True)
                                .push(OpFrame::OpIf));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::UnexpectedEOF);

        let mut engine = ScriptEngine::from(Builder::new()
                                .push(OpFrame::False)
                                .push(OpFrame::OpIf));
        assert_eq!(engine.eval().unwrap_err().err, EvalErrType::UnexpectedEOF);
    }
}
<|endoftext|>


<commit_before>fn empty() -> &'static str {
    ""
}

fn hello() -> &'static str {
    "Hello, world!"
}

fn hello_bytes() -> &'static [u8; 13] {
    b"Hello, world!"
}

fn hello_bytes_fat() -> &'static [u8] {
    b"Hello, world!"
}

fn fat_pointer_on_32_bit() {
    Some(5).expect("foo");
}

fn str_indexing() {
    let mut x = "Hello".to_string();
    let _v = &mut x[..3];  // Test IndexMut on String.
}

fn main() {
    assert_eq!(empty(), "");
    assert_eq!(hello(), "Hello, world!");
    assert_eq!(hello_bytes(), b"Hello, world!");
    assert_eq!(hello_bytes_fat(), b"Hello, world!");

    fat_pointer_on_32_bit(); // Should run without crashing.
    str_indexing();
}
<commit_msg>Auto merge of #1824 - Pointerbender:strings-test, r=RalfJung<commit_after>// compile-flags: -Zmiri-track-raw-pointers

fn empty() -> &'static str {
    ""
}

fn hello() -> &'static str {
    "Hello, world!"
}

fn hello_bytes() -> &'static [u8; 13] {
    b"Hello, world!"
}

fn hello_bytes_fat() -> &'static [u8] {
    b"Hello, world!"
}

fn fat_pointer_on_32_bit() {
    Some(5).expect("foo");
}

fn str_indexing() {
    let mut x = "Hello".to_string();
    let _v = &mut x[..3];  // Test IndexMut on String.
}

fn unique_aliasing() {
    // This is a regression test for the aliasing rules of a `Unique<T>` pointer.
    // At the time of writing this test case, Miri does not treat `Unique<T>`
    // pointers as a special case, these are treated like any other raw pointer.
    // However, there are existing Github issues which may lead to `Unique<T>`
    // becoming a special case through asserting unique ownership over the pointee:
    // - https://github.com/rust-lang/unsafe-code-guidelines/issues/258
    // - https://github.com/rust-lang/unsafe-code-guidelines/issues/262
    // Below, the calls to `String::remove` and `String::insert[_str]` follow
    // code paths that would trigger undefined behavior in case `Unique<T>`
    // would ever assert semantic ownership over the pointee. Internally,
    // these methods call `self.vec.as_ptr()` and `self.vec.as_mut_ptr()` on
    // the vector of bytes that are backing the `String`. That `Vec<u8>` holds a
    // `Unique<u8>` internally. The second call to `Vec::as_mut_ptr(&mut self)`
    // would then invalidate the pointers derived from `Vec::as_ptr(&self)`.
    // Note that as long as `Unique<T>` is treated like any other raw pointer,
    // this test case should pass. It is merely here as a canary test for
    // potential future undefined behavior.
    let mut x = String::from("Hello");
    assert_eq!(x.remove(0), 'H');
    x.insert(0, 'H');
    assert_eq!(x, "Hello");
    x.insert_str(x.len(), ", world!");
    assert_eq!(x, "Hello, world!");
}

fn main() {
    assert_eq!(empty(), "");
    assert_eq!(hello(), "Hello, world!");
    assert_eq!(hello_bytes(), b"Hello, world!");
    assert_eq!(hello_bytes_fat(), b"Hello, world!");

    fat_pointer_on_32_bit(); // Should run without crashing.
    str_indexing();
    unique_aliasing();
}
<|endoftext|>


<commit_before>//! Module for reading and writing Mobile-Originated (MO) SBD messages.
//!
//! Though messages technically come in two flavors, mobile originated and mobile terminated, we
//! only handle mobile originated messages in this library.

use std::cmp::Ordering;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use std::str;
use std::path::Path;

use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use chrono::{DateTime, Duration, TimeZone, UTC};

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

const INFORMATION_ELEMENT_HEADER_LENGTH: u16 = 3;
const MOBILE_ORIGINATED_HEADER_LENGTH: u16 = 28;
const ASCII_ZERO: u8 = 48;

/// The protocol number of an SBD message.
///
/// At this point, this can *only* be one.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
struct ProtocolRevisionNumber(u8);

impl ProtocolRevisionNumber {
    /// Returns true if this is a valid protocol revision number.
    pub fn valid(&self) -> bool {
        self.0 == 1
    }
}

/// The modem IMEI identifier.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Imei([u8; 15]);

impl Default for Imei {
    fn default() -> Imei {
        Imei([ASCII_ZERO; 15])
    }
}

impl Imei {
    /// Returns this IMEI number as a `str`.
    ///
    /// # Panics
    ///
    /// Panics if the string is not valid utf8.
    fn as_str(&self) -> &str {
        str::from_utf8(&self.0).unwrap()
    }
}

/// The status of a mobile-originated session.
///
/// The descriptions for these codes are taken directly from the DirectIP documentation.
#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable)]
pub enum SessionStatus {
    /// The SBD session completed successfully.
    Ok = 0,
    /// The MO message transfer, if any, was successful. The MT message queued at the GSS is too
    /// large to be transferred within a single SBD session.
    OkMobileTerminatedTooLarge = 1,
    /// The MO message transfer, if any, was successful. The reported location was determined to be
    /// of unacceptable quality. This value is only applicable to IMEIs using SBD protocol revision
    /// 1.
    OkLocationUnacceptableQuality = 2,
    /// The SBD session timed out before session completion.
    Timeout = 10,
    /// The MO message being transferred by the IMEI is too large to be transerred within a single
    /// SBD session.
    MobileOriginatedTooLarge = 12,
    /// An RF link loss ocurred during the SBD session.
    RFLinkLoss = 13,
    /// An IMEI protocol anomaly occurred during SBD session.
    IMEIProtocolAnomaly = 14,
    /// The IMEI is prohibited from accessing the GSS.
    Prohibited = 15,
    /// Unknown session status code.
    Unknown,
}

impl Default for SessionStatus {
    fn default() -> SessionStatus {
        SessionStatus::Unknown
    }
}

impl From<u8> for SessionStatus {
    fn from(n: u8) -> Self {
        match n {
            0 => SessionStatus::Ok,
            1 => SessionStatus::OkMobileTerminatedTooLarge,
            2 => SessionStatus::OkLocationUnacceptableQuality,
            10 => SessionStatus::Timeout,
            12 => SessionStatus::MobileOriginatedTooLarge,
            13 => SessionStatus::RFLinkLoss,
            14 => SessionStatus::IMEIProtocolAnomaly,
            15 => SessionStatus::Prohibited,
            _ => SessionStatus::Unknown,
        }
    }
}

/// A mobile-origined Iridium SBD message.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Message {
    protocol_revision_number: ProtocolRevisionNumber,
    cdr_reference: u32,
    imei: Imei,
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time_of_session: DateTime<UTC>,
    payload: Vec<u8>,
}

impl Default for Message {
    fn default() -> Message {
        Message {
            protocol_revision_number: ProtocolRevisionNumber(1),
            cdr_reference: Default::default(),
            imei: Default::default(),
            session_status: Default::default(),
            momsn: Default::default(),
            mtmsn: Default::default(),
            time_of_session: UTC.ymd(1970, 1, 1).and_hms(0, 0, 0),
            payload: Vec::new(),
        }
    }
}

impl PartialOrd for Message {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Message {
    fn cmp(&self, other: &Self) -> Ordering {
        self.time_of_session.cmp(&other.time_of_session)
    }
}

impl Message {
    /// Reads in a message from a file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mo::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::mo::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 = ProtocolRevisionNumber(try!(readable.read_u8()));
        if !message.protocol_revision_number.valid() {
            return Err(Error::InvalidProtocolRevisionNumber(message.protocol_revision_number.0));
        }
        let overall_message_length = try!(readable.read_u16::<BigEndian>());

        let mut information_elements: HashMap<InformationElementType, InformationElement> =
            HashMap::new();
        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();
            information_elements.insert(ie.id(), ie);
            if bytes_read >= overall_message_length {
                break;
            }
        }

        if try!(readable.take(1).read_to_end(&mut Vec::new())) != 0 {
            return Err(Error::Oversized);
        }

        let header =
            match information_elements.remove(&InformationElementType::MobileOriginatedHeader) {
                Some(ie) => ie,
                None => return Err(Error::MissingMobileOriginatedHeader),
            };
        let mut cursor = &mut Cursor::new(header.contents_ref());
        message.cdr_reference = try!(cursor.read_u32::<BigEndian>());
        let bytes_read = try!(cursor.take(message.imei.0.len() as u64).read(&mut message.imei.0));
        if bytes_read != message.imei.0.len() {
            return Err(Error::InvalidImei);
        }
        message.session_status = SessionStatus::from(try!(cursor.read_u8()));
        message.momsn = try!(cursor.read_u16::<BigEndian>());
        message.mtmsn = try!(cursor.read_u16::<BigEndian>());
        message.time_of_session = UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) +
                                  Duration::seconds(try!(cursor.read_u32::<BigEndian>()) as i64);

        let payload =
            match information_elements.remove(&InformationElementType::MobileOriginatedPayload) {
                Some(ie) => ie,
                None => return Err(Error::MissingMobileOriginatedPayload),
            };
        message.payload = payload.into_contents();

        if !information_elements.is_empty() {
            return Err(Error::UnhandledInformationElements(information_elements));
        }

        Ok(message)
    }

    /// Write this message back to a object that can `Write`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use sbd::mo::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// let mut cursor = Cursor::new(Vec::new());
    /// message.write_to(&mut cursor);
    /// ```
    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
        try!(w.write_u8(self.protocol_revision_number.0));
        try!(w.write_u16::<BigEndian>(self.overall_message_length()));
        try!(w.write_u8(InformationElementType::MobileOriginatedHeader as u8));
        try!(w.write_u16::<BigEndian>(MOBILE_ORIGINATED_HEADER_LENGTH));
        try!(w.write_u32::<BigEndian>(self.cdr_reference));
        try!(w.write_all(&self.imei.0));
        try!(w.write_u8(self.session_status as u8));
        try!(w.write_u16::<BigEndian>(self.momsn));
        try!(w.write_u16::<BigEndian>(self.mtmsn));
        try!(w.write_u32::<BigEndian>(self.time_of_session.timestamp() as u32));
        try!(w.write_u8(InformationElementType::MobileOriginatedPayload as u8));
        // TODO can we check to make sure the payload is of appropriate size?
        try!(w.write_u16::<BigEndian>(self.payload.len() as u16));
        try!(w.write_all(&self.payload[..]));
        Ok(())
    }

    /// Returns this message's protocol revision number.
    pub fn protocol_revision_number(&self) -> u8 {
        self.protocol_revision_number.0
    }
    /// Returns this message's IMEI number as a string.
    pub fn imei(&self) -> &str {
        self.imei.as_str()
    }
    /// Returns this message's cdr reference number (also called auto id).
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }
    /// Returns this message's session status.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }
    /// Returns this message's mobile originated message number.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }
    /// Returns this message's mobile terminated message number.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }
    /// Returns this message's time of session.
    pub fn time_of_session(&self) -> DateTime<UTC> {
        self.time_of_session
    }
    /// Returns a reference to this message's payload.
    pub fn payload_ref(&self) -> &Vec<u8> {
        &self.payload
    }
    /// Returns this message's payload as a str.
    pub fn payload_str(&self) -> Result<&str> {
        str::from_utf8(&self.payload).map_err(|e| Error::from(e))
    }

    /// Returns the overall message length, as is contained in the message's header.
    ///
    /// The whole message is actually three bytes longer than this value, thanks to the message
    /// header itself.
    fn overall_message_length(&self) -> u16 {
        INFORMATION_ELEMENT_HEADER_LENGTH + MOBILE_ORIGINATED_HEADER_LENGTH +
        INFORMATION_ELEMENT_HEADER_LENGTH + self.payload.len() as u16
    }
}

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

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

    use chrono::{TimeZone, UTC};

    #[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 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());
    }

    #[test]
    fn values() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1, message.protocol_revision_number());
        assert_eq!(1894516585, message.cdr_reference());
        assert_eq!("300234063904190", message.imei());
        assert_eq!(SessionStatus::Ok, message.session_status());
        assert_eq!(75, message.momsn());
        assert_eq!(0, message.mtmsn());
        assert_eq!(UTC.ymd(2015, 7, 9).and_hms(18, 15, 8),
                   message.time_of_session());
        assert_eq!("test message from pete",
                   str::from_utf8(message.payload_ref()).unwrap());
    }

    #[test]
    fn write() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        let mut cursor = Cursor::new(Vec::new());
        message.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        let message2 = Message::read_from(cursor).unwrap();
        assert_eq!(message, message2);
    }

    #[test]
    fn write_default() {
        let message: Message = Default::default();
        let mut cursor = Cursor::new(Vec::new());
        message.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        let message2 = Message::read_from(cursor).unwrap();
        assert_eq!(message, message2);
    }

    #[test]
    fn order() {
        let default: Message = Default::default();
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert!(message > default);
    }

    #[test]
    fn payload_str() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", message.payload_str().unwrap());
    }
}
<commit_msg>Add note about ordering by time of session.<commit_after>//! Module for reading and writing Mobile-Originated (MO) SBD messages.
//!
//! Though messages technically come in two flavors, mobile originated and mobile terminated, we
//! only handle mobile originated messages in this library.

use std::cmp::Ordering;
use std::collections::HashMap;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use std::str;
use std::path::Path;

use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use chrono::{DateTime, Duration, TimeZone, UTC};

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

const INFORMATION_ELEMENT_HEADER_LENGTH: u16 = 3;
const MOBILE_ORIGINATED_HEADER_LENGTH: u16 = 28;
const ASCII_ZERO: u8 = 48;

/// The protocol number of an SBD message.
///
/// At this point, this can *only* be one.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
struct ProtocolRevisionNumber(u8);

impl ProtocolRevisionNumber {
    /// Returns true if this is a valid protocol revision number.
    pub fn valid(&self) -> bool {
        self.0 == 1
    }
}

/// The modem IMEI identifier.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct Imei([u8; 15]);

impl Default for Imei {
    fn default() -> Imei {
        Imei([ASCII_ZERO; 15])
    }
}

impl Imei {
    /// Returns this IMEI number as a `str`.
    ///
    /// # Panics
    ///
    /// Panics if the string is not valid utf8.
    fn as_str(&self) -> &str {
        str::from_utf8(&self.0).unwrap()
    }
}

/// The status of a mobile-originated session.
///
/// The descriptions for these codes are taken directly from the DirectIP documentation.
#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable)]
pub enum SessionStatus {
    /// The SBD session completed successfully.
    Ok = 0,
    /// The MO message transfer, if any, was successful. The MT message queued at the GSS is too
    /// large to be transferred within a single SBD session.
    OkMobileTerminatedTooLarge = 1,
    /// The MO message transfer, if any, was successful. The reported location was determined to be
    /// of unacceptable quality. This value is only applicable to IMEIs using SBD protocol revision
    /// 1.
    OkLocationUnacceptableQuality = 2,
    /// The SBD session timed out before session completion.
    Timeout = 10,
    /// The MO message being transferred by the IMEI is too large to be transerred within a single
    /// SBD session.
    MobileOriginatedTooLarge = 12,
    /// An RF link loss ocurred during the SBD session.
    RFLinkLoss = 13,
    /// An IMEI protocol anomaly occurred during SBD session.
    IMEIProtocolAnomaly = 14,
    /// The IMEI is prohibited from accessing the GSS.
    Prohibited = 15,
    /// Unknown session status code.
    Unknown,
}

impl Default for SessionStatus {
    fn default() -> SessionStatus {
        SessionStatus::Unknown
    }
}

impl From<u8> for SessionStatus {
    fn from(n: u8) -> Self {
        match n {
            0 => SessionStatus::Ok,
            1 => SessionStatus::OkMobileTerminatedTooLarge,
            2 => SessionStatus::OkLocationUnacceptableQuality,
            10 => SessionStatus::Timeout,
            12 => SessionStatus::MobileOriginatedTooLarge,
            13 => SessionStatus::RFLinkLoss,
            14 => SessionStatus::IMEIProtocolAnomaly,
            15 => SessionStatus::Prohibited,
            _ => SessionStatus::Unknown,
        }
    }
}

/// A mobile-origined Iridium SBD message.
///
/// `Message`s can be ordered by time of session.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Message {
    protocol_revision_number: ProtocolRevisionNumber,
    cdr_reference: u32,
    imei: Imei,
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time_of_session: DateTime<UTC>,
    payload: Vec<u8>,
}

impl Default for Message {
    fn default() -> Message {
        Message {
            protocol_revision_number: ProtocolRevisionNumber(1),
            cdr_reference: Default::default(),
            imei: Default::default(),
            session_status: Default::default(),
            momsn: Default::default(),
            mtmsn: Default::default(),
            time_of_session: UTC.ymd(1970, 1, 1).and_hms(0, 0, 0),
            payload: Vec::new(),
        }
    }
}

impl PartialOrd for Message {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Message {
    fn cmp(&self, other: &Self) -> Ordering {
        self.time_of_session.cmp(&other.time_of_session)
    }
}

impl Message {
    /// Reads in a message from a file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mo::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::mo::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 = ProtocolRevisionNumber(try!(readable.read_u8()));
        if !message.protocol_revision_number.valid() {
            return Err(Error::InvalidProtocolRevisionNumber(message.protocol_revision_number.0));
        }
        let overall_message_length = try!(readable.read_u16::<BigEndian>());

        let mut information_elements: HashMap<InformationElementType, InformationElement> =
            HashMap::new();
        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();
            information_elements.insert(ie.id(), ie);
            if bytes_read >= overall_message_length {
                break;
            }
        }

        if try!(readable.take(1).read_to_end(&mut Vec::new())) != 0 {
            return Err(Error::Oversized);
        }

        let header =
            match information_elements.remove(&InformationElementType::MobileOriginatedHeader) {
                Some(ie) => ie,
                None => return Err(Error::MissingMobileOriginatedHeader),
            };
        let mut cursor = &mut Cursor::new(header.contents_ref());
        message.cdr_reference = try!(cursor.read_u32::<BigEndian>());
        let bytes_read = try!(cursor.take(message.imei.0.len() as u64).read(&mut message.imei.0));
        if bytes_read != message.imei.0.len() {
            return Err(Error::InvalidImei);
        }
        message.session_status = SessionStatus::from(try!(cursor.read_u8()));
        message.momsn = try!(cursor.read_u16::<BigEndian>());
        message.mtmsn = try!(cursor.read_u16::<BigEndian>());
        message.time_of_session = UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) +
                                  Duration::seconds(try!(cursor.read_u32::<BigEndian>()) as i64);

        let payload =
            match information_elements.remove(&InformationElementType::MobileOriginatedPayload) {
                Some(ie) => ie,
                None => return Err(Error::MissingMobileOriginatedPayload),
            };
        message.payload = payload.into_contents();

        if !information_elements.is_empty() {
            return Err(Error::UnhandledInformationElements(information_elements));
        }

        Ok(message)
    }

    /// Write this message back to a object that can `Write`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use sbd::mo::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// let mut cursor = Cursor::new(Vec::new());
    /// message.write_to(&mut cursor);
    /// ```
    pub fn write_to<W: Write>(&self, w: &mut W) -> Result<()> {
        try!(w.write_u8(self.protocol_revision_number.0));
        try!(w.write_u16::<BigEndian>(self.overall_message_length()));
        try!(w.write_u8(InformationElementType::MobileOriginatedHeader as u8));
        try!(w.write_u16::<BigEndian>(MOBILE_ORIGINATED_HEADER_LENGTH));
        try!(w.write_u32::<BigEndian>(self.cdr_reference));
        try!(w.write_all(&self.imei.0));
        try!(w.write_u8(self.session_status as u8));
        try!(w.write_u16::<BigEndian>(self.momsn));
        try!(w.write_u16::<BigEndian>(self.mtmsn));
        try!(w.write_u32::<BigEndian>(self.time_of_session.timestamp() as u32));
        try!(w.write_u8(InformationElementType::MobileOriginatedPayload as u8));
        // TODO can we check to make sure the payload is of appropriate size?
        try!(w.write_u16::<BigEndian>(self.payload.len() as u16));
        try!(w.write_all(&self.payload[..]));
        Ok(())
    }

    /// Returns this message's protocol revision number.
    pub fn protocol_revision_number(&self) -> u8 {
        self.protocol_revision_number.0
    }
    /// Returns this message's IMEI number as a string.
    pub fn imei(&self) -> &str {
        self.imei.as_str()
    }
    /// Returns this message's cdr reference number (also called auto id).
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }
    /// Returns this message's session status.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }
    /// Returns this message's mobile originated message number.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }
    /// Returns this message's mobile terminated message number.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }
    /// Returns this message's time of session.
    pub fn time_of_session(&self) -> DateTime<UTC> {
        self.time_of_session
    }
    /// Returns a reference to this message's payload.
    pub fn payload_ref(&self) -> &Vec<u8> {
        &self.payload
    }
    /// Returns this message's payload as a str.
    pub fn payload_str(&self) -> Result<&str> {
        str::from_utf8(&self.payload).map_err(|e| Error::from(e))
    }

    /// Returns the overall message length, as is contained in the message's header.
    ///
    /// The whole message is actually three bytes longer than this value, thanks to the message
    /// header itself.
    fn overall_message_length(&self) -> u16 {
        INFORMATION_ELEMENT_HEADER_LENGTH + MOBILE_ORIGINATED_HEADER_LENGTH +
        INFORMATION_ELEMENT_HEADER_LENGTH + self.payload.len() as u16
    }
}

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

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

    use chrono::{TimeZone, UTC};

    #[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 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());
    }

    #[test]
    fn values() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1, message.protocol_revision_number());
        assert_eq!(1894516585, message.cdr_reference());
        assert_eq!("300234063904190", message.imei());
        assert_eq!(SessionStatus::Ok, message.session_status());
        assert_eq!(75, message.momsn());
        assert_eq!(0, message.mtmsn());
        assert_eq!(UTC.ymd(2015, 7, 9).and_hms(18, 15, 8),
                   message.time_of_session());
        assert_eq!("test message from pete",
                   str::from_utf8(message.payload_ref()).unwrap());
    }

    #[test]
    fn write() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        let mut cursor = Cursor::new(Vec::new());
        message.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        let message2 = Message::read_from(cursor).unwrap();
        assert_eq!(message, message2);
    }

    #[test]
    fn write_default() {
        let message: Message = Default::default();
        let mut cursor = Cursor::new(Vec::new());
        message.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        let message2 = Message::read_from(cursor).unwrap();
        assert_eq!(message, message2);
    }

    #[test]
    fn order() {
        let default: Message = Default::default();
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert!(message > default);
    }

    #[test]
    fn payload_str() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", message.payload_str().unwrap());
    }
}
<|endoftext|>


<commit_before>//! Netlink module
//!
//! The netlink module contains the netlink-based management capabilities of
//! the socketcan crate. Quoth wikipedia:
//!
//!
//! > Netlink socket family is a Linux kernel interface used for inter-process
//! > communication (IPC) between both the kernel and userspace processes, and
//! > between different userspace processes, in a way similar to the Unix
//! > domain sockets.
//!
//!
//! The netlink module currently relies on the
//! [netlink-rs](https://crates.io/crates/netlink-rs), which has some
//! deficiencies. If those are not fixed, a reimplmentation of netlink-rs'
//! functionality might be required.

use byte_conv::As as AsBytes;
use libc::{self, c_char, c_ushort, c_int, c_uint};
use netlink_rs::socket::{Msg as NetlinkMessage, Socket as NetlinkSocket, NetlinkAddr,
                         Payload as NetlinkPayload, NlMsgHeader};
use netlink_rs::Protocol as NetlinkProtocol;
use nix;
use nix::net::if_::if_nametoindex;
use std::{mem, io};

// linux/rtnetlink.h
const RTM_NEWLINK: u16 = 16;

// linux/socket.h
const AF_UNSPEC: c_char = 0;

// linux/if.h; netdevice(7)
const IFF_UP: c_uint = 1;

/// Mirrors the `struct ifinfomsg` (see rtnetlink(7))
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct IfInfoMsg {
    /// Address family, should always be `AF_UNSPEC`
    family: c_char,

    /// Padding bytes, should be set to 0.
    _pad: c_char, // must be 0

    /// Device type (FIXME: ?)
    dev_type: c_ushort,

    /// The interface index, can be retrieved using `if_nametoindex` from the
    /// `nix` crate.
    index: c_int,

    /// Device flags (FIXME: ?)
    flags: c_uint,

    /// Change mask
    change: c_uint,
}

impl IfInfoMsg {
    fn new(if_index: i32, flags: c_uint, change: c_uint) -> IfInfoMsg {
        IfInfoMsg {
            family: AF_UNSPEC,
            _pad: 0,

            // dev_type: ARPHRD_CAN,
            dev_type: 0,
            index: if_index,
            flags: flags,

            change: change,
        }
    }
}

/// Sends a netlink message down a netlink socket, and checks if an ACK was
/// properly received.
fn send_and_read_ack(sock: &mut NetlinkSocket,
                     msg: NetlinkMessage,
                     dest: &NetlinkAddr)
                     -> io::Result<()> {

    let msg_len = msg.header().msg_length() as usize;
    let bytes_sent = sock.send(msg, dest)?;
    if bytes_sent != msg_len {
        return Err(io::Error::new(io::ErrorKind::Other, "Incomplete write"));
    }

    // receive all pending messages
    let (addr, msgs) = sock.recv()?;

    match msgs.into_iter().nth(0) {
        Some(msg) => {
            println!("Received Address: {:?}", addr);
            println!("Received Message: {:?}", msg);
            match *msg.payload() {
                NetlinkPayload::Ack(_) => (),
                NetlinkPayload::Err(errno, _) => {
                    return Err(io::Error::from_raw_os_error(-errno));
                }
                NetlinkPayload::None => {
                    return Err(io::Error::new(io::ErrorKind::InvalidData,
                                              "Received no payload when
                                              expecting an ACK"));

                }
                NetlinkPayload::Data(_) => {
                    return Err(io::Error::new(io::ErrorKind::InvalidData,
                                              "Received data when expecting an ACK"));
                }
            }
        }
        None => {
            return Err(io::Error::new(io::ErrorKind::UnexpectedEof,
                                      "ACK expected, but got nothing instead"))
        }
    }

    Ok(())

}

/// Opens a new netlink socket, bound to this process' PID
fn open_nl_route_socket() -> io::Result<NetlinkSocket> {
    let sock = NetlinkSocket::new(NetlinkProtocol::Route)?;

    // retrieve PID
    let pid = unsafe { libc::getpid() } as u32;
    let pid = 0;  // FIXME: is this necessary?

    // after opening the socket, bind it to be able to receive messages back
    // groups is set to 0, because we want no notifications
    let bind_addr = NetlinkAddr::new(pid, 0);
    sock.bind(bind_addr)?;

    Ok(sock)
}

/// SocketCAN interface
///
/// Controlled through the kernel's netlink interface, CAN devices can be
/// brought up or down or configured through this.
pub struct CanInterface {
    if_index: c_uint,
}

impl CanInterface {
    /// Open CAN interface by name
    ///
    /// Similar to `open_if`, but looks up the device by name instead
    pub fn open(ifname: &str) -> Result<CanInterface, nix::Error> {
        let if_index = if_nametoindex(ifname)?;
        Ok(CanInterface::open_if(if_index))
    }

    /// Open CAN interface
    ///
    /// Creates a new `CanInterface` instance. No actual "opening" is necessary
    /// or performed when calling this function.
    pub fn open_if(if_index: c_uint) -> CanInterface {
        CanInterface { if_index: if_index }
    }

    /// Bring down CAN interface
    pub fn bring_down(&self) -> io::Result<()> {
        let mut nl = open_nl_route_socket()?;

        // prepare message
        let mut header = NlMsgHeader::user_defined(RTM_NEWLINK, mem::size_of::<IfInfoMsg>() as u32);
        header.ack();

        // settings flags to 0 and change to IFF_UP will disable the IFF_UP flag
        let info = IfInfoMsg::new(self.if_index as i32, 0, IFF_UP);
        let msg = NetlinkMessage::new(header, NetlinkPayload::Data(info.as_bytes()));

        // send the message
        send_and_read_ack(&mut nl, msg, &NetlinkAddr::new(0, 0))
    }

    /// Bring up CAN interface
    pub fn bring_up(&self) -> io::Result<()> {
        let mut nl = open_nl_route_socket()?;

        let mut header = NlMsgHeader::user_defined(RTM_NEWLINK, mem::size_of::<IfInfoMsg>() as u32);
        header.ack();

        let info = IfInfoMsg::new(self.if_index as i32, IFF_UP, IFF_UP);
        let msg = NetlinkMessage::new(header, NetlinkPayload::Data(info.as_bytes()));

        // send the message
        send_and_read_ack(&mut nl, msg, &NetlinkAddr::new(0, 0))
    }
}
<commit_msg>Slightly better docs for CanInterface.<commit_after>//! Netlink module
//!
//! The netlink module contains the netlink-based management capabilities of
//! the socketcan crate. Quoth wikipedia:
//!
//!
//! > Netlink socket family is a Linux kernel interface used for inter-process
//! > communication (IPC) between both the kernel and userspace processes, and
//! > between different userspace processes, in a way similar to the Unix
//! > domain sockets.
//!
//!
//! The netlink module currently relies on the
//! [netlink-rs](https://crates.io/crates/netlink-rs), which has some
//! deficiencies. If those are not fixed, a reimplmentation of netlink-rs'
//! functionality might be required.

use byte_conv::As as AsBytes;
use libc::{self, c_char, c_ushort, c_int, c_uint};
use netlink_rs::socket::{Msg as NetlinkMessage, Socket as NetlinkSocket, NetlinkAddr,
                         Payload as NetlinkPayload, NlMsgHeader};
use netlink_rs::Protocol as NetlinkProtocol;
use nix;
use nix::net::if_::if_nametoindex;
use std::{mem, io};

// linux/rtnetlink.h
const RTM_NEWLINK: u16 = 16;

// linux/socket.h
const AF_UNSPEC: c_char = 0;

// linux/if.h; netdevice(7)
const IFF_UP: c_uint = 1;

/// Mirrors the `struct ifinfomsg` (see rtnetlink(7))
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct IfInfoMsg {
    /// Address family, should always be `AF_UNSPEC`
    family: c_char,

    /// Padding bytes, should be set to 0.
    _pad: c_char, // must be 0

    /// Device type (FIXME: ?)
    dev_type: c_ushort,

    /// The interface index, can be retrieved using `if_nametoindex` from the
    /// `nix` crate.
    index: c_int,

    /// Device flags (FIXME: ?)
    flags: c_uint,

    /// Change mask
    change: c_uint,
}

impl IfInfoMsg {
    fn new(if_index: i32, flags: c_uint, change: c_uint) -> IfInfoMsg {
        IfInfoMsg {
            family: AF_UNSPEC,
            _pad: 0,

            // dev_type: ARPHRD_CAN,
            dev_type: 0,
            index: if_index,
            flags: flags,

            change: change,
        }
    }
}

/// Sends a netlink message down a netlink socket, and checks if an ACK was
/// properly received.
fn send_and_read_ack(sock: &mut NetlinkSocket,
                     msg: NetlinkMessage,
                     dest: &NetlinkAddr)
                     -> io::Result<()> {

    let msg_len = msg.header().msg_length() as usize;
    let bytes_sent = sock.send(msg, dest)?;
    if bytes_sent != msg_len {
        return Err(io::Error::new(io::ErrorKind::Other, "Incomplete write"));
    }

    // receive all pending messages
    let (addr, msgs) = sock.recv()?;

    match msgs.into_iter().nth(0) {
        Some(msg) => {
            println!("Received Address: {:?}", addr);
            println!("Received Message: {:?}", msg);
            match *msg.payload() {
                NetlinkPayload::Ack(_) => (),
                NetlinkPayload::Err(errno, _) => {
                    return Err(io::Error::from_raw_os_error(-errno));
                }
                NetlinkPayload::None => {
                    return Err(io::Error::new(io::ErrorKind::InvalidData,
                                              "Received no payload when
                                              expecting an ACK"));

                }
                NetlinkPayload::Data(_) => {
                    return Err(io::Error::new(io::ErrorKind::InvalidData,
                                              "Received data when expecting an ACK"));
                }
            }
        }
        None => {
            return Err(io::Error::new(io::ErrorKind::UnexpectedEof,
                                      "ACK expected, but got nothing instead"))
        }
    }

    Ok(())

}

/// Opens a new netlink socket, bound to this process' PID
fn open_nl_route_socket() -> io::Result<NetlinkSocket> {
    let sock = NetlinkSocket::new(NetlinkProtocol::Route)?;

    // retrieve PID
    let pid = unsafe { libc::getpid() } as u32;
    let pid = 0;  // FIXME: is this necessary?

    // after opening the socket, bind it to be able to receive messages back
    // groups is set to 0, because we want no notifications
    let bind_addr = NetlinkAddr::new(pid, 0);
    sock.bind(bind_addr)?;

    Ok(sock)
}

/// SocketCAN interface
///
/// Controlled through the kernel's netlink interface, CAN devices can be
/// brought up or down or configured through this.
pub struct CanInterface {
    if_index: c_uint,
}

impl CanInterface {
    /// Open CAN interface by name
    ///
    /// Similar to `open_if`, but looks up the device by name instead
    pub fn open(ifname: &str) -> Result<CanInterface, nix::Error> {
        let if_index = if_nametoindex(ifname)?;
        Ok(CanInterface::open_if(if_index))
    }

    /// Open CAN interface
    ///
    /// Creates a new `CanInterface` instance. No actual "opening" is necessary
    /// or performed when calling this function.
    pub fn open_if(if_index: c_uint) -> CanInterface {
        CanInterface { if_index: if_index }
    }

    /// Bring down CAN interface
    ///
    /// Use a netlink control socket to set the interface status to "down".
    pub fn bring_down(&self) -> io::Result<()> {
        let mut nl = open_nl_route_socket()?;

        // prepare message
        let mut header = NlMsgHeader::user_defined(RTM_NEWLINK, mem::size_of::<IfInfoMsg>() as u32);
        header.ack();

        // settings flags to 0 and change to IFF_UP will disable the IFF_UP flag
        let info = IfInfoMsg::new(self.if_index as i32, 0, IFF_UP);
        let msg = NetlinkMessage::new(header, NetlinkPayload::Data(info.as_bytes()));

        // send the message
        send_and_read_ack(&mut nl, msg, &NetlinkAddr::new(0, 0))
    }

    /// Bring up CAN interface
    ///
    /// Brings the interface up by settings its "up" flag enabled via netlink.
    pub fn bring_up(&self) -> io::Result<()> {
        let mut nl = open_nl_route_socket()?;

        let mut header = NlMsgHeader::user_defined(RTM_NEWLINK, mem::size_of::<IfInfoMsg>() as u32);
        header.ack();

        let info = IfInfoMsg::new(self.if_index as i32, IFF_UP, IFF_UP);
        let msg = NetlinkMessage::new(header, NetlinkPayload::Data(info.as_bytes()));

        // send the message
        send_and_read_ack(&mut nl, msg, &NetlinkAddr::new(0, 0))
    }
}
<|endoftext|>


<commit_before>use blas::c::Layout;
use rand::distributions::normal::Normal;
use rand;
use rand::distributions::IndependentSample;

const LAYOUT: Layout = Layout::RowMajor;

/// Note: The length of the rows should be equivalent to
/// the number of neurons in the previous layer.
pub struct Matrix {
    rows: u16,
    columns: u16,
    entries: Vec<f32>,
    layout: Layout,
}

impl Matrix {
    pub fn random_new(rows: u16, columns: u16) -> Self {
        let mut rng = rand::thread_rng();
        let normal = Normal::new(0.0, 1.0);
        let mut entries = Vec::with_capacity((rows*columns) as usize);
        for _ in 0..entries.len() {
            entries.push(normal.ind_sample(&mut rng) as f32);
        }
        Matrix {
            rows: rows,
            columns: columns,
            entries: entries,
            layout: LAYOUT,
        }
    }

    pub fn as_slice(&self) -> &[f32] {
        &self.entries
    }

    pub fn as_mut_slice(&mut self) -> &mut[f32] {
        &mut self.entries
    }

    pub fn layout(&self) -> &Layout {
        &self.layout
    }
}
<commit_msg>removed matrix.rs<commit_after><|endoftext|>