<commit_before>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![allow(raw_pointer_deriving)]

extern crate libc;

use std::c_str::CString;
use std::string::String;
use std::vec::Vec;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

#[cfg(target_os="windows")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

mod ffi {
    pub use libc::{c_char, c_int, int64_t, uint64_t};
    pub use libc::types::os::arch::c95::size_t;

    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_msgpack_sbuffer {
        pub size: size_t,
        pub data: *const c_char,
        pub alloc: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Object;
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Array {
        pub items: *mut C_Object,
        pub size: size_t,
        pub capacity: size_t,
    }

    extern "C" {
        pub fn nvim_main (argc: c_int, argv: *const *const c_char,
                          read_fd: c_int, write_fd: c_int) -> c_int;
        pub fn vim_array_new () -> *mut C_Array;
        pub fn vim_array_free (arr: *mut C_Array);
        pub fn vim_array_add_int (val: int64_t, arr: *mut C_Array);
        pub fn vim_sbuffer_new () -> *mut C_msgpack_sbuffer;
        pub fn vim_sbuffer_free (sbuf: *mut C_msgpack_sbuffer);
        pub fn vim_serialize_request (request_id: uint64_t, method: *const c_char, args: *const C_Array, sbuf: *mut C_msgpack_sbuffer);
    }
}

#[deriving(Copy)]
pub struct Request {
    id: u64,
    method: &'static str,
    arguments: *mut ffi::C_Array,
}

impl Request {
    pub fn new(id: u64, method: &'static str) -> Request {
        Request {
            id: id,
            method: method,
            arguments: unsafe { ffi::vim_array_new() }
        }
    }

    pub fn add_int(&mut self, val: i64) {
        unsafe { ffi::vim_array_add_int(val, self.arguments) }
    }

    pub fn serialize(&self) -> String {
        unsafe {
            let buf = ffi::vim_sbuffer_new();
            ffi::vim_serialize_request(self.id,
                                       self.method.to_c_str().as_ptr(),
                                       self.arguments as *const ffi::C_Array,
                                       buf);
            let s = String::from_raw_buf_len((*buf).data as *const u8, (*buf).size as uint);
            ffi::vim_sbuffer_free(buf);
            ffi::vim_array_free(self.arguments);
            s
        }
    }
}

#[test]
fn test_request() {
    let mut r = Request::new(1, "attach_ui");
    r.add_int(80);
    r.add_int(24);
    println!("{}", r.serialize());
}

pub fn run_with_fds(args: Vec<String>, read_fd: i32, write_fd: i32) -> i32 {
    let v: Vec<CString> = args.iter().map(|s| s.as_slice().to_c_str()).collect();
    let vp: Vec<*const ffi::c_char> = v.iter().map(|s| s.as_ptr()).collect();
    let p_vp: *const *const ffi::c_char = vp.as_ptr();

    unsafe { ffi::nvim_main(vp.len() as i32, p_vp, read_fd, write_fd) }
}

pub fn run(args: Vec<String>) -> i32 {
    run_with_fds(args, -1, -1)
}
<commit_msg>Allow reuse of Request object<commit_after>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![allow(raw_pointer_deriving)]

extern crate libc;

use std::c_str::CString;
use std::string::String;
use std::vec::Vec;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

#[cfg(target_os="windows")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

mod ffi {
    pub use libc::{c_char, c_int, int64_t, uint64_t};
    pub use libc::types::os::arch::c95::size_t;

    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_msgpack_sbuffer {
        pub size: size_t,
        pub data: *const c_char,
        pub alloc: size_t,
    }
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Object;
    #[repr(C)]
    #[deriving(Copy)]
    pub struct C_Array {
        pub items: *mut C_Object,
        pub size: size_t,
        pub capacity: size_t,
    }

    extern "C" {
        pub fn nvim_main (argc: c_int, argv: *const *const c_char,
                          read_fd: c_int, write_fd: c_int) -> c_int;
        pub fn vim_array_new () -> *mut C_Array;
        pub fn vim_array_free (arr: *mut C_Array);
        pub fn vim_array_add_int (val: int64_t, arr: *mut C_Array);
        pub fn vim_sbuffer_new () -> *mut C_msgpack_sbuffer;
        pub fn vim_sbuffer_free (sbuf: *mut C_msgpack_sbuffer);
        pub fn vim_serialize_request (request_id: uint64_t, method: *const c_char, args: *const C_Array, sbuf: *mut C_msgpack_sbuffer);
    }
}

#[deriving(Copy)]
pub struct Request {
    id: u64,
    method: &'static str,
    arguments: *mut ffi::C_Array,
}

impl Request {
    pub fn new(id: u64, method: &'static str) -> Request {
        Request {
            id: id,
            method: method,
            arguments: ::std::ptr::null_mut()
        }
    }

    fn check_arguments(&mut self) {
        if self.arguments.is_null() {
            self.arguments = unsafe { ffi::vim_array_new() };
        }
    }

    pub fn add_int(&mut self, val: i64) {
        self.check_arguments();
        unsafe { ffi::vim_array_add_int(val, self.arguments) }
    }

