<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");
            }

            use std::sync::TryLockError::*;
            let mut ttable = match ttable_mutex.try_lock() {
                Ok(table) => table,
                Err(Poisoned(_)) => panic!("Transposition table mutex is poisoned"),
                Err(WouldBlock) => {
                    panic!("Couldn't lock transposition table, is another AI thread still running?")
                }
            };
            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| {
        let zobrist = board.zobrist();
        ttable.set(zobrist, eval_type, depth, score);
    };

    match board.outcome() {
        Outcome::Draw => {
            // This only works because the draw Outcome does not consider draw by repetition
            set_ttable(ttable, EvalType::Exact, DRAW);
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Outcome::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;
        }
        Outcome::InProgress => {}
    }

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

        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;
                }
            }
        }
    }

    if depth == 0 {
        let score = quiescence_search(board, alpha, beta);
        set_pv(score, vec![]);
        score
    } else {
        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
    }
}

// TODO: use ttable here?
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),
    }
}
<commit_msg>Block on the TTable mutex<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| {
        let zobrist = board.zobrist();
        ttable.set(zobrist, eval_type, depth, score);
    };

    match board.outcome() {
        Outcome::Draw => {
            // This only works because the draw Outcome does not consider draw by repetition
            set_ttable(ttable, EvalType::Exact, DRAW);
            set_pv(DRAW, vec![]);
            return DRAW;
        }
        Outcome::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;
        }
        Outcome::InProgress => {}
    }

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

        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;
                }
            }
        }
    }

    if depth == 0 {
        let score = quiescence_search(board, alpha, beta);
        set_pv(score, vec![]);
        score
    } else {
        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
    }
}

// TODO: use ttable here?
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>use clap::ArgMatches;
use std::env;
use std::process::Command;

use settings::Settings;

pub fn open_shell_at_repo(settings: &Settings, args: &ArgMatches) -> Result<(), (String, i32)> {
    let repository = args.value_of("repository").unwrap();

    let shell = match env::var("SHELL") {
        Ok(val) => val,
        Err(e) => return Err((format!("Could not determine shell to use: {}", e), 1)),
    };

    let repo_path = settings.repo_path(&repository);
    if !repo_path.exists() {
        return Err((format!("Repo path does not exist: {}", repo_path.display()), 1));
    }

    match Command::new(shell).status() {
        Ok(exit_status) => {
            if exit_status.success() {
                Ok(())
            } else {
                let exit_code = exit_status.code().unwrap_or(1);
                Err((format!("Shell failed (exit {})", exit_code), exit_code))
            }
        },
        Err(e) => {
            Err((format!("Unable to execute shell: {}", e), 1))
        }
    }
}
<commit_msg>Set the path to the repository for the shell when using 'cd' subcommand<commit_after>use clap::ArgMatches;
use std::env;
use std::process::Command;

use settings::Settings;

pub fn open_shell_at_repo(settings: &Settings, args: &ArgMatches) -> Result<(), (String, i32)> {
    let repository = args.value_of("repository").unwrap();

    let shell = match env::var("SHELL") {
        Ok(val) => val,
        Err(e) => return Err((format!("Could not determine shell to use: {}", e), 1)),
    };

    let repo_path = settings.repo_path(&repository);
    if !repo_path.exists() {
        return Err((format!("Repo path does not exist: {}", repo_path.display()), 1));
    }

    match Command::new(shell)
                  .current_dir(repo_path)
                  .status() {
        Ok(exit_status) => {
            if exit_status.success() {
                Ok(())
            } else {
                let exit_code = exit_status.code().unwrap_or(1);
                Err((format!("Shell failed (exit {})", exit_code), exit_code))
            }
        },
        Err(e) => {
            Err((format!("Unable to execute shell: {}", e), 1))
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::{Read, Write, Result};

// The same as in the Rust's standard library.
const DEFAULT_BUF_SIZE: usize = 8 * 1024;

pub fn copy_until<R, W, P>(reader: &mut R, writer: &mut W, mut pred: P)
    -> Result<()>
where
    R: Read,
    W: Write,
    P: FnMut(&R, &W) -> bool,
{
    let mut buf = [0; DEFAULT_BUF_SIZE];
    loop {
        use std::io::ErrorKind::*;
        let len = match reader.read(&mut buf[..]) {
            Ok(0) => break,
            Ok(len) => len,
            Err(ref error) if error.kind() == Interrupted => continue,
            Err(error) => return Err(error),
        };

        writer.write_all(&buf[..len])?;
        if pred(reader, writer) {
            break;
        }
    }

    Ok(())
}

// TODO: Write tests.
<commit_msg>Write tests for the buffer copying utility.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::io::{Read, Write, Result};

// The same as in the Rust's standard library.
const DEFAULT_BUF_SIZE: usize = 8 * 1024;

pub fn copy_until<R, W, P>(reader: &mut R, writer: &mut W, mut pred: P)
    -> Result<()>
where
    R: Read,
    W: Write,
    P: FnMut(&R, &W) -> bool,
{
    let mut buf = [0; DEFAULT_BUF_SIZE];
    loop {
        use std::io::ErrorKind::*;
        let len = match reader.read(&mut buf[..]) {
            Ok(0) => break,
            Ok(len) => len,
            Err(ref error) if error.kind() == Interrupted => continue,
            Err(error) => return Err(error),
        };

        writer.write_all(&buf[..len])?;
        if pred(reader, writer) {
            break;
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_copy_until_with_empty_buffer() {
        let mut reader: &[u8] = b"";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| false).is_ok());
        assert_eq!(writer, b"");
    }

    #[test]
    fn test_copy_until_end() {
        let mut reader: &[u8] = b"foobar";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| false).is_ok());
        assert_eq!(writer, b"foobar");
    }

    #[test]
    fn test_copy_until_specific_size() {
        const limit: usize = 4 * 1024 * 1024;

        // An infinite stream of zeros (see explanation below).
        struct Null;

        impl std::io::Read for Null {

            fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
                // TODO: Rewriter with `slice::fill` once it stabilizes.
                for item in buf.iter_mut() {
                    *item = 0;
                }

                Ok(buf.len() as usize)
            }
        }

        let mut reader = Null;
        let mut writer = vec!();

        // This should verify that copying eventually stops after the condition
        // is met since the reader is inifite.
        assert! {
            copy_until(&mut reader, &mut writer, |_, writer| {
                writer.len() > limit
            }).is_ok()
        }

        assert!(writer.iter().all(|item| *item == 0));
        assert!(writer.len() > limit);
    }
}
<|endoftext|>


<commit_before>use std;
use std::ops::Add;
use std::io::{self, Write, Read};
use time::{self, Timespec, Duration};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        use std::io::{self, Write};
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n > kb.powf(4_f64) => {format!("{:.*} TG", 2, $n / kb.powf(4_f64))},
            $n if $n > kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n > kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 2, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &std::iter::repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
pub enum Units {
    Default,
    Bytes
}

pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    is_finish: bool,
    show_bar: bool,
    show_speed: bool,
    show_percent: bool,
    show_counter: bool,
    show_time_left: bool,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
}

