<commit_before>use tileset::FlipContext;
use map::{Adjacents};
use sdl2::pixels::Color;

#[derive(Clone, Copy)]
pub enum CellState {
    Empty,
    Dirt,
    Stone,
    Wall
}

pub fn get_tileset_sprite(adjacents: &Adjacents) -> (u32, u32, FlipContext) {
    let adjacent_tile_states = adjacents.as_tile_states();
    let mut adjacent_tiles = [false; 8];
    for i in 0..8 {
        adjacent_tiles[i] = match adjacent_tile_states[i] {
            CellState::Empty => true,
            _ => false
        }
    };

    let adjacent_tiles = (
        adjacent_tiles[0],
        adjacent_tiles[1],
        adjacent_tiles[2],
        adjacent_tiles[3],
        adjacent_tiles[4],
        adjacent_tiles[5],
        adjacent_tiles[6],
        adjacent_tiles[7],
    );
    match adjacent_tiles {
        (
            _,     true,  _,
            true,         true,
            _,     true, _
        ) => {
            (4, 0, FlipContext::FlipNone)  // Standalone
        },
        (
            _,     true,  _,
            true,         true,
            _,     false, _
        ) => {
            (4, 1, FlipContext::FlipNone)  // Top nub
        },
        (
            _,     false,  _,
            true,         true,
            _,     false, _
        ) => {
            (4, 2, FlipContext::FlipNone)  // Vertical bar
        },
        (
            _,     false,  _,
            true,         true,
            _,     true, _
        ) => {
            (4, 3, FlipContext::FlipNone)  // Bottom nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     true,  _
        ) => {
            (0, 3, FlipContext::FlipNone)  // Left nub
        },
        (
            _,     true,  _,
            false,        false,
            _,     true,  _
        ) => {
            (6, 1, FlipContext::FlipNone)  // Horizontal bar
        },
        (
            _,     true,  _,
            false,        true,
            _,     true,  _
        ) => {
            (0, 3, FlipContext::FlipHorizontal)  // Right nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     false, false
        ) => {
            (0, 0, FlipContext::FlipNone)  // Top-left
        },
        (
            _,     false,  false,
            true,         false,
            _,     false, false
        ) => {
            (0, 1, FlipContext::FlipNone)  // Left
        },
        (
            _,     false, false,
            true,         false,
            _,     true , _
        ) => {
            (0, 2, FlipContext::FlipNone)  // Bottom-left
        },
        (
            _,     true,  _,
            false,        true,
            false, false, _
        ) => {
            (0, 0, FlipContext::FlipHorizontal)  // Top-right
        },
        (
            false,  false, _,
            false,         true,
            false,  false, _
        ) => {
            (0, 1, FlipContext::FlipHorizontal)  // Right
        },
        (
            false, false, _,
            false,        true,
            _,     true,  _
        ) => {
            (0, 2, FlipContext::FlipHorizontal)  // Bottom-right
        },
        (
            _, true,      _,
            _,            _,
            false, false, false
        ) => {
            (5, 1, FlipContext::FlipNone)  // Top
        },
        (
            false, false, false,
            _,            _,
            _, true,      _
        ) => {
            (5, 3, FlipContext::FlipNone)  // Bottom
        },
        (
            _,    true,   _,
            false,        false,
            false, false, true
        ) => {
            (2, 0, FlipContext::FlipHorizontal)  // T
        },
        (
            _,    true,  _,
            false,       false,
            true, false, false
        ) => {
            (2, 0, FlipContext::FlipNone)  // T
        },
        (
            true, false, false,
            false,       false,
            true, true,  false
        ) => {
            (2, 2, FlipContext::FlipNone)  // T
        },
        (
            false, false, true,
            false,        false,
            false, true,  true
        ) => {
            (2, 2, FlipContext::FlipHorizontal)  // T
        },
        (
            true,  false, false,
            false,        false,
            false, false, false
        ) => {
            (1, 0, FlipContext::FlipNone)  // Inside elbow top left
        },
        (
            false, false, true,
            false,        false,
            false, false, false
        ) => {
            (1, 0, FlipContext::FlipHorizontal)  // Inside elbow top right
        },
        (
            false, false, false,
            false,        false,
            true, false, false
        ) => {
            (1, 2, FlipContext::FlipNone)  // Inside elbow bottom left
        },
        (
            false, false, false,
            false,        false,
            false, false, true
        ) => {
            (1, 2, FlipContext::FlipHorizontal)  // Inside elbow bottom right
        },
        (
            _, _, _,
            _,    _,
            _, _, _
        ) => {
            (5, 2, FlipContext::FlipNone)
        }
    }
}

impl CellState {
    pub fn is_passable(&self) -> bool {
        match *self {
            CellState::Empty | CellState::Dirt => true,
            _ => false
        }
    }

    pub fn get_color(&self) -> Color {
        let BLACK = Color::RGB(0, 0, 0);
        let BROWN = Color::RGB(100, 100, 0);
        let GREY = Color::RGB(100, 100, 100);
        let WHITE = Color::RGB(255, 255, 255);
        match *self {
            CellState::Dirt => BROWN,
            CellState::Stone => GREY,
            CellState::Wall => WHITE,
            CellState::Empty => BLACK
        }
    }

    pub fn get_sprite(&self, adjacents: &Adjacents) -> Option<(u32, u32, FlipContext)> {
        match *self {
            CellState::Dirt => {
                let (x, y, context) = get_tileset_sprite(adjacents);
                Some((x + 9, y, context))
            },
            CellState::Stone => Some((8, 0, FlipContext::FlipNone)),
            CellState::Wall => Some((2, 5, FlipContext::FlipNone)),
            CellState::Empty => None
        }
    }
}

<commit_msg>Walls<commit_after>use tileset::FlipContext;
use map::{Adjacents};
use sdl2::pixels::Color;

#[derive(Clone, Copy)]
pub enum CellState {
    Empty,
    Dirt,
    Stone,
    Wall
}

pub fn get_tileset_sprite(adjacents: &[bool; 8]) -> (u32, u32, FlipContext) {
    let adjacent_tiles = (
        adjacents[0],
        adjacents[1],
        adjacents[2],
        adjacents[3],
        adjacents[4],
        adjacents[5],
        adjacents[6],
        adjacents[7],
    );
    match adjacent_tiles {
        (
            _,     true,  _,
            true,         true,
            _,     true, _
        ) => {
            (5, 0, FlipContext::FlipNone)  // Standalone
        },
        (
            _,     true,  _,
            true,         true,
            _,     false, _
        ) => {
            (5, 1, FlipContext::FlipNone)  // Top nub
        },
        (
            _,     false,  _,
            true,         true,
            _,     false, _
        ) => {
            (5, 2, FlipContext::FlipNone)  // Vertical bar
        },
        (
            _,     false,  _,
            true,         true,
            _,     true, _
        ) => {
            (5, 3, FlipContext::FlipNone)  // Bottom nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     true,  _
        ) => {
            (1, 3, FlipContext::FlipNone)  // Left nub
        },
        (
            _,     true,  _,
            false,        false,
            _,     true,  _
        ) => {
            (7, 1, FlipContext::FlipNone)  // Horizontal bar
        },
        (
            _,     true,  _,
            false,        true,
            _,     true,  _
        ) => {
            (1, 3, FlipContext::FlipHorizontal)  // Right nub
        },
        (
            _,     true,  _,
            true,         false,
            _,     false, false
        ) => {
            (1, 0, FlipContext::FlipNone)  // Top-left
        },
        (
            _,     false,  false,
            true,         false,
            _,     false, false
        ) => {
            (1, 1, FlipContext::FlipNone)  // Left
        },
        (
            _,     false, false,
            true,         false,
            _,     true , _
        ) => {
            (1, 2, FlipContext::FlipNone)  // Bottom-left
        },
        (
            _,     true,  _,
            false,        true,
            false, false, _
        ) => {
            (1, 0, FlipContext::FlipHorizontal)  // Top-right
        },
        (
            false,  false, _,
            false,         true,
            false,  false, _
        ) => {
            (1, 1, FlipContext::FlipHorizontal)  // Right
        },
        (
            false, false, _,
            false,        true,
            _,     true,  _
        ) => {
            (1, 2, FlipContext::FlipHorizontal)  // Bottom-right
        },
        (
            _, true,      _,
            _,            _,
            false, false, false
        ) => {
            (6, 1, FlipContext::FlipNone)  // Top
        },
        (
            false, false, false,
            _,            _,
            _, true,      _
        ) => {
            (6, 3, FlipContext::FlipNone)  // Bottom
        },
        (
            _,    true,   _,
            false,        false,
            false, false, true
        ) => {
            (3, 0, FlipContext::FlipHorizontal)  // T
        },
        (
            _,    true,  _,
            false,       false,
            true, false, false
        ) => {
            (3, 0, FlipContext::FlipNone)  // T
        },
        (
            true, false, false,
            false,       false,
            true, true,  false
        ) => {
            (3, 2, FlipContext::FlipNone)  // T
        },
        (
            false, false, true,
            false,        false,
            false, true,  true
        ) => {
            (3, 2, FlipContext::FlipHorizontal)  // T
        },
        (
            true,  false, false,
            false,        false,
            false, false, false
        ) => {
            (2, 0, FlipContext::FlipNone)  // Inside elbow top left
        },
        (
            false, false, true,
            false,        false,
            false, false, false
        ) => {
            (2, 0, FlipContext::FlipHorizontal)  // Inside elbow top right
        },
        (
            false, false, false,
            false,        false,
            true, false, false
        ) => {
            (2, 2, FlipContext::FlipNone)  // Inside elbow bottom left
        },
        (
            false, false, false,
            false,        false,
            false, false, true
        ) => {
            (2, 2, FlipContext::FlipHorizontal)  // Inside elbow bottom right
        },
        (
            _, _, _,
            _,    _,
            _, _, _
        ) => {
            (6, 2, FlipContext::FlipNone)
        }
    }
}

impl CellState {
    pub fn is_passable(&self) -> bool {
        match *self {
            CellState::Empty | CellState::Dirt => true,
            _ => false
        }
    }

    pub fn get_color(&self) -> Color {
        let BLACK = Color::RGB(0, 0, 0);
        let BROWN = Color::RGB(100, 100, 0);
        let GREY = Color::RGB(100, 100, 100);
        let WHITE = Color::RGB(255, 255, 255);
        match *self {
            CellState::Dirt => BROWN,
            CellState::Stone => GREY,
            CellState::Wall => WHITE,
            CellState::Empty => BLACK
        }
    }