    pub fn serialize(&mut self) -> String {
        unsafe {
            let buf = ffi::vim_sbuffer_new();
            ffi::vim_serialize_request(self.id,
                                       self.method.to_c_str().as_ptr(),
                                       self.arguments as *const ffi::C_Array,
                                       buf);
            let s = String::from_raw_buf_len((*buf).data as *const u8, (*buf).size as uint);
            ffi::vim_sbuffer_free(buf);
            ffi::vim_array_free(self.arguments);
            self.arguments = ::std::ptr::null_mut();
            s
        }
    }
}

#[test]
fn test_request() {
    let mut r = Request::new(1, "attach_ui");
    r.add_int(80);
    r.add_int(24);
    println!("{}", r.serialize());
}

pub fn run_with_fds(args: Vec<String>, read_fd: i32, write_fd: i32) -> i32 {
    let v: Vec<CString> = args.iter().map(|s| s.as_slice().to_c_str()).collect();
    let vp: Vec<*const ffi::c_char> = v.iter().map(|s| s.as_ptr()).collect();
    let p_vp: *const *const ffi::c_char = vp.as_ptr();

    unsafe { ffi::nvim_main(vp.len() as i32, p_vp, read_fd, write_fd) }
}

pub fn run(args: Vec<String>) -> i32 {
    run_with_fds(args, -1, -1)
}
<|endoftext|>


<commit_before>extern crate lru_cache;

pub mod board;
pub mod transposition_table;

use board::Board;
use transposition_table::TranspositionTable;

use std::i32;
use std::ops::Neg;
use std::hash::Hash;
use std::cmp::Ordering;

#[derive(Copy,Clone,Debug)]
pub enum Team
{
    Enemy,
    Ally,
}

impl Team
{
    pub fn other_team(self) -> Team
    {
        match self
        {
            Team::Enemy => Team::Ally,
            Team::Ally => Team::Enemy,
        }
    }
}

#[derive(PartialEq,Eq,PartialOrd,Ord,Copy,Clone,Debug,Hash)]
pub enum Score
{
    Lose,
    Heuristic(i32),
    Win,
}

impl Neg for Score
{
    type Output = Self;
    fn neg(self) -> Self
    {
        match self
        {
            Score::Win => Score::Lose,
            Score::Lose => Score::Win,
            Score::Heuristic(val) => Score::Heuristic(-val),
        }
    }
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub struct MoveStats<M>
{
    pub mv: Option<M>,
    pub score: Score,
    pub turns: u32,
    pub nodes_visited: u64,
}

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

impl<M> Ord for MoveStats<M>
    where M: Eq
{
    fn cmp(&self, other: &Self) -> Ordering {
        match self.score.cmp(&other.score) {
            Ordering::Less => Ordering::Less,
            Ordering::Equal => {
                match self.score.cmp(&Score::Heuristic(0)) {
                    Ordering::Less => self.turns.cmp(&other.turns),
                    Ordering::Equal => Ordering::Equal,
                    Ordering::Greater => other.turns.cmp(&self.turns)
                }
            }
            Ordering::Greater => Ordering::Greater,
        }
    }
}

#[derive(Clone)]
pub struct Minimax<B>
    where B: Board + Eq + Hash
{
    ally_ttable: TranspositionTable<B, MoveStats<B::Move>>,
    enemy_ttable: TranspositionTable<B, MoveStats<B::Move>>,
}

impl<B> Minimax<B>
    where B: Board + Eq + Hash
{
    pub fn new(ttable_size: usize) -> Minimax<B>
    {
        Minimax
        {
            ally_ttable: TranspositionTable::new(ttable_size),
            enemy_ttable: TranspositionTable::new(ttable_size),
        }
    }

    /// Minimax driver function.
    ///
    /// `turn` is the current player.
    pub fn minimax(&mut self, board: &B, turn: Team, plies: u32) -> MoveStats<B::Move>
    {
        let mut optimal_move = match turn
        {
            Team::Ally =>
                self.max(board, plies, Score::Lose, Score::Win),
            Team::Enemy =>
                self.min(board, plies, Score::Lose, Score::Win),
        };
        optimal_move.nodes_visited += 1;
        optimal_move
    }

    /// Generates best move for ally
    fn max(&mut self, board: &B, plies: u32, mut alpha: Score, beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_ally_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                score: Score::Lose,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            score: Score::Lose,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.ally_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {

            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find enemy's best move */
            let enemy_move = self.min(&board_clone, plies - 1, alpha, beta);

            if best.mv.is_none() || enemy_move > best
            {
                best.mv = Some(mv);
                best.score = enemy_move.score;
                best.turns = enemy_move.turns + 1;
            }

            best.nodes_visited += enemy_move.nodes_visited + 1;

            /* Set α and break on β ≤ α */
            if best.score > alpha
            {
                alpha = enemy_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.ally_ttable.insert(board.clone(), best.clone(), plies);

        best
    }

    /// Generates best move for enemy
    fn min(&mut self, board: &B, plies: u32, alpha: Score, mut beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_enemy_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                /* If enemy can't move, we win. */
                score: Score::Win,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            /* Technically doesn't matter, but for consistancy's sake */
            score: Score::Win,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.enemy_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {
            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find ally's best move */
            let ally_move = self.max(&board_clone, plies - 1, alpha, beta);

            if best.mv.is_none() || ally_move < best
            {
                best.mv = Some(mv);
                best.score = ally_move.score;
                best.turns = ally_move.turns + 1;
            }

            best.nodes_visited += ally_move.nodes_visited + 1;

            /* Set β and break on β ≤ α */
            if best.score < beta
            {
                beta = ally_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.enemy_ttable.insert(board.clone(), best.clone(), plies);

        best
    }
}

#[cfg(test)]
mod tests
{
    use super::{Team, Score, Minimax, MoveStats};
    use board::Board;

    #[derive(Clone,PartialEq,Eq,Debug)]
    struct SimpleMove(usize);
    #[derive(Clone,PartialEq,Eq,Hash,Debug)]
    enum SimpleBoard
    {
        Node(Vec<SimpleBoard>),
        Leaf(Score),
    }

    /* These aren't really efficient, but they
     * work for testing.
     */
    impl Board for SimpleBoard
    {
        type Move = SimpleMove;

        fn gen_ally_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn gen_enemy_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn do_move(&mut self, mv: &Self::Move)
        {
            let child = match *self
            {
                SimpleBoard::Node(ref v) =>
                    Some(v[mv.0].clone()),
                SimpleBoard::Leaf(_) =>
                    None,
            };

            if let Some(c) = child
            {
                *self = c;
            }
        }

        fn score(&self) -> Score
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    unreachable!(),
                SimpleBoard::Leaf(s) =>
                    s,
            }
        }

        fn is_game_over(&self) -> bool
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    false,
                SimpleBoard::Leaf(_) =>
                    true,
            }
        }
    }

    #[test]
    fn test_score_ord()
    {
        assert!(Score::Win > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Lose);
        assert!(Score::Win > Score::Lose);
        assert!(Score::Heuristic(100) > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Heuristic(-100));
        assert!(Score::Win == Score::Win);
        assert!(Score::Lose == Score::Lose);
        assert!(Score::Heuristic(0) == Score::Heuristic(0));
    }

    #[test]
    fn test_minimax()
    {
        let mut minimax = Minimax::new(100);
        let game1 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                        SimpleBoard::Leaf(Score::Heuristic(4)),
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(3)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(8)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
        ]);
        let game2 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(12)),
                        SimpleBoard::Leaf(Score::Heuristic(10)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-18)),
                        SimpleBoard::Leaf(Score::Heuristic(-7)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(-17)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-3)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-19)),
                        SimpleBoard::Leaf(Score::Heuristic(-16)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-4)),
                        SimpleBoard::Leaf(Score::Heuristic(-6)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-1)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(15)),
                    ]),
                ]),
            ]),
        ]);

        println!();
        println!("Game 1");
        assert_eq!(game1.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1), SimpleMove(2)]);
        let move_stats1 = minimax.minimax(&game1, Team::Ally, 4);
        let optimal_move1 = MoveStats {
            mv: Some(SimpleMove(1)),
            score: Score::Heuristic(6),
            turns: 4,
            nodes_visited: 25,
        };
        assert_eq!(move_stats1, optimal_move1);

        println!();
        println!("Game 2");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);

        println!();
        println!("Testing caching");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);
    }
}
<commit_msg>improved min compare and split compares<commit_after>extern crate lru_cache;

pub mod board;
pub mod transposition_table;

use board::Board;
use transposition_table::TranspositionTable;

use std::i32;
use std::ops::Neg;
use std::hash::Hash;
use std::cmp::Ordering;

#[derive(Copy,Clone,Debug)]
pub enum Team
{
    Enemy,
    Ally,
}

impl Team
{
    pub fn other_team(self) -> Team
    {
        match self
        {
            Team::Enemy => Team::Ally,
            Team::Ally => Team::Enemy,
        }
    }
}

#[derive(PartialEq,Eq,PartialOrd,Ord,Copy,Clone,Debug,Hash)]
pub enum Score
{
    Lose,
    Heuristic(i32),
    Win,
}