impl ProgressBar {

    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: false,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_string();
            self.bar_current = v[2].to_string();
            self.bar_current_n = v[3].to_string();
            self.bar_remain = v[4].to_string();
            self.bar_end = v[5].to_string();
        }
    }

    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    fn draw(&self) {
        let width = 149;    // replace to -> get_tty_size()
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        // TODO: ADD KB FORMAT, SHOULD BE IN THE COUNTER TOO.
        if !self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = suffix + match self.units {
                Units::Default => &format!("{}/s ", speed as usize),
                Units::Bytes => &format!("{}/s ", speed as usize),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            prefix = format!("{} / {} ", self.current, self.total);
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.to_string();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(buf.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

// TODO: Implement io::Reader
<commit_msg>feat(pb): add bytes units format<commit_after>use std;
use std::ops::Add;
use std::io::{self, Write, Read};
use time::{self, Timespec, Duration};

macro_rules! printfl {
    ($($tt:tt)*) => {{
        use std::io::{self, Write};
        print!($($tt)*);
        io::stdout().flush().ok().expect("flush() fail");
    }}
}

macro_rules! kb_fmt {
    ($n: ident) => {{
        let kb = 1024f64;
        match $n {
            $n if $n > kb.powf(4_f64) => format!("{:.*} TG", 2, $n / kb.powf(4_f64)),
            $n if $n > kb.powf(3_f64) => format!("{:.*} GB", 2, $n / kb.powf(3_f64)),
            $n if $n > kb => format!("{:.*} KB", 2, $n / kb),
            _ => format!("{:.*} B", 0, $n)
        }
    }}
}

macro_rules! repeat {
    ($s: expr, $n: expr) => {{
        &std::iter::repeat($s).take($n).collect::<String>()
    }}
}

static FORMAT: &'static str = "[=>-]";

// Output type format, indicate which format wil be used in
// the speed box.
pub enum Units {
    Default,
    Bytes
}

pub struct ProgressBar {
    start_time: Timespec,
    units: Units,
    total: usize,
    current: usize,
    is_finish: bool,
    show_bar: bool,
    show_speed: bool,
    show_percent: bool,
    show_counter: bool,
    show_time_left: bool,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_remain: String,
    bar_end: String,
}

impl ProgressBar {

    pub fn new(total: usize) -> ProgressBar {
        let mut pb = ProgressBar {
            total: total,
            current: 0,
            start_time: time::get_time(),
            units: Units::Default,
            is_finish: false,
            show_bar: true,
            show_speed: false,
            show_percent: true,
            show_counter: true,
            show_time_left: true,
            bar_start: String::new(),
            bar_current: String::new(),
            bar_current_n: String::new(),
            bar_remain: String::new(),
            bar_end: String::new(),
        };
        pb.format(FORMAT);
        pb
    }

    pub fn set_units(&mut self, u: Units) {
        self.units = u;
    }

    fn format(&mut self, fmt: &str) {
        if fmt.len() >= 5 {
            let v: Vec<&str> = fmt.split("").collect();
            self.bar_start = v[1].to_string();
            self.bar_current = v[2].to_string();
            self.bar_current_n = v[3].to_string();
            self.bar_remain = v[4].to_string();
            self.bar_end = v[5].to_string();
        }
    }

    pub fn add(&mut self, i: usize) -> usize {
        self.current += i;
        if self.current <= self.total {
            self.draw()
        };
        self.current
    }

    fn draw(&self) {
        let width = 145;    // replace to -> get_tty_size()
        let mut base = String::new();
        let mut suffix = String::new();
        let mut prefix = String::new();
        let mut out;
        // precent box
        if self.show_percent {
            let percent = self.current as f64 / (self.total as f64 / 100f64);
            suffix = suffix + &format!(" {:.*} % ", 2, percent);
        }
        // speed box
        if !self.show_speed {
            let from_start = (time::get_time() - self.start_time).num_nanoseconds().unwrap() as f64;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as f64;
            let speed = self.current as f64 / (from_start / sec_nano);
            suffix = match self.units {
                Units::Default => suffix + &format!("{}/s ", speed),
                Units::Bytes => suffix + &format!("{}/s ", kb_fmt!(speed)),
            };
        }
        // time left box
        if self.show_time_left {
            let from_start = time::get_time() - self.start_time;
            let sec_nano = Duration::seconds(1).num_nanoseconds().unwrap() as i32;
            let per_entry = from_start / self.current as i32;
            let mut left = per_entry * (self.total - self.current) as i32;
            left = (left / sec_nano) * sec_nano;
            if left.num_seconds() > 0 {
                if left.num_seconds() < Duration::minutes(1).num_seconds() {
                    suffix = suffix + &format!("{}s", left.num_seconds());
                } else {
                    suffix = suffix + &format!("{}m", left.num_minutes());
                }
            }
        }
        // counter box
        if self.show_counter {
            let (c, t) = (self.current as f64, self.total as f64);
            prefix = match self.units {
                Units::Default => format!("{} / {} ", c, t),
                Units::Bytes => format!("{} / {} ", kb_fmt!(c), kb_fmt!(t)),
            };
        }
        // bar box
        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let curr_count =
                    ((self.current as f64 / self.total as f64) * size as f64).ceil() as usize;
                let rema_count = size - curr_count;
                base = self.bar_start.to_string();
                if rema_count > 0 {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count - 1) +
                           &self.bar_current_n;
                } else {
                    base = base + repeat!(self.bar_current.as_ref(), curr_count);
                }
                base = base + repeat!(self.bar_remain.as_ref(), rema_count) + &self.bar_end;
            }
        }
        out = prefix + &base + &suffix;
        // pad
        if out.len() < width {
            let gap = width - out.len();
            out = out + repeat!(" ", gap);
        }
        // print
        printfl!("\r{}", out);
    }

    pub fn finish(&mut self) {
        if self.current < self.total {
            self.current = self.total;
            self.draw();
        }
        println!("");
        self.is_finish = true;
    }
}

// Implement io::Writer
impl Write for ProgressBar {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.add(n);
        Ok(buf.len())
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

// TODO: Implement io::Reader
<|endoftext|>


<commit_before>//! EVM Program Counter.

#[cfg(not(feature = "std"))]
use alloc::Vec;

use bigint::M256;
use util::opcode::Opcode;
#[cfg(feature = "std")] use std::cmp::min;
#[cfg(feature = "std")] use std::marker::PhantomData;
#[cfg(not(feature = "std"))] use core::cmp::min;
#[cfg(not(feature = "std"))] use core::marker::PhantomData;

use super::Patch;
use super::errors::OnChainError;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(missing_docs)]
/// Instructions for the program counter. This is the same as `Opcode`
/// except `PUSH`, which might take longer length.
pub enum Instruction {
    STOP, ADD, MUL, SUB, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP,
    SIGNEXTEND, LT, GT, SLT, SGT, EQ, ISZERO, AND, OR, XOR, NOT, BYTE,
    SHA3, ADDRESS, BALANCE, ORIGIN, CALLER, CALLVALUE, CALLDATALOAD,
    CALLDATASIZE, CALLDATACOPY, CODESIZE, CODECOPY, GASPRICE,
    EXTCODESIZE, EXTCODECOPY, BLOCKHASH, COINBASE, TIMESTAMP, NUMBER,
    DIFFICULTY, GASLIMIT, POP, MLOAD, MSTORE, MSTORE8, SLOAD, SSTORE,
    JUMP, JUMPI, PC, MSIZE, GAS, JUMPDEST, CREATE, CALL, CALLCODE,
    RETURN, DELEGATECALL, SUICIDE,

    PUSH(M256),
    DUP(usize),
    SWAP(usize),
    LOG(usize),
}

/// Represents a program counter in EVM.
pub struct PC<P: Patch> {
    position: usize,
    code: Vec<u8>,
    valids: Vec<bool>,
    _patch: PhantomData<P>,
}

impl<P: Patch> PC<P> {
    /// Create a new program counter from the given code.
    pub fn new(code: &[u8]) -> Self {
        let code: Vec<u8> = code.into();
        let mut valids: Vec<bool> = Vec::with_capacity(code.len());
        valids.resize(code.len(), false);

        let mut i = 0;
        while i < code.len() {
            let opcode: Opcode = code[i].into();
            match opcode {
                Opcode::JUMPDEST => {
                    valids[i] = true;
                    i = i + 1;
                },
                Opcode::PUSH(v) => {
                    i = i + v + 1;
                },
                _ => {
                    i = i + 1;
                }
            }
        }

        PC {
            code,
            position: 0,
            valids: valids,
            _patch: PhantomData,
        }
    }

    fn read_bytes(&self, from_position: usize, byte_count: usize) -> Result<M256, OnChainError> {
        if from_position > self.code.len() {
            return Err(OnChainError::PCOverflow);
        }
        let position = from_position;
        let max = min(position.saturating_add(byte_count), self.code.len());
        Ok(M256::from(&self.code[position..max]))
    }

