<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::fmt::Write;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex, RwLock};
use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant};

use glium::glutin::EventsLoopProxy;

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

const AI_MOVE_DELAY: Duration = Duration::from_millis(300);

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

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

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())),
            debug_info: Arc::new(RwLock::new(String::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,
        events_proxy: EventsLoopProxy,
        delay: bool,
        ply_count: u64,
    ) {
        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 debug_info = self.debug_info.clone();

        let handle = thread::spawn(move || {
            let start = Instant::now();

            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(_poison_error) => panic!("Transposition table mutex is poisoned"),
            };

            if let Ok(mut debug_info) = debug_info.write() {
                debug_info.clear();
                writeln!(
                    debug_info,
                    "Turn {}: {:?} (Depth {})",
                    ply_count / 2 + 1,
                    board.turn,
                    depth
                ).unwrap();
            }

            if let SearchResult::Move(mv) = search_root(
                depth,
                board,
                board_list,
                &mut ttable,
                &stop_signal_clone,
                debug_info,
                &events_proxy,
            ) {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }

                let elapsed = Instant::now() - start;
                if delay && elapsed < AI_MOVE_DELAY {
                    thread::sleep(AI_MOVE_DELAY - elapsed);
                }

                move_sender.send(mv).expect("AI failed to send Move");
                events_proxy
                    .wakeup()
                    .expect("Failed to wake up events loop");
            }
        });

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

enum SearchResult {
    Move(Move),
    Stopped,
}

fn search_root(
    depth: u8,
    board: Board,
    board_list: Vec<Board>,
    ttable: &mut TTable,
    stop_signal: &Arc<AtomicBool>,
    debug_info: Arc<RwLock<String>>,
    events_proxy: &EventsLoopProxy,
) -> SearchResult {
    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;
    let mut iter_score = evaluate(&board);
    for depth in 0..depth {
        if stop_signal.load(Ordering::Relaxed) {
            return SearchResult::Stopped;
        }

        // Aspiration window search loop
        let mut asp_width = ASPIRATION_WIDTH;
        loop {
            let mut max_score = iter_score - asp_width;
            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,
                    -(iter_score + asp_width),
                    -max_score,
                    depth,
                    ttable,
                );

                if score > max_score {
                    max_score = score;
                    pv = Some(new_pv);
                }
                pair.1 = score;
            }

            if max_score == iter_score + asp_width || max_score == iter_score - asp_width {
                // True score lies outside the window, so we search this position again
                asp_width *= 2;
            } else {
                break;
            }
        }

        moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
        iter_score = moves[0].1;

        if let Ok(mut debug_info) = debug_info.write() {
            writeln!(debug_info, "\nDepth {}: {:>6}", depth, moves[0].1).unwrap();
            if let Some(ref mut pv) = pv {
                pv.push(moves[0].0);
                for mv in pv.iter().rev() {
                    writeln!(debug_info, "    {}", mv).unwrap();
                }
            }
            events_proxy
                .wakeup()
                .expect("Failed to wake up events loop");
        }
    }
    SearchResult::Move(moves[0].0)
}

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, score| {
        ttable.set(board.zobrist, score, depth as i8);
    };

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

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

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

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

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

    let mut new_pv = vec![];
    for mv in board.generate_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, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, Score::Exact(best_score));
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(
    board: &Board,
    mut alpha: i16,
    mut beta: i16,
    depth: i8,
    ttable: &mut TTable,
) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if stand_pat + 200 < alpha {
        // Delta pruning: we bet that no capture move will raise alpha by more than 200 centipieces
        // We don't switch this off in the endgame because a draw by insufficient material is very
        // unlikely (both sides must be unable to exchange and each have only 1 piece left)
        return alpha;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    match ttable.get(board.zobrist, depth) {
        Some(Score::Exact(score)) => {
            return score;
        }
        Some(Score::Beta(score)) => {
            if score >= beta {
                return score;
            }
            beta = score;
        }
        None => {}
    };

    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth);
    };

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

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

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    set_ttable(ttable, Score::Exact(alpha));
    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>Pass debug_info Arc by reference<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::fmt::Write;
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, TryRecvError};
use std::sync::{Arc, Mutex, RwLock};
use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant};

use glium::glutin::EventsLoopProxy;

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

const AI_MOVE_DELAY: Duration = Duration::from_millis(300);

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

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

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())),
            debug_info: Arc::new(RwLock::new(String::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,
        events_proxy: EventsLoopProxy,
        delay: bool,
        ply_count: u64,
    ) {
        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 debug_info = self.debug_info.clone();

        let handle = thread::spawn(move || {
            let start = Instant::now();

            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(_poison_error) => panic!("Transposition table mutex is poisoned"),
            };

            if let Ok(mut debug_info) = debug_info.write() {
                debug_info.clear();
                writeln!(
                    debug_info,
                    "Turn {}: {:?} (Depth {})",
                    ply_count / 2 + 1,
                    board.turn,
                    depth
                ).unwrap();
            }

            if let SearchResult::Move(mv) = search_root(
                depth,
                board,
                board_list,
                &mut ttable,
                &stop_signal_clone,
                &debug_info,
                &events_proxy,
            ) {
                if stop_signal_clone.load(Ordering::Relaxed) {
                    return;
                }

                let elapsed = Instant::now() - start;
                if delay && elapsed < AI_MOVE_DELAY {
                    thread::sleep(AI_MOVE_DELAY - elapsed);
                }

                move_sender.send(mv).expect("AI failed to send Move");
                events_proxy
                    .wakeup()
                    .expect("Failed to wake up events loop");
            }
        });

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

enum SearchResult {
    Move(Move),
    Stopped,
}

fn search_root(
    depth: u8,
    board: Board,
    board_list: Vec<Board>,
    ttable: &mut TTable,
    stop_signal: &Arc<AtomicBool>,
    debug_info: &Arc<RwLock<String>>,
    events_proxy: &EventsLoopProxy,
) -> SearchResult {
    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;
    let mut iter_score = evaluate(&board);
    for depth in 0..depth {
        if stop_signal.load(Ordering::Relaxed) {
            return SearchResult::Stopped;
        }

        // Aspiration window search loop
        let mut asp_width = ASPIRATION_WIDTH;
        loop {
            let mut max_score = iter_score - asp_width;
            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,
                    -(iter_score + asp_width),
                    -max_score,
                    depth,
                    ttable,
                );

                if score > max_score {
                    max_score = score;
                    pv = Some(new_pv);
                }
                pair.1 = score;
            }

            if max_score == iter_score + asp_width || max_score == iter_score - asp_width {
                // True score lies outside the window, so we search this position again
                asp_width *= 2;
            } else {
                break;
            }
        }

        moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
        iter_score = moves[0].1;

        if let Ok(mut debug_info) = debug_info.write() {
            writeln!(debug_info, "\nDepth {}: {:>6}", depth, moves[0].1).unwrap();
            if let Some(ref mut pv) = pv {
                pv.push(moves[0].0);
                for mv in pv.iter().rev() {
                    writeln!(debug_info, "    {}", mv).unwrap();
                }
            }
            events_proxy
                .wakeup()
                .expect("Failed to wake up events loop");
        }
    }
    SearchResult::Move(moves[0].0)
}

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, score| {
        ttable.set(board.zobrist, score, depth as i8);
    };

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

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

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

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

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

    let mut new_pv = vec![];
    for mv in board.generate_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, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
            best_move = Some(mv);
        }
    }
    set_ttable(ttable, Score::Exact(best_score));
    if let Some(mv) = best_move {
        new_pv.push(mv);
        set_pv(alpha, new_pv);
    }
    alpha
}