impl Neg for Score
{
    type Output = Self;
    fn neg(self) -> Self
    {
        match self
        {
            Score::Win => Score::Lose,
            Score::Lose => Score::Win,
            Score::Heuristic(val) => Score::Heuristic(-val),
        }
    }
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub struct MoveStats<M>
{
    pub mv: Option<M>,
    pub score: Score,
    pub turns: u32,
    pub nodes_visited: u64,
}

impl<M> MoveStats<M> {
    fn enemy_cmp(&self, other: &Self) -> Ordering {
        match self.score.cmp(&other.score) {
            Ordering::Less => Ordering::Less,
            Ordering::Equal => {
                match self.score.cmp(&Score::Heuristic(0)) {
                    Ordering::Less => other.turns.cmp(&self.turns),
                    Ordering::Equal => Ordering::Equal,
                    Ordering::Greater => self.turns.cmp(&other.turns)
                }
            }
            Ordering::Greater => Ordering::Greater,
        }
    }
}

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

impl<M> Ord for MoveStats<M>
    where M: Eq
{
    fn cmp(&self, other: &Self) -> Ordering {
        match self.score.cmp(&other.score) {
            Ordering::Less => Ordering::Less,
            Ordering::Equal => {
                match self.score.cmp(&Score::Heuristic(0)) {
                    Ordering::Less => self.turns.cmp(&other.turns),
                    Ordering::Equal => Ordering::Equal,
                    Ordering::Greater => other.turns.cmp(&self.turns)
                }
            }
            Ordering::Greater => Ordering::Greater,
        }
    }
}

#[derive(Clone)]
pub struct Minimax<B>
    where B: Board + Eq + Hash
{
    ally_ttable: TranspositionTable<B, MoveStats<B::Move>>,
    enemy_ttable: TranspositionTable<B, MoveStats<B::Move>>,
}

impl<B> Minimax<B>
    where B: Board + Eq + Hash
{
    pub fn new(ttable_size: usize) -> Minimax<B>
    {
        Minimax
        {
            ally_ttable: TranspositionTable::new(ttable_size),
            enemy_ttable: TranspositionTable::new(ttable_size),
        }
    }

    /// Minimax driver function.
    ///
    /// `turn` is the current player.
    pub fn minimax(&mut self, board: &B, turn: Team, plies: u32) -> MoveStats<B::Move>
    {
        let mut optimal_move = match turn
        {
            Team::Ally =>
                self.max(board, plies, Score::Lose, Score::Win),
            Team::Enemy =>
                self.min(board, plies, Score::Lose, Score::Win),
        };
        optimal_move.nodes_visited += 1;
        optimal_move
    }

    /// Generates best move for ally
    fn max(&mut self, board: &B, plies: u32, mut alpha: Score, beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_ally_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                score: Score::Lose,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            score: Score::Lose,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.ally_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {

            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find enemy's best move */
            let enemy_move = self.min(&board_clone, plies - 1, alpha, beta);

            if best.mv.is_none() || enemy_move > best
            {
                best.mv = Some(mv);
                best.score = enemy_move.score;
                best.turns = enemy_move.turns + 1;
            }

            best.nodes_visited += enemy_move.nodes_visited + 1;

            /* Set α and break on β ≤ α */
            if best.score > alpha
            {
                alpha = enemy_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.ally_ttable.insert(board.clone(), best.clone(), plies);

        best
    }

    /// Generates best move for enemy
    fn min(&mut self, board: &B, plies: u32, alpha: Score, mut beta: Score) -> MoveStats<B::Move>
    {
        let moves = board.gen_enemy_moves();

        /* Fail state if you can't move */
        if moves.len() == 0
        {
            return MoveStats
            {
                mv: None,
                /* If enemy can't move, we win. */
                score: Score::Win,
                turns: 0,
                nodes_visited: 0,
            };
        }

        /* If you cannot proceed further */
        if plies == 0 || board.is_game_over()
        {
            return MoveStats
            {
                mv: None,
                score: board.score(),
                turns: 0,
                nodes_visited: 0,
            }
        }

        let mut best = MoveStats {
            mv: None,
            /* Technically doesn't matter, but for consistancy's sake */
            score: Score::Win,
            turns: 0,
            nodes_visited: 0,
        };

        if let Some(precomputed_move) = self.enemy_ttable.get(board, plies)
        {
            return precomputed_move;
        }

        for mv in moves
        {
            /* Make a clone of the board so we don't break this one */
            let mut board_clone = board.clone();
            board_clone.do_move(&mv);

            /* Find ally's best move */
            let ally_move = self.max(&board_clone, plies - 1, alpha, beta);

            if best.mv.is_none() || ally_move.enemy_cmp(&best) == Ordering::Less
            {
                best.mv = Some(mv);
                best.score = ally_move.score;
                best.turns = ally_move.turns + 1;
            }

            best.nodes_visited += ally_move.nodes_visited + 1;

            /* Set β and break on β ≤ α */
            if best.score < beta
            {
                beta = ally_move.score;
            }
            if alpha >= beta
            {
                break;
            }
        }

        self.enemy_ttable.insert(board.clone(), best.clone(), plies);

        best
    }
}

#[cfg(test)]
mod tests
{
    use super::{Team, Score, Minimax, MoveStats};
    use board::Board;

    #[derive(Clone,PartialEq,Eq,Debug)]
    struct SimpleMove(usize);
    #[derive(Clone,PartialEq,Eq,Hash,Debug)]
    enum SimpleBoard
    {
        Node(Vec<SimpleBoard>),
        Leaf(Score),
    }

    /* These aren't really efficient, but they
     * work for testing.
     */
    impl Board for SimpleBoard
    {
        type Move = SimpleMove;

        fn gen_ally_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn gen_enemy_moves(&self) -> Vec<Self::Move>
        {
            match *self
            {
                SimpleBoard::Node(ref v) =>
                    (0..v.len()).map(|u| SimpleMove(u)).collect(),
                SimpleBoard::Leaf(_) =>
                    /* Trick the algorithm into thinking
                     * we still have moves left
                     */
                    vec![SimpleMove(1)],
            }
        }

        fn do_move(&mut self, mv: &Self::Move)
        {
            let child = match *self
            {
                SimpleBoard::Node(ref v) =>
                    Some(v[mv.0].clone()),
                SimpleBoard::Leaf(_) =>
                    None,
            };

            if let Some(c) = child
            {
                *self = c;
            }
        }

        fn score(&self) -> Score
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    unreachable!(),
                SimpleBoard::Leaf(s) =>
                    s,
            }
        }

        fn is_game_over(&self) -> bool
        {
            match *self
            {
                SimpleBoard::Node(_) =>
                    false,
                SimpleBoard::Leaf(_) =>
                    true,
            }
        }
    }

    #[test]
    fn test_score_ord()
    {
        assert!(Score::Win > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Lose);
        assert!(Score::Win > Score::Lose);
        assert!(Score::Heuristic(100) > Score::Heuristic(0));
        assert!(Score::Heuristic(0) > Score::Heuristic(-100));
        assert!(Score::Win == Score::Win);
        assert!(Score::Lose == Score::Lose);
        assert!(Score::Heuristic(0) == Score::Heuristic(0));
    }

    #[test]
    fn test_minimax()
    {
        let mut minimax = Minimax::new(100);
        let game1 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                        SimpleBoard::Leaf(Score::Heuristic(4)),
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(3)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(7)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(5)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(8)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
        ]);
        let game2 =
        SimpleBoard::Node(vec![
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(12)),
                        SimpleBoard::Leaf(Score::Heuristic(10)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-18)),
                        SimpleBoard::Leaf(Score::Heuristic(-7)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                        SimpleBoard::Leaf(Score::Heuristic(-17)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-3)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                ]),
            ]),
            SimpleBoard::Node(vec![
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-19)),
                        SimpleBoard::Leaf(Score::Heuristic(-16)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-4)),
                        SimpleBoard::Leaf(Score::Heuristic(-6)),
                    ]),
                ]),
                SimpleBoard::Node(vec![
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(-1)),
                        SimpleBoard::Leaf(Score::Heuristic(6)),
                    ]),
                    SimpleBoard::Node(vec![
                        SimpleBoard::Leaf(Score::Heuristic(9)),
                        SimpleBoard::Leaf(Score::Heuristic(15)),
                    ]),
                ]),
            ]),
        ]);

        println!();
        println!("Game 1");
        assert_eq!(game1.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1), SimpleMove(2)]);
        let move_stats1 = minimax.minimax(&game1, Team::Ally, 4);
        let optimal_move1 = MoveStats {
            mv: Some(SimpleMove(1)),
            score: Score::Heuristic(6),
            turns: 4,
            nodes_visited: 25,
        };
        assert_eq!(move_stats1, optimal_move1);

        println!();
        println!("Game 2");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);

        println!();
        println!("Testing caching");
        assert_eq!(game2.gen_ally_moves(), vec![SimpleMove(0), SimpleMove(1)]);
        let move_stats2 = minimax.minimax(&game2, Team::Ally, 4);
        let optimal_move2 = MoveStats {
            mv: Some(SimpleMove(0)),
            score: Score::Heuristic(-3),
            turns: 4,
            nodes_visited: 21,
        };
        assert_eq!(move_stats2, optimal_move2);
    }
}
<|endoftext|>


<commit_before>extern crate md5;

pub fn puzzle(door_id: &str) -> String {
    let mut answer = String::new();
    let mut index = 0;

    for _ in 0..9 {
        let (new_index, next_char) = next_interesting_md5(door_id, index);
        index = new_index + 1;
        answer.push(next_char);
    }
    answer
}

pub fn next_interesting_md5(door_id: &str, mut index: u32) -> (u32, char) {
    loop {
        if index % 1000000 == 0 {
            println!("at index {}", index);
        }
        let candidate = format!("{}{}", door_id, index);

        let digest = md5::compute(candidate.as_bytes());

        if digest.starts_with(b"00000") {
            return (index, *digest.iter().nth(5).expect("no 5th char") as char)
        } else {
            index += 1;
        }
    }
}

pub fn starts_with_five_zeroes(door_id: &str, mut index: u32) -> bool {
    let candidate = format!("{}{}", door_id, index);

    println!("candidate = ({})", candidate);

    let mut digest = String::new();
    for b in &md5::compute(candidate.as_bytes())[..] {
        digest.push_str(&format!("{:02x}", b));
    }

    println!("digest = {:?}", digest);

    println!("starts_with = {:?}", "00000");

    digest.starts_with("00000")

}

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

    #[test]
    fn check_hashing_is_working() {
        assert!(starts_with_five_zeroes("abc", 3231929));
    }

    // #[test]
    // fn once() {
    //     assert_eq!(next_interesting_md5("abc", 0), (3231929, '1'));
    // }

}
<commit_msg>lolol off by one but it works if i leave off the last<commit_after>extern crate md5;