    /// Get the code bytearray.
    pub fn code(&self) -> &[u8] {
        &self.code
    }

    /// Jump to a position in the code. The destination must be valid
    /// to jump.
    pub fn jump(&mut self, position: usize) -> Result<(), OnChainError> {
        if position >= self.code.len() {
            return Err(OnChainError::PCOverflow);
        }

        if !self.valids[position] {
            return Err(OnChainError::BadJumpDest);
        }

        self.position = position;
        Ok(())
    }

    /// Get the current program counter position.
    pub fn position(&self) -> usize {
        self.position
    }

    /// Get the current opcode position. Should only be used when debugging.
    pub fn opcode_position(&self) -> usize {
        let mut o = 0;
        let mut i = 0;
        while i <= self.position {
            let opcode: Opcode = self.code[i].into();
            match opcode {
                Opcode::PUSH(v) => {
                    i = i + v + 1;
                },
                _ => {
                    i = i + 1;
                }
            }
            o = o + 1;
        }
        o
    }

    /// Returns `true` if the position is a valid jump destination. If
    /// not, returns `false`.
    pub fn is_valid(&self, position: usize) -> bool {
        if position >= self.code.len() {
            return false;
        }

        if !self.valids[position] {
            return false;
        }

        return true;
    }

    /// Check whether the PC is ended. Next `read` on this PC would
    /// result in `PCError::PCOverflow`.
    pub fn is_end(&self) -> bool {
        self.position == self.code.len()
    }

    /// Peek the next instruction.
    pub fn peek(&self) -> Result<Instruction, OnChainError> {
        let position = self.position;
        if position >= self.code.len() {
            return Err(OnChainError::PCOverflow);
        }
        let opcode: Opcode = self.code[position].into();
        Ok(match opcode {
            Opcode::STOP => Instruction::STOP,
            Opcode::ADD => Instruction::ADD,
            Opcode::MUL => Instruction::MUL,
            Opcode::SUB => Instruction::SUB,
            Opcode::DIV => Instruction::DIV,
            Opcode::SDIV => Instruction::SDIV,
            Opcode::MOD => Instruction::MOD,
            Opcode::SMOD => Instruction::SMOD,
            Opcode::ADDMOD => Instruction::ADDMOD,
            Opcode::MULMOD => Instruction::MULMOD,
            Opcode::EXP => Instruction::EXP,
            Opcode::SIGNEXTEND => Instruction::SIGNEXTEND,

            Opcode::LT => Instruction::LT,
            Opcode::GT => Instruction::GT,
            Opcode::SLT => Instruction::SLT,
            Opcode::SGT => Instruction::SGT,
            Opcode::EQ => Instruction::EQ,
            Opcode::ISZERO => Instruction::ISZERO,
            Opcode::AND => Instruction::AND,
            Opcode::OR => Instruction::OR,
            Opcode::XOR => Instruction::XOR,
            Opcode::NOT => Instruction::NOT,
            Opcode::BYTE => Instruction::BYTE,

            Opcode::SHA3 => Instruction::SHA3,

            Opcode::ADDRESS => Instruction::ADDRESS,
            Opcode::BALANCE => Instruction::BALANCE,
            Opcode::ORIGIN => Instruction::ORIGIN,
            Opcode::CALLER => Instruction::CALLER,
            Opcode::CALLVALUE => Instruction::CALLVALUE,
            Opcode::CALLDATALOAD => Instruction::CALLDATALOAD,
            Opcode::CALLDATASIZE => Instruction::CALLDATASIZE,
            Opcode::CALLDATACOPY => Instruction::CALLDATACOPY,
            Opcode::CODESIZE => Instruction::CODESIZE,
            Opcode::CODECOPY => Instruction::CODECOPY,
            Opcode::GASPRICE => Instruction::GASPRICE,
            Opcode::EXTCODESIZE => Instruction::EXTCODESIZE,
            Opcode::EXTCODECOPY => Instruction::EXTCODECOPY,

            Opcode::BLOCKHASH => Instruction::BLOCKHASH,
            Opcode::COINBASE => Instruction::COINBASE,
            Opcode::TIMESTAMP => Instruction::TIMESTAMP,
            Opcode::NUMBER => Instruction::NUMBER,
            Opcode::DIFFICULTY => Instruction::DIFFICULTY,
            Opcode::GASLIMIT => Instruction::GASLIMIT,

            Opcode::POP => Instruction::POP,
            Opcode::MLOAD => Instruction::MLOAD,
            Opcode::MSTORE => Instruction::MSTORE,
            Opcode::MSTORE8 => Instruction::MSTORE8,
            Opcode::SLOAD => Instruction::SLOAD,
            Opcode::SSTORE => Instruction::SSTORE,
            Opcode::JUMP => Instruction::JUMP,
            Opcode::JUMPI => Instruction::JUMPI,
            Opcode::PC => Instruction::PC,
            Opcode::MSIZE => Instruction::MSIZE,
            Opcode::GAS => Instruction::GAS,
            Opcode::JUMPDEST => Instruction::JUMPDEST,

            Opcode::PUSH(v) => {
                let param = self.read_bytes(position + 1, v)?;
                Instruction::PUSH(param)
            },

            Opcode::DUP(v) => Instruction::DUP(v),
            Opcode::SWAP(v) => Instruction::SWAP(v),
            Opcode::LOG(v) => Instruction::LOG(v),

            Opcode::CREATE => Instruction::CREATE,
            Opcode::CALL => Instruction::CALL,
            Opcode::CALLCODE => Instruction::CALLCODE,
            Opcode::RETURN => Instruction::RETURN,
            Opcode::DELEGATECALL => {
                if P::has_delegate_call() {
                    Instruction::DELEGATECALL
                } else {
                    return Err(OnChainError::InvalidOpcode);
                }
            },

            Opcode::INVALID => {
                return Err(OnChainError::InvalidOpcode);
            },
            Opcode::SUICIDE => Instruction::SUICIDE,
        })
    }

    /// Read the next instruction and step the program counter.
    pub fn read(&mut self) -> Result<Instruction, OnChainError> {
        let result = self.peek()?;
        let opcode: Opcode = self.code[self.position].into();
        match opcode {
            Opcode::PUSH(v) => {
                self.position = min(self.position + v + 1, self.code.len());
            },
            _ => {
                self.position = self.position + 1;
            },
        }
        Ok(result)
    }
}
<commit_msg>Set up PC at given location<commit_after>//! EVM Program Counter.

#[cfg(not(feature = "std"))]
use alloc::Vec;

use bigint::M256;
use util::opcode::Opcode;
#[cfg(feature = "std")] use std::cmp::min;
#[cfg(feature = "std")] use std::marker::PhantomData;
#[cfg(not(feature = "std"))] use core::cmp::min;
#[cfg(not(feature = "std"))] use core::marker::PhantomData;

use super::Patch;
use super::errors::OnChainError;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(missing_docs)]
/// Instructions for the program counter. This is the same as `Opcode`
/// except `PUSH`, which might take longer length.
pub enum Instruction {
    STOP, ADD, MUL, SUB, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP,
    SIGNEXTEND, LT, GT, SLT, SGT, EQ, ISZERO, AND, OR, XOR, NOT, BYTE,
    SHA3, ADDRESS, BALANCE, ORIGIN, CALLER, CALLVALUE, CALLDATALOAD,
    CALLDATASIZE, CALLDATACOPY, CODESIZE, CODECOPY, GASPRICE,
    EXTCODESIZE, EXTCODECOPY, BLOCKHASH, COINBASE, TIMESTAMP, NUMBER,
    DIFFICULTY, GASLIMIT, POP, MLOAD, MSTORE, MSTORE8, SLOAD, SSTORE,
    JUMP, JUMPI, PC, MSIZE, GAS, JUMPDEST, CREATE, CALL, CALLCODE,
    RETURN, DELEGATECALL, SUICIDE,

    PUSH(M256),
    DUP(usize),
    SWAP(usize),
    LOG(usize),
}