fn quiescence_search(
    board: &Board,
    mut alpha: i16,
    mut beta: i16,
    depth: i8,
    ttable: &mut TTable,
) -> i16 {
    let stand_pat = evaluate(board);
    if stand_pat >= beta {
        return beta;
    } else if stand_pat + 200 < alpha {
        // Delta pruning: we bet that no capture move will raise alpha by more than 200 centipieces
        // We don't switch this off in the endgame because a draw by insufficient material is very
        // unlikely (both sides must be unable to exchange and each have only 1 piece left)
        return alpha;
    } else if alpha < stand_pat {
        alpha = stand_pat;
    }

    match ttable.get(board.zobrist, depth) {
        Some(Score::Exact(score)) => {
            return score;
        }
        Some(Score::Beta(score)) => {
            if score >= beta {
                return score;
            }
            beta = score;
        }
        None => {}
    };

    let set_ttable = |ttable: &mut TTable, score| {
        ttable.set(board.zobrist, score, depth);
    };

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

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

        if score >= beta {
            set_ttable(ttable, Score::Beta(score));
            return beta;
        } else if score > alpha {
            alpha = score;
        }
    }
    set_ttable(ttable, Score::Exact(alpha));
    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 crate::Error;
use loading::*;
use semver::Version;
use std::ffi::CStr;
use std::mem::transmute;
use std::os::raw::c_char;
use std::path::PathBuf;

pub fn find_python() -> Result<PathBuf, Error> {
    let locations = get_candidate_locations();
    for path in locations {
        unsafe {
            match load_library(&path, true) {
                Ok(handle) => {
                    if let Ok(ptr) = find_symbol(handle, "Py_GetVersion") {
                        let py_getversion: unsafe extern "C" fn() -> *const c_char = transmute(ptr);
                        let version = CStr::from_ptr(py_getversion());
                        if let Ok(version) = version.to_str() {
                            if let Some(version) = version.split(" ").next() {
                                if let Ok(version) = Version::parse(version) {
                                    if version.major == 3 && version.minor >= 3 {
                                        free_library(handle)?;
                                        return Ok(path);
                                    }
                                }
                            }
                        }
                    }
                    free_library(handle)?;
                }
                Err(_) => {}
            }
        }
    }
    Err("No suitable Python 3 installation found.".into())
}

#[cfg(target_os = "linux")]
fn get_candidate_locations() -> Vec<PathBuf> {
    use std::io::{BufRead, BufReader};

    fn query_sysconfig() -> Result<PathBuf, Error> {
        let result = std::process::Command::new("python3")
            .arg("-c")
            .arg("import sysconfig; print(sysconfig.get_config_var('INSTSONAME'))")
            .output()?;
        if !result.status.success() {
            return Err(format!("python exit code: {:?}", result.status.code()).into());
        }
        let stdout = BufReader::new(&result.stdout[..]);
        let mut lines = stdout.lines();
        let path = PathBuf::from(lines.next().unwrap()?);
        Ok(path)
    }

    match query_sysconfig() {
        Ok(path) => vec![path],
        Err(err) => {
            eprintln!("{}", err);
            vec![]
        }
    }
}

#[cfg(target_os = "macos")]
fn get_candidate_locations() -> Vec<PathBuf> {
    use std::io::{BufRead, BufReader};

    fn query_sysconfig() -> Result<Vec<PathBuf>, Error> {
        let result = std::process::Command::new("python3")
            .arg("-c")
            .arg("import sysconfig; print(sysconfig.get_config_var('PYTHONFRAMEWORKPREFIX')); print(sysconfig.get_config_var('LDLIBRARY'))")
            .output()?;
        if !result.status.success() {
            return Err(format!("python exit code: {:?}", result.status.code()).into());
        }
        let stdout = BufReader::new(&result.stdout[..]);
        let mut lines = stdout.lines();
        let prefix = lines.next().unwrap()?;
        let libname = lines.next().unwrap()?;

        let mut results = vec![];
        results.push(PathBuf::from(&libname));

        let mut path = PathBuf::from(prefix);
        path.push(&libname);
        results.push(path);

        let mut path = PathBuf::from("/Library/Developer/CommandLineTools/Library/Frameworks");
        path.push(&libname);
        results.push(path);

        Ok(results)
    }

    match query_sysconfig() {
        Ok(results) => results,
        Err(err) => {
            eprintln!("{}", err);
            vec![]
        }
    }
}

#[cfg(target_os = "windows")]
fn get_candidate_locations() -> Vec<PathBuf> {
    use winreg::enums::*;

    fn probe_version(hk_version: winreg::RegKey) -> Result<PathBuf, Error> {
        let sys_version: String = hk_version.get_value("SysVersion")?;
        if sys_version.as_str() >= "3" {
            let hk_install_path = hk_version.open_subkey("InstallPath")?;
            let install_path: String = hk_install_path.get_value("")?;
            let mut path = PathBuf::from(install_path);
            path.push("python3.dll");
            Ok(path)
        } else {
            Err("Nope".into())
        }
    }

    let mut results = vec![];
    for hive in &[HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE] {
        if let Ok(hk_python) = winreg::RegKey::predef(*hive).open_subkey("Software\\Python\\PythonCore") {
            for ver_tag in hk_python.enum_keys() {
                if let Ok(ver_tag) = ver_tag {
                    if let Ok(hk_version) = hk_python.open_subkey(ver_tag) {
                        if let Ok(path) = probe_version(hk_version) {
                            results.push(path);
                        }
                    }
                }
            }
        }
    }
    results
}
<commit_msg>Support Anaconda Python.<commit_after>use crate::Error;
use loading::*;
use semver::Version;
use std::ffi::CStr;
use std::mem::transmute;
use std::os::raw::c_char;
use std::path::PathBuf;

pub fn find_python() -> Result<PathBuf, Error> {
    let locations = get_candidate_locations();
    for path in locations {
        unsafe {
            match load_library(&path, true) {
                Ok(handle) => {
                    if let Ok(ptr) = find_symbol(handle, "Py_GetVersion") {
                        let py_getversion: unsafe extern "C" fn() -> *const c_char = transmute(ptr);
                        let version = CStr::from_ptr(py_getversion());
                        if let Ok(version) = version.to_str() {
                            if let Some(version) = version.split(" ").next() {
                                if let Ok(version) = Version::parse(version) {
                                    if version.major == 3 && version.minor >= 3 {
                                        free_library(handle)?;
                                        return Ok(path);
                                    }
                                }
                            }
                        }
                    }
                    free_library(handle)?;
                }
                Err(_) => {}
            }
        }
    }
    Err("No suitable Python 3 installation found.".into())
}

#[cfg(target_os = "linux")]
fn get_candidate_locations() -> Vec<PathBuf> {
    use std::io::{BufRead, BufReader};

    fn query_sysconfig() -> Result<PathBuf, Error> {
        let result = std::process::Command::new("python3")
            .arg("-c")
            .arg("import sysconfig; print(sysconfig.get_config_var('INSTSONAME'))")
            .output()?;
        if !result.status.success() {
            return Err(format!("python exit code: {:?}", result.status.code()).into());
        }
        let stdout = BufReader::new(&result.stdout[..]);
        let mut lines = stdout.lines();
        let path = PathBuf::from(lines.next().unwrap()?);
        Ok(path)
    }

    match query_sysconfig() {
        Ok(path) => vec![path],
        Err(err) => {
            eprintln!("{}", err);
            vec![]
        }
    }
}