pub fn puzzle(door_id: &str) -> String {
    let door_id = door_id.trim();

    let mut answer = String::new();
    let mut index = 0;

    for i in 0..9 {
        let (new_index, next_char) = next_interesting_md5(door_id, index);
        index = new_index + 1;
        answer.push(next_char);
        println!("found {} char: {}", i + 1, next_char);
    }
    answer
}

pub fn next_interesting_md5(door_id: &str, mut index: u32) -> (u32, char) {
    let mut digest = String::with_capacity(2 * 16);

    loop {
        if index % 1000000 == 0 {
            println!("at index {}", index);
        }
        let candidate = format!("{}{}", door_id, index);
        digest.clear();

        for b in &md5::compute(candidate.as_bytes())[..] {
            digest.push_str(&format!("{:02x}", b));
        }

        if digest.starts_with("00000") {
            return (index, digest.chars().nth(5).expect("no 5th char"))
        } else {
            index += 1;
        }
    }
}

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

    #[test]
    fn once() {
        assert_eq!(next_interesting_md5("abc", 0), (3231929, '1'));
    }

}
<|endoftext|>


<commit_before>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```ignore
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::LogLevel;
//!
//! fn native_activity_create() {
//!     android_logger::init_once(LogLevel::Trace);
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//!
//!     if log_enabled!(LogLevel::Info) {
//!         let x = 3 * 4; // expensive computation
//!         info!("the answer was: {}", x);
//!     }
//! }
//! ```

extern crate android_log_sys as log_ffi;
#[macro_use] extern crate log;

use log_ffi::LogPriority;
use log::{Log,LogLevel,LogMetadata,LogRecord};
use std::ffi::CStr;
use std::mem;
use std::fmt;
use std::ptr;

/// Output log to android system.
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe { log_ffi::__android_log_write(prio as log_ffi::c_int, tag.as_ptr() as *const log_ffi::c_char, msg.as_ptr() as *const log_ffi::c_char) };
}

struct PlatformLogger;

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl Log for PlatformLogger {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, record);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(
            record.level(),
            tag
        );

        // use PlatformLogWriter to output chunks if they exceed max size
        let _ = fmt::write(&mut writer, *record.args());

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }
}

impl PlatformLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], record: &LogRecord) {
        let tag_bytes_iter = record.location().module_path().bytes();
        if tag_bytes_iter.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag_bytes_iter
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        } else {
            for (input, output) in tag_bytes_iter
                .chain(b"\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        }
    }
}

struct PlatformLogWriter<'a> {
    priority: LogPriority,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    pub fn new<'r>(level: LogLevel, tag: &'r CStr) -> PlatformLogWriter<'r> {
        PlatformLogWriter {
            priority: match level {
                LogLevel::Warn => LogPriority::WARN,
                LogLevel::Info => LogPriority::INFO,
                LogLevel::Debug => LogPriority::DEBUG,
                LogLevel::Error => LogPriority::ERROR,
                LogLevel::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag: tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe { self.buffer.get_unchecked_mut(len) });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while incomming_bytes.len() > 0 {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN].iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' { Some(i) } else { acc }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(log_level: LogLevel) {
    match log::set_logger(|max_log_level| {
        max_log_level.set(log_level.to_log_level_filter());
        return Box::new(PlatformLogger);
    }) {
        Err(e) => debug!("{}", e),
        _ => (),
    }
}
<commit_msg>add log4rs support<commit_after>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```ignore
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::LogLevel;
//!
//! fn native_activity_create() {
//!     android_logger::init_once(LogLevel::Trace);
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//!
//!     if log_enabled!(LogLevel::Info) {
//!         let x = 3 * 4; // expensive computation
//!         info!("the answer was: {}", x);
//!     }
//! }
//! ```

extern crate android_log_sys as log_ffi;
#[macro_use] extern crate log;

use log_ffi::LogPriority;
use log::{Log,LogLevel,LogMetadata,LogRecord};
use std::ffi::CStr;
use std::mem;
use std::fmt;
use std::ptr;

/// Output log to android system.
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe { log_ffi::__android_log_write(prio as log_ffi::c_int, tag.as_ptr() as *const log_ffi::c_char, msg.as_ptr() as *const log_ffi::c_char) };
}

pub struct PlatformLogger;

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl Log for PlatformLogger {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, record);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(
            record.level(),
            tag
        );

        // use PlatformLogWriter to output chunks if they exceed max size
        let _ = fmt::write(&mut writer, *record.args());

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }
}

impl PlatformLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], record: &LogRecord) {
        let tag_bytes_iter = record.location().module_path().bytes();
        if tag_bytes_iter.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag_bytes_iter
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        } else {
            for (input, output) in tag_bytes_iter
                .chain(b"\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        }
    }
}

struct PlatformLogWriter<'a> {
    priority: LogPriority,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    pub fn new<'r>(level: LogLevel, tag: &'r CStr) -> PlatformLogWriter<'r> {
        PlatformLogWriter {
            priority: match level {
                LogLevel::Warn => LogPriority::WARN,
                LogLevel::Info => LogPriority::INFO,
                LogLevel::Debug => LogPriority::DEBUG,
                LogLevel::Error => LogPriority::ERROR,
                LogLevel::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag: tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe { self.buffer.get_unchecked_mut(len) });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while incomming_bytes.len() > 0 {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN].iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' { Some(i) } else { acc }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(log_level: LogLevel) {
    match log::set_logger(|max_log_level| {
        max_log_level.set(log_level.to_log_level_filter());
        return Box::new(PlatformLogger);
    }) {
        Err(e) => debug!("{}", e),
        _ => (),
    }
}
<|endoftext|>


<commit_before>#![allow(unstable)]

extern crate libc;
extern crate "libforestdb-sys" as ffi;

#[macro_use] extern crate log;

use std::default::Default;
use std::error;
use std::ffi::{CString, c_str_to_bytes};
use std::mem;
use std::ptr;

#[derive(Copy)]
pub struct Error {code: i32}

impl Error {
    pub fn from_code(code: i32) -> Error {
        Error {code: code}
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "ForestDB error"
    }

    fn detail(&self) -> Option<String> {
        unsafe { String::from_utf8(c_str_to_bytes(&ffi::fdb_error_msg(self.code)).to_vec()).ok() }
    }
}

impl std::fmt::Show for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str(&format!("fdb error: {:?}", (self as &error::Error).detail())[])
    }
}

pub type FdbResult<T> = Result<T, Error>;

macro_rules! lift_error {
    ($e: expr) => (lift_error!($e, ()));
    ($e: expr, $r: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => Ok($r),
            _ => return Err(Error::from_code(t))
        }
    })
}

macro_rules! try_fdb {
    ($e: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => (),
            _ => return Err(Error::from_code(t))
        }
    })
}

#[repr(u8)]
#[derive(Copy)]
pub enum CommitOptions {
    Normal = ffi::FDB_COMMIT_NORMAL as u8,
    ManualWalFlush = ffi::FDB_COMMIT_MANUAL_WAL_FLUSH as u8
}

#[repr(u8)]
#[derive(Copy)]
/// Transaction isolation level
pub enum IsolationLevel {
    // Serializable = 0, // unsupported yet
    // RepeatableRead = 1, // unsupported yet
    /// Prevent a transaction from reading uncommitted data from other
    /// transactions.
    ReadCommitted = 2,
    ///  Allow a transaction to see uncommitted data from other transaction.
    ReadUncommited = 3,
}

#[derive(Copy)]
pub struct Config {
    raw: ffi::fdb_config
}

impl Config {
}

impl Default for Config {
    fn default() -> Config {
        Config {
            raw: unsafe { ffi::fdb_get_default_config() }
        }
    }
}

/// Initializes ForestDB. Usually is called automatically
pub fn init(config: Config) -> Result<(), Error> {
    lift_error!(unsafe {ffi::fdb_init(mem::transmute(&config.raw))})
}

/// Forces ForestDB shutdown: closing everything and terminating
/// compactor thread
pub fn shutdown() -> Result<(), Error> {
    lift_error!(unsafe {ffi::fdb_shutdown()})
}

/// Represents ForestDB file handle
pub struct FileHandle {
    path: Path,
    config: Config,
    raw: *mut ffi::fdb_file_handle,
}