    pub fn get_sprite(&self, adjacents: &Adjacents) -> Option<(u32, u32, FlipContext)> {
        match *self {
            CellState::Dirt => {
                let adjacent_tile_states = adjacents.as_tile_states();
                let mut adjacent_tiles = [false; 8];
                for i in 0..8 {
                    adjacent_tiles[i] = match adjacent_tile_states[i] {
                        CellState::Empty => true,
                        _ => false
                    }
                };
                let (x, y, context) = get_tileset_sprite(&adjacent_tiles);
                Some((x + 8, y, context))
            },
            CellState::Stone => {
                let adjacent_tile_states = adjacents.as_tile_states();
                let mut adjacent_tiles = [false; 8];
                for i in 0..8 {
                    adjacent_tiles[i] = match adjacent_tile_states[i] {
                        CellState::Empty | CellState::Dirt => true,
                        _ => false
                    }
                };
                let (x, y, context) = get_tileset_sprite(&adjacent_tiles);
                Some((x + 8, y + 12, context))
            },
            CellState::Wall => Some((2, 5, FlipContext::FlipNone)),
            CellState::Empty => None
        }
    }
}

<|endoftext|>


<commit_before>//! Defines what information a chess move should contain.

use basetypes::*;
use castling_rights::*;

/// Represents a move on the chessboard.
///
/// `Move` contains 3 types of information:
///
/// 1. Information about the played move itself.
///
/// 2. Information needed so as to be able to undo the move and
///    restore the board into the exact same state as before.
///
/// 3. The move score -- moves with higher score are tried
///    first. Ideally the best move should have the highest score.
///
/// `Move` is a 32-bit unsigned number. The lowest 16 bits contain the
/// whole needed information about the move itself (type 1). And is
/// laid out the following way:
///
///  ```text
///   15                                                           0
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
///  | Move  |    Origin square      |   Destination square  | Aux   |
///  | type  |       6 bits          |        6 bits         | data  |
///  | 2 bits|   |   |   |   |   |   |   |   |   |   |   |   | 2 bits|
///  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |       |
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  ```
///
/// There are 4 "move type"s: `0`) en-passant capture; `1`) pawn
/// promotion; `2`) castling; `3`) normal move. "Aux data" encodes the
/// type of the promoted piece if the move type is a pawn promotion,
/// otherwise it encodes castling rights (see below).
///
/// The highest 16 bits contain the rest ot the info:
///
///  ```text
///   31                                                          16
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
///  | Move  |  Captured | Reser-|  Played   | Cast- |   En-passant  |
///  | score |  piece    |  ved  |  piece    | ling  |      file     |
///  | 2 bits|  3 bits   | 2 bits|  3 bits   | 2 bits|     4 bits    |
///  |   |   |   |   |   |   |   |   |   |   |       |   |   |   |   |
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  ```
///
/// "En-passant file" tells on what vertical line on the board there
/// was a passing pawn before the move was played. If there was no
/// passing pawn, "en-passant file" should be `8`.
///
/// Castling rights are a bit complex. The castling rights for the side
/// that makes the move, before the move was made, are stored in the
/// "Aux data" field. This is OK, because promoting a pawn never
/// changes the moving player's castling rights. The castling rights
/// for the opposite side are stored in "Castling" field. (A move can
/// change the castling rights for the other side when a rook in the
/// corner is captured.)
///
/// When "Captured piece" is stored, its bits are inverted, so that
/// MVV-LVA (Most valuable victim -- least valuable aggressor) ordering
/// of the moves is preserved, even when the "Move score" field stays
/// the same.
#[derive(Debug)]
#[derive(Clone, Copy)]
#[derive(PartialOrd, Ord, PartialEq, Eq)]
pub struct Move(u32);

impl Move {
    /// Creates a new instance of `Move`.
    ///
    /// `us` is the side that makes the move. `castling` are the
    /// castling rights before the move was played. `en_passant_file`
    /// is the file on which there were a passing pawn before the move
    /// was played (or `8` if there was no passing
    /// pawn). `promoted_piece_code` should be a number between `0`
    /// and `3` and is used only when the `move_type` is a pawn
    /// promotion, otherwise it is ignored.
    #[inline]
    pub fn new(us: Color,
               score: usize,
               move_type: MoveType,
               piece: PieceType,
               orig_square: Square,
               dest_square: Square,
               captured_piece: PieceType,
               en_passant_file: File,
               castling: CastlingRights,
               promoted_piece_code: usize)
               -> Move {
        assert!(us <= 1);
        assert!(score <= 0b11);
        assert!(move_type <= 0x11);
        assert!(piece < NO_PIECE);
        assert!(orig_square <= 63);
        assert!(dest_square <= 63);
        assert!(captured_piece != KING && captured_piece <= NO_PIECE);
        assert!(en_passant_file <= 0b1000);
        assert!(promoted_piece_code <= 0b11);
        let aux_data = match move_type {
            MOVE_PROMOTION => promoted_piece_code,
            _ => castling.get_for(us),
        };
        Move((score << M_SHIFT_SCORE | (!captured_piece & 0b111) << M_SHIFT_CAPTURED_PIECE |
              piece << M_SHIFT_PIECE |
              castling.get_for(1 ^ us) << M_SHIFT_CASTLING_DATA |
              en_passant_file << M_SHIFT_ENPASSANT_FILE |
              move_type << M_SHIFT_MOVE_TYPE | orig_square << M_SHIFT_ORIG_SQUARE |
              dest_square << M_SHIFT_DEST_SQUARE |
              aux_data << M_SHIFT_AUX_DATA) as u32)
    }

    /// Sets the "move score" field to a particular value.
    #[inline]
    pub fn set_score(&mut self, score: usize) {
        assert!(score <= 0b11);
        self.0 &= !M_MASK_SCORE;
        self.0 |= (score << M_SHIFT_SCORE) as u32;
    }

    /// Sets a particular bit in the "move score" field to `1`.
    #[inline]
    pub fn set_score_bit(&mut self, b: usize) {
        assert!(b <= 1);
        self.0 |= 1 << b << M_SHIFT_SCORE;
    }

    /// Sets a particular bit in the "move score" field to `0`.
    #[inline]
    pub fn clear_score_bit(&mut self, b: usize) {
        assert!(b <= 1);
        self.0 &= !(1 << b << M_SHIFT_SCORE);
    }

    #[inline]
    pub fn score(&self) -> usize {
        ((self.0 & M_MASK_SCORE) >> M_SHIFT_SCORE) as usize
    }

    #[inline]
    pub fn move_type(&self) -> MoveType {
        ((self.0 & M_MASK_MOVE_TYPE) >> M_SHIFT_MOVE_TYPE) as MoveType
    }

    #[inline]
    pub fn piece(&self) -> PieceType {
        ((self.0 & M_MASK_PIECE) >> M_SHIFT_PIECE) as PieceType
    }

    #[inline]
    pub fn orig_square(&self) -> Square {
        ((self.0 & M_MASK_ORIG_SQUARE) >> M_SHIFT_ORIG_SQUARE) as Square
    }

    #[inline]
    pub fn dest_square(&self) -> Square {
        ((self.0 & M_MASK_DEST_SQUARE) >> M_SHIFT_DEST_SQUARE) as Square
    }

    #[inline]
    pub fn captured_piece(&self) -> PieceType {
        ((!self.0 & M_MASK_CAPTURED_PIECE) >> M_SHIFT_CAPTURED_PIECE) as PieceType
    }

    /// Returns the file on which there were a passing pawn before the
    /// move was played (or `8` if there was no passing pawn).
    #[inline]
    pub fn en_passant_file(&self) -> File {
        ((self.0 & M_MASK_ENPASSANT_FILE) >> M_SHIFT_ENPASSANT_FILE) as File
    }

    /// Returns a 2-bit value representing the castling rights for the
    /// side that does not make the move, as they were before the move
    /// was played.
    #[inline(always)]
    pub fn castling_data(&self) -> usize {
        ((self.0 & M_MASK_CASTLING_DATA) >> M_SHIFT_CASTLING_DATA) as usize
    }

    /// Returns 2-bit value representing auxiliary data.
    ///
    /// When the move type is pawn promotion, "aux data" holds the
    /// promoted piece type encoded with a value from 0 to 3. For all
    /// other move types "aux data" holds the castling rights for the
    /// side that makes the move, as they were before the move was
    /// played.
    #[inline(always)]
    pub fn aux_data(&self) -> usize {
        ((self.0 & M_MASK_AUX_DATA) >> M_SHIFT_AUX_DATA) as usize
    }

    /// Returns the algebraic notation of the move.
    ///
    /// Examples: `e2e4`, `e7e5`, `e1g1` (white short castling),
    /// `e7e8q` (for promotion).
    pub fn notation(&self) -> String {
        format!("{}{}{}",
                notation(self.orig_square()),
                notation(self.dest_square()),
                match self.move_type() {
                    MOVE_PROMOTION => ["q", "r", "b", "n"][self.aux_data()],
                    _ => "",
                })
    }

    /// Decodes the promoted piece type from the raw value of "aux
    /// data".
    #[inline]
    pub fn piece_from_aux_data(pp_code: usize) -> PieceType {
        assert!(pp_code <= 3);
        match pp_code {
            0 => QUEEN,
            1 => ROOK,
            2 => BISHOP,
            _ => KNIGHT,
        }
    }

    #[allow(dead_code)]
    #[inline]
    fn reserved(&self) -> usize {
        ((self.0 & M_MASK_RESERVED) >> M_SHIFT_RESERVED) as usize
    }
}

/// `MOVE_ENPASSANT`, `MOVE_PROMOTION`, `MOVE_CASTLING`, or
/// `MOVE_NORMAL`.
pub type MoveType = usize;

/// En-passant capture move type.
pub const MOVE_ENPASSANT: MoveType = 0;

/// Pawn promotion move type.
pub const MOVE_PROMOTION: MoveType = 1;

/// Castling move type.
pub const MOVE_CASTLING: MoveType = 2;

/// Normal move type.
pub const MOVE_NORMAL: MoveType = 3;

// Field shifts
const M_SHIFT_SCORE: u32 = 30;
const M_SHIFT_CAPTURED_PIECE: u32 = 27;
const M_SHIFT_RESERVED: u32 = 25;
const M_SHIFT_PIECE: u32 = 22;
const M_SHIFT_CASTLING_DATA: u32 = 20;
const M_SHIFT_ENPASSANT_FILE: u32 = 16;
const M_SHIFT_MOVE_TYPE: u32 = 14;
const M_SHIFT_ORIG_SQUARE: u32 = 8;
const M_SHIFT_DEST_SQUARE: u32 = 2;
const M_SHIFT_AUX_DATA: u32 = 0;

// Field masks
const M_MASK_SCORE: u32 = 0b11 << M_SHIFT_SCORE;
const M_MASK_CAPTURED_PIECE: u32 = 0b111 << M_SHIFT_CAPTURED_PIECE;
const M_MASK_RESERVED: u32 = 0b11 << M_SHIFT_RESERVED;
const M_MASK_PIECE: u32 = 0b111 << M_SHIFT_PIECE;
const M_MASK_CASTLING_DATA: u32 = 0b11 << M_SHIFT_CASTLING_DATA;
const M_MASK_ENPASSANT_FILE: u32 = 0b1111 << M_SHIFT_ENPASSANT_FILE;
const M_MASK_MOVE_TYPE: u32 = 0b11 << M_SHIFT_MOVE_TYPE;
const M_MASK_ORIG_SQUARE: u32 = 0b111111 << M_SHIFT_ORIG_SQUARE;
const M_MASK_DEST_SQUARE: u32 = 0b111111 << M_SHIFT_DEST_SQUARE;
const M_MASK_AUX_DATA: u32 = 0b11 << M_SHIFT_AUX_DATA;

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

    #[test]
    fn test_move() {
        use basetypes::*;
        const NO_ENPASSANT_FILE: File = 8;

        let mut cr = CastlingRights::new();
        cr.set_for(WHITE, 0b10);
        cr.set_for(BLACK, 0b11);
        let mut m = Move::new(WHITE,
                              2,
                              MOVE_NORMAL,
                              PAWN,
                              E2,
                              E4,
                              NO_PIECE,
                              NO_ENPASSANT_FILE,
                              cr,
                              0);
        let n1 = Move::new(WHITE,
                           2,
                           MOVE_NORMAL,
                           PAWN,
                           F3,
                           E4,
                           KNIGHT,
                           NO_ENPASSANT_FILE,
                           CastlingRights::new(),
                           0);
        let n2 = Move::new(WHITE,
                           2,
                           MOVE_NORMAL,
                           KING,
                           F3,
                           E4,
                           NO_PIECE,
                           NO_ENPASSANT_FILE,
                           CastlingRights::new(),
                           0);
        let n3 = Move::new(BLACK,
                           0,
                           MOVE_PROMOTION,
                           PAWN,
                           F2,
                           F1,
                           NO_PIECE,
                           NO_ENPASSANT_FILE,
                           CastlingRights::new(),
                           1);
        assert!(n1 > m);
        assert!(n2 < m);
        assert_eq!(m.score(), 2);
        assert_eq!(m.piece(), PAWN);
        assert_eq!(m.captured_piece(), NO_PIECE);
        assert_eq!(m.orig_square(), E2);
        assert_eq!(m.dest_square(), E4);
        assert_eq!(m.en_passant_file(), 8);
        assert_eq!(m.aux_data(), 0b10);
        assert_eq!(m.castling_data(), 0b11);
        let m2 = m;
        assert_eq!(m, m2);
        m.set_score(3);
        assert_eq!(m.score(), 3);
        assert!(m > m2);
        m.clear_score_bit(0);
        assert_eq!(m, m2);
        m.set_score_bit(0);
        assert_eq!(m.score(), 3);
        m.set_score(0);
        assert_eq!(m.score(), 0);
        assert_eq!(n3.aux_data(), 1);
        assert_eq!(n1.reserved(), 0);
    }
}
<commit_msg>implemented ordering of "quiet" moves.<commit_after>//! Defines what information a chess move should contain.

use basetypes::*;
use castling_rights::*;

/// Represents a move on the chessboard.
///
/// `Move` contains 3 types of information:
///
/// 1. Information about the played move itself.
///
/// 2. Information needed so as to be able to undo the move and
///    restore the board into the exact same state as before.
///
/// 3. The move score -- moves with higher score are tried
///    first. Ideally the best move should have the highest score.
///
/// `Move` is a 32-bit unsigned number. The lowest 16 bits contain the
/// whole needed information about the move itself (type 1). And is
/// laid out the following way:
///
///  ```text
///   15                                                           0
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
///  | Move  |    Origin square      |   Destination square  | Aux   |
///  | type  |       6 bits          |        6 bits         | data  |
///  | 2 bits|   |   |   |   |   |   |   |   |   |   |   |   | 2 bits|
///  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |       |
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  ```
///
/// There are 4 "move type"s: `0`) en-passant capture; `1`) pawn
/// promotion; `2`) castling; `3`) normal move. "Aux data" encodes the
/// type of the promoted piece if the move type is a pawn promotion,
/// otherwise it encodes castling rights (see below).
///
/// The highest 16 bits contain the rest ot the info:
///
///  ```text
///   31                                                          16
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
///  | Move  |  Captured | Reser-|  Played   | Cast- |   En-passant  |
///  | score |  piece    |  ved  |  piece    | ling  |      file     |
///  | 2 bits|  3 bits   | 2 bits|  3 bits   | 2 bits|     4 bits    |
///  |   |   |   |   |   |   |   |   |   |   |       |   |   |   |   |
///  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
///  ```
///
/// "En-passant file" tells on what vertical line on the board there
/// was a passing pawn before the move was played. If there was no
/// passing pawn, "en-passant file" should be `8`.
///
/// Castling rights are a bit complex. The castling rights for the side
/// that makes the move, before the move was made, are stored in the
/// "Aux data" field. This is OK, because promoting a pawn never
/// changes the moving player's castling rights. The castling rights
/// for the opposite side are stored in "Castling" field. (A move can
/// change the castling rights for the other side when a rook in the
/// corner is captured.)
///
/// When "Captured piece" is stored, its bits are inverted, so that
/// MVV-LVA (Most valuable victim -- least valuable aggressor) ordering
/// of the moves is preserved, even when the "Move score" field stays
/// the same.
#[derive(Debug)]
#[derive(Clone, Copy)]
#[derive(PartialOrd, Ord, PartialEq, Eq)]
pub struct Move(u32);