/// Represents a program counter in EVM.
pub struct PC<P: Patch> {
    position: usize,
    code: Vec<u8>,
    valids: Vec<bool>,
    _patch: PhantomData<P>,
}

impl<P: Patch> PC<P> {
    /// Create a new program counter from the given code.
    pub fn new(code: &[u8]) -> Self {
        let code: Vec<u8> = code.into();
        let mut valids: Vec<bool> = Vec::with_capacity(code.len());
        valids.resize(code.len(), false);

        let mut i = 0;
        while i < code.len() {
            let opcode: Opcode = code[i].into();
            match opcode {
                Opcode::JUMPDEST => {
                    valids[i] = true;
                    i = i + 1;
                },
                Opcode::PUSH(v) => {
                    i = i + v + 1;
                },
                _ => {
                    i = i + 1;
                }
            }
        }

        PC {
            code,
            position: 0,
            valids: valids,
            _patch: PhantomData,
        }
    }

    /// Create a new program counter at given position.
    pub(crate) fn with_position(code: &[u8], position: usize) -> Self {
        let mut pc = Self::new(code);
        pc.position = position;
        pc
    }

    fn read_bytes(&self, from_position: usize, byte_count: usize) -> Result<M256, OnChainError> {
        if from_position > self.code.len() {
            return Err(OnChainError::PCOverflow);
        }
        let position = from_position;
        let max = min(position.saturating_add(byte_count), self.code.len());
        Ok(M256::from(&self.code[position..max]))
    }

    /// Get the code bytearray.
    pub fn code(&self) -> &[u8] {
        &self.code
    }

    /// Jump to a position in the code. The destination must be valid
    /// to jump.
    pub fn jump(&mut self, position: usize) -> Result<(), OnChainError> {
        if position >= self.code.len() {
            return Err(OnChainError::PCOverflow);
        }

        if !self.valids[position] {
            return Err(OnChainError::BadJumpDest);
        }

        self.position = position;
        Ok(())
    }

    /// Get the current program counter position.
    pub fn position(&self) -> usize {
        self.position
    }

    /// Get the current opcode position. Should only be used when debugging.
    pub fn opcode_position(&self) -> usize {
        let mut o = 0;
        let mut i = 0;
        while i <= self.position {
            let opcode: Opcode = self.code[i].into();
            match opcode {
                Opcode::PUSH(v) => {
                    i = i + v + 1;
                },
                _ => {
                    i = i + 1;
                }
            }
            o = o + 1;
        }
        o
    }

    /// Returns `true` if the position is a valid jump destination. If
    /// not, returns `false`.
    pub fn is_valid(&self, position: usize) -> bool {
        if position >= self.code.len() {
            return false;
        }

        if !self.valids[position] {
            return false;
        }

        return true;
    }

    /// Check whether the PC is ended. Next `read` on this PC would
    /// result in `PCError::PCOverflow`.
    pub fn is_end(&self) -> bool {
        self.position == self.code.len()
    }

    /// Peek the next instruction.
    pub fn peek(&self) -> Result<Instruction, OnChainError> {
        let position = self.position;
        if position >= self.code.len() {
            return Err(OnChainError::PCOverflow);
        }
        let opcode: Opcode = self.code[position].into();
        Ok(match opcode {
            Opcode::STOP => Instruction::STOP,
            Opcode::ADD => Instruction::ADD,
            Opcode::MUL => Instruction::MUL,
            Opcode::SUB => Instruction::SUB,
            Opcode::DIV => Instruction::DIV,
            Opcode::SDIV => Instruction::SDIV,
            Opcode::MOD => Instruction::MOD,
            Opcode::SMOD => Instruction::SMOD,
            Opcode::ADDMOD => Instruction::ADDMOD,
            Opcode::MULMOD => Instruction::MULMOD,
            Opcode::EXP => Instruction::EXP,
            Opcode::SIGNEXTEND => Instruction::SIGNEXTEND,

            Opcode::LT => Instruction::LT,
            Opcode::GT => Instruction::GT,
            Opcode::SLT => Instruction::SLT,
            Opcode::SGT => Instruction::SGT,
            Opcode::EQ => Instruction::EQ,
            Opcode::ISZERO => Instruction::ISZERO,
            Opcode::AND => Instruction::AND,
            Opcode::OR => Instruction::OR,
            Opcode::XOR => Instruction::XOR,
            Opcode::NOT => Instruction::NOT,
            Opcode::BYTE => Instruction::BYTE,

            Opcode::SHA3 => Instruction::SHA3,

            Opcode::ADDRESS => Instruction::ADDRESS,
            Opcode::BALANCE => Instruction::BALANCE,
            Opcode::ORIGIN => Instruction::ORIGIN,
            Opcode::CALLER => Instruction::CALLER,
            Opcode::CALLVALUE => Instruction::CALLVALUE,
            Opcode::CALLDATALOAD => Instruction::CALLDATALOAD,
            Opcode::CALLDATASIZE => Instruction::CALLDATASIZE,
            Opcode::CALLDATACOPY => Instruction::CALLDATACOPY,
            Opcode::CODESIZE => Instruction::CODESIZE,
            Opcode::CODECOPY => Instruction::CODECOPY,
            Opcode::GASPRICE => Instruction::GASPRICE,
            Opcode::EXTCODESIZE => Instruction::EXTCODESIZE,
            Opcode::EXTCODECOPY => Instruction::EXTCODECOPY,

            Opcode::BLOCKHASH => Instruction::BLOCKHASH,
            Opcode::COINBASE => Instruction::COINBASE,
            Opcode::TIMESTAMP => Instruction::TIMESTAMP,
            Opcode::NUMBER => Instruction::NUMBER,
            Opcode::DIFFICULTY => Instruction::DIFFICULTY,
            Opcode::GASLIMIT => Instruction::GASLIMIT,

            Opcode::POP => Instruction::POP,
            Opcode::MLOAD => Instruction::MLOAD,
            Opcode::MSTORE => Instruction::MSTORE,
            Opcode::MSTORE8 => Instruction::MSTORE8,
            Opcode::SLOAD => Instruction::SLOAD,
            Opcode::SSTORE => Instruction::SSTORE,
            Opcode::JUMP => Instruction::JUMP,
            Opcode::JUMPI => Instruction::JUMPI,
            Opcode::PC => Instruction::PC,
            Opcode::MSIZE => Instruction::MSIZE,
            Opcode::GAS => Instruction::GAS,
            Opcode::JUMPDEST => Instruction::JUMPDEST,

            Opcode::PUSH(v) => {
                let param = self.read_bytes(position + 1, v)?;
                Instruction::PUSH(param)
            },

            Opcode::DUP(v) => Instruction::DUP(v),
            Opcode::SWAP(v) => Instruction::SWAP(v),
            Opcode::LOG(v) => Instruction::LOG(v),

            Opcode::CREATE => Instruction::CREATE,
            Opcode::CALL => Instruction::CALL,
            Opcode::CALLCODE => Instruction::CALLCODE,
            Opcode::RETURN => Instruction::RETURN,
            Opcode::DELEGATECALL => {
                if P::has_delegate_call() {
                    Instruction::DELEGATECALL
                } else {
                    return Err(OnChainError::InvalidOpcode);
                }
            },

            Opcode::INVALID => {
                return Err(OnChainError::InvalidOpcode);
            },
            Opcode::SUICIDE => Instruction::SUICIDE,
        })
    }