impl FileHandle {
    fn from_raw(handle: *mut ffi::fdb_file_handle, path: &Path, config: Config) -> FileHandle {
        FileHandle {
            raw: handle,
            path: path.clone(),
            config: config
        }
    }

    /// Opens database with specified config
    pub fn open(path: &Path, config: Config) -> FdbResult<FileHandle> {
        let mut handle: *mut ffi::fdb_file_handle = ptr::null_mut();
        let c_path = CString::from_slice(path.as_vec());

        try_fdb!(unsafe { ffi::fdb_open(mem::transmute(&mut handle),
                                        c_path.as_ptr(),
                                        mem::transmute(&config.raw)) });
        let res = FileHandle::from_raw(handle, path, config);
        Ok(res)
    }

    /// Retrieves default store
    pub fn get_default_store(&self, config: StoreConfig) -> FdbResult<Store> {
        self._get_store(None, config)
    }

    /// Retrieves store by name
    pub fn get_store(&self, name: &str, config: StoreConfig) -> FdbResult<Store> {
        let c_name = CString::from_slice(name.as_bytes());
        self._get_store(Some(c_name), config)
    }

    fn _get_store(&self, name: Option<CString>, config: StoreConfig) -> FdbResult<Store> {
        let mut handle: *mut ffi::fdb_kvs_handle = ptr::null_mut();
        try_fdb!(unsafe { ffi::fdb_kvs_open(self.raw,
                                            mem::transmute(&mut handle),
                                            if name.is_some() {name.unwrap().as_ptr()} else {ptr::null()},
                                            mem::transmute(&config.raw)
                                            )});
        Ok(Store::from_raw(handle, config))
    }

    /// Commit all pending doc changes
    pub fn commit(&self, options: CommitOptions) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_commit(self.raw, options as u8)})
    }

    /// Writes compacted database to new_path. If it is set to None - it'll be in-place
    /// compaction
    pub fn compact(&self, new_path: Option<Path>) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_compact(self.raw,
                                             if new_path.is_none() {
                                                 ptr::null_mut()
                                             } else {
                                                 CString::from_slice(new_path.unwrap().as_vec()).as_ptr()
                                             })})
    }

    pub fn estimate_size(&self) -> u64 {
        unsafe {ffi::fdb_estimate_space_used(self.raw) as u64}
    }

    pub fn begin_transaction(&self, isolation: IsolationLevel) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_begin_transaction(self.raw, isolation as u8)})
    }

    pub fn end_transaction(&self, options: CommitOptions) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_end_transaction(self.raw, options as u8)})
    }

    pub fn abort_transaction(&self) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_abort_transaction(self.raw)})
    }
}

unsafe impl Send for FileHandle {}

// FIXME: do not use trait, implement clone as method
// which returns result? Otherwise it is impossible
// to handle cloning right
impl Clone for FileHandle {
    fn clone(&self) -> FileHandle {
        match FileHandle::open(&self.path, self.config) {
            Ok(res) => res,
            Err(_) => panic!("failed to clone")
        }
    }
}

impl Drop for FileHandle {
    fn drop(&mut self) {
        debug!("Dropping");
        unsafe { ffi::fdb_close(self.raw); }
    }
}

impl std::fmt::Show for FileHandle {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str(&format!("FdbFileHandle {{path: {:?}}}", self.path)[])
    }
}

#[derive(Copy)]
pub struct StoreConfig {
    raw: ffi::fdb_kvs_config
}

impl Default for StoreConfig {
    fn default() -> StoreConfig {
        StoreConfig {
            raw: unsafe {ffi::fdb_get_default_kvs_config()}
        }
    }
}

/// Represents ForestDB key value store
pub struct Store {
    raw: *mut ffi::fdb_kvs_handle,
    config: StoreConfig
}

impl Store {
    fn from_raw(raw: *mut ffi::fdb_kvs_handle, config: StoreConfig) -> Store {
        Store {
            raw: raw,
            config: config
        }
    }

    /// Retrieves a value by key (plain KV mode)
    pub fn set_value<K, V>(&self, key: &K, value: &V) -> FdbResult<()>
        where K: AsSlice<u8>,
              V: AsSlice<u8>
    {
        let mut doc = try!(Doc::new(key));
        try!(doc.set_body(value));

        lift_error!(unsafe {
            ffi::fdb_set(self.raw, doc.raw)
        })
    }

    /// Sets a value for key (plain KV mode)
    pub fn get_value<K>(&self, key: &K) -> FdbResult<Vec<u8>> where K: AsSlice<u8> {
        let mut doc = try!(Doc::new(key));
        try_fdb!(unsafe {
            ffi::fdb_get(self.raw, doc.raw)
        });
        Ok(unsafe {
            Vec::from_raw_buf(mem::transmute((*doc.raw).body),
                              (*doc.raw).bodylen as usize)
        })
    }

}

impl Drop for Store {
    fn drop(&mut self) {
        unsafe {ffi::fdb_kvs_close(self.raw); }
    }
}

pub struct Doc {
    raw: *mut ffi::fdb_doc
}

impl Doc {
    pub fn new<K>(key: &K) -> FdbResult<Doc> where K: AsSlice<u8>{
        let mut handle: *mut ffi::fdb_doc = ptr::null_mut();
        let key = key.as_slice();
        try_fdb!(unsafe {ffi::fdb_doc_create(&mut handle,
                                             mem::transmute(key.as_ptr()), key.len() as u64,
                                             ptr::null(), 0,
                                             ptr::null(), 0)});
        Ok(Doc {raw: handle})
    }

    pub fn set_body<B>(&mut self, body: &B) -> FdbResult<()> where B: AsSlice<u8> {
        let body = body.as_slice();
        lift_error!(unsafe {ffi::fdb_doc_update(&mut self.raw,
                                                ptr::null(), 0,
                                                mem::transmute(body.as_ptr()), body.len() as u64)
        })
    }

    pub fn set_meta<M>(&mut self, meta: &M) -> FdbResult<()> where M: AsSlice<u8> {
        let meta = meta.as_slice();
        lift_error!(unsafe {ffi::fdb_doc_update(&mut self.raw,
                                                mem::transmute(meta.as_ptr()), meta.len() as u64,
                                                ptr::null(), 0)
        })
    }
}

impl Drop for Doc {
    fn drop(&mut self) {
        unsafe {ffi::fdb_doc_free(self.raw)};
    }
}

pub struct UnsafeDoc<'a> {
    raw: ffi::fdb_doc,
}

fn empty_doc() -> ffi::fdb_doc {
    ffi::fdb_doc {
        key: ptr::null_mut(),
        keylen: 0,
        body: ptr::null_mut(),
        bodylen: 0,
        meta: ptr::null_mut(),
        metalen: 0,
        size_ondisk: 0,
        seqnum: 0,
        offset: 0,
        deleted: 0
    }
}