impl Move {
    /// Creates a new instance of `Move`.
    ///
    /// `us` is the side that makes the move. `castling` are the
    /// castling rights before the move was played. `en_passant_file`
    /// is the file on which there were a passing pawn before the move
    /// was played (or `8` if there was no passing
    /// pawn). `promoted_piece_code` should be a number between `0`
    /// and `3` and is used only when the `move_type` is a pawn
    /// promotion, otherwise it is ignored.
    #[inline]
    pub fn new(us: Color,
               score: usize,
               move_type: MoveType,
               piece: PieceType,
               orig_square: Square,
               dest_square: Square,
               captured_piece: PieceType,
               en_passant_file: File,
               castling: CastlingRights,
               promoted_piece_code: usize)
               -> Move {
        assert!(us <= 1);
        assert!(score <= 0b11);
        assert!(move_type <= 0x11);
        assert!(piece < NO_PIECE);
        assert!(orig_square <= 63);
        assert!(dest_square <= 63);
        assert!(captured_piece != KING && captured_piece <= NO_PIECE);
        assert!(en_passant_file <= 0b1000);
        assert!(promoted_piece_code <= 0b11);

        // We use the reserved field (2 bits) to properly order
        // "quiet" movies. Moves which destination square is more
        // advanced into enemy's territory is tried first.
        let reserved = if captured_piece == NO_PIECE {
            let rank = rank(dest_square);
            let advance = if us == WHITE {
                rank
            } else {
                7 - rank
            };
            match advance {
                0 => 0,
                x if x < 3 => 1,
                x if x < 5 => 2,
                _ => 3,
            }
        } else {
            0
        };
        let aux_data = match move_type {
            MOVE_PROMOTION => promoted_piece_code,
            _ => castling.get_for(us),
        };
        Move((score << M_SHIFT_SCORE | (!captured_piece & 0b111) << M_SHIFT_CAPTURED_PIECE |
              reserved << M_SHIFT_RESERVED | piece << M_SHIFT_PIECE |
              castling.get_for(1 ^ us) << M_SHIFT_CASTLING_DATA |
              en_passant_file << M_SHIFT_ENPASSANT_FILE |
              move_type << M_SHIFT_MOVE_TYPE | orig_square << M_SHIFT_ORIG_SQUARE |
              dest_square << M_SHIFT_DEST_SQUARE |
              aux_data << M_SHIFT_AUX_DATA) as u32)
    }

    /// Sets the "move score" field to a particular value.
    #[inline]
    pub fn set_score(&mut self, score: usize) {
        assert!(score <= 0b11);
        self.0 &= !M_MASK_SCORE;
        self.0 |= (score << M_SHIFT_SCORE) as u32;
    }

    /// Sets a particular bit in the "move score" field to `1`.
    #[inline]
    pub fn set_score_bit(&mut self, b: usize) {
        assert!(b <= 1);
        self.0 |= 1 << b << M_SHIFT_SCORE;
    }

    /// Sets a particular bit in the "move score" field to `0`.
    #[inline]
    pub fn clear_score_bit(&mut self, b: usize) {
        assert!(b <= 1);
        self.0 &= !(1 << b << M_SHIFT_SCORE);
    }

    #[inline]
    pub fn score(&self) -> usize {
        ((self.0 & M_MASK_SCORE) >> M_SHIFT_SCORE) as usize
    }

    #[inline]
    pub fn move_type(&self) -> MoveType {
        ((self.0 & M_MASK_MOVE_TYPE) >> M_SHIFT_MOVE_TYPE) as MoveType
    }

    #[inline]
    pub fn piece(&self) -> PieceType {
        ((self.0 & M_MASK_PIECE) >> M_SHIFT_PIECE) as PieceType
    }

    #[inline]
    pub fn orig_square(&self) -> Square {
        ((self.0 & M_MASK_ORIG_SQUARE) >> M_SHIFT_ORIG_SQUARE) as Square
    }

    #[inline]
    pub fn dest_square(&self) -> Square {
        ((self.0 & M_MASK_DEST_SQUARE) >> M_SHIFT_DEST_SQUARE) as Square
    }

    #[inline]
    pub fn captured_piece(&self) -> PieceType {
        ((!self.0 & M_MASK_CAPTURED_PIECE) >> M_SHIFT_CAPTURED_PIECE) as PieceType
    }

    /// Returns the file on which there were a passing pawn before the
    /// move was played (or `8` if there was no passing pawn).
    #[inline]
    pub fn en_passant_file(&self) -> File {
        ((self.0 & M_MASK_ENPASSANT_FILE) >> M_SHIFT_ENPASSANT_FILE) as File
    }

    /// Returns a 2-bit value representing the castling rights for the
    /// side that does not make the move, as they were before the move
    /// was played.
    #[inline(always)]
    pub fn castling_data(&self) -> usize {
        ((self.0 & M_MASK_CASTLING_DATA) >> M_SHIFT_CASTLING_DATA) as usize
    }