    /// Read the next instruction and step the program counter.
    pub fn read(&mut self) -> Result<Instruction, OnChainError> {
        let result = self.peek()?;
        let opcode: Opcode = self.code[self.position].into();
        match opcode {
            Opcode::PUSH(v) => {
                self.position = min(self.position + v + 1, self.code.len());
            },
            _ => {
                self.position = self.position + 1;
            },
        }
        Ok(result)
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Joseph Crail <jbcrail@gmail.com>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

#[cfg(unix)]
use libc::{
    mode_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP,
    S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH,
    S_IXUSR,
};
use std::borrow::Cow;
use std::env;
use std::fs;
#[cfg(target_os = "redox")]
use std::io;
use std::io::Result as IOResult;
use std::io::{Error, ErrorKind};
#[cfg(any(unix, target_os = "redox"))]
use std::os::unix::fs::MetadataExt;
use std::path::{Component, Path, PathBuf};

#[cfg(unix)]
#[macro_export]
macro_rules! has {
    ($mode:expr, $perm:expr) => {
        $mode & $perm != 0
    };
}

pub fn resolve_relative_path(path: &Path) -> Cow<Path> {
    if path.components().all(|e| e != Component::ParentDir) {
        return path.into();
    }
    let root = Component::RootDir.as_os_str();
    let mut result = env::current_dir().unwrap_or_else(|_| PathBuf::from(root));
    for comp in path.components() {
        match comp {
            Component::ParentDir => {
                if let Ok(p) = result.read_link() {
                    result = p;
                }
                result.pop();
            }
            Component::CurDir => (),
            Component::RootDir | Component::Normal(_) | Component::Prefix(_) => {
                result.push(comp.as_os_str())
            }
        }
    }
    result.into()
}

/// Controls how symbolic links should be handled when canonicalizing a path.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CanonicalizeMode {
    /// Do not resolve any symbolic links.
    None,

    /// Resolve all symbolic links.
    Normal,

    /// Resolve symbolic links, ignoring errors on the final component.
    Existing,

    /// Resolve symbolic links, ignoring errors on the non-final components.
    Missing,
}

// copied from https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
// both projects are MIT https://github.com/rust-lang/cargo/blob/master/LICENSE-MIT
// for std impl progress see rfc https://github.com/rust-lang/rfcs/issues/2208
// replace this once that lands
pub fn normalize_path(path: &Path) -> PathBuf {
    let mut components = path.components().peekable();
    let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
        components.next();
        PathBuf::from(c.as_os_str())
    } else {
        PathBuf::new()
    };

    for component in components {
        match component {
            Component::Prefix(..) => unreachable!(),
            Component::RootDir => {
                ret.push(component.as_os_str());
            }
            Component::CurDir => {}
            Component::ParentDir => {
                ret.pop();
            }
            Component::Normal(c) => {
                ret.push(c);
            }
        }
    }
    ret
}

fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
    const MAX_LINKS_FOLLOWED: u32 = 255;
    let mut followed = 0;
    let mut result = original.as_ref().to_path_buf();
    loop {
        if followed == MAX_LINKS_FOLLOWED {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                "maximum links followed",
            ));
        }

        if !fs::symlink_metadata(&result)?.file_type().is_symlink() {
            break;
        }

        followed += 1;
        let path = fs::read_link(&result)?;
        result.pop();
        result.push(path);
    }
    Ok(result)
}

/// Return the canonical, absolute form of a path.
///
/// This function is a generalization of [`std::fs::canonicalize`] that
/// allows controlling how symbolic links are resolved and how to deal
/// with missing components. It returns the canonical, absolute form of
/// a path. The `can_mode` parameter controls how symbolic links are
/// resolved:
///
/// * [`CanonicalizeMode::Normal`] makes this function behave like
///   [`std::fs::canonicalize`], resolving symbolic links and returning
///   an error if the path does not exist.
/// * [`CanonicalizeMode::Missing`] makes this function ignore non-final
///   components of the path that could not be resolved.
/// * [`CanonicalizeMode::Existing`] makes this function return an error
///   if the final component of the path does not exist.
/// * [`CanonicalizeMode::None`] makes this function not try to resolve
///   any symbolic links.
///
pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) -> IOResult<PathBuf> {
    // Create an absolute path
    let original = original.as_ref();
    let original = if original.is_absolute() {
        original.to_path_buf()
    } else {
        dunce::canonicalize(env::current_dir().unwrap())
            .unwrap()
            .join(original)
    };

    let mut result = PathBuf::new();
    let mut parts = vec![];

    // Split path by directory separator; add prefix (Windows-only) and root
    // directory to final path buffer; add remaining parts to temporary
    // vector for canonicalization.
    for part in original.components() {
        match part {
            Component::Prefix(_) | Component::RootDir => {
                result.push(part.as_os_str());
            }
            Component::CurDir => (),
            Component::ParentDir => {
                parts.pop();
            }
            Component::Normal(_) => {
                parts.push(part.as_os_str());
            }
        }
    }

    // Resolve the symlinks where possible
    if !parts.is_empty() {
        for part in parts[..parts.len() - 1].iter() {
            result.push(part);

            if can_mode == CanonicalizeMode::None {
                continue;
            }

            match resolve(&result) {
                Err(_) if can_mode == CanonicalizeMode::Missing => continue,
                Err(e) => return Err(e),
                Ok(path) => {
                    result.pop();
                    result.push(path);
                }
            }
        }

        result.push(parts.last().unwrap());

        if can_mode == CanonicalizeMode::None {
            return Ok(result);
        }

        match resolve(&result) {
            Err(e) if can_mode == CanonicalizeMode::Existing => {
                return Err(e);
            }
            Ok(path) => {
                result.pop();
                result.push(path);
            }
            Err(_) => (),
        }
    }
    Ok(result)
}

#[cfg(not(unix))]
#[allow(unused_variables)]
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
    if display_file_type {
        return String::from("----------");
    }
    String::from("---------")
}

#[cfg(unix)]
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
    let mode: mode_t = metadata.mode() as mode_t;
    display_permissions_unix(mode, display_file_type)
}

#[cfg(unix)]
pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String {
    let mut result;
    if display_file_type {
        result = String::with_capacity(10);
        result.push(match mode & S_IFMT {
            S_IFDIR => 'd',
            S_IFCHR => 'c',
            S_IFBLK => 'b',
            S_IFREG => '-',
            S_IFIFO => 'p',
            S_IFLNK => 'l',
            S_IFSOCK => 's',
            // TODO: Other file types
            _ => '?',
        });
    } else {
        result = String::with_capacity(9);
    }

    result.push(if has!(mode, S_IRUSR) { 'r' } else { '-' });
    result.push(if has!(mode, S_IWUSR) { 'w' } else { '-' });
    result.push(if has!(mode, S_ISUID as mode_t) {
        if has!(mode, S_IXUSR) {
            's'
        } else {
            'S'
        }
    } else if has!(mode, S_IXUSR) {
        'x'
    } else {
        '-'
    });

    result.push(if has!(mode, S_IRGRP) { 'r' } else { '-' });
    result.push(if has!(mode, S_IWGRP) { 'w' } else { '-' });
    result.push(if has!(mode, S_ISGID as mode_t) {
        if has!(mode, S_IXGRP) {
            's'
        } else {
            'S'
        }
    } else if has!(mode, S_IXGRP) {
        'x'
    } else {
        '-'
    });

    result.push(if has!(mode, S_IROTH) { 'r' } else { '-' });
    result.push(if has!(mode, S_IWOTH) { 'w' } else { '-' });
    result.push(if has!(mode, S_ISVTX as mode_t) {
        if has!(mode, S_IXOTH) {
            't'
        } else {
            'T'
        }
    } else if has!(mode, S_IXOTH) {
        'x'
    } else {
        '-'
    });

    result
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    struct NormalizePathTestCase<'a> {
        path: &'a str,
        test: &'a str,
    }

    const NORMALIZE_PATH_TESTS: [NormalizePathTestCase; 8] = [
        NormalizePathTestCase {
            path: "./foo/bar.txt",
            test: "foo/bar.txt",
        },
        NormalizePathTestCase {
            path: "bar/../foo/bar.txt",
            test: "foo/bar.txt",
        },
        NormalizePathTestCase {
            path: "foo///bar.txt",
            test: "foo/bar.txt",
        },
        NormalizePathTestCase {
            path: "foo///bar",
            test: "foo/bar",
        },
        NormalizePathTestCase {
            path: "foo//./bar",
            test: "foo/bar",
        },
        NormalizePathTestCase {
            path: "/foo//./bar",
            test: "/foo/bar",
        },
        NormalizePathTestCase {
            path: r"C:/you/later/",
            test: "C:/you/later",
        },
        NormalizePathTestCase {
            path: "\\networkShare/a//foo//./bar",
            test: "\\networkShare/a/foo/bar",
        },
    ];

    #[test]
    fn test_normalize_path() {
        for test in NORMALIZE_PATH_TESTS.iter() {
            let path = Path::new(test.path);
            let normalized = normalize_path(path);
            assert_eq!(
                test.test
                    .replace("/", std::path::MAIN_SEPARATOR.to_string().as_str()),
                normalized.to_str().expect("Path is not valid utf-8!")
            );
        }
    }

    #[cfg(unix)]
    #[test]
    fn test_display_permissions() {
        // spell-checker:ignore (perms) brwsr drwxr rwxr
        assert_eq!(
            "drwxr-xr-x",
            display_permissions_unix(S_IFDIR | 0o755, true)
        );
        assert_eq!(
            "rwxr-xr-x",
            display_permissions_unix(S_IFDIR | 0o755, false)
        );
        assert_eq!(
            "-rw-r--r--",
            display_permissions_unix(S_IFREG | 0o644, true)
        );
        assert_eq!(
            "srw-r-----",
            display_permissions_unix(S_IFSOCK | 0o640, true)
        );
        assert_eq!(
            "lrw-r-xr-x",
            display_permissions_unix(S_IFLNK | 0o655, true)
        );
        assert_eq!("?rw-r-xr-x", display_permissions_unix(0o655, true));

        assert_eq!(
            "brwSr-xr-x",
            display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o655, true)
        );
        assert_eq!(
            "brwsr-xr-x",
            display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o755, true)
        );

        assert_eq!(
            "prw---sr--",
            display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o614, true)
        );
        assert_eq!(
            "prw---Sr--",
            display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o604, true)
        );

        assert_eq!(
            "c---r-xr-t",
            display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o055, true)
        );
        assert_eq!(
            "c---r-xr-T",
            display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o054, true)
        );
    }
}
<commit_msg>core/fs: remove unused import on redox<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Joseph Crail <jbcrail@gmail.com>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