#[cfg(target_os = "macos")]
fn get_candidate_locations() -> Vec<PathBuf> {
    use std::io::{BufRead, BufReader};

    fn query_sysconfig() -> Result<Vec<PathBuf>, Error> {
        let result = std::process::Command::new("python3")
            .arg("-c")
            .arg("import sysconfig; print(sysconfig.get_config_var('PYTHONFRAMEWORKPREFIX')); print(sysconfig.get_config_var('LDLIBRARY'))")
            .output()?;
        if !result.status.success() {
            return Err(format!("python exit code: {:?}", result.status.code()).into());
        }
        let stdout = BufReader::new(&result.stdout[..]);
        let mut lines = stdout.lines();
        let prefix = lines.next().unwrap()?;
        let libname = lines.next().unwrap()?;

        let mut results = vec![];
        results.push(PathBuf::from(&libname));

        let mut path = PathBuf::from(prefix);
        path.push(&libname);
        results.push(path);

        let mut path = PathBuf::from("/Library/Developer/CommandLineTools/Library/Frameworks");
        path.push(&libname);
        results.push(path);

        Ok(results)
    }

    match query_sysconfig() {
        Ok(results) => results,
        Err(err) => {
            eprintln!("{}", err);
            vec![]
        }
    }
}

#[cfg(target_os = "windows")]
fn get_candidate_locations() -> Vec<PathBuf> {
    use winreg::enums::*;

    let mut results = vec![];
    for hive in &[HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE] {
        if let Ok(hk_python) = winreg::RegKey::predef(*hive).open_subkey("Software\\Python\\PythonCore") {
            for ver_tag in hk_python.enum_keys() {
                if let Ok(ver_tag) = ver_tag {
                    if let Ok(hk_version) = hk_python.open_subkey(ver_tag) {
                        if let Ok(hk_install_path) = hk_version.open_subkey("InstallPath") {
                            if let Ok(install_path) = hk_install_path.get_value::<String, _>("") {
                                let mut path = PathBuf::from(install_path);
                                path.push("python3.dll");
                                results.push(path);
                            }
                        }
                    }
                }
            }
        }
    }
    results
}
<|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.

//! Extensions to the standard I/O utilities.
//!
//! This module essentially provides useful, RRG-specific utilities that are not
//! available in `std::io`, mostly operating on the standard `Read` and `Write`
//! traits.

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

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

/// Copies contents of one buffer into the other until a condition is met.
///
/// This function should behave similarly to the standard `std::io::copy` with
/// the difference that it can be given a condition when copying should stop.
///
/// Note that the predicate is not checked after each copied byte. Therefore,
/// there are no guarantees about when exactly and how often it will be called.
///
/// # Errors
///
/// The errors is reported immediately if there is an error when reading from
/// the input or writing to the output.
///
/// Like `std::io::copy`, this function will retry instances of `Interrupted`
/// errors.
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let mut rand = File::open("/dev/random").unwrap();
/// let mut buf = vec!();
///
/// rrg::io::copy_until(&mut rand, &mut buf, |_, writer| writer.len() >= 1024);
///
/// println!("random bytes: {:?}", buf);
/// ```
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 {
        if pred(reader, writer) {
            break;
        }

        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])?;
    }

    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_begin() {
        let mut reader: &[u8] = b"foobar";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| true).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() {
        let limit = 4 * 1024 * 1024;

        let mut reader = std::io::repeat(0);
        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);
    }
}
<commit_msg>Use more unique assertion value in a copying test.<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.

//! Extensions to the standard I/O utilities.
//!
//! This module essentially provides useful, RRG-specific utilities that are not
//! available in `std::io`, mostly operating on the standard `Read` and `Write`
//! traits.

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

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

/// Copies contents of one buffer into the other until a condition is met.
///
/// This function should behave similarly to the standard `std::io::copy` with
/// the difference that it can be given a condition when copying should stop.
///
/// Note that the predicate is not checked after each copied byte. Therefore,
/// there are no guarantees about when exactly and how often it will be called.
///
/// # Errors
///
/// The errors is reported immediately if there is an error when reading from
/// the input or writing to the output.
///
/// Like `std::io::copy`, this function will retry instances of `Interrupted`
/// errors.
///
/// # Examples
///
/// ```no_run
/// use std::fs::File;
///
/// let mut rand = File::open("/dev/random").unwrap();
/// let mut buf = vec!();
///
/// rrg::io::copy_until(&mut rand, &mut buf, |_, writer| writer.len() >= 1024);
///
/// println!("random bytes: {:?}", buf);
/// ```
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 {
        if pred(reader, writer) {
            break;
        }

        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])?;
    }

    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_begin() {
        let mut reader: &[u8] = b"foobar";
        let mut writer = vec!();

        assert!(copy_until(&mut reader, &mut writer, |_, _| true).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() {
        let limit = 4 * 1024 * 1024;

        let mut reader = std::io::repeat(0x42);
        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 == 0x42));
        assert!(writer.len() > limit);
    }
}
<|endoftext|>


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

//! Routines for initialization of STM32F1.
//!
//! This module includes code for setting up the clock, flash, access time and
//! performing initial peripheral configuration.

//use hal::mem_init::init_data;
use core::default;
use core::intrinsics::abort;
use core::option::Option;
use core::marker::Copy;

use self::MsiSpeed::*;
use self::SystemClockSource::*;

#[path="../../util/wait_for.rs"]
#[macro_use] mod wait_for;

/// Phase-locked loop clock source.
#[repr(u8)]
#[derive(Clone)]
pub enum PllClockSource {
  /// Takes base clock from HSI.
  PllSourceHSI = 0,
  /// Takes base clock from HSE.
  PllSourceHSE = 1,
}

impl Copy for PllClockSource {}

/// PLL multiplier: 3, 4, 6, 8, 12, 16, 24, 32, 48
pub type PllMultiplier = u8;

/// PLL divisor: 1, 2, 3, 4
pub type PllDivisor = u8;

/// Multi-speed internal clock divisor.
#[repr(u8)]
#[derive(Clone)]
pub enum MsiSpeed {
  /// 65.536 kHz
  Msi65   = 0,
  /// 131.072 kHz
  Msi131  = 1,
  /// 262.144 kHz
  Msi262  = 2,
  /// 524.288 kHz
  Msi524  = 3,
  /// 1.048 MHz
  Msi1048 = 4,
  /// 2.097 MHz
  Msi2097 = 5,
  /// 4.194 MHz
  Msi4194 = 6,
}

impl Copy for MsiSpeed {}

/// System clock source.
#[derive(Clone)]
pub enum SystemClockSource {
  /// Multi-speed internal clock,
  SystemClockMSI(MsiSpeed),
  /// High-speed internal oscillator, 16MHz.
  SystemClockHSI,
  /// High-speed external oscillator with configurable frequency.
  SystemClockHSE(u32),
  /// PLL.
  SystemClockPLL(PllClockSource, PllMultiplier, PllDivisor),
}

impl default::Default for SystemClockSource {
  fn default() -> SystemClockSource {
    SystemClockMSI(Msi2097)
  }
}

impl SystemClockSource {
  /// Returns the system clock frequency.
  pub fn frequency(&self) -> u32 {
    match *self {
        SystemClockMSI(Msi65) => 65_536,
        SystemClockMSI(Msi131) => 131_072,
        SystemClockMSI(Msi262) => 262_144,
        SystemClockMSI(Msi524) => 524_288,
        SystemClockMSI(Msi1048) => 1_048_000,
        SystemClockMSI(Msi2097) => 2_097_000,
        SystemClockMSI(Msi4194) => 4_194_000,
        SystemClockHSI => 16_000_000,
        SystemClockHSE(_) => unsafe { abort() }, //TODO(kvark)
        SystemClockPLL(_, _, _) => unsafe { abort() }, //TODO(kvark)
    }
  }
}

impl Copy for SystemClockSource {}

#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone)]
pub enum McoSource {
  McoClockSystem = 1,
  McoClockHSI = 2,
  McoClockMSI = 3,
  McoClockHSE = 4,
  McoClockPLL = 5,
  McoClockLSI = 6,
  McoClockLSE = 7,
}