    /// Returns 2-bit value representing auxiliary data.
    ///
    /// When the move type is pawn promotion, "aux data" holds the
    /// promoted piece type encoded with a value from 0 to 3. For all
    /// other move types "aux data" holds the castling rights for the
    /// side that makes the move, as they were before the move was
    /// played.
    #[inline(always)]
    pub fn aux_data(&self) -> usize {
        ((self.0 & M_MASK_AUX_DATA) >> M_SHIFT_AUX_DATA) as usize
    }

    /// Returns the algebraic notation of the move.
    ///
    /// Examples: `e2e4`, `e7e5`, `e1g1` (white short castling),
    /// `e7e8q` (for promotion).
    pub fn notation(&self) -> String {
        format!("{}{}{}",
                notation(self.orig_square()),
                notation(self.dest_square()),
                match self.move_type() {
                    MOVE_PROMOTION => ["q", "r", "b", "n"][self.aux_data()],
                    _ => "",
                })
    }

    /// Decodes the promoted piece type from the raw value of "aux
    /// data".
    #[inline]
    pub fn piece_from_aux_data(pp_code: usize) -> PieceType {
        assert!(pp_code <= 3);
        match pp_code {
            0 => QUEEN,
            1 => ROOK,
            2 => BISHOP,
            _ => KNIGHT,
        }
    }

    #[allow(dead_code)]
    #[inline]
    fn reserved(&self) -> usize {
        ((self.0 & M_MASK_RESERVED) >> M_SHIFT_RESERVED) as usize
    }
}

/// `MOVE_ENPASSANT`, `MOVE_PROMOTION`, `MOVE_CASTLING`, or
/// `MOVE_NORMAL`.
pub type MoveType = usize;

/// En-passant capture move type.
pub const MOVE_ENPASSANT: MoveType = 0;

/// Pawn promotion move type.
pub const MOVE_PROMOTION: MoveType = 1;

/// Castling move type.
pub const MOVE_CASTLING: MoveType = 2;

/// Normal move type.
pub const MOVE_NORMAL: MoveType = 3;

// Field shifts
const M_SHIFT_SCORE: u32 = 30;
const M_SHIFT_CAPTURED_PIECE: u32 = 27;
const M_SHIFT_RESERVED: u32 = 25;
const M_SHIFT_PIECE: u32 = 22;
const M_SHIFT_CASTLING_DATA: u32 = 20;
const M_SHIFT_ENPASSANT_FILE: u32 = 16;
const M_SHIFT_MOVE_TYPE: u32 = 14;
const M_SHIFT_ORIG_SQUARE: u32 = 8;
const M_SHIFT_DEST_SQUARE: u32 = 2;
const M_SHIFT_AUX_DATA: u32 = 0;

// Field masks
const M_MASK_SCORE: u32 = 0b11 << M_SHIFT_SCORE;
const M_MASK_CAPTURED_PIECE: u32 = 0b111 << M_SHIFT_CAPTURED_PIECE;
const M_MASK_RESERVED: u32 = 0b11 << M_SHIFT_RESERVED;
const M_MASK_PIECE: u32 = 0b111 << M_SHIFT_PIECE;
const M_MASK_CASTLING_DATA: u32 = 0b11 << M_SHIFT_CASTLING_DATA;
const M_MASK_ENPASSANT_FILE: u32 = 0b1111 << M_SHIFT_ENPASSANT_FILE;
const M_MASK_MOVE_TYPE: u32 = 0b11 << M_SHIFT_MOVE_TYPE;
const M_MASK_ORIG_SQUARE: u32 = 0b111111 << M_SHIFT_ORIG_SQUARE;
const M_MASK_DEST_SQUARE: u32 = 0b111111 << M_SHIFT_DEST_SQUARE;
const M_MASK_AUX_DATA: u32 = 0b11 << M_SHIFT_AUX_DATA;

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

    #[test]
    fn test_move() {
        use basetypes::*;
        const NO_ENPASSANT_FILE: File = 8;

        let mut cr = CastlingRights::new();
        cr.set_for(WHITE, 0b10);
        cr.set_for(BLACK, 0b11);
        let mut m = Move::new(WHITE,
                              2,
                              MOVE_NORMAL,
                              PAWN,
                              E2,
                              E4,
                              NO_PIECE,
                              NO_ENPASSANT_FILE,
                              cr,
                              0);
        let n1 = Move::new(WHITE,
                           2,
                           MOVE_NORMAL,
                           PAWN,
                           F3,
                           E4,
                           KNIGHT,
                           NO_ENPASSANT_FILE,
                           CastlingRights::new(),
                           0);
        let n2 = Move::new(WHITE,
                           2,
                           MOVE_NORMAL,
                           KING,
                           F3,
                           E4,
                           NO_PIECE,
                           NO_ENPASSANT_FILE,
                           CastlingRights::new(),
                           0);
        let n3 = Move::new(BLACK,
                           0,
                           MOVE_PROMOTION,
                           PAWN,
                           F2,
                           F1,
                           NO_PIECE,
                           NO_ENPASSANT_FILE,
                           CastlingRights::new(),
                           1);
        assert!(n1 > m);
        assert!(n2 < m);
        assert_eq!(m.score(), 2);
        assert_eq!(m.piece(), PAWN);
        assert_eq!(m.captured_piece(), NO_PIECE);
        assert_eq!(m.orig_square(), E2);
        assert_eq!(m.dest_square(), E4);
        assert_eq!(m.en_passant_file(), 8);
        assert_eq!(m.aux_data(), 0b10);
        assert_eq!(m.castling_data(), 0b11);
        let m2 = m;
        assert_eq!(m, m2);
        m.set_score(3);
        assert_eq!(m.score(), 3);
        assert!(m > m2);
        m.clear_score_bit(0);
        assert_eq!(m, m2);
        m.set_score_bit(0);
        assert_eq!(m.score(), 3);
        m.set_score(0);
        assert_eq!(m.score(), 0);
        assert_eq!(n3.aux_data(), 1);
        assert_eq!(n1.reserved(), 0);
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::large_enum_variant)]
#![allow(dead_code)]

use crate::rustup_mode::CompletionCommand;

use std::io;
use std::path::PathBuf;

use clap::Shell;
use error_chain::error_chain;
use rustup::dist::temp;

error_chain! {
    links {
        Rustup(rustup::Error, rustup::ErrorKind);
    }

    foreign_links {
        Temp(temp::Error);
        Io(io::Error);
        Term(term::Error);
    }

    errors {
        PermissionDenied {
            description("permission denied")
        }
        ToolchainNotInstalled(t: String) {
            description("toolchain is not installed")
            display("toolchain '{}' is not installed", t)
        }
        InvalidToolchainName(t: String) {
            description("invalid toolchain name")
            display("invalid toolchain name: '{}'", t)
        }
        InfiniteRecursion {
            description("infinite recursion detected")
        }
        NoExeName {
            description("couldn't determine self executable name")
        }
        NotSelfInstalled(p: PathBuf) {
            description("rustup is not installed")
            display("rustup is not installed at '{}'", p.display())
        }
        WindowsUninstallMadness {
            description("failure during windows uninstall")
        }
        UnsupportedCompletionShell(shell: Shell, cmd: CompletionCommand) {
            description("completion script for shell not yet supported for tool")
            display("{} does not currently support completions for {}", cmd, shell)
        }
        TargetAllSpecifiedWithTargets(t: Vec<String>) {
            description(
                "the `all` target, which installs all available targets, \
                 cannot be combined with other targets"
            )
            display("`rustup target add {}` includes `all`", t.join(" "))
        }
        WritingShellProfile {
            path: PathBuf,
        } {
            description("could not amend shell profile")
            display("could not amend shell profile: '{}'", path.display())
        }
    }
}
<commit_msg>Suggest toolchain (channel) fix on bad toolchain name<commit_after>#![allow(clippy::large_enum_variant)]
#![allow(dead_code)]

use crate::rustup_mode::CompletionCommand;

use std::io;
use std::path::PathBuf;

use clap::Shell;
use error_chain::error_chain;
use lazy_static::lazy_static;
use regex::Regex;
use rustup::dist::temp;
use strsim::damerau_levenshtein;

error_chain! {
    links {
        Rustup(rustup::Error, rustup::ErrorKind);
    }

    foreign_links {
        Temp(temp::Error);
        Io(io::Error);
        Term(term::Error);
    }

    errors {
        PermissionDenied {
            description("permission denied")
        }
        ToolchainNotInstalled(t: String) {
            description("toolchain is not installed")
            display("toolchain '{}' is not installed", t)
        }
        InvalidToolchainName(t: String) {
            description("invalid toolchain name")
            display("invalid toolchain name: '{}'{}", t, maybe_suggest_toolchain(t))
        }
        InfiniteRecursion {
            description("infinite recursion detected")
        }
        NoExeName {
            description("couldn't determine self executable name")
        }
        NotSelfInstalled(p: PathBuf) {
            description("rustup is not installed")
            display("rustup is not installed at '{}'", p.display())
        }
        WindowsUninstallMadness {
            description("failure during windows uninstall")
        }
        UnsupportedCompletionShell(shell: Shell, cmd: CompletionCommand) {
            description("completion script for shell not yet supported for tool")
            display("{} does not currently support completions for {}", cmd, shell)
        }
        TargetAllSpecifiedWithTargets(t: Vec<String>) {
            description(
                "the `all` target, which installs all available targets, \
                 cannot be combined with other targets"
            )
            display("`rustup target add {}` includes `all`", t.join(" "))
        }
        WritingShellProfile {
            path: PathBuf,
        } {
            description("could not amend shell profile")
            display("could not amend shell profile: '{}'", path.display())
        }
    }
}