impl<'a> UnsafeDoc<'a> {
    fn with_key(key: &'a [u8]) -> UnsafeDoc<'a> {
        UnsafeDoc {
            raw: ffi::fdb_doc {
                key: unsafe { mem::transmute(key.as_ptr()) },
                keylen: key.len() as libc::size_t,
                .. empty_doc()
            }
        }
    }

    fn with_key_value(key: &'a [u8], value: &'a [u8]) -> UnsafeDoc<'a> {
        UnsafeDoc {
            raw: ffi::fdb_doc {
                key: unsafe { mem::transmute(key.as_ptr()) },
                keylen: key.len() as libc::size_t,
                body: unsafe { mem::transmute(value.as_ptr()) },
                bodylen: value.len() as libc::size_t,
                .. empty_doc()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{FileHandle, Error};

    use std::default::Default;
    use std::error;
    use std::thread::Thread;

    use ffi;

    #[test]
    fn test_error_msg(){
        let err = Error::from_code(ffi::FDB_RESULT_OPEN_FAIL);
        assert_eq!("error opening file", (&err as &error::Error).detail().unwrap().as_slice());
    }

    #[test]
    fn test_init() {
        assert!(super::init(Default::default()).is_ok());
    }

    #[test]
    fn test_open_file() {
        assert!(super::init(Default::default()).is_ok());
        assert!(FileHandle::open(&Path::new("test1"), Default::default()).is_ok());
    }

    #[test]
    fn test_clone() {
        let fh1 = FileHandle::open(&Path::new("test2"), Default::default()).unwrap();
        let fh2 = fh1.clone();

        Thread::scoped(move || {
            let fh_cloned = fh2;
            // FIXME: test something useful
        }).join();
    }

    #[test]
    fn test_open_store() {
        let db = FileHandle::open(&Path::new("test3"), Default::default()).unwrap();
        assert!(db.get_default_store(Default::default()).is_ok());
        assert!(db.get_store("hello", Default::default()).is_ok())
    }

    #[test]
    fn test_simple_keys() {
        let db = FileHandle::open(&Path::new("test4"), Default::default()).unwrap();
        let store = db.get_default_store(Default::default()).unwrap();
        assert!(store.get_value(&"hello".as_bytes()).is_err());
        assert!(store.set_value(&"hello".as_bytes(), &"world".as_bytes()).is_ok());
        let value = store.get_value(&"hello".as_bytes()).unwrap();
        assert_eq!(value.as_slice(), "world".as_bytes());
    }
}
<commit_msg>Auto-generated paths for test dbs<commit_after>#![allow(unstable)]

extern crate libc;
extern crate "libforestdb-sys" as ffi;

#[macro_use] extern crate log;

use std::default::Default;
use std::error;
use std::ffi::{CString, c_str_to_bytes};
use std::mem;
use std::ptr;

#[derive(Copy)]
pub struct Error {code: i32}

impl Error {
    pub fn from_code(code: i32) -> Error {
        Error {code: code}
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        "ForestDB error"
    }

    fn detail(&self) -> Option<String> {
        unsafe { String::from_utf8(c_str_to_bytes(&ffi::fdb_error_msg(self.code)).to_vec()).ok() }
    }
}

impl std::fmt::Show for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str(&format!("fdb error: {:?}", (self as &error::Error).detail())[])
    }
}

pub type FdbResult<T> = Result<T, Error>;

macro_rules! lift_error {
    ($e: expr) => (lift_error!($e, ()));
    ($e: expr, $r: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => Ok($r),
            _ => return Err(Error::from_code(t))
        }
    })
}

macro_rules! try_fdb {
    ($e: expr) => ({
        let t = $e;
        match t {
            ffi::FDB_RESULT_SUCCESS => (),
            _ => return Err(Error::from_code(t))
        }
    })
}

#[repr(u8)]
#[derive(Copy)]
pub enum CommitOptions {
    Normal = ffi::FDB_COMMIT_NORMAL as u8,
    ManualWalFlush = ffi::FDB_COMMIT_MANUAL_WAL_FLUSH as u8
}

#[repr(u8)]
#[derive(Copy)]
/// Transaction isolation level
pub enum IsolationLevel {
    // Serializable = 0, // unsupported yet
    // RepeatableRead = 1, // unsupported yet
    /// Prevent a transaction from reading uncommitted data from other
    /// transactions.
    ReadCommitted = 2,
    ///  Allow a transaction to see uncommitted data from other transaction.
    ReadUncommited = 3,
}

#[derive(Copy)]
pub struct Config {
    raw: ffi::fdb_config
}

impl Config {
}

impl Default for Config {
    fn default() -> Config {
        Config {
            raw: unsafe { ffi::fdb_get_default_config() }
        }
    }
}

/// Initializes ForestDB. Usually is called automatically
pub fn init(config: Config) -> Result<(), Error> {
    lift_error!(unsafe {ffi::fdb_init(mem::transmute(&config.raw))})
}

/// Forces ForestDB shutdown: closing everything and terminating
/// compactor thread
pub fn shutdown() -> Result<(), Error> {
    lift_error!(unsafe {ffi::fdb_shutdown()})
}

/// Represents ForestDB file handle
pub struct FileHandle {
    path: Path,
    config: Config,
    raw: *mut ffi::fdb_file_handle,
}

impl FileHandle {
    fn from_raw(handle: *mut ffi::fdb_file_handle, path: &Path, config: Config) -> FileHandle {
        FileHandle {
            raw: handle,
            path: path.clone(),
            config: config
        }
    }

    /// Opens database with specified config
    pub fn open(path: &Path, config: Config) -> FdbResult<FileHandle> {
        let mut handle: *mut ffi::fdb_file_handle = ptr::null_mut();
        let c_path = CString::from_slice(path.as_vec());

        try_fdb!(unsafe { ffi::fdb_open(mem::transmute(&mut handle),
                                        c_path.as_ptr(),
                                        mem::transmute(&config.raw)) });
        let res = FileHandle::from_raw(handle, path, config);
        Ok(res)
    }

    /// Retrieves default store
    pub fn get_default_store(&self, config: StoreConfig) -> FdbResult<Store> {
        self._get_store(None, config)
    }

    /// Retrieves store by name
    pub fn get_store(&self, name: &str, config: StoreConfig) -> FdbResult<Store> {
        let c_name = CString::from_slice(name.as_bytes());
        self._get_store(Some(c_name), config)
    }

    fn _get_store(&self, name: Option<CString>, config: StoreConfig) -> FdbResult<Store> {
        let mut handle: *mut ffi::fdb_kvs_handle = ptr::null_mut();
        try_fdb!(unsafe { ffi::fdb_kvs_open(self.raw,
                                            mem::transmute(&mut handle),
                                            if name.is_some() {name.unwrap().as_ptr()} else {ptr::null()},
                                            mem::transmute(&config.raw)
                                            )});
        Ok(Store::from_raw(handle, config))
    }

    /// Commit all pending doc changes
    pub fn commit(&self, options: CommitOptions) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_commit(self.raw, options as u8)})
    }

    /// Writes compacted database to new_path. If it is set to None - it'll be in-place
    /// compaction
    pub fn compact(&self, new_path: Option<Path>) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_compact(self.raw,
                                             if new_path.is_none() {
                                                 ptr::null_mut()
                                             } else {
                                                 CString::from_slice(new_path.unwrap().as_vec()).as_ptr()
                                             })})
    }

    pub fn estimate_size(&self) -> u64 {
        unsafe {ffi::fdb_estimate_space_used(self.raw) as u64}
    }

    pub fn begin_transaction(&self, isolation: IsolationLevel) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_begin_transaction(self.raw, isolation as u8)})
    }

    pub fn end_transaction(&self, options: CommitOptions) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_end_transaction(self.raw, options as u8)})
    }

    pub fn abort_transaction(&self) -> FdbResult<()> {
        lift_error!(unsafe {ffi::fdb_abort_transaction(self.raw)})
    }
}

unsafe impl Send for FileHandle {}

// FIXME: do not use trait, implement clone as method
// which returns result? Otherwise it is impossible
// to handle cloning right
impl Clone for FileHandle {
    fn clone(&self) -> FileHandle {
        match FileHandle::open(&self.path, self.config) {
            Ok(res) => res,
            Err(_) => panic!("failed to clone")
        }
    }
}

impl Drop for FileHandle {
    fn drop(&mut self) {
        debug!("Dropping");
        unsafe { ffi::fdb_close(self.raw); }
    }
}

impl std::fmt::Show for FileHandle {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str(&format!("FdbFileHandle {{path: {:?}}}", self.path)[])
    }
}

#[derive(Copy)]
pub struct StoreConfig {
    raw: ffi::fdb_kvs_config
}

impl Default for StoreConfig {
    fn default() -> StoreConfig {
        StoreConfig {
            raw: unsafe {ffi::fdb_get_default_kvs_config()}
        }
    }
}

/// Represents ForestDB key value store
pub struct Store {
    raw: *mut ffi::fdb_kvs_handle,
    config: StoreConfig
}

impl Store {
    fn from_raw(raw: *mut ffi::fdb_kvs_handle, config: StoreConfig) -> Store {
        Store {
            raw: raw,
            config: config
        }
    }

    /// Retrieves a value by key (plain KV mode)
    pub fn set_value<K, V>(&self, key: &K, value: &V) -> FdbResult<()>
        where K: AsSlice<u8>,
              V: AsSlice<u8>
    {
        let mut doc = try!(Doc::new(key));
        try!(doc.set_body(value));

        lift_error!(unsafe {
            ffi::fdb_set(self.raw, doc.raw)
        })
    }

    /// Sets a value for key (plain KV mode)
    pub fn get_value<K>(&self, key: &K) -> FdbResult<Vec<u8>> where K: AsSlice<u8> {
        let mut doc = try!(Doc::new(key));
        try_fdb!(unsafe {
            ffi::fdb_get(self.raw, doc.raw)
        });
        Ok(unsafe {
            Vec::from_raw_buf(mem::transmute((*doc.raw).body),
                              (*doc.raw).bodylen as usize)
        })
    }

}