impl Copy for McoSource {}

/// Microchip clock output configuration.
#[derive(Clone)]
pub struct McoConfig {
  /// MCO clock source
  source: McoSource,
  /// Log2(divisor) for MCO.
  clock_shift: u8,
}

impl Copy for McoConfig {}

/// System clock configuration.
#[derive(Clone)]
pub struct ClockConfig {
  /// System clock source
  pub source : SystemClockSource,
  /// Log2(divisor) for Ahb bus.
  pub ahb_shift : u8,
  /// Log2(divisor) for Apb1 bus.
  pub apb1_shift : u8,
  /// Log2(divisor) for Apb2 bus.
  pub apb2_shift : u8,
  /// Microchip clock output.
  pub mco : Option<McoConfig>,
}

impl Copy for ClockConfig {}

impl default::Default for ClockConfig {
  fn default() -> ClockConfig {
    ClockConfig {
      source: default::Default::default(),
      ahb_shift: 0,
      apb1_shift: 0,
      apb2_shift: 0,
      mco: Option::None,
    }
  }
}

impl ClockConfig {
  /// Return the default clock configuration that hardware go to after reset.
  pub fn new_default() -> ClockConfig {
    default::Default::default()
  }

  /// Set this configuration on the hardware.
  pub fn setup(&self) {
    let r = &reg::RCC;

    let source_type = match self.source {
      SystemClockMSI(msi) => {
        r.cr.set_msi_on(true);
        wait_for!(r.cr.msi_ready());
        r.icscr.set_msi_range(msi as u32);
        0
      },
      SystemClockHSI => {
        r.cr.set_hsi_on(true);
        wait_for!(r.cr.hsi_ready());
        1
      },
      SystemClockHSE(_) => {
        r.cr.set_hse_on(true);
        wait_for!(r.cr.hse_ready());
        //TODO(kvark): HSE config
        2
      },
      SystemClockPLL(pll_source, mul, div) => {
        r.cr.set_pll_on(true);
        wait_for!(r.cr.pll_ready());
        r.cfgr.set_pll_clock_source(pll_source as usize != 0);
        let factor = match mul {
          3 => 0,
          4 => 1,
          6 => 2,
          8 => 3,
          12 => 4,
          16 => 5,
          24 => 6,
          32 => 7,
          48 => 8,
          _ => unsafe { abort() } // not supported
        };
        r.cfgr.set_pll_mul_factor(factor);
        r.cfgr.set_pll_output_div(div as u32);
        3
      }
    };

    r.cfgr.set_system_clock(source_type);
    wait_for!(r.cfgr.system_clock_status() == source_type);

    if self.ahb_shift > 9 || self.apb1_shift > 4 || self.apb2_shift > 4 {
      unsafe { abort() } // not supported
    }
    r.cfgr.set_ahb_prescaler(self.ahb_shift as u32);
    r.cfgr.set_apb1_prescaler(self.apb1_shift as u32);
    r.cfgr.set_apb2_prescaler(self.apb2_shift as u32);

    match self.mco {
      Option::Some(mco) => {
        if mco.clock_shift > 4 {
            unsafe { abort() } // not supported
        }
        r.cfgr.set_mco(mco.source as u32);
        r.cfgr.set_mco_prescaler(mco.clock_shift as u32);
      },
      Option::None => {
        r.cfgr.set_mco(0);
      },
    }
  }

  /// Returns AHB clock frequency
  pub fn get_ahb_frequency(&self) -> u32 {
    self.source.frequency() >> self.ahb_shift as usize
  }

  /// Returns APB1 clock frequency
  pub fn get_apb1_frequency(&self) -> u32 {
    self.source.frequency() >> self.apb1_shift as usize
  }

  /// Returns APB2 clock frequency
  pub fn get_apb2_frequency(&self) -> u32 {
    self.source.frequency() >> self.apb2_shift as usize
  }
}

// TODO(farcaller): this mod is pub as it's being used in peripheral_clock.rs.
//                  This is not the best design solution and a good reason to
//                  split RCC into distinct registers.
#[allow(missing_docs)]
pub mod reg {
  use volatile_cell::VolatileCell;
  use core::ops::Drop;

  ioregs!(RCC = {
    0x00 => reg32 cr {          // clock control
      0 => hsi_on : rw,
      1 => hsi_ready : ro,
      8 => msi_on : rw,
      9 => msi_ready : ro,
      16 => hse_on : rw,
      17 => hse_ready : ro,
      18 => hse_bypass : rw,
      24 => pll_on : rw,
      25 => pll_ready : ro,
      28 => security_on : rw,
      30..29 => rtc_prescaler : rw,
    },
    0x04 => reg32 icscr {       // internal clock sources calibration
      7..0   => hsi_calibration : rw,
      12..8  => hsi_trimming : rw,
      15..13 => msi_range : rw,
      23..16 => msi_calibration : rw,
      31..24 => msi_trimming : rw,
    },
    0x08 => reg32 cfgr {        // clock configuration
      1..0   => system_clock : rw,
      3..2   => system_clock_status: ro,
      7..4   => ahb_prescaler : rw,
      10..8  => apb1_prescaler : rw,
      13..11 => apb2_prescaler : rw,
      16     => pll_clock_source : rw,
      21..18 => pll_mul_factor : rw,
      23..22 => pll_output_div : rw,
      26..24 => mco : rw,   // microcontroller clock output
      30..28 => mco_prescaler : rw,
    },
    0x0C => reg32 cir {         // clock interrupt
      31..0 => clock_interrupt : rw,
    },
    0x10 => reg32 ahbrstr {     // AHB peripheral reset
      31..0 => reset : rw,
    },
    0x14 => reg32 apb2rstr {    // APB2 peripheral reset
      31..0 => reset : rw,
    },
    0x18 => reg32 apb1rstr {    // APB1 peripheral reset
      31..0 => reset : rw,
    },
    0x1C => reg32 ahbenr {      // AHB peripheral clock enable
      31..0 => enable : rw,
    },
    0x20 => reg32 apb2enr {     // APB2 peripheral clock enable
      31..0 => enable : rw,
    },
    0x24 => reg32 apb1enr {     // ABB1 peripheral clock enable
      31..0 => enable : rw,
    },
    0x28 => reg32 ahblpenr {    // AHB peripheral clock enable in low power mode
      31..0 => enable_low_power : rw,
    },
    0x2C => reg32 apb2lpenr {   // APB2 peripheral clock enable in low power mode
      31..0 => enable_low_power : rw,
    },
    0x30 => reg32 apb1lpenr {   // APB1 peripheral clock enable in low power mode
      31..0 => enable_low_power : rw,
    },
    0x34 => reg32 csr {         // control/status
      0 => lsi_on : rw, // internal low speed oscillator
      1 => lsi_ready : ro,
      8 => lse_on : rw, // external low speed oscillator
      9 => lse_ready : ro,
      10 => lse_bypass : rw,
      11 => lse_css_on : rw,
      12 => lse_css_detected : ro,
      17..16 => rtc_source : rw,
      22 => rtc_on : rw,
      23 => rtc_reset : rw,
      24 => remove_reset : rw,
      25 => option_bytes_loader_reset : rw,
      26 => pin_reset : rw,
      27 => pop_pdr_reset : rw,
      28 => software_reset : rw,
      29 => independent_watchdog_reset : rw,
      30 => window_watchdog_reset : rw,
      31 => low_power_reset : rw,
    },
  });