fn maybe_suggest_toolchain(bad_name: &str) -> String {
    let bad_name = &bad_name.to_ascii_lowercase();
    static VALID_CHANNELS: &[&str] = &["stable", "beta", "nightly"];
    lazy_static! {
        static ref NUMBERED: Regex = Regex::new(r"^\d+\.\d+$").unwrap();
    }

    if NUMBERED.is_match(bad_name) {
        return format!(
            ". Toolchain numbers tend to have three parts, e.g. {}.0",
            bad_name
        );
    }

    // Suggest only for very small differences
    // High number can result in inaccurate suggestions for short queries e.g. `rls`
    const MAX_DISTANCE: usize = 3;

    let mut scored: Vec<_> = VALID_CHANNELS
        .iter()
        .filter_map(|s| {
            let distance = damerau_levenshtein(bad_name, s);
            if distance <= MAX_DISTANCE {
                Some((distance, s))
            } else {
                None
            }
        })
        .collect();
    scored.sort();
    if scored.is_empty() {
        String::new()
    } else {
        format!(". Did you mean '{}'?", scored[0].1)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn hydrogen_start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };
    trace!("Address: ");

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { *kill_tx = k_tx.clone(); }

    // // Writer thread's channel
    // let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // unsafe { *writer_tx = w_tx.clone(); }
    //
    // debug!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     error!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // debug!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    // let r_client = client.clone();
    // let w_client = client.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, data_handler)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(writer_rx, w_client)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         error!("Error on kill channel: {}", e);
    //         on_disconnect_handler();
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn hydrogen_kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

use std::{str};
use std::ffi::{CStr, CString};
use std::thread;
use std::net::TcpStream;
use std::sync::mpsc::{channel, Sender, Receiver};

use super::libc::{c_int, c_char};
use super::simple_stream::bstream::Bstream;

/// This client's writer channel
static mut writer_tx: *mut Sender<Vec<u8>> = 0 as *mut Sender<Vec<u8>>;

/// This client's kill channel
static mut kill_tx: *mut Sender<()> = 0 as *mut Sender<()>;

#[no_mangle]
pub extern "C" fn hydrogen_start(address: *const c_char,
    data_handler: extern fn(*const c_char),
    on_connect_handler: extern fn(),
    on_disconnect_handler: extern fn()) -> c_int {

    // TODO - adjust this to accept a log level adjustable by whoever is running
    // the application
    super::init();

    trace!("Rust - start()");

    let mut r_address;
    unsafe {
        r_address = CStr::from_ptr(address);
    }
    let s_address = r_address.to_bytes();
    let host_address = match str::from_utf8(s_address) {
        Ok(safe_str) => safe_str,
        Err(_) => {
            error!("Invalid host address");
            return -1 as c_int;
        }
    };
    trace!("Address: ");

    // Create and register a way to kill this client
    let (k_tx, kill_rx): (Sender<()>, Receiver<()>) = channel();
    unsafe { kill_tx = &mut k_tx.clone(); }

    // // Writer thread's channel
    // let (w_tx, writer_rx): (Sender<Vec<u8>>, Receiver<Vec<u8>>) = channel();
    // unsafe { *writer_tx = w_tx.clone(); }
    //
    // debug!("Attempting connect to: {}", host_address);
    //
    // let result = TcpStream::connect(host_address);
    // if result.is_err() {
    //     error!("Error connecting to {} - {}", host_address, result.unwrap_err());
    //     return -1 as c_int;
    // }
    // debug!("Connected");
    // on_connect_handler();
    //
    // let stream = result.unwrap();
    // let client = Bstream::new(stream);
    // let r_client = client.clone();
    // let w_client = client.clone();
    //
    // // Start the reader thread
    // thread::Builder::new()
    //     .name("ReaderThread".to_string())
    //     .spawn(move||{
    //         reader_thread(r_client, data_handler)
    //     }).unwrap();
    //
    // // Start the writer thread
    // thread::Builder::new()
    //     .name("WriterThread".to_string())
    //     .spawn(move||{
    //         writer_thread(writer_rx, w_client)
    //     }).unwrap();
    //
    // // Wait for the kill signal
    // match kill_rx.recv() {
    //     Ok(_) => { }
    //     Err(e) => {
    //         error!("Error on kill channel: {}", e);
    //         on_disconnect_handler();
    //         return -1 as c_int;
    //     }
    // };
    // on_disconnect_handler();

    // Exit out in standard C fashion
    0 as c_int
}

/// Writes the complete contents of buffer to the server
/// Returns -1 on error
#[no_mangle]
pub extern "C" fn hydrogen_write(buffer: *const c_char) -> c_int {
    trace!("Rust.write");

    let mut buf_as_cstr;
    unsafe {
        buf_as_cstr = CStr::from_ptr(buffer);
    }
    let buf_as_slice = buf_as_cstr.to_bytes();

    let mut n_buffer = Vec::<u8>::with_capacity(buf_as_slice.len());
    for byte in buf_as_slice.iter() {
        n_buffer.push(*byte);
    }

    unsafe {
        match (*writer_tx).send(n_buffer) {
            Ok(_) => { }
            Err(e) => {
                warn!("Error sending buffer: {}", e);
                let _ = (*kill_tx).send(());
                return -1 as c_int;
            }
        };
    }

    0 as c_int
}

/// Forever listens to incoming data and when a complete message is received,
/// the passed callback is hit
fn reader_thread(client: Bstream, handler: extern fn(*const c_char)) {
    trace!("Rust.reader_thread started");

    let mut reader = client.clone();
    loop {
        match reader.read() {
            Ok(buffer) => {
                // Launch the handler in a new thread
                thread::Builder::new()
                    .name("Reader-Worker".to_string())
                    .spawn(move||{
                        let slice = &buffer[..];
                        let c_buffer = CString::new(slice).unwrap();
                        handler(c_buffer.as_ptr());
                    }).unwrap();
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }
    debug!("Rust.reader_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Forever listens to Receiver<Vec<u8>> waiting on messages to come in
/// Once available, blocks until the entire message has been written
fn writer_thread(rx: Receiver<Vec<u8>>, client: Bstream) {
    trace!("Rust.writer_thread started");

    let mut writer = client.clone();
    loop {
        match rx.recv() {
            Ok(ref mut buffer) => {
                match writer.write(buffer) {
                    Ok(_) => { }
                    Err(e) => {
                        error!("Error: {}", e);
                        break;
                    }
                };
            }
            Err(e) => {
                error!("Error: {}", e);
                break;
            }
        };
    }

    debug!("Rust.writer_thread finished");
    unsafe { let _ = (*kill_tx).send(()); }
}

/// Drops the current connection and kills all current threads
#[no_mangle]
pub extern "C" fn hydrogen_kill() {
    unsafe { let _ = (*kill_tx).send(()); }
}
<|endoftext|>


<commit_before>extern crate serialize;

use std::io::{File, Open, Append, Read, ReadWrite};
use std::io::fs::PathExtensions;
use std::io::{BufferedReader, BufferedWriter};
use std::path::BytesContainer;
use self::serialize::base64::{STANDARD, FromBase64, ToBase64};

// A result type that's specfici to the Reader module.
// TODO Decide if this is necessary
pub type ReaderResult<T, E> = Result<T, E>;

// Reader struct of its basic properties.
pub struct Reader {
    // Path to file where the Reader is created.
    path: Path,
    // BufferedReader for reading the file. Initialized with the Path.
    read_buffer: BufferedReader<File>,
    // BufferedWriter for writing to the file. Initialized with the Path.
    write_buffer: BufferedWriter<File>,
}

// ReaderFile traits
pub trait ReaderFile {
    // Opens a new File to the Path provided.
    // Returns a boxed File.
    fn open(&self) -> Box<File>;
    // Inserts a string to the database.
    fn insert_string(&mut self, String);
    // Encodes a record to Base64 (with standard encoding).
    fn encode_record(&self, String) -> String;
    // Decoes a record from Base64.
    fn decode_record(&self, String) -> String; //TODO Maybe change this to JSON later?

}

impl Reader {
    // Creates a new Reader from the Path provided.
    // Opens a new BufferedReader and BufferedWriter (with Append mode) to the file.
    // If the file doesn't exist, it is created.
    pub fn new(apath: Path) -> Reader {
        Reader {
            path: {
                if !apath.exists() { File::create(&apath.clone()); }
                apath.clone()
            },
            read_buffer: {
                match File::open_mode(&apath.clone(), Open, Read) {
                    Ok(file)    => { BufferedReader::new(file) },
                    Err(..)     => { panic!("Failed to create a read buffer to file path."); }
                }
            },
            write_buffer: {
                match File::open_mode(&apath.clone(), Append, ReadWrite) {
                    Ok(file)    => { BufferedWriter::new(file) },
                    Err(..)     => { panic!("Failed to create a write buffer to file path."); }
                }
            }
        }
    }

    // This is a helder function that realistically shouldn't exist in production.
    // Used primarily for "spilling" the entire database file into a Vec<String>
    fn spill(&mut self) -> Vec<String> {
        let mut result: Vec<String> = Vec::new();
        for line_iter in self.read_buffer.lines() {
        //    println!("{}", line_iter.unwrap());
        //    let line = String::from_str(file.read_line().ok().expect("Nothing to read.").trim());
            result.push(line_iter.unwrap().trim().to_string());
        }
        return result;
    }

    // Inserts a &str into the database.
    fn insert_str(&mut self, item: &str) {
        self.write_buffer.write_line(item);
        self.write_buffer.flush();
    }

    // Inserts a byte array into the database.
    fn insert(&mut self, item: &[u8]) {
        self.write_buffer.write(item);
        self.write_buffer.flush();
    }

}

impl ReaderFile for Reader {

    fn open(&self) -> Box<File> {
        match File::open_mode(&self.path, Open, ReadWrite) {
            Ok(file)    => box file,
            Err(..)     => { panic!("File {} couldn't be opened!", &self.path.display()); },
        }
    }

    fn insert_string(&mut self, item: String) {
        self.insert_str(item.as_slice());
    }

    fn encode_record(&self, item: String) -> String {
        item.container_as_bytes().to_base64(STANDARD)
    }

    fn decode_record(&self, item: String) -> String {
        match item.as_slice().from_base64() {
            Ok(vec) => {
                match String::from_utf8(vec) {
                    Ok(e)       => e,
                    Err(..)  => panic!("Invalid UTF-8 sequence."),
                }
            }
            Err(..) => panic!("Corrupt data, unable to decode."),
        }
    }

}

#[test]
fn test_open_file() {
    let reader = Reader::new(Path::new("tests/base-test.txt"));
}

#[test]
fn test_create_file() {
    use std::io::fs;
    use std::io::fs::PathExtensions;
    let path = Path::new("tests/base-test-created.txt");
    assert!(!path.exists());
    let reader = Reader::new(path.clone());
    assert!(path.exists());
}

#[test]
fn test_read_file() {
    // We should output the entire contents of the database file we open
    // into standard output.
    let mut reader = setup();
    let expected = vec!["10 11".to_string(), "20 21".to_string()];
    assert_eq!(expected, reader.spill());
}

#[test]
fn test_write_string_to_file() {
    let mut reader = setup();
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_string("30 31".to_string());
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_write_str_to_file() {
    let mut reader = setup();
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_str("30 31");
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_encode_string() {
    let reader = setup();
    let expected = "dGhpcyBpcyBhIGxpbmU=";
    let result = reader.encode_record("this is a line".to_string());
    assert_eq![expected, result];
}

#[test]
fn test_decode_string() {
    let reader = setup();
    let expected = "this is a line";
    let result = reader.decode_record("dGhpcyBpcyBhIGxpbmU=".to_string());
    assert_eq![expected, result];
}

// Test setup code. Current functions:
//  - Create a new file 'tests/base-test.txt'
//  - Write a 2x2 matrix of records into the base-test.txt file
//  - If 'tests/base-test-created.txt' is created, delete it
//  - Returns a Reader object to 'tests/base-test.txt'
#[allow(dead_code, unused_must_use)]
fn setup() -> Reader {
    use std::io::fs;

    let mut file = File::create(&Path::new("tests/base-test.txt")).ok().expect("Unable to create test file");
    file.write_str("10 11\n20 21\n");

    let p = Path::new("tests/base-test-created.txt");
    if p.exists() {
        fs::unlink(&p);
    }

    Reader::new(Path::new("tests/base-test.txt"))
}
<commit_msg>Fixed boxing for Rust 1.0 alpha<commit_after>extern crate serialize;

use std::io::{File, Open, Append, Read, ReadWrite};
use std::io::fs::PathExtensions;
use std::io::{BufferedReader, BufferedWriter};
use std::path::BytesContainer;
use self::serialize::base64::{STANDARD, FromBase64, ToBase64};

// A result type that's specfici to the Reader module.
// TODO Decide if this is necessary
pub type ReaderResult<T, E> = Result<T, E>;

// Reader struct of its basic properties.
pub struct Reader {
    // Path to file where the Reader is created.
    path: Path,
    // BufferedReader for reading the file. Initialized with the Path.
    read_buffer: BufferedReader<File>,
    // BufferedWriter for writing to the file. Initialized with the Path.
    write_buffer: BufferedWriter<File>,
}

// ReaderFile traits
pub trait ReaderFile {
    // Opens a new File to the Path provided.
    // Returns a boxed File.
    fn open(&self) -> Box<File>;
    // Inserts a string to the database.
    fn insert_string(&mut self, String);
    // Encodes a record to Base64 (with standard encoding).
    fn encode_record(&self, String) -> String;
    // Decoes a record from Base64.
    fn decode_record(&self, String) -> String; //TODO Maybe change this to JSON later?

}

impl Reader {
    // Creates a new Reader from the Path provided.
    // Opens a new BufferedReader and BufferedWriter (with Append mode) to the file.
    // If the file doesn't exist, it is created.
    pub fn new(apath: Path) -> Reader {
        Reader {
            path: {
                if !apath.exists() { File::create(&apath.clone()); }
                apath.clone()
            },
            read_buffer: {
                match File::open_mode(&apath.clone(), Open, Read) {
                    Ok(file)    => { BufferedReader::new(file) },
                    Err(..)     => { panic!("Failed to create a read buffer to file path."); }
                }
            },
            write_buffer: {
                match File::open_mode(&apath.clone(), Append, ReadWrite) {
                    Ok(file)    => { BufferedWriter::new(file) },
                    Err(..)     => { panic!("Failed to create a write buffer to file path."); }
                }
            }
        }
    }

    // This is a helder function that realistically shouldn't exist in production.
    // Used primarily for "spilling" the entire database file into a Vec<String>
    fn spill(&mut self) -> Vec<String> {
        let mut result: Vec<String> = Vec::new();
        for line_iter in self.read_buffer.lines() {
        //    println!("{}", line_iter.unwrap());
        //    let line = String::from_str(file.read_line().ok().expect("Nothing to read.").trim());
            result.push(line_iter.unwrap().trim().to_string());
        }
        return result;
    }

    // Inserts a &str into the database.
    fn insert_str(&mut self, item: &str) {
        self.write_buffer.write_line(item);
        self.write_buffer.flush();
    }

    // Inserts a byte array into the database.
    fn insert(&mut self, item: &[u8]) {
        self.write_buffer.write(item);
        self.write_buffer.flush();
    }

}

impl ReaderFile for Reader {

    fn open(&self) -> Box<File> {
        match File::open_mode(&self.path, Open, ReadWrite) {
            Ok(file)    => Box::new(file),
            Err(..)     => { panic!("File {} couldn't be opened!", &self.path.display()); },
        }
    }

    fn insert_string(&mut self, item: String) {
        self.insert_str(item.as_slice());
    }

    fn encode_record(&self, item: String) -> String {
        item.container_as_bytes().to_base64(STANDARD)
    }

    fn decode_record(&self, item: String) -> String {
        match item.as_slice().from_base64() {
            Ok(vec) => {
                match String::from_utf8(vec) {
                    Ok(e)       => e,
                    Err(..)  => panic!("Invalid UTF-8 sequence."),
                }
            }
            Err(..) => panic!("Corrupt data, unable to decode."),
        }
    }

}

#[test]
fn test_open_file() {
    let reader = Reader::new(Path::new("tests/base-test.txt"));
}

#[test]
fn test_create_file() {
    use std::io::fs;
    use std::io::fs::PathExtensions;
    let path = Path::new("tests/base-test-created.txt");
    assert!(!path.exists());
    let reader = Reader::new(path.clone());
    assert!(path.exists());
}

#[test]
fn test_read_file() {
    // We should output the entire contents of the database file we open
    // into standard output.
    let mut reader = setup();
    let expected = vec!["10 11".to_string(), "20 21".to_string()];
    assert_eq!(expected, reader.spill());
}

#[test]
fn test_write_string_to_file() {
    let mut reader = setup();
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_string("30 31".to_string());
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_write_str_to_file() {
    let mut reader = setup();
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_str("30 31");
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_encode_string() {
    let reader = setup();
    let expected = "dGhpcyBpcyBhIGxpbmU=";
    let result = reader.encode_record("this is a line".to_string());
    assert_eq![expected, result];
}

#[test]
fn test_decode_string() {
    let reader = setup();
    let expected = "this is a line";
    let result = reader.decode_record("dGhpcyBpcyBhIGxpbmU=".to_string());
    assert_eq![expected, result];
}

// Test setup code. Current functions:
//  - Create a new file 'tests/base-test.txt'
//  - Write a 2x2 matrix of records into the base-test.txt file
//  - If 'tests/base-test-created.txt' is created, delete it
//  - Returns a Reader object to 'tests/base-test.txt'
#[allow(dead_code, unused_must_use)]
fn setup() -> Reader {
    use std::io::fs;

    let mut file = File::create(&Path::new("tests/base-test.txt")).ok().expect("Unable to create test file");
    file.write_str("10 11\n20 21\n");

    let p = Path::new("tests/base-test-created.txt");
    if p.exists() {
        fs::unlink(&p);
    }

    Reader::new(Path::new("tests/base-test.txt"))
}
<|endoftext|>


<commit_before>use metrics_worker::MetricsWorker;
use events::Events;
use log::LogLevelFilter;
use logger::MetricsLoggerFactory;
use logger::MetricsLogger;
use std::sync::{Arc, Mutex};

#[allow(non_upper_case_globals)]
// Shortcut to MetricsLoggerFactory function that gets the logger instance.
const logger: fn() -> &'static MetricsLogger = MetricsLoggerFactory::get_logger;

pub struct EventInfo {
    pub locale: String,
    pub os: String,
    pub os_version: String,
    pub device: String,
    pub arch: String,
    pub app_name: String,
    pub app_version: String,
    pub app_update_channel: String,
    pub app_platform: String,
}

impl EventInfo {
    pub fn new(locale: &str,
               os: &str,
               os_version: &str,
               device: &str,
               app_name: &str,
               app_version: &str,
               app_update_channel: &str,
               app_platform: &str,
               arch: &str)
               -> EventInfo {

        EventInfo {
            locale: locale.to_owned(),
            os: os.to_owned(),
            os_version: os_version.to_owned(),
            device: device.to_owned(),
            app_name: app_name.to_owned(),
            app_version: app_version.to_owned(),
            app_update_channel: app_update_channel.to_owned(),
            app_platform: app_platform.to_owned(),
            arch: arch.to_owned()
        }
    }
}

pub struct AnalyticsProperty;

impl AnalyticsProperty {
    #[cfg(not(test))]
    pub fn get() -> String {
        use config::Config;
        const ANALYTICS_KEY: &'static str = "analytics";

        let mut cfg = Config::new();
        cfg.init("metricsconfig.json");
        cfg.get_string(ANALYTICS_KEY)
    }
    #[cfg(test)]
    pub fn get() -> String {
        "test_analytics_property".to_string()
    }
}

/// The metrics controller for the CD Metrics Library
pub struct MetricsController {
    events: Arc<Mutex<Events>>,
    mw: MetricsWorker,
}

impl MetricsController {
    //  Note: The following code example produces an 'unused variable' warning
    //        so it is being ignored for the purpose of running tests.

    /// Constructs a new `MetricsController`. Caller passes information
    /// about their application and environment. This information will be associated
    /// with the metrics data recorded by the `record_event` function.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use metrics_controller::controller::MetricsController;
    /// let mc = MetricsController::new(
    ///     "foxbox".to_string(),
    ///     "1.0".to_string(),
    ///     "nightly".to_string(),
    ///     "20160305".to_string(),
    ///     "rust".to_string(),
    ///     "en-us".to_string(),
    ///     "raspberry-pi".to_string(),
    ///     "arm".to_string(),
    ///     "linux".to_string(),
    ///     "1.2.3".to_string());
    /// ```
    pub fn new(app_name: &str,
               app_version: &str,
               app_update_channel: &str,
               app_platform: &str,
               locale: &str,
               device: &str,
               arch: &str,
               os: &str,
               os_version: &str)
               -> MetricsController {
        logger().log(LogLevelFilter::Info, "Creating Controller");

        let analytics_property = AnalyticsProperty::get();

        let event_info = EventInfo::new(locale,
                                        os,
                                        os_version,
                                        device,
                                        app_name,
                                        app_version,
                                        app_update_channel,
                                        app_platform,
                                        arch);
        let events = Arc::new(Mutex::new(Events::new(event_info, analytics_property)));

        MetricsController {
            events: events.clone(),
            mw: MetricsWorker::new(events),
        }

    }

    // TODO determine if we still want this function
    /// This function is called to start the metrics service.  It also starts the
    /// worker thread needed to operate the metrics service.  The worker thread
    /// is responsible for periodically: persisting the histogram data and
    /// transmitting it to the telemetry server.
    pub fn start_metrics(&mut self) -> bool {

        // Data needs to be read from disk here.  Let's assume that the controller
        // owns the histogram data for now.
        // Needs to call persistence module to read the data file.
        // Call config.init()
        // Call persistence.read() and populate histograms in memory in controller.
        // histograms in separate structs in separate files.  Controller maintains
        // a refernce to the in memory histograms.  Worker thread also needs it.
        // We would prefer to use a singleton pattern.
        // MetricsWorker::new();
        true
    }

    // TODO determine if we still want this function
    /// Stops the metrics service and deletes metrics data that has been collected
    /// but not sent to the server.
    pub fn stop_collecting(&mut self) {
        // TODO:  Eventually, this API will need to also delete the Histograms
        // from memory and delete the ones on disk.
        self.mw.quit();
    }

    /// Constructs a new event which is batched and sent to the Google Analytics
    /// server.
    ///
    /// Params:
    ///
    /// *event_category* -- Category of the event. For example, 'eng' or 'user'
    ///
    /// *event_action* -- Action that the user took or what happened to trigger. For example, 'open-app'
    ///
    /// *event_label* -- Description of what the metric is. For example, 'memory'
    ///
    /// *event_value* -- Numeric value of the metric, which is an Integer
    ///
    /// Returns:
    ///
    /// *true* - Success
    ///
    /// *false* - Error, unable to record the event
    pub fn record_event(&mut self,
                        event_category: &str,
                        event_action: &str,
                        event_label: &str,
                        event_value: u64)
                        -> bool {
        let mut events = self.events.lock().unwrap();
        events.insert_event(event_category, event_action, event_label, event_value)
    }

   /// Constructs a new event which is batched and sent to the Google Analytics
   /// server.
   ///
   /// Params:
   ///
  /// *event_category* -- Category of the event. For example, 'eng' or 'user'
  ///
  /// *event_action* -- Action that the user took or what happened to trigger. For example, 'open-app'
  ///
  /// *event_label* -- Description of what the metric is. For example, 'memory'
  ///
  /// *event_value* -- Numeric value of the metric, which is a floating point
  ///
  /// Returns:
  ///
  /// *true* - Success
  ///
  /// *false* - Error, unable to record the event
  pub fn record_floating_point_event(&mut self,
                                     event_category: &str,
                                     event_action: &str,
                                     event_label: &str,
                                     event_value: f64)
                                     -> bool {
      let mut events = self.events.lock().unwrap();
      events.insert_floating_point_event(event_category, event_action, event_label, event_value)
  }

}
<commit_msg>Rustdoc improvements<commit_after>use metrics_worker::MetricsWorker;
use events::Events;
use log::LogLevelFilter;
use logger::MetricsLoggerFactory;
use logger::MetricsLogger;
use std::sync::{Arc, Mutex};

#[allow(non_upper_case_globals)]
// Shortcut to MetricsLoggerFactory function that gets the logger instance.
const logger: fn() -> &'static MetricsLogger = MetricsLoggerFactory::get_logger;

pub struct EventInfo {
    pub locale: String,
    pub os: String,
    pub os_version: String,
    pub device: String,
    pub arch: String,
    pub app_name: String,
    pub app_version: String,
    pub app_update_channel: String,
    pub app_platform: String,
}

impl EventInfo {
    pub fn new(locale: &str,
               os: &str,
               os_version: &str,
               device: &str,
               app_name: &str,
               app_version: &str,
               app_update_channel: &str,
               app_platform: &str,
               arch: &str)
               -> EventInfo {

        EventInfo {
            locale: locale.to_owned(),
            os: os.to_owned(),
            os_version: os_version.to_owned(),
            device: device.to_owned(),
            app_name: app_name.to_owned(),
            app_version: app_version.to_owned(),
            app_update_channel: app_update_channel.to_owned(),
            app_platform: app_platform.to_owned(),
            arch: arch.to_owned()
        }
    }
}

pub struct AnalyticsProperty;

impl AnalyticsProperty {
    #[cfg(not(test))]
    pub fn get() -> String {
        use config::Config;
        const ANALYTICS_KEY: &'static str = "analytics";

        let mut cfg = Config::new();
        cfg.init("metricsconfig.json");
        cfg.get_string(ANALYTICS_KEY)
    }
    #[cfg(test)]
    pub fn get() -> String {
        "test_analytics_property".to_string()
    }
}

/// The metrics controller for the CD Metrics Library
pub struct MetricsController {
    events: Arc<Mutex<Events>>,
    mw: MetricsWorker,
}

impl MetricsController {
    //  Note: The following code example produces an 'unused variable' warning
    //        so it is being ignored for the purpose of running tests.

    /// Constructs a new `MetricsController` object. The caller passes information
    /// about their application and environment. This information will be associated
    /// with the metrics data recorded by the `record_event` function.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use metrics_controller::controller::MetricsController;
    /// let mc = MetricsController::new(
    ///     "foxbox".to_string(),
    ///     "1.0".to_string(),
    ///     "nightly".to_string(),
    ///     "20160305".to_string(),
    ///     "rust".to_string(),
    ///     "en-us".to_string(),
    ///     "raspberry-pi".to_string(),
    ///     "arm".to_string(),
    ///     "linux".to_string(),
    ///     "1.2.3".to_string());
    /// ```
    pub fn new(app_name: &str,
               app_version: &str,
               app_update_channel: &str,
               app_platform: &str,
               locale: &str,
               device: &str,
               arch: &str,
               os: &str,
               os_version: &str)
               -> MetricsController {
        logger().log(LogLevelFilter::Info, "Creating Controller");

        let analytics_property = AnalyticsProperty::get();

        let event_info = EventInfo::new(locale,
                                        os,
                                        os_version,
                                        device,
                                        app_name,
                                        app_version,
                                        app_update_channel,
                                        app_platform,
                                        arch);
        let events = Arc::new(Mutex::new(Events::new(event_info, analytics_property)));

        MetricsController {
            events: events.clone(),
            mw: MetricsWorker::new(events),
        }

    }

    // TODO determine if we still want this function
    /// This function starts the metrics service, which also starts the
    /// worker thread needed to operate the metrics service.  The worker thread
    /// is responsible for periodically persisting the metrics data and
    /// transmitting it to the server.
    pub fn start_metrics(&mut self) -> bool {

        // Data needs to be read from disk here.  Let's assume that the controller
        // owns the histogram data for now.
        // Needs to call persistence module to read the data file.
        // Call config.init()
        // Call persistence.read() and populate histograms in memory in controller.
        // histograms in separate structs in separate files.  Controller maintains
        // a refernce to the in memory histograms.  Worker thread also needs it.
        // We would prefer to use a singleton pattern.
        // MetricsWorker::new();
        true
    }

    // TODO determine if we still want this function
    /// This function stops the metrics service and deletes metrics data that has been collected
    /// but not sent to the server.
    pub fn stop_collecting(&mut self) {
        // TODO:  Eventually, this API will need to also delete the Histograms
        // from memory and delete the ones on disk.
        self.mw.quit();
    }

    /// Constructs and records an event. The recorded events are sent to the server based on an internal
    /// schedule (when twenty events are recorded). Currently, this schedule is not configurable.
    ///
    /// Params:
    ///
    /// **event_category** - Category of the event. For example, `eng` or `user`
    ///
    /// **event_action** - Action that triggered the event. For example, `open-app`
    ///
    /// **event_label** - Label, or description, of the metric. For example, `memory`
    ///
    /// **event_value** - Numeric value of the metric.
    ///
    /// Returns:
    ///
    /// **true** - Success
    ///
    /// **false** - Unable to record the event
    pub fn record_event(&mut self,
                        event_category: &str,
                        event_action: &str,
                        event_label: &str,
                        event_value: u64)
                        -> bool {
        let mut events = self.events.lock().unwrap();
        events.insert_event(event_category, event_action, event_label, event_value)
    }

   /// Constructs and records an event with a floating point value. The recorded events are sent to the
   /// server based on an internal schedule (when twenty events are recorded). Currently, this schedule
   /// is not configurable.
   ///
   /// Params:
   ///
  /// **event_category** - Category of the event. For example, `eng` or `user`
  ///
  /// **event_action** - Action that triggered the event. For example, `open-app`
  ///
  /// **event_label** - Label, or description, of the metric. For example, `memory`
  ///
  /// **event_value** - Numeric (floating point) value of the metric.
  ///
  /// Returns:
  ///
  /// **true** - Success
  ///
  /// **false** - Unable to record the event
  pub fn record_floating_point_event(&mut self,
                                     event_category: &str,
                                     event_action: &str,
                                     event_label: &str,
                                     event_value: f64)
                                     -> bool {
      let mut events = self.events.lock().unwrap();
      events.insert_floating_point_event(event_category, event_action, event_label, event_value)
  }

}
<|endoftext|>


<commit_before>use nalgebra;
use num::Num;
use std::cmp;
use std::convert::From;
use std::marker::PhantomData;
use std::ops;

pub trait ClampRange {
    type Output;

    fn max_value() -> Self::Output;
    fn min_value() -> Self::Output;
}

#[derive(Clone, Copy)]
pub struct Clamped<T, R>(T, PhantomData<R>)
    where T: Num + cmp::PartialOrd,
          R: ClampRange<Output = T>;

impl<T, R> Clamped<T, R>
    where T: Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    pub fn new(value: T) -> Self {
        Clamped(nalgebra::clamp(value, R::min_value(), R::max_value()), PhantomData)
    }

    pub fn max_value() -> Self {
        Clamped(R::max_value(), PhantomData)
    }

    pub fn min_value() -> Self {
        Clamped(R::min_value(), PhantomData)
    }
}

impl<T, R> From<T> for Clamped<T, R>
    where T: Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn from(value: T) -> Self {
        Self::new(value)
    }
}

impl<T, R> cmp::Eq for Clamped<T, R>
    where T: cmp::Eq + Num + cmp::PartialOrd,
          R: ClampRange<Output = T> {}

impl<T, R> cmp::Ord for Clamped<T, R>
    where T: Num + cmp::Ord + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<T, R> cmp::PartialEq for Clamped<T, R>
    where T: Num + cmp::PartialEq + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl<T, R> cmp::PartialOrd for Clamped<T, R>
    where T: Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl<T, R> ops::Add for Clamped<T, R>
    where T: ops::Add<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn add(self, other: Self) -> Self::Output {
        Self::new(self.0.add(other.0))
    }
}

impl<T, R> ops::Deref for Clamped<T, R>
    where T: Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T, R> ops::Div for Clamped<T, R>
    where T: ops::Div<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn div(self, other: Self) -> Self::Output {
        Self::new(self.0.div(other.0))
    }
}

impl<T, R> ops::Mul for Clamped<T, R>
    where T: ops::Mul<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn mul(self, other: Self) -> Self::Output {
        Self::new(self.0.mul(other.0))
    }
}

impl<T, R> ops::Neg for Clamped<T, R>
    where T: ops::Neg<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn neg(self) -> Self::Output {
        Self::new(self.0.neg())
    }
}

impl<T, R> ops::Sub for Clamped<T, R>
    where T: ops::Sub<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output {
        Self::new(self.0.sub(other.0))
    }
}
<commit_msg>Implement `Clone` and `Copy` for `Clamped`.<commit_after>use nalgebra;
use num::Num;
use std::cmp;
use std::convert::From;
use std::marker::PhantomData;
use std::ops;

pub trait ClampRange {
    type Output;

    fn max_value() -> Self::Output;
    fn min_value() -> Self::Output;
}

pub struct Clamped<T, R>(T, PhantomData<R>)
    where T: Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>;

impl<T, R> Clamped<T, R>
    where T: Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    pub fn new(value: T) -> Self {
        Clamped(nalgebra::clamp(value, R::min_value(), R::max_value()), PhantomData)
    }

    pub fn max_value() -> Self {
        Clamped(R::max_value(), PhantomData)
    }

    pub fn min_value() -> Self {
        Clamped(R::min_value(), PhantomData)
    }
}

impl<T, R> Clone for Clamped<T, R>
    where T: Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn clone(&self) -> Self {
        Clamped(self.0, PhantomData)
    }
}