#[cfg(unix)]
use libc::{
    mode_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, S_IRGRP,
    S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH,
    S_IXUSR,
};
use std::borrow::Cow;
use std::env;
use std::fs;
use std::io::Result as IOResult;
use std::io::{Error, ErrorKind};
#[cfg(any(unix, target_os = "redox"))]
use std::os::unix::fs::MetadataExt;
use std::path::{Component, Path, PathBuf};

#[cfg(unix)]
#[macro_export]
macro_rules! has {
    ($mode:expr, $perm:expr) => {
        $mode & $perm != 0
    };
}

pub fn resolve_relative_path(path: &Path) -> Cow<Path> {
    if path.components().all(|e| e != Component::ParentDir) {
        return path.into();
    }
    let root = Component::RootDir.as_os_str();
    let mut result = env::current_dir().unwrap_or_else(|_| PathBuf::from(root));
    for comp in path.components() {
        match comp {
            Component::ParentDir => {
                if let Ok(p) = result.read_link() {
                    result = p;
                }
                result.pop();
            }
            Component::CurDir => (),
            Component::RootDir | Component::Normal(_) | Component::Prefix(_) => {
                result.push(comp.as_os_str())
            }
        }
    }
    result.into()
}

/// Controls how symbolic links should be handled when canonicalizing a path.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CanonicalizeMode {
    /// Do not resolve any symbolic links.
    None,

    /// Resolve all symbolic links.
    Normal,

    /// Resolve symbolic links, ignoring errors on the final component.
    Existing,

    /// Resolve symbolic links, ignoring errors on the non-final components.
    Missing,
}

// copied from https://github.com/rust-lang/cargo/blob/2e4cfc2b7d43328b207879228a2ca7d427d188bb/src/cargo/util/paths.rs#L65-L90
// both projects are MIT https://github.com/rust-lang/cargo/blob/master/LICENSE-MIT
// for std impl progress see rfc https://github.com/rust-lang/rfcs/issues/2208
// replace this once that lands
pub fn normalize_path(path: &Path) -> PathBuf {
    let mut components = path.components().peekable();
    let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
        components.next();
        PathBuf::from(c.as_os_str())
    } else {
        PathBuf::new()
    };

    for component in components {
        match component {
            Component::Prefix(..) => unreachable!(),
            Component::RootDir => {
                ret.push(component.as_os_str());
            }
            Component::CurDir => {}
            Component::ParentDir => {
                ret.pop();
            }
            Component::Normal(c) => {
                ret.push(c);
            }
        }
    }
    ret
}

fn resolve<P: AsRef<Path>>(original: P) -> IOResult<PathBuf> {
    const MAX_LINKS_FOLLOWED: u32 = 255;
    let mut followed = 0;
    let mut result = original.as_ref().to_path_buf();
    loop {
        if followed == MAX_LINKS_FOLLOWED {
            return Err(Error::new(
                ErrorKind::InvalidInput,
                "maximum links followed",
            ));
        }

        if !fs::symlink_metadata(&result)?.file_type().is_symlink() {
            break;
        }

        followed += 1;
        let path = fs::read_link(&result)?;
        result.pop();
        result.push(path);
    }
    Ok(result)
}

/// Return the canonical, absolute form of a path.
///
/// This function is a generalization of [`std::fs::canonicalize`] that
/// allows controlling how symbolic links are resolved and how to deal
/// with missing components. It returns the canonical, absolute form of
/// a path. The `can_mode` parameter controls how symbolic links are
/// resolved:
///
/// * [`CanonicalizeMode::Normal`] makes this function behave like
///   [`std::fs::canonicalize`], resolving symbolic links and returning
///   an error if the path does not exist.
/// * [`CanonicalizeMode::Missing`] makes this function ignore non-final
///   components of the path that could not be resolved.
/// * [`CanonicalizeMode::Existing`] makes this function return an error
///   if the final component of the path does not exist.
/// * [`CanonicalizeMode::None`] makes this function not try to resolve
///   any symbolic links.
///
pub fn canonicalize<P: AsRef<Path>>(original: P, can_mode: CanonicalizeMode) -> IOResult<PathBuf> {
    // Create an absolute path
    let original = original.as_ref();
    let original = if original.is_absolute() {
        original.to_path_buf()
    } else {
        dunce::canonicalize(env::current_dir().unwrap())
            .unwrap()
            .join(original)
    };

    let mut result = PathBuf::new();
    let mut parts = vec![];

    // Split path by directory separator; add prefix (Windows-only) and root
    // directory to final path buffer; add remaining parts to temporary
    // vector for canonicalization.
    for part in original.components() {
        match part {
            Component::Prefix(_) | Component::RootDir => {
                result.push(part.as_os_str());
            }
            Component::CurDir => (),
            Component::ParentDir => {
                parts.pop();
            }
            Component::Normal(_) => {
                parts.push(part.as_os_str());
            }
        }
    }

    // Resolve the symlinks where possible
    if !parts.is_empty() {
        for part in parts[..parts.len() - 1].iter() {
            result.push(part);

            if can_mode == CanonicalizeMode::None {
                continue;
            }

            match resolve(&result) {
                Err(_) if can_mode == CanonicalizeMode::Missing => continue,
                Err(e) => return Err(e),
                Ok(path) => {
                    result.pop();
                    result.push(path);
                }
            }
        }

        result.push(parts.last().unwrap());

        if can_mode == CanonicalizeMode::None {
            return Ok(result);
        }

        match resolve(&result) {
            Err(e) if can_mode == CanonicalizeMode::Existing => {
                return Err(e);
            }
            Ok(path) => {
                result.pop();
                result.push(path);
            }
            Err(_) => (),
        }
    }
    Ok(result)
}

#[cfg(not(unix))]
#[allow(unused_variables)]
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
    if display_file_type {
        return String::from("----------");
    }
    String::from("---------")
}