  ioregs!(FLASH = {
    0x00 => reg32 acr {     // access control
      31..0 => access_control : rw,
    },
    0x04 => reg32 pecr {    // program/erase control
      31..0 => program_control : rw,
    },
    0x08 => reg32 pdkeyr {  // power down key
      31..0 => power_down : rw,
    },
    0x0C => reg32 pekeyr {  // program/erase key
      31..0 => program_key : rw,
    },
    0x10 => reg32 prtkeyr { // program memory key
      31..0 => program_memory : rw,
    },
    0x14 => reg32 optkeyr { // option byte key
      31..0 => option_byte : rw,
    },
    0x18 => reg32 sr {      // status register
      31..0 => status : rw,
    },
    0x1C => reg32 obr {     // option byte
      31..0 => option : rw,
    },
    0x20 => reg32 wrpr {    // write protection
      31..0 => protect : rw,
    },
    0x28 => reg32 wrpr1 {   // write protection register 1
      31..0 => protect : rw,
    },
    0x2C => reg32 wrpr2 {   // write protection register 2
      31..0 => protect : rw,
    },
  });

  ioregs!(PWR = {
    0x0 => reg32 cr {   // power control
      31..0 => control : rw,
    },
    0x4 => reg32 csr {  // power control/status
      31..0 => status : rw,
    },
  });

  extern {
    #[link_name="stm32f1_iomem_RCC"] pub static RCC: RCC;
    #[link_name="stm32f1_iomem_FLASH"] pub static FLASH: FLASH;
    #[link_name="stm32f1_iomem_PWR"] pub static PWR: PWR;
  }
}
<commit_msg>device initialization<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Dzmitry "kvark" Malyshau <kvarkus@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Routines for initialization of STM32F1.
//!
//! This module includes code for setting up the clock, flash, access time and
//! performing initial peripheral configuration.

//use hal::mem_init::init_data;
use core::default;
use core::intrinsics::abort;

use self::SystemClockSource::*;
use self::ClockAhbPrescaler::*;
use self::ClockApbPrescaler::*;
use self::McoSource::*;

#[path="../../util/wait_for.rs"]
#[macro_use] mod wait_for;

/// Phase-locked loop clock source.
#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum PllClockSource {
  /// Takes base clock from HSI/2.
  PllSourceHSIDiv2,
  /// Takes base clock from HSE.
  PllSourceHSE(u32),
}

#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum PllMult {
  PllMul2,
  PllMul3,
  PllMul4,
  PllMul5,
  PllMul6,
  PllMul7,
  PllMul8,
  PllMul9,
  PllMul10,
  PllMul11,
  PllMul12,
  PllMul13,
  PllMul14,
  PllMul15,
  PllMul16,
}

#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum PllHsePrediv {
    PllHsePrediv1,
    PllHsePrediv2,
}

#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum PllUsbDiv {
    PllUsbDiv1,
    PllUsbDiv1p5,
}

#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub struct PllConf {
    pub source: PllClockSource,
    pub mult: PllMult,
    pub hse_prediv: PllHsePrediv,
    pub usb_psc: PllUsbDiv,
}

/// System clock source.
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum SystemClockSource {
  /// High-speed internal oscillator, 8MHz.
  SystemClockHSI,
  /// High-speed external oscillator with configurable frequency.
  SystemClockHSE(u32),
  /// PLL.
  SystemClockPLL(PllConf),
}

impl default::Default for SystemClockSource {
  fn default() -> SystemClockSource {
    SystemClockHSI
  }
}

impl SystemClockSource {
  /// Returns the system clock frequency.
  pub fn frequency(&self) -> u32 {
    match *self {
        SystemClockHSI => 8_000_000,
        SystemClockHSE(_) => unsafe { abort() }, //TODO(kvark)
        SystemClockPLL(_) => unsafe { abort() }, //TODO(kvark)
    }
  }
}

#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum McoSource {
  McoClockNone,
  McoClockSys,
  McoClockHSI,
  McoClockHSE,
  McoClockPLL,
}

#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum ClockAhbPrescaler {
    AhbDivNone,
    AhbDiv2,
    AhbDiv4,
    AhbDiv8,
    AhbDiv16,
    AhbDiv64,
    AhbDiv128,
    AhbDiv256,
    AhbDiv512,
}

#[allow(missing_docs)]
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum ClockApbPrescaler {
    ApbDivNone,
    ApbDiv2,
    ApbDiv4,
    ApbDiv8,
    ApbDiv16,
}

/// System clock configuration.
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub struct ClockConfig {
  pub source : SystemClockSource,
  pub ahb_prescaler : ClockAhbPrescaler,
  pub apb1_prescaler : ClockApbPrescaler,
  pub apb2_prescaler : ClockApbPrescaler,
  pub mco : McoSource,
}

impl default::Default for ClockConfig {
  fn default() -> ClockConfig {
    ClockConfig {
      source: default::Default::default(),
      ahb_prescaler : AhbDivNone,
      apb1_prescaler : ApbDivNone,
      apb2_prescaler : ApbDivNone,
      mco: McoClockNone,
    }
  }
}

impl ClockConfig {
  /// Return the default clock configuration that hardware go to after reset.
  pub fn new_default() -> ClockConfig {
    default::Default::default()
  }

  /// Set this configuration on the hardware.
  pub fn setup(&self) {
    let r = &reg::RCC;

    let source_type = match self.source {
      SystemClockHSI => {
        r.cr.set_hsi_on(true);
        wait_for!(r.cr.hsi_ready());
        0b00
      },
      SystemClockHSE(_) => {
        r.cr.set_hse_on(true);
        wait_for!(r.cr.hse_ready());
        //TODO(kvark): HSE config
        0b01
      },
      SystemClockPLL(_pll_conf) => {
        r.cr.set_pll_on(true);
        wait_for!(r.cr.pll_ready());
        //TODO(blazewicz): PLL config
        0b10
      }
    };

    r.cfgr.set_system_clock(source_type);
    wait_for!(r.cfgr.system_clock_status() == source_type);

    let ahb_select = match self.ahb_prescaler {
        AhbDivNone => 0b0000u32,
        AhbDiv2    => 0b1000u32,
        AhbDiv4    => 0b1001u32,
        AhbDiv8    => 0b1010u32,
        AhbDiv16   => 0b1011u32,
        AhbDiv64   => 0b1100u32,
        AhbDiv128  => 0b1101u32,
        AhbDiv256  => 0b1110u32,
        AhbDiv512  => 0b1111u32,
    };
    r.cfgr.set_ahb_prescaler(ahb_select);

    let apb1_select = match self.apb1_prescaler {
        ApbDivNone => 0b000u32,
        ApbDiv2    => 0b100u32,
        ApbDiv4    => 0b101u32,
        ApbDiv8    => 0b110u32,
        ApbDiv16   => 0b111u32,
    };
    r.cfgr.set_apb1_prescaler(apb1_select);

    let apb2_select = match self.apb2_prescaler {
        ApbDivNone => 0b000u32,
        ApbDiv2    => 0b100u32,
        ApbDiv4    => 0b101u32,
        ApbDiv8    => 0b110u32,
        ApbDiv16   => 0b111u32,
    };
    r.cfgr.set_apb2_prescaler(apb2_select);

    let mco_select = match self.mco {
        McoClockNone => 0b000u32,
        McoClockSys  => 0b100u32,
        McoClockHSI  => 0b101u32,
        McoClockHSE  => 0b110u32,
        McoClockPLL  => 0b111u32,
    };
    r.cfgr.set_mco(mco_select);
  }

  /// Returns AHB clock frequency
  pub fn get_ahb_frequency(&self) -> u32 {
    0 //TODO(blazewicz)
  }

  /// Returns APB1 clock frequency
  pub fn get_apb1_frequency(&self) -> u32 {
    0 //TODO(blazewicz)
  }