impl<T, R> Copy for Clamped<T, R>
    where T: Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T> {}

impl<T, R> From<T> for Clamped<T, R>
    where T: Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn from(value: T) -> Self {
        Self::new(value)
    }
}

impl<T, R> cmp::Eq for Clamped<T, R>
    where T: Copy + cmp::Eq + Num + cmp::PartialOrd,
          R: ClampRange<Output = T> {}

impl<T, R> cmp::Ord for Clamped<T, R>
    where T: Copy + Num + cmp::Ord + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<T, R> cmp::PartialEq for Clamped<T, R>
    where T: Copy + Num + cmp::PartialEq + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl<T, R> cmp::PartialOrd for Clamped<T, R>
    where T: Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.0.partial_cmp(&other.0)
    }
}

impl<T, R> ops::Add for Clamped<T, R>
    where T: ops::Add<Output = T> + Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn add(self, other: Self) -> Self::Output {
        Self::new(self.0.add(other.0))
    }
}

impl<T, R> ops::Deref for Clamped<T, R>
    where T: Copy + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T, R> ops::Div for Clamped<T, R>
    where T: Copy + ops::Div<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn div(self, other: Self) -> Self::Output {
        Self::new(self.0.div(other.0))
    }
}

impl<T, R> ops::Mul for Clamped<T, R>
    where T: Copy + ops::Mul<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn mul(self, other: Self) -> Self::Output {
        Self::new(self.0.mul(other.0))
    }
}