#[cfg(unix)]
pub fn display_permissions(metadata: &fs::Metadata, display_file_type: bool) -> String {
    let mode: mode_t = metadata.mode() as mode_t;
    display_permissions_unix(mode, display_file_type)
}

#[cfg(unix)]
pub fn display_permissions_unix(mode: mode_t, display_file_type: bool) -> String {
    let mut result;
    if display_file_type {
        result = String::with_capacity(10);
        result.push(match mode & S_IFMT {
            S_IFDIR => 'd',
            S_IFCHR => 'c',
            S_IFBLK => 'b',
            S_IFREG => '-',
            S_IFIFO => 'p',
            S_IFLNK => 'l',
            S_IFSOCK => 's',
            // TODO: Other file types
            _ => '?',
        });
    } else {
        result = String::with_capacity(9);
    }

    result.push(if has!(mode, S_IRUSR) { 'r' } else { '-' });
    result.push(if has!(mode, S_IWUSR) { 'w' } else { '-' });
    result.push(if has!(mode, S_ISUID as mode_t) {
        if has!(mode, S_IXUSR) {
            's'
        } else {
            'S'
        }
    } else if has!(mode, S_IXUSR) {
        'x'
    } else {
        '-'
    });

    result.push(if has!(mode, S_IRGRP) { 'r' } else { '-' });
    result.push(if has!(mode, S_IWGRP) { 'w' } else { '-' });
    result.push(if has!(mode, S_ISGID as mode_t) {
        if has!(mode, S_IXGRP) {
            's'
        } else {
            'S'
        }
    } else if has!(mode, S_IXGRP) {
        'x'
    } else {
        '-'
    });

    result.push(if has!(mode, S_IROTH) { 'r' } else { '-' });
    result.push(if has!(mode, S_IWOTH) { 'w' } else { '-' });
    result.push(if has!(mode, S_ISVTX as mode_t) {
        if has!(mode, S_IXOTH) {
            't'
        } else {
            'T'
        }
    } else if has!(mode, S_IXOTH) {
        'x'
    } else {
        '-'
    });

    result
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;

    struct NormalizePathTestCase<'a> {
        path: &'a str,
        test: &'a str,
    }

    const NORMALIZE_PATH_TESTS: [NormalizePathTestCase; 8] = [
        NormalizePathTestCase {
            path: "./foo/bar.txt",
            test: "foo/bar.txt",
        },
        NormalizePathTestCase {
            path: "bar/../foo/bar.txt",
            test: "foo/bar.txt",
        },
        NormalizePathTestCase {
            path: "foo///bar.txt",
            test: "foo/bar.txt",
        },
        NormalizePathTestCase {
            path: "foo///bar",
            test: "foo/bar",
        },
        NormalizePathTestCase {
            path: "foo//./bar",
            test: "foo/bar",
        },
        NormalizePathTestCase {
            path: "/foo//./bar",
            test: "/foo/bar",
        },
        NormalizePathTestCase {
            path: r"C:/you/later/",
            test: "C:/you/later",
        },
        NormalizePathTestCase {
            path: "\\networkShare/a//foo//./bar",
            test: "\\networkShare/a/foo/bar",
        },
    ];

    #[test]
    fn test_normalize_path() {
        for test in NORMALIZE_PATH_TESTS.iter() {
            let path = Path::new(test.path);
            let normalized = normalize_path(path);
            assert_eq!(
                test.test
                    .replace("/", std::path::MAIN_SEPARATOR.to_string().as_str()),
                normalized.to_str().expect("Path is not valid utf-8!")
            );
        }
    }

    #[cfg(unix)]
    #[test]
    fn test_display_permissions() {
        // spell-checker:ignore (perms) brwsr drwxr rwxr
        assert_eq!(
            "drwxr-xr-x",
            display_permissions_unix(S_IFDIR | 0o755, true)
        );
        assert_eq!(
            "rwxr-xr-x",
            display_permissions_unix(S_IFDIR | 0o755, false)
        );
        assert_eq!(
            "-rw-r--r--",
            display_permissions_unix(S_IFREG | 0o644, true)
        );
        assert_eq!(
            "srw-r-----",
            display_permissions_unix(S_IFSOCK | 0o640, true)
        );
        assert_eq!(
            "lrw-r-xr-x",
            display_permissions_unix(S_IFLNK | 0o655, true)
        );
        assert_eq!("?rw-r-xr-x", display_permissions_unix(0o655, true));

        assert_eq!(
            "brwSr-xr-x",
            display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o655, true)
        );
        assert_eq!(
            "brwsr-xr-x",
            display_permissions_unix(S_IFBLK | S_ISUID as mode_t | 0o755, true)
        );

        assert_eq!(
            "prw---sr--",
            display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o614, true)
        );
        assert_eq!(
            "prw---Sr--",
            display_permissions_unix(S_IFIFO | S_ISGID as mode_t | 0o604, true)
        );

        assert_eq!(
            "c---r-xr-t",
            display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o055, true)
        );
        assert_eq!(
            "c---r-xr-T",
            display_permissions_unix(S_IFCHR | S_ISVTX as mode_t | 0o054, true)
        );
    }
}
<|endoftext|>


<commit_before>use super::*;
use xpath_reader::XpathReader;

/// Checks if there is an error in the document provided by the reader and
/// returns Ok if there
/// wasn't and Err parsing the MusicBrainz error if the API actually returned
/// an error.
pub fn check_response_error<'d, R>(reader: &'d R) -> Result<(), ClientError>
where
    R: XpathReader<'d>,
{
    match reader.read_vec::<String>("//error/text") {
        Ok(errs) => {
            if errs.len() > 0 {
                let text = errs.join("\n");
                Err(ClientErrorKind::MusicbrainzServerError(text).into())
            } else {
                Ok(())
            }
        }
        Err(_) => Ok(()),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use error_chain::ChainedError;
    use xpath_reader::XpathStrReader;

    const XML_ERR: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?>
<error>
  <text>Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.</text>
  <text>For usage, please see: http://musicbrainz.org/development/mmd</text>
</error>"#;

    const XML_OK: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#"><release-group type="Album" type-id="f529b476-6e62-324f-b0aa-1f3e33d313fc" id="23c74936-ad4f-45bb-8b6b-527d4aeaaad6"><title>A.I Complex</title><first-release-date>2014-07-26</first-release-date><primary-type id="f529b476-6e62-324f-b0aa-1f3e33d313fc">Album</primary-type></release-group></metadata>"#;

    #[test]
    fn error()
    {
        let context = ::util::musicbrainz_context();
        let reader = XpathStrReader::new(XML_ERR, &context).unwrap();

        let res = check_response_error(&reader);
        let err = res.err().unwrap();

        assert_eq!(err.description(), "MusicBrainz server error");

        // If backtraces are enabled `error-chain` will appends some backtrace
        // information to the end of the display string.
        if ::std::env::var("RUST_BACKTRACE").unwrap_or_else(|_| "".to_string()).as_str() == "1" {
            assert!(err.display().to_string().starts_with("Error: MusicBrainz server error: Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.\nFor usage, please see: http://musicbrainz.org/development/mmd\n"));

        } else {
            assert_eq!(err.display().to_string(), "Error: MusicBrainz server error: Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.\nFor usage, please see: http://musicbrainz.org/development/mmd\n".to_string());
        }
    }

    #[test]
    fn ok()
    {
        let context = ::util::musicbrainz_context();
        let reader = XpathStrReader::new(XML_OK, &context).unwrap();

        // should not raise error
        check_response_error(&reader).unwrap();
    }
}
<commit_msg>Fix test after error-chain update.<commit_after>use super::*;
use xpath_reader::XpathReader;

/// Checks if there is an error in the document provided by the reader and
/// returns Ok if there
/// wasn't and Err parsing the MusicBrainz error if the API actually returned
/// an error.
pub fn check_response_error<'d, R>(reader: &'d R) -> Result<(), ClientError>
where
    R: XpathReader<'d>,
{
    match reader.read_vec::<String>("//error/text") {
        Ok(errs) => {
            if errs.len() > 0 {
                let text = errs.join("\n");
                Err(ClientErrorKind::MusicbrainzServerError(text).into())
            } else {
                Ok(())
            }
        }
        Err(_) => Ok(()),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use error_chain::ChainedError;
    use xpath_reader::XpathStrReader;

    const XML_ERR: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?>
<error>
  <text>Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.</text>
  <text>For usage, please see: http://musicbrainz.org/development/mmd</text>
</error>"#;

    const XML_OK: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#"><release-group type="Album" type-id="f529b476-6e62-324f-b0aa-1f3e33d313fc" id="23c74936-ad4f-45bb-8b6b-527d4aeaaad6"><title>A.I Complex</title><first-release-date>2014-07-26</first-release-date><primary-type id="f529b476-6e62-324f-b0aa-1f3e33d313fc">Album</primary-type></release-group></metadata>"#;

    #[test]
    fn error()
    {
        let context = ::util::musicbrainz_context();
        let reader = XpathStrReader::new(XML_ERR, &context).unwrap();

        let res = check_response_error(&reader);
        let err = res.err().unwrap();

        assert_eq!(err.description(), "MusicBrainz server error");

        assert!(format!("{}", err).starts_with("MusicBrainz server error: Your requests are exceeding the allowable rate limit. Please see http://wiki.musicbrainz.org/XMLWebService for more information.\nFor usage, please see: http://musicbrainz.org/development/mmd"));
    }

    #[test]
    fn ok()
    {
        let context = ::util::musicbrainz_context();
        let reader = XpathStrReader::new(XML_OK, &context).unwrap();

        // should not raise error
        check_response_error(&reader).unwrap();
    }
}
<|endoftext|>


<commit_before>use super::http;

use quire::validate::{Nothing, Enum, Structure, Scalar, Numeric};

#[derive(RustcDecodable, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum Mode {
    /// Means forward all headers including Host header
    forward,
}