impl Drop for Store {
    fn drop(&mut self) {
        unsafe {ffi::fdb_kvs_close(self.raw); }
    }
}

pub struct Doc {
    raw: *mut ffi::fdb_doc
}

impl Doc {
    pub fn new<K>(key: &K) -> FdbResult<Doc> where K: AsSlice<u8>{
        let mut handle: *mut ffi::fdb_doc = ptr::null_mut();
        let key = key.as_slice();
        try_fdb!(unsafe {ffi::fdb_doc_create(&mut handle,
                                             mem::transmute(key.as_ptr()), key.len() as u64,
                                             ptr::null(), 0,
                                             ptr::null(), 0)});
        Ok(Doc {raw: handle})
    }

    pub fn set_body<B>(&mut self, body: &B) -> FdbResult<()> where B: AsSlice<u8> {
        let body = body.as_slice();
        lift_error!(unsafe {ffi::fdb_doc_update(&mut self.raw,
                                                ptr::null(), 0,
                                                mem::transmute(body.as_ptr()), body.len() as u64)
        })
    }

    pub fn set_meta<M>(&mut self, meta: &M) -> FdbResult<()> where M: AsSlice<u8> {
        let meta = meta.as_slice();
        lift_error!(unsafe {ffi::fdb_doc_update(&mut self.raw,
                                                mem::transmute(meta.as_ptr()), meta.len() as u64,
                                                ptr::null(), 0)
        })
    }
}

impl Drop for Doc {
    fn drop(&mut self) {
        unsafe {ffi::fdb_doc_free(self.raw)};
    }
}

pub struct UnsafeDoc<'a> {
    raw: ffi::fdb_doc,
}

fn empty_doc() -> ffi::fdb_doc {
    ffi::fdb_doc {
        key: ptr::null_mut(),
        keylen: 0,
        body: ptr::null_mut(),
        bodylen: 0,
        meta: ptr::null_mut(),
        metalen: 0,
        size_ondisk: 0,
        seqnum: 0,
        offset: 0,
        deleted: 0
    }
}