  /// Returns APB2 clock frequency
  pub fn get_apb2_frequency(&self) -> u32 {
    0 //TODO(blazewicz)
  }
}

// TODO(farcaller): this mod is pub as it's being used in peripheral_clock.rs.
//                  This is not the best design solution and a good reason to
//                  split RCC into distinct registers.
#[allow(missing_docs)]
pub mod reg {
  use volatile_cell::VolatileCell;
  use core::ops::Drop;

  ioregs!(RCC = {
    0x00 => reg32 cr {          // clock control
      0 => hsi_on : rw,
      1 => hsi_ready : ro,
      7..3 => hsi_trim : rw,
      15..8 => hsi_cal : rw,
      16 => hse_on : rw,
      17 => hse_ready : ro,
      18 => hse_bypass : rw,
      24 => pll_on : rw,
      25 => pll_ready : ro,
    },
    0x04 => reg32 cfgr {        // clock configuration
      1..0   => system_clock : rw,
      3..2   => system_clock_status: ro,
      7..4   => ahb_prescaler : rw,
      10..8  => apb1_prescaler : rw,
      13..11 => apb2_prescaler : rw,
      15..14 => adc_prescaler : rw,
      16     => pll_clock_source : rw,
      17     => pll_hse_divider : rw,
      21..18 => pll_mul_factor : rw,
      22     => usb_prescaler : rw,
      26..24 => mco : rw,
    },
    0x08 => reg32 cir {         // clock interrupt
      31..0 => clock_interrupt : rw,
    },
    0x0C => reg32 apb2rstr {    // APB2 peripheral reset
      31..0 => reset : rw,
    },
    0x10 => reg32 apb1rstr {    // APB1 peripheral reset
      31..0 => reset : rw,

    },
    0x14 => reg32 ahbenr {      // AHB peripheral clock enable
      31..0 => enable : rw,
    },
    0x18 => reg32 apb2enr {     // APB2 peripheral clock enable
      31..0 => enable : rw,
    },
    0x1C => reg32 apb1enr {     // ABB1 peripheral clock enable
      31..0 => enable : rw,
    },
    0x20 => reg32 bdcr {
      0    => lse_on : rw,
      1    => lse_ready : ro,
      2    => lse_bypass : rw,
      9..8 => rtc_source : rw,
      15   => rtc_on : rw,
      16   => backup_reset : rw,
    }
    0x24 => reg32 csr {         // control/status
      0 => lsi_on : rw, // internal low speed oscillator
      1 => lsi_ready : ro,
      24 => remove_reset : rw,
      26 => pin_reset : rw,
      27 => pop_pdr_reset : rw,
      28 => software_reset : rw,
      29 => independent_watchdog_reset : rw,
      30 => window_watchdog_reset : rw,
      31 => low_power_reset : rw,
    },
  });

  ioregs!(FLASH = {
    0x00 => reg32 acr {     // access control
      2..0 => latency : rw,
      3    => flash_half_cycle_on : rw,
      4    => prefetch_buf_on : rw,
      5    => prefetch_buf_status : ro,
    },
  });

  ioregs!(PWR = {
    0x0 => reg32 cr {   // power control
      31..0 => control : rw,
    },
    0x4 => reg32 csr {  // power control/status
      31..0 => status : rw,
    },
  });

  extern {
    #[link_name="stm32f1_iomem_RCC"] pub static RCC: RCC;
    #[link_name="stm32f1_iomem_FLASH"] pub static FLASH: FLASH;
    #[link_name="stm32f1_iomem_PWR"] pub static PWR: PWR;
  }
}
<|endoftext|>


<commit_before>use decoders::RawImage;
use imageops::OpBuffer;

pub fn convert(img: &RawImage) -> OpBuffer {
  // Calculate the resulting width/height and top-left corner after crops
  let width = img.width - img.crops[1] - img.crops[3];
  let height = img.height - img.crops[0] - img.crops[2];
  let x = img.crops[3];
  let y = img.crops[0];

  if img.cpp == 1 && !img.cfa.is_valid() {
    // We're in a monochrome image so turn it into RGB
    let mut out = OpBuffer::new(width, height, 4);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+x)+y..].chunks(1)) {
        o[0] = i[0] as f32;
        o[1] = i[0] as f32;
        o[2] = i[0] as f32;
        o[3] = 0.0;
      }
    }));
    out
  } else if img.cpp == 3 {
    // We're in an RGB image, turn it into four channel
    let mut out = OpBuffer::new(width, height, 4);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+x)+y..].chunks(3)) {
        o[0] = i[0] as f32;
        o[1] = i[1] as f32;
        o[2] = i[2] as f32;
        o[3] = 0.0;
      }
    }));
    out
  } else {
    let mut out = OpBuffer::new(width, height, img.cpp);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(1).zip(img.data[img.width*(row+x)+y..].chunks(1)) {
        o[0] = i[0] as f32;
      }
    }));
    out
  }
}
<commit_msg>gofloat: fix broken crop calculations<commit_after>use decoders::RawImage;
use imageops::OpBuffer;

pub fn convert(img: &RawImage) -> OpBuffer {
  // Calculate the resulting width/height and top-left corner after crops
  let width = img.width - img.crops[1] - img.crops[3];
  let height = img.height - img.crops[0] - img.crops[2];
  let x = img.crops[3];
  let y = img.crops[0];

  if img.cpp == 1 && !img.cfa.is_valid() {
    // We're in a monochrome image so turn it into RGB
    let mut out = OpBuffer::new(width, height, 4);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+y)+x..].chunks(1)) {
        o[0] = i[0] as f32;
        o[1] = i[0] as f32;
        o[2] = i[0] as f32;
        o[3] = 0.0;
      }
    }));
    out
  } else if img.cpp == 3 {
    // We're in an RGB image, turn it into four channel
    let mut out = OpBuffer::new(width, height, 4);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+y)+x..].chunks(3)) {
        o[0] = i[0] as f32;
        o[1] = i[1] as f32;
        o[2] = i[2] as f32;
        o[3] = 0.0;
      }
    }));
    out
  } else {
    let mut out = OpBuffer::new(width, height, img.cpp);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(1).zip(img.data[img.width*(row+y)+x..].chunks(1)) {
        o[0] = i[0] as f32;
      }
    }));
    out
  }
}
<|endoftext|>


<commit_before>use decoders::RawImage;
use imageops::{OpBuffer,ImageOp,Pipeline};

#[derive(Copy, Clone, Debug)]
pub struct OpGoFloat {
}

impl OpGoFloat {
  pub fn new(_img: &RawImage) -> OpGoFloat {
    OpGoFloat{}
  }
}

impl ImageOp for OpGoFloat {
  fn name(&self) -> &str {"gofloat"}
  fn run(&self, pipeline: &Pipeline, _buf: &OpBuffer) -> OpBuffer {
    convert(pipeline.image)
  }
}

pub fn convert(img: &RawImage) -> OpBuffer {
  // Calculate the resulting width/height and top-left corner after crops
  let width = img.width - img.crops[1] - img.crops[3];
  let height = img.height - img.crops[0] - img.crops[2];
  let x = img.crops[3];
  let y = img.crops[0];

  if img.cpp == 1 && !img.cfa.is_valid() {
    // We're in a monochrome image so turn it into RGB
    let mut out = OpBuffer::new(width, height, 4);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+y)+x..].chunks(1)) {
        o[0] = i[0] as f32;
        o[1] = i[0] as f32;
        o[2] = i[0] as f32;
        o[3] = 0.0;
      }
    }));
    out
  } else if img.cpp == 3 {
    // We're in an RGB image, turn it into four channel
    let mut out = OpBuffer::new(width, height, 4);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+y)+x..].chunks(3)) {
        o[0] = i[0] as f32;
        o[1] = i[1] as f32;
        o[2] = i[2] as f32;
        o[3] = 0.0;
      }
    }));
    out
  } else {
    let mut out = OpBuffer::new(width, height, img.cpp);
    out.mutate_lines(&(|line: &mut [f32], row| {
      for (o, i) in line.chunks_mut(1).zip(img.data[img.width*(row+y)+x..].chunks(1)) {
        o[0] = i[0] as f32;
      }
    }));
    out
  }
}
<commit_msg>gofloat: store all its inputs in the struct<commit_after>use decoders::RawImage;
use imageops::{OpBuffer,ImageOp,Pipeline};