#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Proxy {
    pub mode: Mode,
    pub ip_header: Option<String>,
    pub destination: http::Destination,
    pub max_payload_size: u64,
}

pub fn validator<'x>() -> Structure<'x> {
    Structure::new()
    .member("mode", Enum::new()
        .option("forward", Nothing)
        .allow_plain()
        .plain_default("forward"))
    .member("ip_header", Scalar::new().optional())
    .member("max_payload_size", Numeric::new().min(0).max(10 << 20))
    .member("destination", http::destination_validator())
}
<commit_msg>fixup! default max-payload-size<commit_after>use super::http;

use quire::validate::{Nothing, Enum, Structure, Scalar, Numeric};

#[derive(RustcDecodable, Debug, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum Mode {
    /// Means forward all headers including Host header
    forward,
}

#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct Proxy {
    pub mode: Mode,
    pub ip_header: Option<String>,
    pub destination: http::Destination,
    pub max_payload_size: u64,
}

pub fn validator<'x>() -> Structure<'x> {
    Structure::new()
    .member("mode", Enum::new()
        .option("forward", Nothing)
        .allow_plain()
        .plain_default("forward"))
    .member("ip_header", Scalar::new().optional())
    .member("max_payload_size",
        Numeric::new().min(0).max(1 << 40).default(10 << 20))
    .member("destination", http::destination_validator())
}
<|endoftext|>


<commit_before>use std::old_io;
use std::old_io::{fs, File, IoErrorKind};
use std::collections::BTreeMap;
use std::sync::mpsc::{Sender, Receiver};

use toml::{self, Value};

pub struct ConfigReader
{
    config_map: BTreeMap<String, Value>,
}

impl ConfigReader
{
    pub fn new() -> ConfigReader
    {
        // Create the configuration directory if it does not exist yet.
        let configdir = Path::new("config/");
        // This returns a Result with an error if the directory already exists or the user does not
        // have write permissions. We ignore that possibility for now.
        fs::mkdir(&configdir, old_io::USER_RWX);

        // Open the configuration file.
        let configpath  =  Path::new("config/redstart.toml");
        let mut configfile = match File::open(&configpath)
        {
            // File does exists -> return file descriptor.
            Ok(f) => f,
            // File does *not* exist -> create file, dump default config, return file descriptor
            Err(e) =>
            {
                match e.kind
                {
                    IoErrorKind::PermissionDenied => panic!("Could not read config file. Does it have the correct permissions?"),
                    IoErrorKind::FileNotFound =>
                    {
                        let mut fd = File::create(&configpath);
                        fd.write(b"[RedStart]\n\taddress = \"127.0.0.1\"\n\tport = 8080\n\tloglevel = \"NORMAL\"\n\tlogfile = \"log/default.log\"");
                        let mut fd = File::open(&configpath);
                        fd.unwrap()
                    }
                    _ => panic!("File error: {}!", e),
                }
            }
        };
        let configstring = configfile.read_to_string().unwrap();
        let mut configparser = toml::Parser::new(configstring.as_slice());
        let value = match configparser.parse()
        {
            Some(val) => val,
            None => panic!("Configfile parse error! Check config syntax!"),
        };
        // println!("{:?}", value);
        ConfigReader { config_map: value }
    }

    /// Return the value that belongs to the key given. If no value was found, `None` will be returned
    pub fn get_value<T>(&mut self, key: &str) -> Option<T>
    {
        None
    }

    /// Return the value that belongs to the key given. If no value was found, a given default value
    /// will be returned.
    pub fn get_value_or<T>(&mut self, key: &str, default: T) -> T
    {
        match self.get_value::<T>(key)
        {
            Some(e) => e,
            None => default,
        }
    }
}
<commit_msg>Updated default config<commit_after>use std::old_io;
use std::old_io::{fs, File, IoErrorKind};
use std::collections::BTreeMap;
use std::sync::mpsc::{Sender, Receiver};

use toml::{self, Value};

pub struct ConfigReader
{
    config_map: BTreeMap<String, Value>,
}

impl ConfigReader
{
    pub fn new() -> ConfigReader
    {
        // Create the configuration directory if it does not exist yet.
        let configdir = Path::new("config/");
        // This returns a Result with an error if the directory already exists or the user does not
        // have write permissions. We ignore that possibility for now.
        fs::mkdir(&configdir, old_io::USER_RWX);

        // Open the configuration file.
        let configpath  =  Path::new("config/redstart.toml");
        let mut configfile = match File::open(&configpath)
        {
            // File does exists -> return file descriptor.
            Ok(f) => f,
            // File does *not* exist -> create file, dump default config, return file descriptor
            Err(e) =>
            {
                match e.kind
                {
                    IoErrorKind::PermissionDenied => panic!("Could not read config file. Does it have the correct permissions?"),
                    IoErrorKind::FileNotFound =>
                    {
                        let mut fd = File::create(&configpath);
                        fd.write(b"[General]\r\nname=\"RedStart\"\r\n[Networking]\r\naddress = \"127.0.0.1\"\r\nport = 8080\r\n[Logging]\r\nloglevel = \"NORMAL\"\r\nlogfile = \"log/default.log\"\r\n"
);
                        let mut fd = File::open(&configpath);
                        fd.unwrap()
                    }
                    _ => panic!("File error: {}!", e),
                }
            }
        };
        let configstring = configfile.read_to_string().unwrap();
        let mut configparser = toml::Parser::new(configstring.as_slice());
        let value = match configparser.parse()
        {
            Some(val) => val,
            None => panic!("Configfile parse error! Check config syntax!"),
        };
        println!("{:?}", value);
        ConfigReader { config_map: value }
    }

    /// Return the value that belongs to the key given. If no value was found, `None` will be returned
    pub fn get_value<T>(&mut self, key: &str) -> Option<T>
    {
        None
    }

    /// Return the value that belongs to the key given. If no value was found, a given default value
    /// will be returned.
    pub fn get_value_or<T>(&mut self, key: &str, default: T) -> T
    {
        match self.get_value::<T>(key)
        {
            Some(e) => e,
            None => default,
        }
    }
}
<|endoftext|>