impl<'a> UnsafeDoc<'a> {
    fn with_key(key: &'a [u8]) -> UnsafeDoc<'a> {
        UnsafeDoc {
            raw: ffi::fdb_doc {
                key: unsafe { mem::transmute(key.as_ptr()) },
                keylen: key.len() as libc::size_t,
                .. empty_doc()
            }
        }
    }

    fn with_key_value(key: &'a [u8], value: &'a [u8]) -> UnsafeDoc<'a> {
        UnsafeDoc {
            raw: ffi::fdb_doc {
                key: unsafe { mem::transmute(key.as_ptr()) },
                keylen: key.len() as libc::size_t,
                body: unsafe { mem::transmute(value.as_ptr()) },
                bodylen: value.len() as libc::size_t,
                .. empty_doc()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{FileHandle, Error};

    use std::sync::atomic::{AtomicUint, ATOMIC_UINT_INIT, Ordering};
    use std::default::Default;
    use std::error;
    use std::io::{self, USER_DIR};
    use std::io::fs::PathExtensions;
    use std::os;
    use std::sync::{Once, ONCE_INIT};
    use std::thread::Thread;

    use ffi;

    fn next_db_path() -> Path {
        static NEXT_TEST: AtomicUint = ATOMIC_UINT_INIT;
        static CLEAR_DIR_ONCE: Once = ONCE_INIT;
        let cur_test = NEXT_TEST.fetch_add(1, Ordering::SeqCst);
        let db_dir = os::self_exe_path().unwrap().join("db_tests");

        CLEAR_DIR_ONCE.call_once(|| {
            if db_dir.exists() {
                assert!(io::fs::rmdir_recursive(&db_dir).is_ok());
            }

            assert!(io::fs::mkdir(&db_dir, USER_DIR).is_ok());
        });

        db_dir.join(format!("db-{}", cur_test))
    }

    #[test]
    fn test_error_msg(){
        let err = Error::from_code(ffi::FDB_RESULT_OPEN_FAIL);
        assert_eq!("error opening file", (&err as &error::Error).detail().unwrap().as_slice());
    }

    #[test]
    fn test_init() {
        assert!(super::init(Default::default()).is_ok());
    }

    #[test]
    fn test_open_file() {
        assert!(super::init(Default::default()).is_ok());
        assert!(FileHandle::open(&next_db_path(), Default::default()).is_ok());
    }

    #[test]
    fn test_clone() {
        let fh1 = FileHandle::open(&next_db_path(), Default::default()).unwrap();
        let fh2 = fh1.clone();

        Thread::scoped(move || {
            let fh_cloned = fh2;
            // FIXME: test something useful
        }).join();
    }

    #[test]
    fn test_open_store() {
        let db = FileHandle::open(&next_db_path(), Default::default()).unwrap();
        assert!(db.get_default_store(Default::default()).is_ok());
        assert!(db.get_store("hello", Default::default()).is_ok())
    }

    #[test]
    fn test_simple_keys() {
        let db = FileHandle::open(&next_db_path(), Default::default()).unwrap();
        let store = db.get_default_store(Default::default()).unwrap();
        assert!(store.get_value(&"hello".as_bytes()).is_err());
        assert!(store.set_value(&"hello".as_bytes(), &"world".as_bytes()).is_ok());
        let value = store.get_value(&"hello".as_bytes()).unwrap();
        assert_eq!(value.as_slice(), "world".as_bytes());
    }
}
<|endoftext|>


<commit_before>//! A library for creating and modifying Tree structures.
//!
//! # Overview
//! In this implementation, the `Tree` owns all of the `Node`s and all inter-`Node` relationships are
//! managed with `NodeId`s.
//!
//! `Tree`s in this library are "just" trees.  They do not allow cycles.  They do not allow
//! the creation of arbitrary Graph structures.  There is no weight associated with edges between
//! `Node`s.  In addition, each `Node` can have an arbitrary number of child `Node`s.
//!
//! It is important to note that this library does not support comparison-based `Node` insertion.
//! In other words, this is not a Binary Search Tree (or any other kind of search tree) library.
//! It is purely a library for storing data in a hierarchical manner.  The caller must know the
//! structure that they wish to build and then use this library to do so;  this library will not
//! make those structural decisions for you.
//!
//! ## Project Goals
//! * Allow caller control of as many allocations as possible (through pre-allocation)
//! * Fast `Node` insertion and removal
//! * Arbitrary _Tree_ structure creation and manipulation
//!
//! ## Non-Goals
//! * Arbitrary _Graph_ structure creation and manipulation
//! * Comparison-based node insertion of any kind
//!
//! #### Drawbacks of this Library
//! Sadly, Rust's ownership/reference system is sidestepped a bit by this implementation and this
//! can cause issues if the caller doesn't pay attention to what they are doing with `NodeId`s.
//!
//! We try to solve these issues with very careful usage of `NodeId`s so that the caller doesn't
//! have to be bothered (too much) with these concerns.
//!
//! Please see the [Potential `NodeId` Issues](struct.NodeId.html#potential-nodeid-issues) section
//! of the `NodeId` documentation for more info on what these issues are and how this library
//! attempts to solve them.
//!
//! -----------------------------------------------------------------------------------------------
//!
//! _**Disclaimer:** This library should be considered a Work-in-Progress until it reaches v1.0.0.
//! Breaking changes will be avoided at all costs, but until v1.0.0 hits, they are a definite
//! possibility. With that in mind, it would be wise to find a version number that works for you
//! (preferably whatever the current version is when you read this) and stick with it until you are
//! ready to upgrade to the next version._
//!

extern crate snowflake;
use self::snowflake::ProcessUniqueId;

mod error;
mod node;
mod tree;

pub use error::NodeIdError;
pub use node::NodeBuilder;
pub use node::Node;
pub use tree::TreeBuilder;
pub use tree::Tree;

///
/// An identifier used to differentiate between `Node`s within a `Tree`.
///
/// `NodeId`s are not something that the calling context will ever have to worry about generating.
/// `Tree`s generate `NodeId`s as `Node`s are inserted into them.
///
/// In addition, each `NodeId` is specific to the `Tree` that generated it.  This means that if
/// there are two `Tree`s - `A` and `B` - there's no worry of trying to access a `Node` in `A` with
/// an identifier that came from `B`.  Doing so will return a `Result::Err` value instead of
/// returning the wrong `Node`.
///
/// #### Potential `NodeId` Issues
///
/// Because `Tree`s pass out `NodeId`s as `Node`s are inserted, several issues can occur:
///
/// 1. If a `Node` is removed, the `NodeId` that previously identified it now points to nothing
/// (technically a `None` value in this case).
/// 2. If a `Node` is removed and then another is inserted later, the "new" `NodeId` that is
/// returned can (and will) be the same `NodeId` that was used to identify a different `Node`
/// previously.
///
/// The above issues may seem like deal-breakers, but our situation isn't as bad as it seems:
///
/// The first issue can be easily detected by the library itself.  In this situation, a
/// `Result::Err` will be returned with the appropriate `NodeIdError`. The second issue, however, is
/// not something that the library can detect. To mitigate this problem, this library ensures the
/// following:
///
/// 1. All `Node` methods that provide `NodeId`s will **return** `&NodeId`s instead of `NodeId`s.
/// 2. All `Tree` methods that **read** or **insert** data accept `&NodeId`s instead of taking
/// `NodeId`s.
/// 3. All `Tree` methods that **remove** data take `NodeId`s instead of accepting `&NodeId`s.
/// 4. All `Node`s that have been removed from a `Tree` will have their parent and child references
/// cleared (to avoid leaking extra `NodeId` copies).
/// 5. `NodeId`s themselves are `Clone`, but not `Copy`.
///
/// This means that no methods will ever take ownership of a `NodeId` except for methods that remove
/// a `Node` from a `Tree`. The resulting behavior is that unless the caller **explicitly `Clone`s a
/// `NodeId`** they should never be in a situation where they accidentally hold onto a `NodeId` too
/// long.  This means that we have "almost safe references" that the caller can clone if they choose
/// to.  Doing so, however, will open up the possibility of confusing which `NodeId`s refer to which
/// `Node`s in the calling context.
///
/// This _does_ transfer some of the burden to the caller, but any errors should be fairly easy to
/// sort out because an explicit `Clone` is required for such an error to occur.
///
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub struct NodeId {
    tree_id: ProcessUniqueId,
    index: usize,
}

trait MutableNode {
    fn set_parent(&mut self, parent: Option<NodeId>);
    fn add_child(&mut self, child: NodeId);
    fn children_mut(&mut self) -> &mut Vec<NodeId>;
}
<commit_msg>add example in main docs page.<commit_after>//! A library for creating and modifying Tree structures.
//!
//! # Overview
//! In this implementation, the `Tree` owns all of the `Node`s and all inter-`Node` relationships are
//! managed with `NodeId`s.
//!
//! `Tree`s in this library are "just" trees.  They do not allow cycles.  They do not allow
//! the creation of arbitrary Graph structures.  There is no weight associated with edges between
//! `Node`s.  In addition, each `Node` can have an arbitrary number of child `Node`s.
//!
//! It is important to note that this library does not support comparison-based `Node` insertion.
//! In other words, this is not a Binary Search Tree (or any other kind of search tree) library.
//! It is purely a library for storing data in a hierarchical manner.  The caller must know the
//! structure that they wish to build and then use this library to do so;  this library will not
//! make those structural decisions for you.
//!
//! ## Example Usage
//! ```
//! use id_tree::*;
//!
//! let mut tree: Tree<i32> = Tree::new();
//!
//! let root_id: NodeId = tree.set_root(Node::new(1));
//! let child_id: NodeId = tree.insert_with_parent(Node::new(2), &root_id).ok().unwrap();
//!
//! {
//!     let root: &Node<i32> = tree.get(&root_id).unwrap();
//!     assert_eq!(root.data(), &1);
//! }
//! {
//!     let mut child: &mut Node<i32> = tree.get_mut(&child_id).unwrap();
//!     assert_eq!(child.data_mut(), &mut 2);
//! }
//! ```
//!
//! ## Project Goals
//! * Allow caller control of as many allocations as possible (through pre-allocation)
//! * Fast `Node` insertion and removal
//! * Arbitrary _Tree_ structure creation and manipulation
//!
//! ## Non-Goals
//! * Arbitrary _Graph_ structure creation and manipulation
//! * Comparison-based node insertion of any kind
//!
//! #### Drawbacks of this Library
//! Sadly, Rust's ownership/reference system is sidestepped a bit by this implementation and this
//! can cause issues if the caller doesn't pay attention to what they are doing with `NodeId`s.
//!
//! We try to solve these issues with very careful usage of `NodeId`s so that the caller doesn't
//! have to be bothered (too much) with these concerns.
//!
//! Please see the [Potential `NodeId` Issues](struct.NodeId.html#potential-nodeid-issues) section
//! of the `NodeId` documentation for more info on what these issues are and how this library
//! attempts to solve them.
//!
//! -----------------------------------------------------------------------------------------------
//!
//! _**Disclaimer:** This library should be considered a Work-in-Progress until it reaches v1.0.0.
//! Breaking changes will be avoided at all costs, but until v1.0.0 hits, they are a definite
//! possibility. With that in mind, it would be wise to find a version number that works for you
//! (preferably whatever the current version is when you read this) and stick with it until you are
//! ready to upgrade to the next version._
//!

extern crate snowflake;
use self::snowflake::ProcessUniqueId;

mod error;
mod node;
mod tree;

pub use error::NodeIdError;
pub use node::NodeBuilder;
pub use node::Node;
pub use tree::TreeBuilder;
pub use tree::Tree;

///
/// An identifier used to differentiate between `Node`s within a `Tree`.
///
/// `NodeId`s are not something that the calling context will ever have to worry about generating.
/// `Tree`s generate `NodeId`s as `Node`s are inserted into them.
///
/// In addition, each `NodeId` is specific to the `Tree` that generated it.  This means that if
/// there are two `Tree`s - `A` and `B` - there's no worry of trying to access a `Node` in `A` with
/// an identifier that came from `B`.  Doing so will return a `Result::Err` value instead of
/// returning the wrong `Node`.
///
/// #### Potential `NodeId` Issues
///
/// Because `Tree`s pass out `NodeId`s as `Node`s are inserted, several issues can occur:
///
/// 1. If a `Node` is removed, the `NodeId` that previously identified it now points to nothing
/// (technically a `None` value in this case).
/// 2. If a `Node` is removed and then another is inserted later, the "new" `NodeId` that is
/// returned can (and will) be the same `NodeId` that was used to identify a different `Node`
/// previously.
///
/// The above issues may seem like deal-breakers, but our situation isn't as bad as it seems:
///
/// The first issue can be easily detected by the library itself.  In this situation, a
/// `Result::Err` will be returned with the appropriate `NodeIdError`. The second issue, however, is
/// not something that the library can detect. To mitigate this problem, this library ensures the
/// following:
///
/// 1. All `Node` methods that provide `NodeId`s will **return** `&NodeId`s instead of `NodeId`s.
/// 2. All `Tree` methods that **read** or **insert** data accept `&NodeId`s instead of taking
/// `NodeId`s.
/// 3. All `Tree` methods that **remove** data take `NodeId`s instead of accepting `&NodeId`s.
/// 4. All `Node`s that have been removed from a `Tree` will have their parent and child references
/// cleared (to avoid leaking extra `NodeId` copies).
/// 5. `NodeId`s themselves are `Clone`, but not `Copy`.
///
/// This means that no methods will ever take ownership of a `NodeId` except for methods that remove
/// a `Node` from a `Tree`. The resulting behavior is that unless the caller **explicitly `Clone`s a
/// `NodeId`** they should never be in a situation where they accidentally hold onto a `NodeId` too
/// long.  This means that we have "almost safe references" that the caller can clone if they choose
/// to.  Doing so, however, will open up the possibility of confusing which `NodeId`s refer to which
/// `Node`s in the calling context.
///
/// This _does_ transfer some of the burden to the caller, but any errors should be fairly easy to
/// sort out because an explicit `Clone` is required for such an error to occur.
///
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub struct NodeId {
    tree_id: ProcessUniqueId,
    index: usize,
}

trait MutableNode {
    fn set_parent(&mut self, parent: Option<NodeId>);
    fn add_child(&mut self, child: NodeId);
    fn children_mut(&mut self) -> &mut Vec<NodeId>;
}
<|endoftext|>