impl<T, R> ops::Neg for Clamped<T, R>
    where T: Copy + ops::Neg<Output = T> + Num + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn neg(self) -> Self::Output {
        Self::new(self.0.neg())
    }
}

impl<T, R> ops::Sub for Clamped<T, R>
    where T: Copy + Num + ops::Sub<Output = T> + cmp::PartialOrd,
          R: ClampRange<Output = T>
{
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output {
        Self::new(self.0.sub(other.0))
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;

use std::result::*;
use std::io;
use std::io::{BufWriter, ErrorKind, Write, Seek, SeekFrom, Error};
use self::byteorder::{ByteOrder, ReadBytesExt, LittleEndian};

use definitions::*;

// decompressed_len returns the length of the decoded block.
pub fn decompressed_len(src: &[u8]) -> u64 {
	LittleEndian::read_u64(src)
}

// Decompress reads the decoded form of src into dst and returns the length
// read.
// Returns an error if dst was not large enough to hold the entire decoded
// block.
fn decompress(dst: &mut [u8], src: &mut [u8]) -> io::Result<usize> {
	// TODO: Handle Error
	let dLen = decompressed_len(src) as usize;
	// TODO FIX!!
	// For now, always assume 8 bytes used for src header length
	let mut s: usize = 8;

	if dst.len() < dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: destination buffer is too short"));
    }

    let (mut d, mut offset, mut length): (usize, usize, usize) = (0,0,0);

    while s < src.len() {
    	match src[s] & 0x03 {

			// Parse a Literal Chunk
    		TAG_LITERAL => {

    			let mut x = (src[s] >> 2) as usize;
    			match x {
	    			0...59 => s += 1,
    				60 => {
    					s += 2;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = src[s-1] as usize;
    				},
    				61 => {
    					s += 3;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-2] as usize) | ((src[s-1] as usize) << 8);
    				},
    				62 => {
    					s += 4;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-3] as usize) | ((src[s-2] as usize) << 8) | ((src[s-1] as usize) << 16);
    				},
    				63 => {
    					s += 5;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-4] as usize) | ((src[s-3] as usize) << 8) | ((src[s-2] as usize) << 16) | ((src[s-1] as usize) << 24);
    				},
    				_ => {}
    			}
    			length = x.wrapping_add(1);

    			if length as isize <= 0 {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported literal length"));
    			}
    			if length > dst.len() - d || length > src.len() - s {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    			}

    			// Copy src[s: s+length] to dst[d:0]
    			for (id, is) in dst.split_at_mut(d).1.iter_mut().zip(src.split_at(s).1.split_at(s+length).0.iter()) {
			        *id = *is;
			    }
			    d += length;
			    s += length;
			    continue;
    		},

    		// Parse a Copy1 Chunk
    		TAG_COPY_1 => {
    			s+= 2;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 4 + (src[s-2] as usize)>>2&0x7;
				offset = ((src[s-2] as usize)&0xe0<<3) | (src[s-1] as usize);
    		},

    		// Parse a Copy2 Chunk
    		TAG_COPY_2 => {
    			s+= 3;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 1 + (src[s-3] as usize)>>2;
				offset = (src[s-2] as usize) | ((src[s-1] as usize)<<8);
    		},

    		// Parse a Copy4
    		TAG_COPY_4 => {
		        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported COPY_4 tag"));
    		},

    		_ => unreachable!()
    	};

    	let end = d + length;
    	if offset > d || end > dst.len() {
	        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
	    }
    	while d < end {
    		d += 1;
    		dst[d] = dst[d-offset];
    	}
    }
    if d != dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    }
    Ok(d)
}