#[derive(Copy, Clone, Debug)]
pub struct OpGoFloat {
  width: usize,
  height: usize,
  x: usize,
  y: usize,
  cpp: usize,
  is_cfa: bool,
}

impl OpGoFloat {
  pub fn new(img: &RawImage) -> OpGoFloat {
    // Calculate the resulting width/height and top-left corner after crops
    OpGoFloat{
      width: img.width - img.crops[1] - img.crops[3],
      height: img.height - img.crops[0] - img.crops[2],
      x: img.crops[3],
      y: img.crops[0],
      cpp: img.cpp,
      is_cfa: img.cfa.is_valid(),
    }
  }
}

impl ImageOp for OpGoFloat {
  fn name(&self) -> &str {"gofloat"}
  fn run(&self, pipeline: &Pipeline, _buf: &OpBuffer) -> OpBuffer {
    let img = pipeline.image;
    let x = self.x;
    let y = self.y;

    if self.cpp == 1 && !self.is_cfa {
      // We're in a monochrome image so turn it into RGB
      let mut out = OpBuffer::new(self.width, self.height, 4);
      out.mutate_lines(&(|line: &mut [f32], row| {
        for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+y)+x..].chunks(1)) {
          o[0] = i[0] as f32;
          o[1] = i[0] as f32;
          o[2] = i[0] as f32;
          o[3] = 0.0;
        }
      }));
      out
    } else if self.cpp == 3 {
      // We're in an RGB image, turn it into four channel
      let mut out = OpBuffer::new(self.width, self.height, 4);
      out.mutate_lines(&(|line: &mut [f32], row| {
        for (o, i) in line.chunks_mut(4).zip(img.data[img.width*(row+y)+x..].chunks(3)) {
          o[0] = i[0] as f32;
          o[1] = i[1] as f32;
          o[2] = i[2] as f32;
          o[3] = 0.0;
        }
      }));
      out
    } else {
      let mut out = OpBuffer::new(self.width, self.height, img.cpp);
      out.mutate_lines(&(|line: &mut [f32], row| {
        for (o, i) in line.chunks_mut(1).zip(img.data[img.width*(row+y)+x..].chunks(1)) {
          o[0] = i[0] as f32;
        }
      }));
      out
    }
  }
}
<|endoftext|>