<commit_msg>Define Decompressor 1/2<commit_after>extern crate byteorder;

use std::result::*;
use std::io;
use std::io::{BufReader, ErrorKind, Read, Seek, SeekFrom, Error};
use self::byteorder::{ByteOrder, ReadBytesExt, LittleEndian};

use definitions::*;
use compress::{max_compressed_len};

// The Max Encoded Length of the Max Chunk of 65536 bytes
const MAX_BUFFER_SIZE: usize = 76_490;

pub struct Decompressor<R: Read> {
	inner: BufReader<R>,
	decoded: [u8; MAX_UNCOMPRESSED_CHUNK_LEN as usize],
	buf: [u8; MAX_BUFFER_SIZE + (CHECK_SUM_SIZE as usize)],
	// decoded[i:j] contains decoded bytes that have not yet been passed on.
	i: usize,
	j: usize,
	read_header: bool,
}

impl <R: Read> Decompressor<R> {

	pub fn new(inner: R) -> Decompressor<R> {
		Decompressor {
			inner: BufReader::new(inner),
			decoded: [0; MAX_UNCOMPRESSED_CHUNK_LEN as usize],
			buf: [0; MAX_BUFFER_SIZE + (CHECK_SUM_SIZE as usize)],
			i: 0,
			j: 0,
			read_header: false,
		}
	}
}

impl <R: Read> Read for Decompressor<R> {
	// Implement Read
	// Source (Inner) Buffer into Destination Buffer, returning how many bytes were read.
	fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
		loop {

			if self.i < self.j {
				let mut s = 0;
				for (d, l) in dst.iter_mut().zip(self.decoded.split_at(self.i).1.split_at(self.j).0.iter()) {
			        *d = *l;
			        s += 1;
			    }
			    self.i += s;
			    return Ok(s)
			}

			// Read ChunkType
			if self.inner.read(self.buf.split_at_mut(4).0).unwrap() != 4 {
				return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
			}
			let chunk_type = self.buf[0];

			if !self.read_header {
				if chunk_type != CHUNK_TYPE_STREAM_IDENTIFIER {
					return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"))
				}
				self.read_header = true;
			}

		}
	}
}

// decompressed_len returns the length of the decoded block.
pub fn decompressed_len(src: &[u8]) -> u64 {
	LittleEndian::read_u64(src)
}

// Decompress reads the decoded form of src into dst and returns the length
// read.
// Returns an error if dst was not large enough to hold the entire decoded
// block.
fn decompress(dst: &mut [u8], src: &mut [u8]) -> io::Result<usize> {
	// TODO: Handle Error
	let dLen = decompressed_len(src) as usize;
	// TODO FIX!!
	// For now, always assume 8 bytes used for src header length
	let mut s: usize = 8;

	if dst.len() < dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: destination buffer is too short"));
    }

    let (mut d, mut offset, mut length): (usize, usize, usize) = (0,0,0);

    while s < src.len() {
    	match src[s] & 0x03 {

			// Parse a Literal Chunk
    		TAG_LITERAL => {

    			let mut x = (src[s] >> 2) as usize;
    			match x {
	    			0...59 => s += 1,
    				60 => {
    					s += 2;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = src[s-1] as usize;
    				},
    				61 => {
    					s += 3;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-2] as usize) | ((src[s-1] as usize) << 8);
    				},
    				62 => {
    					s += 4;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-3] as usize) | ((src[s-2] as usize) << 8) | ((src[s-1] as usize) << 16);
    				},
    				63 => {
    					s += 5;
    					if s > src.len() {
					        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    					};
    					x = (src[s-4] as usize) | ((src[s-3] as usize) << 8) | ((src[s-2] as usize) << 16) | ((src[s-1] as usize) << 24);
    				},
    				_ => {}
    			}
    			length = x.wrapping_add(1);

    			if length as isize <= 0 {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported literal length"));
    			}
    			if length > dst.len() - d || length > src.len() - s {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    			}

    			// Copy src[s: s+length] to dst[d:0]
    			for (id, is) in dst.split_at_mut(d).1.iter_mut().zip(src.split_at(s).1.split_at(s+length).0.iter()) {
			        *id = *is;
			    }
			    d += length;
			    s += length;
			    continue;
    		},

    		// Parse a Copy1 Chunk
    		TAG_COPY_1 => {
    			s+= 2;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 4 + (src[s-2] as usize)>>2&0x7;
				offset = ((src[s-2] as usize)&0xe0<<3) | (src[s-1] as usize);
    		},

    		// Parse a Copy2 Chunk
    		TAG_COPY_2 => {
    			s+= 3;
    			if s > src.len() {
			        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
				};
				length = 1 + (src[s-3] as usize)>>2;
				offset = (src[s-2] as usize) | ((src[s-1] as usize)<<8);
    		},

    		// Parse a Copy4
    		TAG_COPY_4 => {
		        return Err(Error::new(ErrorKind::InvalidInput, "snappy: unsupported COPY_4 tag"));
    		},

    		_ => unreachable!()
    	};

    	let end = d + length;
    	if offset > d || end > dst.len() {
	        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
	    }
    	while d < end {
    		d += 1;
    		dst[d] = dst[d-offset];
    	}
    }
    if d != dLen {
        return Err(Error::new(ErrorKind::InvalidInput, "snappy: corrupt input"));
    }
    Ok(d)
}

<|endoftext|>