<commit_before>#![crate_id = "jit_macro"]
#![comment = "LibJIT Macro"]
#![crate_type = "dylib"]
#![crate_type = "rlib"]
#![feature(quote, globs, macro_registrar, managed_boxes)]
#![deny(non_uppercase_statics, missing_doc, unnecessary_parens, unrecognized_lint, unreachable_code, unnecessary_allocation, unnecessary_typecast, unnecessary_allocation, uppercase_variables, non_camel_case_types, unused_must_use)]
//! This crate provides a macro `jit_compile` which can compile a Rust type identifier
//! into its LibJIT counterpart.
//!
//! For example:
//! ```rust
//! # #![feature(phase)]
//! # extern crate regex;
//! # #[phase(syntax)]
//! # extern crate regex_macros;
//! # fn main() {
//! let ty = jit_compile!(i64);
//! assert_eq(ty.get_size(), 8);
//! #
//! ```
extern crate syntax;
use syntax::ext::quote::rt::ToSource;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::parse::token::*;
use std::iter::Peekable;
use std::slice::Items;
#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
	let expander = box BasicMacroExpander { expander: jit_compile, span: None };
	register(token::intern("jit_compile"), NormalTT(expander, None))
}
fn error(e:Option<TokenTree>, tok:Token) -> Result<(), String> {
	match e {
		Some(TTTok(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTNonterminal(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTDelim(ref trees)) => error(Some(trees.deref().get(0).clone()), tok),
		Some(_) => Err("Bad syntax".into_string()),
		None => Err("Abrupt end".into_string())
	}
}
fn expect<'a>(_: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>, tok:Token) -> Result<(), String> {
	match tts.next().map(|v| v.clone()) {
		Some(TTTok(_, ref got)) if *got == tok => {
			Ok(())
		},
		Some(TTDelim(ref trees)) => {
			let curr = trees.deref().get(0).clone();
			match curr {
				TTTok(_, ref got) if *got == tok =>
					Ok(()),
				_ =>
					Err("Bad result".into_string())
			}
		},
		v => error(v, tok)
	}
}
fn jit_parse_type<'a>(cx: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>) -> Result<P<Expr>, String> {
	let val:Option<&TokenTree> = tts.peek().map(|v|*v);
	match val {
		Some(ref val) => {
			match **val {
				TTDelim(ref toks) => {
					match *toks.get(0) {
						TTTok(span, LPAREN) => {
							let toks = toks.slice(1, toks.len() - 1);
							if toks.len() == 0 {
								Ok(quote_expr!(cx, ::jit::Types::get_void()))
							} else {
								let mut arg_types : Vec<@Expr> = Vec::new();
								{
									let mut tts = toks.iter().peekable();
									loop {
										let parsed = try!(jit_parse_type(cx, &mut tts));
										// &*parsed
										let derefed = cx.expr_deref(span, parsed);
										arg_types.push((&mut *cx).expr_addr_of(span, derefed));
										match tts.next() {
											Some(v) =>
												match *v {
													TTTok(_, COMMA) => {
														continue;
													},
													_ => return Err("Unexpected token in function decl".into_string())
												},
											None => break
										}
									}
								}
								tts.next();
								try!(expect(cx, tts, RARROW));
								let ret = try!(jit_parse_type(cx, tts));
								let arg_types_ex = cx.expr(ret.span, ExprVec(arg_types));
								let func = quote_expr!(&mut*cx, ::jit::Type::create_signature);
								let cdecl = quote_expr!(&mut*cx, ::jit::CDECL);
								let arg_types_ex = cx.expr_mut_addr_of(span, arg_types_ex);
								let call = cx.expr_call(span, func, vec!(cdecl, ret, arg_types_ex));
								Ok(call)

							}
						},
						_ => Err("Expected bracker".into_string())
					}
				},
				TTTok(_, IDENT(ident, _)) => {
					let ident = ident.to_source();
					let res = match ident.as_slice() {
						"void" => Ok(quote_expr!(cx, ::jit::Types::get_void())),
						"int" | "i32" => Ok(quote_expr!(cx, ::jit::Types::get_int())),
						"uint" | "u32" => Ok(quote_expr!(cx, ::jit::Types::get_uint())),
						"i64" => Ok(quote_expr!(cx, ::jit::Types::get_long())),
						"u64" => Ok(quote_expr!(cx, ::jit::Types::get_ulong())),
						"char" => Ok(quote_expr!(cx, ::jit::Types::get_char())),
						"bool" => Ok(quote_expr!(cx, ::jit::Types::get_bool())),
						"f64" => Ok(quote_expr!(cx, ::jit::Types::get_float64())),
						"f32" => Ok(quote_expr!(cx, ::jit::Types::get_float32())),
						"Vec" => Ok(quote_expr!(cx, ::jit::Types::get_vec())),
						id => Err(format!("Unexpected identifier {}", id))
					};
					if res.is_ok() {
						tts.next();
					}
					res
				},
				TTTok(span, BINOP(STAR)) => {
					tts.next();
					let func = quote_expr!(&mut*cx, ::jit::Type::create_pointer);
					let parsed_type = try!(jit_parse_type(&mut*cx, tts));
					let wv = cx.expr_addr_of(span, parsed_type);
					Ok(cx.expr_call(span, func, vec!(wv)))
				},
				TTTok(_, DOLLAR) => {
					tts.next();
					match tts.next() {
						Some(ref tks) => {
							match **tks {
								TTTok(span, IDENT(ident, _)) => {
									Ok(cx.expr_ident(span, ident))
								}
								_ => Err("Expected next".into_string())
							}
						},
						None => Err("Abrupt end".into_string())
					}
				},
				ref token => {
					try!(error(Some(token.clone()), DOLLAR));
					Err("...".into_string())
				}
			}
		},
		None =>
			Err("Not a type".into_string())
	}
}
fn jit_compile(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
	let mut iter = tts.iter().peekable();
	MacExpr::new(match jit_parse_type(cx, &mut iter) {
		Ok(v) => v,
		Err(e) => {
			cx.span_err(sp, e.as_slice());
			quote_expr!(cx, fail!("Invalid"))
		}
	})
}<commit_msg>Fix formatting error in example in libjit_macro<commit_after>#![crate_id = "jit_macro"]
#![comment = "LibJIT Macro"]
#![crate_type = "dylib"]
#![crate_type = "rlib"]
#![feature(quote, globs, macro_registrar, managed_boxes)]
#![deny(non_uppercase_statics, missing_doc, unnecessary_parens, unrecognized_lint, unreachable_code, unnecessary_allocation, unnecessary_typecast, unnecessary_allocation, uppercase_variables, non_camel_case_types, unused_must_use)]
//! This crate provides a macro `jit_compile` which can compile a Rust type identifier
//! into its LibJIT counterpart.
//!
//! For example:
//!
//! ```rust
//! #![feature(phase)]
//! extern crate jit;
//! #[phase(syntax)]
//! extern crate jit_macro;
//! fn main() {
//! 	let ty = jit_compile!(i64);
//! 	assert_eq(ty.get_size(), 8);
//! }
//! ```
extern crate syntax;
use syntax::ext::quote::rt::ToSource;
use syntax::ast::*;
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::parse::token::*;
use std::iter::Peekable;
use std::slice::Items;
#[macro_registrar]
#[doc(hidden)]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
	let expander = box BasicMacroExpander { expander: jit_compile, span: None };
	register(token::intern("jit_compile"), NormalTT(expander, None))
}
fn error(e:Option<TokenTree>, tok:Token) -> Result<(), String> {
	match e {
		Some(TTTok(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTNonterminal(_, ref got)) => Err(format!("Bad syntax - got {} but expected {}", got, tok)),
		Some(TTDelim(ref trees)) => error(Some(trees.deref().get(0).clone()), tok),
		Some(_) => Err("Bad syntax".into_string()),
		None => Err("Abrupt end".into_string())
	}
}
fn expect<'a>(_: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>, tok:Token) -> Result<(), String> {
	match tts.next().map(|v| v.clone()) {
		Some(TTTok(_, ref got)) if *got == tok => {
			Ok(())
		},
		Some(TTDelim(ref trees)) => {
			let curr = trees.deref().get(0).clone();
			match curr {
				TTTok(_, ref got) if *got == tok =>
					Ok(()),
				_ =>
					Err("Bad result".into_string())
			}
		},
		v => error(v, tok)
	}
}
fn jit_parse_type<'a>(cx: &mut ExtCtxt, tts:&mut Peekable<&'a TokenTree, Items<'a, TokenTree>>) -> Result<P<Expr>, String> {
	let val:Option<&TokenTree> = tts.peek().map(|v|*v);
	match val {
		Some(ref val) => {
			match **val {
				TTDelim(ref toks) => {
					match *toks.get(0) {
						TTTok(span, LPAREN) => {
							let toks = toks.slice(1, toks.len() - 1);
							if toks.len() == 0 {
								Ok(quote_expr!(cx, ::jit::Types::get_void()))
							} else {
								let mut arg_types : Vec<@Expr> = Vec::new();
								{
									let mut tts = toks.iter().peekable();
									loop {
										let parsed = try!(jit_parse_type(cx, &mut tts));
										// &*parsed
										let derefed = cx.expr_deref(span, parsed);
										arg_types.push((&mut *cx).expr_addr_of(span, derefed));
										match tts.next() {
											Some(v) =>
												match *v {
													TTTok(_, COMMA) => {
														continue;
													},
													_ => return Err("Unexpected token in function decl".into_string())
												},
											None => break
										}
									}
								}
								tts.next();
								try!(expect(cx, tts, RARROW));
								let ret = try!(jit_parse_type(cx, tts));
								let arg_types_ex = cx.expr(ret.span, ExprVec(arg_types));
								let func = quote_expr!(&mut*cx, ::jit::Type::create_signature);
								let cdecl = quote_expr!(&mut*cx, ::jit::CDECL);
								let arg_types_ex = cx.expr_mut_addr_of(span, arg_types_ex);
								let call = cx.expr_call(span, func, vec!(cdecl, ret, arg_types_ex));
								Ok(call)

							}
						},
						_ => Err("Expected bracker".into_string())
					}
				},
				TTTok(_, IDENT(ident, _)) => {
					let ident = ident.to_source();
					let res = match ident.as_slice() {
						"void" => Ok(quote_expr!(cx, ::jit::Types::get_void())),
						"int" | "i32" => Ok(quote_expr!(cx, ::jit::Types::get_int())),
						"uint" | "u32" => Ok(quote_expr!(cx, ::jit::Types::get_uint())),
						"i64" => Ok(quote_expr!(cx, ::jit::Types::get_long())),
						"u64" => Ok(quote_expr!(cx, ::jit::Types::get_ulong())),
						"char" => Ok(quote_expr!(cx, ::jit::Types::get_char())),
						"bool" => Ok(quote_expr!(cx, ::jit::Types::get_bool())),
						"f64" => Ok(quote_expr!(cx, ::jit::Types::get_float64())),
						"f32" => Ok(quote_expr!(cx, ::jit::Types::get_float32())),
						"Vec" => Ok(quote_expr!(cx, ::jit::Types::get_vec())),
						id => Err(format!("Unexpected identifier {}", id))
					};
					if res.is_ok() {
						tts.next();
					}
					res
				},
				TTTok(span, BINOP(STAR)) => {
					tts.next();
					let func = quote_expr!(&mut*cx, ::jit::Type::create_pointer);
					let parsed_type = try!(jit_parse_type(&mut*cx, tts));
					let wv = cx.expr_addr_of(span, parsed_type);
					Ok(cx.expr_call(span, func, vec!(wv)))
				},
				TTTok(_, DOLLAR) => {
					tts.next();
					match tts.next() {
						Some(ref tks) => {
							match **tks {
								TTTok(span, IDENT(ident, _)) => {
									Ok(cx.expr_ident(span, ident))
								}
								_ => Err("Expected next".into_string())
							}
						},
						None => Err("Abrupt end".into_string())
					}
				},
				ref token => {
					try!(error(Some(token.clone()), DOLLAR));
					Err("...".into_string())
				}
			}
		},
		None =>
			Err("Not a type".into_string())
	}
}
fn jit_compile(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
	let mut iter = tts.iter().peekable();
	MacExpr::new(match jit_parse_type(cx, &mut iter) {
		Ok(v) => v,
		Err(e) => {
			cx.span_err(sp, e.as_slice());
			quote_expr!(cx, fail!("Invalid"))
		}
	})
}<|endoftext|>