<commit_before>pub mod fen;
pub mod make;

use self::fen::*;
use super::util::grid_to_string_with_props;
use bb::*;
use castling_rights::*;
use hash::{Zobrist, DEFAULT_ZOBRISH_HASH};
use piece::*;
use side::Side;
use side::*;
use square;
use square::Square;
use std::fmt;

use std;

pub const STARTING_POSITION_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w \
                                                 QqKk - 0 1";

/// State encodes all game state except position
#[derive(Debug, Clone)]
pub struct State {
    pub castling_rights: CastlingRights,
    pub ep_square: Option<Square>,
    pub stm: Side,
    pub full_move_number: usize,
    pub half_move_clock: usize,
}

/// Position encodes all positional information and non-positional game state
pub struct Position {
    // grid is an array representation of position
    grid: [Piece; 64],
    // bb_sides represents a bitboard for each side
    bb_sides: [BB; 2],
    // bb_pieces represents a bitboard for each piece
    bb_pieces: [BB; 12],
    // state represents non-positional game state (eg side to move)
    state: State,

    key: u64,

    hash: &'static Zobrist,
}

impl std::clone::Clone for Position {
    fn clone(&self) -> Self {
        Position {
            grid: self.grid,
            bb_sides: self.bb_sides.clone(),
            bb_pieces: self.bb_pieces.clone(),
            state: self.state.clone(),
            key: self.key,
            hash: self.hash,
        }
    }
}

impl fmt::Debug for Position {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl fmt::Display for Position {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl Position {
    pub fn new(grid: [Piece; 64], state: State) -> Position {
        let mut bb_pieces = [EMPTY; 12];
        let mut bb_sides = [EMPTY; 2];

        for (idx, pc) in grid.iter().enumerate().filter(|&(_, &pc)| pc.is_some()) {
            let bb_mask = BB::new(Square::new(idx as square::Internal));
            bb_sides[pc.side().raw()] |= bb_mask;
            bb_pieces[pc.to_usize()] |= bb_mask;
        }

        let hash = &DEFAULT_ZOBRISH_HASH;
        let key = hash.position(&grid, &state);

        Position {
            grid: grid,
            bb_pieces: bb_pieces,
            bb_sides: bb_sides,
            state: state,
            hash: &DEFAULT_ZOBRISH_HASH,
            key: key,
        }
    }

    /// Construct a new position from a FEN string
    pub fn from_fen(fen: &str) -> Result<Position, String> {
        from_fen(fen).map(|(grid, state)| Position::new(grid, state))
    }

    // Convert position to FEN representation
    pub fn to_fen(&self) -> String {
        to_fen(&self.grid, &self.state)
    }

    #[inline]
    pub fn hash_key(&self) -> u64 {
        self.key
    }

    /// Get position non-positional state
    #[inline]
    pub fn state(&self) -> &State {
        &self.state
    }

    /// Get position position
    #[inline]
    pub fn grid(&self) -> &[Piece; 64] {
        &self.grid
    }

    /// Get piece at square
    #[inline]
    pub fn at(&self, sq: Square) -> Piece {
        unsafe { return *self.grid.get_unchecked(sq.to_usize()) }
    }

    pub fn to_string(&self) -> String {
        let props = vec![
            ("    side to move", self.state.stm.to_string()),
            (" castling rights", self.state.castling_rights.to_string()),
            (
                "      en-passant",
                self.state
                    .ep_square
                    .map_or("-".to_string(), |s| s.to_string()),
            ),
            (" half-move clock", self.state.half_move_clock.to_string()),
            ("full-move number", self.state.full_move_number.to_string()),
            ("             FEN", self.to_fen()),
        ];
        grid_to_string_with_props(
            |sq: Square| -> char {
                let pc = self.at(sq);
                if pc.is_none() {
                    '.'
                } else {
                    pc.to_char()
                }
            },
            props.as_slice(),
        )
    }

    #[inline]
    fn put_piece(&mut self, pc: Piece, sq: Square) {
        debug_assert!(self.at(sq).is_none());
        self.grid[sq.to_usize()] = pc;
        self.update_grid(sq, pc);
        let bb_mask = BB::new(sq);
        let idx = pc.to_usize();
        self.bb_sides[idx & 1] |= bb_mask;
        self.bb_pieces[idx] |= bb_mask;
    }

    #[inline]
    fn remove_piece(&mut self, sq: Square) {
        debug_assert!(self.at(sq).is_some());

        let pc = self.at(sq);
        self.grid[sq.to_usize()] = NULL_PIECE;
        let bb_mask = !BB::new(sq);

        self.update_grid(sq, NULL_PIECE);
        let idx = pc.to_usize();
        self.bb_sides[idx & 1] &= bb_mask;
        self.bb_pieces[idx] &= bb_mask;
    }

    #[inline]
    fn move_piece(&mut self, from: Square, to: Square) -> BB {
        debug_assert!(self.at(from).is_some());
        debug_assert!(self.at(to).is_none());

        let pc = self.at(from);
        self.update_grid(from, NULL_PIECE);
        self.update_grid(to, pc);

        let bb_mask = BB::new(from) | BB::new(to);
        let idx = pc.to_usize();
        self.bb_sides[idx & 1] ^= bb_mask;
        self.bb_pieces[idx] ^= bb_mask;

        bb_mask
    }

    fn promote_piece(&mut self, sq: Square, new_pc: Piece) {
        let old_pc = self.at(sq);
        debug_assert!(old_pc.is_some());
        debug_assert_eq!(old_pc.side(), new_pc.side());

        self.grid[sq.to_usize()] = new_pc;
        self.update_grid(sq, new_pc);

        let bb_mask = BB::new(sq);
        self.bb_pieces[old_pc.to_usize()] ^= bb_mask;
        self.bb_pieces[new_pc.to_usize()] |= bb_mask;
    }

    /// Get bitboard of pieces for a particular side
    #[inline]
    pub fn bb_side(&self, side: Side) -> BB {
        unsafe { return *self.bb_sides.get_unchecked(side.to_usize() & 1) }
    }

    /// Get bitboard of pieces for a particular piece
    #[inline]
    pub fn bb_pc(&self, pc: Piece) -> BB {
        unsafe { return *self.bb_pieces.get_unchecked(pc.to_usize()) }
    }

    /// Get bitboard of sliding pieces for a particular side
    #[inline]
    pub fn bb_sliders(&self, side: Side) -> (BB, BB) {
        let queens = self.bb_pc(QUEEN.pc(side));
        let rooks = self.bb_pc(ROOK.pc(side));
        let bishops = self.bb_pc(BISHOP.pc(side));
        (queens | bishops, queens | rooks)
    }

    /// Get bitboard of all occupied squares
    #[inline]
    pub fn bb_occupied(&self) -> BB {
        self.bb_side(WHITE) | self.bb_side(BLACK)
    }

    /// Get bitboard of all empty squares
    #[inline]
    pub fn bb_empty(&self) -> BB {
        !self.bb_occupied()
    }

    #[inline]
    fn update_grid(&mut self, sq: Square, pc: Piece) {
        unsafe {
            *(self.grid.get_unchecked_mut(sq.to_usize())) = pc;
        }
    }
}

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

    #[test]
    fn test_to_string() {
        let position =
            &Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w").unwrap();

        let expected = unindent::unindent(
            "
          ABCDEFGH
        8|rnbqkbnr|8     side to move: white
        7|pppppppp|7  castling rights: QqKk
        6|........|6       en-passant: -
        5|........|5  half-move clock: 0
        4|........|4 full-move number: 1
        3|........|3              FEN: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QqKk - 0 1
        2|PPPPPPPP|2
        1|RNBQKBNR|1
          ABCDEFGH
        ",
        );
        assert_eq!(position.to_string(), expected);
    }
}
<commit_msg>over optimise more stuff<commit_after>pub mod fen;
pub mod make;

use self::fen::*;
use super::util::grid_to_string_with_props;
use bb::*;
use castling_rights::*;
use hash::{Zobrist, DEFAULT_ZOBRISH_HASH};
use piece::*;
use side::Side;
use side::*;
use square;
use square::Square;
use std::fmt;

use std;

pub const STARTING_POSITION_FEN: &'static str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w \
                                                 QqKk - 0 1";

/// State encodes all game state except position
#[derive(Debug, Clone)]
pub struct State {
    pub castling_rights: CastlingRights,
    pub ep_square: Option<Square>,
    pub stm: Side,
    pub full_move_number: usize,
    pub half_move_clock: usize,
}

/// Position encodes all positional information and non-positional game state
pub struct Position {
    // grid is an array representation of position
    grid: [Piece; 64],
    // bb_sides represents a bitboard for each side
    bb_sides: [BB; 2],
    // bb_pieces represents a bitboard for each piece
    bb_pieces: [BB; 12],
    // state represents non-positional game state (eg side to move)
    state: State,

    key: u64,

    hash: &'static Zobrist,
}

impl std::clone::Clone for Position {
    fn clone(&self) -> Self {
        Position {
            grid: self.grid,
            bb_sides: self.bb_sides.clone(),
            bb_pieces: self.bb_pieces.clone(),
            state: self.state.clone(),
            key: self.key,
            hash: self.hash,
        }
    }
}

impl fmt::Debug for Position {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl fmt::Display for Position {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

impl Position {
    pub fn new(grid: [Piece; 64], state: State) -> Position {
        let mut bb_pieces = [EMPTY; 12];
        let mut bb_sides = [EMPTY; 2];

        for (idx, pc) in grid.iter().enumerate().filter(|&(_, &pc)| pc.is_some()) {
            let bb_mask = BB::new(Square::new(idx as square::Internal));
            bb_sides[pc.side().raw()] |= bb_mask;
            bb_pieces[pc.to_usize()] |= bb_mask;
        }

        let hash = &DEFAULT_ZOBRISH_HASH;
        let key = hash.position(&grid, &state);

        Position {
            grid: grid,
            bb_pieces: bb_pieces,
            bb_sides: bb_sides,
            state: state,
            hash: &DEFAULT_ZOBRISH_HASH,
            key: key,
        }
    }

    /// Construct a new position from a FEN string
    pub fn from_fen(fen: &str) -> Result<Position, String> {
        from_fen(fen).map(|(grid, state)| Position::new(grid, state))
    }

    // Convert position to FEN representation
    pub fn to_fen(&self) -> String {
        to_fen(&self.grid, &self.state)
    }

    #[inline]
    pub fn hash_key(&self) -> u64 {
        self.key
    }

    /// Get position non-positional state
    #[inline]
    pub fn state(&self) -> &State {
        &self.state
    }

    /// Get position position
    #[inline]
    pub fn grid(&self) -> &[Piece; 64] {
        &self.grid
    }

    /// Get piece at square
    #[inline]
    pub fn at(&self, sq: Square) -> Piece {
        unsafe { return *self.grid.get_unchecked(sq.to_usize()) }
    }

    pub fn to_string(&self) -> String {
        let props = vec![
            ("    side to move", self.state.stm.to_string()),
            (" castling rights", self.state.castling_rights.to_string()),
            (
                "      en-passant",
                self.state
                    .ep_square
                    .map_or("-".to_string(), |s| s.to_string()),
            ),
            (" half-move clock", self.state.half_move_clock.to_string()),
            ("full-move number", self.state.full_move_number.to_string()),
            ("             FEN", self.to_fen()),
        ];
        grid_to_string_with_props(
            |sq: Square| -> char {
                let pc = self.at(sq);
                if pc.is_none() {
                    '.'
                } else {
                    pc.to_char()
                }
            },
            props.as_slice(),
        )
    }

    #[inline]
    fn put_piece(&mut self, pc: Piece, sq: Square) {
        debug_assert!(self.at(sq).is_none());

        let bb_mask = BB::new(sq);

        self.update_grid(sq, pc);

        debug_assert_eq!(self.bb_pc(pc) & bb_mask, EMPTY);
        debug_assert_eq!(self.bb_side(pc.side()) & bb_mask, EMPTY);

        unsafe {
            *self.bb_pieces.get_unchecked_mut(pc.to_usize()) ^= bb_mask;
            *self.bb_sides.get_unchecked_mut(pc.side().to_usize()) ^= bb_mask;
        }
    }

    #[inline]
    fn remove_piece(&mut self, sq: Square) {
        debug_assert!(self.at(sq).is_some());

        let pc = self.at(sq);
        let bb_mask = BB::new(sq);

        self.update_grid(sq, NULL_PIECE);

        debug_assert_eq!(self.bb_pc(pc) & bb_mask, bb_mask);
        debug_assert_eq!(self.bb_side(pc.side()) & bb_mask, bb_mask);

        unsafe {
            *self.bb_pieces.get_unchecked_mut(pc.to_usize()) ^= bb_mask;
            *self.bb_sides.get_unchecked_mut(pc.side().to_usize()) ^= bb_mask;
        }
    }

    #[inline]
    fn move_piece(&mut self, from: Square, to: Square) -> BB {
        debug_assert!(self.at(from).is_some());
        debug_assert!(self.at(to).is_none());

        let pc = self.at(from);
        let bb_mask = BB::new(from) | BB::new(to);

        debug_assert_eq!(self.bb_pc(pc) & BB::new(from), BB::new(from));
        debug_assert_eq!(self.bb_side(pc.side()) & BB::new(from), BB::new(from));
        debug_assert_eq!(self.bb_pc(pc) & BB::new(to), EMPTY);
        debug_assert_eq!(self.bb_side(pc.side()) & BB::new(to), EMPTY);

        self.update_grid(from, NULL_PIECE);
        self.update_grid(to, pc);

        unsafe {
            *self.bb_pieces.get_unchecked_mut(pc.to_usize()) ^= bb_mask;
            *self.bb_sides.get_unchecked_mut(pc.side().to_usize()) ^= bb_mask;
        }

        bb_mask
    }

    fn promote_piece(&mut self, sq: Square, new_pc: Piece) {
        let old_pc = self.at(sq);
        let bb_mask = BB::new(sq);

        debug_assert!(old_pc.is_some());
        debug_assert_eq!(old_pc.side(), new_pc.side());

        self.update_grid(sq, new_pc);

        debug_assert_eq!(self.bb_pc(old_pc) & bb_mask, bb_mask);
        debug_assert_eq!(self.bb_pc(new_pc) & bb_mask, EMPTY);
        debug_assert_eq!(self.bb_side(old_pc.side()) & bb_mask, bb_mask);

        unsafe {
            *(self.bb_pieces.get_unchecked_mut(old_pc.to_usize())) ^= bb_mask;
            *(self.bb_pieces.get_unchecked_mut(new_pc.to_usize())) |= bb_mask;
        }
    }

    /// Get bitboard of pieces for a particular side
    #[inline]
    pub fn bb_side(&self, side: Side) -> BB {
        unsafe { return *self.bb_sides.get_unchecked(side.to_usize() & 1) }
    }

    /// Get bitboard of pieces for a particular piece
    #[inline]
    pub fn bb_pc(&self, pc: Piece) -> BB {
        unsafe { return *self.bb_pieces.get_unchecked(pc.to_usize()) }
    }

    /// Get bitboard of sliding pieces for a particular side
    #[inline]
    pub fn bb_sliders(&self, side: Side) -> (BB, BB) {
        let queens = self.bb_pc(QUEEN.pc(side));
        let rooks = self.bb_pc(ROOK.pc(side));
        let bishops = self.bb_pc(BISHOP.pc(side));
        (queens | bishops, queens | rooks)
    }

    /// Get bitboard of all occupied squares
    #[inline]
    pub fn bb_occupied(&self) -> BB {
        self.bb_side(WHITE) | self.bb_side(BLACK)
    }

    /// Get bitboard of all empty squares
    #[inline]
    pub fn bb_empty(&self) -> BB {
        !self.bb_occupied()
    }

    #[inline]
    fn update_grid(&mut self, sq: Square, pc: Piece) {
        unsafe {
            *(self.grid.get_unchecked_mut(sq.to_usize())) = pc;
        }
    }
}

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

    #[test]
    fn test_to_string() {
        let position =
            &Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w").unwrap();

        let expected = unindent::unindent(
            "
          ABCDEFGH
        8|rnbqkbnr|8     side to move: white
        7|pppppppp|7  castling rights: QqKk
        6|........|6       en-passant: -
        5|........|5  half-move clock: 0
        4|........|4 full-move number: 1
        3|........|3              FEN: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QqKk - 0 1
        2|PPPPPPPP|2
        1|RNBQKBNR|1
          ABCDEFGH
        ",
        );
        assert_eq!(position.to_string(), expected);
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2021, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//! Proportional Rate Reduction
//!
//! This implementation is based on the following RFC:
//!
//! <https://datatracker.ietf.org/doc/html/rfc6937>

use std::cmp;

#[derive(Default, Debug)]
pub struct PRR {
    // Total bytes delivered during recovery.
    prr_delivered: usize,

    // FlightSize at the start of recovery.
    recoverfs: usize,

    // Total bytes sent during recovery.
    prr_out: usize,

    // Total additional bytes can be sent for retransmit during recovery.
    pub snd_cnt: usize,
}

impl PRR {
    pub fn on_packet_sent(&mut self, sent_bytes: usize) {
        self.prr_out += sent_bytes;

        self.snd_cnt = self.snd_cnt.saturating_sub(sent_bytes);
    }

    pub fn congestion_event(&mut self, bytes_in_flight: usize) {
        self.prr_delivered = 0;

        self.recoverfs = bytes_in_flight;

        self.prr_out = 0;

        self.snd_cnt = 0;
    }

    pub fn on_packet_acked(
        &mut self, delivered_data: usize, pipe: usize, ssthresh: usize,
        max_datagram_size: usize,
    ) {
        self.prr_delivered += delivered_data;

        self.snd_cnt = if pipe > ssthresh {
            // Proportional Rate Reduction.
            ((self.prr_delivered * ssthresh + self.recoverfs - 1) /
                self.recoverfs) -
                self.prr_out
        } else {
            // PRR-SSRB.
            let limit = cmp::max(
                self.prr_delivered.saturating_sub(self.prr_out),
                delivered_data,
            ) + max_datagram_size;

            // Attempt to catch up, as permitted by limit
            cmp::min(ssthresh - pipe, limit)
        };

        // snd_cnt should be a positive number.
        self.snd_cnt = cmp::max(self.snd_cnt, 0);
    }
}

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

    #[test]
    fn congestion_event() {
        let mut prr = PRR::default();
        let bytes_in_flight = 1000;

        prr.congestion_event(bytes_in_flight);

        assert_eq!(prr.recoverfs, bytes_in_flight);
        assert_eq!(prr.snd_cnt, 0);
    }

    #[test]
    fn on_packet_sent() {
        let mut prr = PRR::default();
        let bytes_in_flight = 1000;
        let bytes_sent = 500;

        prr.congestion_event(bytes_in_flight);

        prr.on_packet_sent(bytes_sent);

        assert_eq!(prr.prr_out, bytes_sent);
        assert_eq!(prr.snd_cnt, 0);
    }

    #[test]
    fn on_packet_acked_prr() {
        let mut prr = PRR::default();
        let max_datagram_size = 1000;
        let bytes_in_flight = max_datagram_size * 10;
        let ssthresh = bytes_in_flight / 2;
        let acked = 1000;

        prr.congestion_event(bytes_in_flight);

        // pipe > ssthresh uses PRR algorithm.
        let pipe = bytes_in_flight;

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 500);

        let snd_cnt = prr.snd_cnt;

        // send one more allowed by snd_cnt
        prr.on_packet_sent(snd_cnt);

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 500);
    }

    #[test]
    fn on_packet_acked_prr_ssrb() {
        let mut prr = PRR::default();
        let max_datagram_size = 1000;
        let bytes_in_flight = max_datagram_size * 10;
        let ssthresh = bytes_in_flight / 2;
        let acked = 1000;

        prr.congestion_event(bytes_in_flight);

        // pipe <= ssthresh uses PRR-SSRB algorithm.
        let pipe = max_datagram_size;

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 2000);

        let snd_cnt = prr.snd_cnt;

        // send one more allowed by snd_cnt
        prr.on_packet_sent(snd_cnt);

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 2000);
    }
}
<commit_msg>recovery: fix overflow in PRR limit calculation<commit_after>// Copyright (C) 2021, Cloudflare, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

//! Proportional Rate Reduction
//!
//! This implementation is based on the following RFC:
//!
//! <https://datatracker.ietf.org/doc/html/rfc6937>

use std::cmp;

#[derive(Default, Debug)]
pub struct PRR {
    // Total bytes delivered during recovery.
    prr_delivered: usize,

    // FlightSize at the start of recovery.
    recoverfs: usize,

    // Total bytes sent during recovery.
    prr_out: usize,

    // Total additional bytes can be sent for retransmit during recovery.
    pub snd_cnt: usize,
}

impl PRR {
    pub fn on_packet_sent(&mut self, sent_bytes: usize) {
        self.prr_out += sent_bytes;

        self.snd_cnt = self.snd_cnt.saturating_sub(sent_bytes);
    }

    pub fn congestion_event(&mut self, bytes_in_flight: usize) {
        self.prr_delivered = 0;

        self.recoverfs = bytes_in_flight;

        self.prr_out = 0;

        self.snd_cnt = 0;
    }

    pub fn on_packet_acked(
        &mut self, delivered_data: usize, pipe: usize, ssthresh: usize,
        max_datagram_size: usize,
    ) {
        self.prr_delivered += delivered_data;

        self.snd_cnt = if pipe > ssthresh {
            // Proportional Rate Reduction.
            ((self.prr_delivered * ssthresh + self.recoverfs - 1) /
                self.recoverfs)
                .saturating_sub(self.prr_out)
        } else {
            // PRR-SSRB.
            let limit = cmp::max(
                self.prr_delivered.saturating_sub(self.prr_out),
                delivered_data,
            ) + max_datagram_size;

            // Attempt to catch up, as permitted by limit
            cmp::min(ssthresh - pipe, limit)
        };

        // snd_cnt should be a positive number.
        self.snd_cnt = cmp::max(self.snd_cnt, 0);
    }
}

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

    #[test]
    fn congestion_event() {
        let mut prr = PRR::default();
        let bytes_in_flight = 1000;

        prr.congestion_event(bytes_in_flight);

        assert_eq!(prr.recoverfs, bytes_in_flight);
        assert_eq!(prr.snd_cnt, 0);
    }

    #[test]
    fn on_packet_sent() {
        let mut prr = PRR::default();
        let bytes_in_flight = 1000;
        let bytes_sent = 500;

        prr.congestion_event(bytes_in_flight);

        prr.on_packet_sent(bytes_sent);

        assert_eq!(prr.prr_out, bytes_sent);
        assert_eq!(prr.snd_cnt, 0);
    }

    #[test]
    fn on_packet_acked_prr() {
        let mut prr = PRR::default();
        let max_datagram_size = 1000;
        let bytes_in_flight = max_datagram_size * 10;
        let ssthresh = bytes_in_flight / 2;
        let acked = 1000;

        prr.congestion_event(bytes_in_flight);

        // pipe > ssthresh uses PRR algorithm.
        let pipe = bytes_in_flight;

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 500);

        let snd_cnt = prr.snd_cnt;

        // send one more allowed by snd_cnt
        prr.on_packet_sent(snd_cnt);

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 500);
    }

    #[test]
    fn on_packet_acked_prr_ssrb() {
        let mut prr = PRR::default();
        let max_datagram_size = 1000;
        let bytes_in_flight = max_datagram_size * 10;
        let ssthresh = bytes_in_flight / 2;
        let acked = 1000;

        prr.congestion_event(bytes_in_flight);

        // pipe <= ssthresh uses PRR-SSRB algorithm.
        let pipe = max_datagram_size;

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 2000);

        let snd_cnt = prr.snd_cnt;

        // send one more allowed by snd_cnt
        prr.on_packet_sent(snd_cnt);

        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);

        assert_eq!(prr.snd_cnt, 2000);
    }
}
<|endoftext|>


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

//! Batches are structures containing all the data required for the draw call,
//! except for the target frame. Here we define the `Batch` trait as well as
//! `RefBatch` and `OwnedBatch` implementations.

use std::fmt;
use std::num::from_uint;
use std::cmp::Ordering;
use std::marker::PhantomData;
use device::back;
use device::{Resources, PrimitiveType, ProgramHandle};
use device::shade::ProgramInfo;
use render::mesh;
use render::mesh::ToSlice;
use shade::{ParameterError, ShaderParam};
use render::state::DrawState;

/// An error with a defined Mesh.
#[derive(Clone, Debug, PartialEq)]
pub enum MeshError {
    /// A required attribute was missing.
    AttributeMissing(String),
    /// An attribute's type from the vertex format differed from the type used in the shader.
    AttributeType,
    /// Internal error due to mesh link limitations
    MeshLink(mesh::LinkError),
}

/// An error occurring at batch creation
#[derive(Clone, Debug, PartialEq)]
pub enum BatchError {
    /// Error connecting mesh attributes
    Mesh(MeshError),
    /// Error connecting shader parameters
    Parameters(ParameterError),
    /// Error context is full
    ContextFull,
}

/// Match mesh attributes against shader inputs, produce a mesh link.
/// Exposed to public to allow external `Batch` implementations to use it.
pub fn link_mesh(mesh: &mesh::Mesh<back::GlResources>, pinfo: &ProgramInfo) -> Result<mesh::Link, MeshError> {
    let mut indices = Vec::new();
    for sat in pinfo.attributes.iter() {
        match mesh.attributes.iter().enumerate()
                  .find(|&(_, a)| a.name == sat.name) {
            Some((attrib_id, vat)) => match vat.format.elem_type.is_compatible(sat.base_type) {
                Ok(_) => indices.push(attrib_id),
                Err(_) => return Err(MeshError::AttributeType),
            },
            None => return Err(MeshError::AttributeMissing(sat.name.clone())),
        }
    }
    mesh::Link::from_iter(indices.into_iter())
        .map_err(|e| MeshError::MeshLink(e))
}

/// Return type for `Batch::get_data()``
pub type BatchData<'a> = (&'a mesh::Mesh<back::GlResources>, mesh::AttributeIter,
                          &'a mesh::Slice<back::GlResources>, &'a DrawState);

/// Abstract batch trait
pub trait Batch {
    type Resources: Resources;
    /// Possible errors occurring at batch access
    type Error: fmt::Debug;
    /// Obtain information about the mesh, program, and state
    fn get_data(&self) -> Result<BatchData, Self::Error>;
    /// Fill shader parameter values
    fn fill_params(&self, ::shade::ParamValues)
                   -> Result<&ProgramHandle<Self::Resources>, Self::Error>;
}

impl<'a, T: ShaderParam> Batch for (&'a mesh::Mesh<back::GlResources>, mesh::Slice<back::GlResources>,
                                    &'a ProgramHandle<back::GlResources>, &'a T, &'a DrawState) {
    type Resources = back::GlResources;
    type Error = BatchError;

    fn get_data(&self) -> Result<BatchData, BatchError> {
        let (mesh, ref slice, program, _, state) = *self;
        match link_mesh(mesh, program.get_info()) {
            Ok(link) => Ok((mesh, link.to_iter(), &slice, state)),
            Err(e) => Err(BatchError::Mesh(e)),
        }
    }

    fn fill_params(&self, values: ::shade::ParamValues)
                   -> Result<&ProgramHandle<back::GlResources>, BatchError> {
        let (_, _, program, params, _) = *self;
        match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(link) => {
                params.fill_params(&link, values);
                Ok(program)
            },
            Err(e) => return Err(BatchError::Parameters(e)),
        }
    }
}

/// Owned batch - self-contained, but has heap-allocated data
pub struct OwnedBatch<T: ShaderParam> {
    mesh: mesh::Mesh<back::GlResources>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<back::GlResources>,
    /// Parameter data.
    pub param: T,
    program: ProgramHandle<back::GlResources>,
    param_link: T::Link,
    /// Draw state
    pub state: DrawState,
}

impl<T: ShaderParam> OwnedBatch<T> {
    /// Create a new owned batch
    pub fn new(mesh: mesh::Mesh<back::GlResources>, program: ProgramHandle<back::GlResources>, param: T)
           -> Result<OwnedBatch<T>, BatchError> {
        let slice = mesh.to_slice(PrimitiveType::TriangleList);
        let mesh_link = match link_mesh(&mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Mesh(e)),
        };
        let param_link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Parameters(e)),
        };
        Ok(OwnedBatch {
            mesh: mesh,
            mesh_link: mesh_link,
            slice: slice,
            program: program,
            param: param,
            param_link: param_link,
            state: DrawState::new(),
        })
    }
}

impl<T: ShaderParam> Batch for OwnedBatch<T> {
    type Resources = back::GlResources;
    type Error = ();

    fn get_data(&self) -> Result<BatchData, ()> {
        Ok((&self.mesh, self.mesh_link.to_iter(), &self.slice, &self.state))
    }

    fn fill_params(&self, values: ::shade::ParamValues)
                   -> Result<&ProgramHandle<back::GlResources>, ()> {
        self.param.fill_params(&self.param_link, values);
        Ok(&self.program)
    }
}

type Index = u16;

//#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Id<T>(Index, PhantomData<T>);

impl<T> Copy for Id<T> {}

impl<T> Id<T> {
    fn unwrap(&self) -> Index {
        let Id(i, _) = *self;
        i
    }
}

impl<T> fmt::Debug for Id<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Id(i, _) = *self;
        write!(f, "Id({})", i)
    }
}

impl<T> PartialEq for Id<T> {
    fn eq(&self, other: &Id<T>) -> bool {
        self.unwrap() == other.unwrap()
    }
}

impl<T> Eq for Id<T> {}

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

impl<T> Ord for Id<T> {
    fn cmp(&self, other: &Id<T>) -> Ordering {
        self.unwrap().cmp(&other.unwrap())
    }
}

struct Array<T> {
    data: Vec<T>,
    //generation: u16,
}

/// Error accessing outside of the array
#[derive(Debug)]
pub struct OutOfBounds(pub usize);

impl<T> Array<T> {
    fn new() -> Array<T> {
        Array {
            data: Vec::new(),
            //generation: 0,
        }
    }

    fn get(&self, id: Id<T>) -> Result<&T, OutOfBounds> {
        let Id(i, _) = id;
        if (i as usize) < self.data.len() {
            Ok(&self.data[i as usize])
        }else {
            Err(OutOfBounds(i as usize))
        }
    }
}

impl<T: Clone + PartialEq> Array<T> {
    fn find_or_insert(&mut self, value: &T) -> Option<Id<T>> {
        match self.data.iter().position(|v| v == value) {
            Some(i) => from_uint::<Index>(i).map(|id| Id(id, PhantomData)),
            None => {
                from_uint::<Index>(self.data.len()).map(|id| {
                    self.data.push(value.clone());
                    Id(id, PhantomData)
                })
            },
        }
    }
}

/// Ref batch - copyable and smaller, but depends on the `Context`.
/// It has references to the resources (mesh, program, state), that are held
/// by the context that created the batch, so these have to be used together.
pub struct RefBatch<T: ShaderParam> {
    mesh_id: Id<mesh::Mesh<back::GlResources>>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<back::GlResources>,
    program_id: Id<ProgramHandle<back::GlResources>>,
    param_link: T::Link,
    state_id: Id<DrawState>,
}

impl<T: ShaderParam> Copy for RefBatch<T> where T::Link: Copy {}

impl<T: ShaderParam> fmt::Debug for RefBatch<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RefBatch(mesh: {:?}, slice: {:?}, program: {:?}, state: {:?})",
            self.mesh_id, self.slice, self.program_id, self.state_id)
    }
}

impl<T: ShaderParam> PartialEq for RefBatch<T> {
    fn eq(&self, other: &RefBatch<T>) -> bool {
        self.program_id == other.program_id &&
        self.state_id == other.state_id &&
        self.mesh_id == other.mesh_id
    }
}

impl<T: ShaderParam> Eq for RefBatch<T> {}

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

impl<T: ShaderParam> Ord for RefBatch<T> {
    fn cmp(&self, other: &RefBatch<T>) -> Ordering {
        (&self.program_id, &self.state_id, &self.mesh_id).cmp(
        &(&other.program_id, &other.state_id, &other.mesh_id))
    }
}

impl<T: ShaderParam> RefBatch<T> {
    /// Compare meshes by Id
    pub fn cmp_mesh(&self, other: &RefBatch<T>) -> Ordering {
        self.mesh_id.cmp(&other.mesh_id)
    }
    /// Compare programs by Id
    pub fn cmp_program(&self, other: &RefBatch<T>) -> Ordering {
        self.program_id.cmp(&other.program_id)
    }
    /// Compare draw states by Id
    pub fn cmp_state(&self, other: &RefBatch<T>) -> Ordering {
        self.state_id.cmp(&other.state_id)
    }
}

/// Factory of ref batches, required to always be used with them.
pub struct Context {
    meshes: Array<mesh::Mesh<back::GlResources>>,
    programs: Array<ProgramHandle<back::GlResources>>,
    states: Array<DrawState>,
}

impl Context {
    /// Create a new empty `Context`
    pub fn new() -> Context {
        Context {
            meshes: Array::new(),
            programs: Array::new(),
            states: Array::new(),
        }
    }
}

impl Context {
    /// Produce a new ref batch
    pub fn make_batch<T: ShaderParam>(&mut self,
                      program: &ProgramHandle<back::GlResources>,
                      mesh: &mesh::Mesh<back::GlResources>,
                      slice: mesh::Slice<back::GlResources>,
                      state: &DrawState)
                      -> Result<RefBatch<T>, BatchError> {
        let mesh_link = match link_mesh(mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Mesh(e)),
        };
        let link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Parameters(e))
        };
        let mesh_id = match self.meshes.find_or_insert(mesh) {
            Some(id) => id,
            None => return Err(BatchError::ContextFull),
        };
        let program_id = match self.programs.find_or_insert(program) {
            Some(id) => id,
            None => return Err(BatchError::ContextFull),
        };
        let state_id = match self.states.find_or_insert(state) {
            Some(id) => id,
            None => return Err(BatchError::ContextFull),
        };

        Ok(RefBatch {
            mesh_id: mesh_id,
            mesh_link: mesh_link,
            slice: slice,
            program_id: program_id,
            param_link: link,
            state_id: state_id,
        })
    }
}

impl<'a, T: ShaderParam> Batch for (&'a RefBatch<T>, &'a T, &'a Context) {
    type Resources = back::GlResources;
    type Error = OutOfBounds;

    fn get_data(&self) -> Result<BatchData, OutOfBounds> {
        let (b, _, ctx) = *self;
        Ok((try!(ctx.meshes.get(b.mesh_id)),
            b.mesh_link.to_iter(),
            &b.slice,
            try!(ctx.states.get(b.state_id))
        ))
    }

    fn fill_params(&self, values: ::shade::ParamValues)
                   -> Result<&ProgramHandle<back::GlResources>, OutOfBounds> {
        let (b, data, ctx) = *self;
        data.fill_params(&b.param_link, values);
        ctx.programs.get(b.program_id)
    }
}
<commit_msg>Parameterise batch::link_mesh over Resources<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Batches are structures containing all the data required for the draw call,
//! except for the target frame. Here we define the `Batch` trait as well as
//! `RefBatch` and `OwnedBatch` implementations.

use std::fmt;
use std::num::from_uint;
use std::cmp::Ordering;
use std::marker::PhantomData;
use device::back;
use device::{Resources, PrimitiveType, ProgramHandle};
use device::shade::ProgramInfo;
use render::mesh;
use render::mesh::ToSlice;
use shade::{ParameterError, ShaderParam};
use render::state::DrawState;

/// An error with a defined Mesh.
#[derive(Clone, Debug, PartialEq)]
pub enum MeshError {
    /// A required attribute was missing.
    AttributeMissing(String),
    /// An attribute's type from the vertex format differed from the type used in the shader.
    AttributeType,
    /// Internal error due to mesh link limitations
    MeshLink(mesh::LinkError),
}

/// An error occurring at batch creation
#[derive(Clone, Debug, PartialEq)]
pub enum BatchError {
    /// Error connecting mesh attributes
    Mesh(MeshError),
    /// Error connecting shader parameters
    Parameters(ParameterError),
    /// Error context is full
    ContextFull,
}

/// Match mesh attributes against shader inputs, produce a mesh link.
/// Exposed to public to allow external `Batch` implementations to use it.
pub fn link_mesh<R: Resources>(mesh: &mesh::Mesh<R>, pinfo: &ProgramInfo) -> Result<mesh::Link, MeshError> {
    let mut indices = Vec::new();
    for sat in pinfo.attributes.iter() {
        match mesh.attributes.iter().enumerate()
                  .find(|&(_, a)| a.name == sat.name) {
            Some((attrib_id, vat)) => match vat.format.elem_type.is_compatible(sat.base_type) {
                Ok(_) => indices.push(attrib_id),
                Err(_) => return Err(MeshError::AttributeType),
            },
            None => return Err(MeshError::AttributeMissing(sat.name.clone())),
        }
    }
    mesh::Link::from_iter(indices.into_iter())
        .map_err(|e| MeshError::MeshLink(e))
}

/// Return type for `Batch::get_data()``
pub type BatchData<'a> = (&'a mesh::Mesh<back::GlResources>, mesh::AttributeIter,
                          &'a mesh::Slice<back::GlResources>, &'a DrawState);

/// Abstract batch trait
pub trait Batch {
    type Resources: Resources;
    /// Possible errors occurring at batch access
    type Error: fmt::Debug;
    /// Obtain information about the mesh, program, and state
    fn get_data(&self) -> Result<BatchData, Self::Error>;
    /// Fill shader parameter values
    fn fill_params(&self, ::shade::ParamValues)
                   -> Result<&ProgramHandle<Self::Resources>, Self::Error>;
}

impl<'a, T: ShaderParam> Batch for (&'a mesh::Mesh<back::GlResources>, mesh::Slice<back::GlResources>,
                                    &'a ProgramHandle<back::GlResources>, &'a T, &'a DrawState) {
    type Resources = back::GlResources;
    type Error = BatchError;

    fn get_data(&self) -> Result<BatchData, BatchError> {
        let (mesh, ref slice, program, _, state) = *self;
        match link_mesh(mesh, program.get_info()) {
            Ok(link) => Ok((mesh, link.to_iter(), &slice, state)),
            Err(e) => Err(BatchError::Mesh(e)),
        }
    }

    fn fill_params(&self, values: ::shade::ParamValues)
                   -> Result<&ProgramHandle<back::GlResources>, BatchError> {
        let (_, _, program, params, _) = *self;
        match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(link) => {
                params.fill_params(&link, values);
                Ok(program)
            },
            Err(e) => return Err(BatchError::Parameters(e)),
        }
    }
}

/// Owned batch - self-contained, but has heap-allocated data
pub struct OwnedBatch<T: ShaderParam> {
    mesh: mesh::Mesh<back::GlResources>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<back::GlResources>,
    /// Parameter data.
    pub param: T,
    program: ProgramHandle<back::GlResources>,
    param_link: T::Link,
    /// Draw state
    pub state: DrawState,
}

impl<T: ShaderParam> OwnedBatch<T> {
    /// Create a new owned batch
    pub fn new(mesh: mesh::Mesh<back::GlResources>, program: ProgramHandle<back::GlResources>, param: T)
           -> Result<OwnedBatch<T>, BatchError> {
        let slice = mesh.to_slice(PrimitiveType::TriangleList);
        let mesh_link = match link_mesh(&mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Mesh(e)),
        };
        let param_link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Parameters(e)),
        };
        Ok(OwnedBatch {
            mesh: mesh,
            mesh_link: mesh_link,
            slice: slice,
            program: program,
            param: param,
            param_link: param_link,
            state: DrawState::new(),
        })
    }
}

impl<T: ShaderParam> Batch for OwnedBatch<T> {
    type Resources = back::GlResources;
    type Error = ();

    fn get_data(&self) -> Result<BatchData, ()> {
        Ok((&self.mesh, self.mesh_link.to_iter(), &self.slice, &self.state))
    }

    fn fill_params(&self, values: ::shade::ParamValues)
                   -> Result<&ProgramHandle<back::GlResources>, ()> {
        self.param.fill_params(&self.param_link, values);
        Ok(&self.program)
    }
}

type Index = u16;

//#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Id<T>(Index, PhantomData<T>);

impl<T> Copy for Id<T> {}

impl<T> Id<T> {
    fn unwrap(&self) -> Index {
        let Id(i, _) = *self;
        i
    }
}

impl<T> fmt::Debug for Id<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let Id(i, _) = *self;
        write!(f, "Id({})", i)
    }
}

impl<T> PartialEq for Id<T> {
    fn eq(&self, other: &Id<T>) -> bool {
        self.unwrap() == other.unwrap()
    }
}

impl<T> Eq for Id<T> {}

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

impl<T> Ord for Id<T> {
    fn cmp(&self, other: &Id<T>) -> Ordering {
        self.unwrap().cmp(&other.unwrap())
    }
}

struct Array<T> {
    data: Vec<T>,
    //generation: u16,
}

/// Error accessing outside of the array
#[derive(Debug)]
pub struct OutOfBounds(pub usize);

impl<T> Array<T> {
    fn new() -> Array<T> {
        Array {
            data: Vec::new(),
            //generation: 0,
        }
    }

    fn get(&self, id: Id<T>) -> Result<&T, OutOfBounds> {
        let Id(i, _) = id;
        if (i as usize) < self.data.len() {
            Ok(&self.data[i as usize])
        }else {
            Err(OutOfBounds(i as usize))
        }
    }
}

impl<T: Clone + PartialEq> Array<T> {
    fn find_or_insert(&mut self, value: &T) -> Option<Id<T>> {
        match self.data.iter().position(|v| v == value) {
            Some(i) => from_uint::<Index>(i).map(|id| Id(id, PhantomData)),
            None => {
                from_uint::<Index>(self.data.len()).map(|id| {
                    self.data.push(value.clone());
                    Id(id, PhantomData)
                })
            },
        }
    }
}

/// Ref batch - copyable and smaller, but depends on the `Context`.
/// It has references to the resources (mesh, program, state), that are held
/// by the context that created the batch, so these have to be used together.
pub struct RefBatch<T: ShaderParam> {
    mesh_id: Id<mesh::Mesh<back::GlResources>>,
    mesh_link: mesh::Link,
    /// Mesh slice
    pub slice: mesh::Slice<back::GlResources>,
    program_id: Id<ProgramHandle<back::GlResources>>,
    param_link: T::Link,
    state_id: Id<DrawState>,
}

impl<T: ShaderParam> Copy for RefBatch<T> where T::Link: Copy {}

impl<T: ShaderParam> fmt::Debug for RefBatch<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RefBatch(mesh: {:?}, slice: {:?}, program: {:?}, state: {:?})",
            self.mesh_id, self.slice, self.program_id, self.state_id)
    }
}

impl<T: ShaderParam> PartialEq for RefBatch<T> {
    fn eq(&self, other: &RefBatch<T>) -> bool {
        self.program_id == other.program_id &&
        self.state_id == other.state_id &&
        self.mesh_id == other.mesh_id
    }
}

impl<T: ShaderParam> Eq for RefBatch<T> {}

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

impl<T: ShaderParam> Ord for RefBatch<T> {
    fn cmp(&self, other: &RefBatch<T>) -> Ordering {
        (&self.program_id, &self.state_id, &self.mesh_id).cmp(
        &(&other.program_id, &other.state_id, &other.mesh_id))
    }
}

impl<T: ShaderParam> RefBatch<T> {
    /// Compare meshes by Id
    pub fn cmp_mesh(&self, other: &RefBatch<T>) -> Ordering {
        self.mesh_id.cmp(&other.mesh_id)
    }
    /// Compare programs by Id
    pub fn cmp_program(&self, other: &RefBatch<T>) -> Ordering {
        self.program_id.cmp(&other.program_id)
    }
    /// Compare draw states by Id
    pub fn cmp_state(&self, other: &RefBatch<T>) -> Ordering {
        self.state_id.cmp(&other.state_id)
    }
}

/// Factory of ref batches, required to always be used with them.
pub struct Context {
    meshes: Array<mesh::Mesh<back::GlResources>>,
    programs: Array<ProgramHandle<back::GlResources>>,
    states: Array<DrawState>,
}

impl Context {
    /// Create a new empty `Context`
    pub fn new() -> Context {
        Context {
            meshes: Array::new(),
            programs: Array::new(),
            states: Array::new(),
        }
    }
}

impl Context {
    /// Produce a new ref batch
    pub fn make_batch<T: ShaderParam>(&mut self,
                      program: &ProgramHandle<back::GlResources>,
                      mesh: &mesh::Mesh<back::GlResources>,
                      slice: mesh::Slice<back::GlResources>,
                      state: &DrawState)
                      -> Result<RefBatch<T>, BatchError> {
        let mesh_link = match link_mesh(mesh, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Mesh(e)),
        };
        let link = match ShaderParam::create_link(None::<&T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(BatchError::Parameters(e))
        };
        let mesh_id = match self.meshes.find_or_insert(mesh) {
            Some(id) => id,
            None => return Err(BatchError::ContextFull),
        };
        let program_id = match self.programs.find_or_insert(program) {
            Some(id) => id,
            None => return Err(BatchError::ContextFull),
        };
        let state_id = match self.states.find_or_insert(state) {
            Some(id) => id,
            None => return Err(BatchError::ContextFull),
        };

        Ok(RefBatch {
            mesh_id: mesh_id,
            mesh_link: mesh_link,
            slice: slice,
            program_id: program_id,
            param_link: link,
            state_id: state_id,
        })
    }
}

impl<'a, T: ShaderParam> Batch for (&'a RefBatch<T>, &'a T, &'a Context) {
    type Resources = back::GlResources;
    type Error = OutOfBounds;

    fn get_data(&self) -> Result<BatchData, OutOfBounds> {
        let (b, _, ctx) = *self;
        Ok((try!(ctx.meshes.get(b.mesh_id)),
            b.mesh_link.to_iter(),
            &b.slice,
            try!(ctx.states.get(b.state_id))
        ))
    }

    fn fill_params(&self, values: ::shade::ParamValues)
                   -> Result<&ProgramHandle<back::GlResources>, OutOfBounds> {
        let (b, data, ctx) = *self;
        data.fill_params(&b.param_link, values);
        ctx.programs.get(b.program_id)
    }
}
<|endoftext|>


<commit_before>use clap::{Arg, App, SubCommand};

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    app
        .subcommand(SubCommand::with_name("internal")
                    .about("Add, remove and list internal links")
                    .version("0.1")
                    .subcommand(SubCommand::with_name("add")
                                .about("Add link from one entry to another (and vice-versa)")
                                .version("0.1")
                                .arg(Arg::with_name("from")
                                     .long("from")
                                     .short("f")
                                     .takes_value(true)
                                     .required(true)
                                     .help("Link from this entry"))
                                .arg(Arg::with_name("to")
                                     .long("to")
                                     .short("t")
                                     .takes_value(true)
                                     .required(true)
                                     .multiple(true)
                                     .help("Link to this entries"))
                                )

                    .subcommand(SubCommand::with_name("remove")
                            .about("Remove a link between two or more entries")
                            .version("0.1")
                            .arg(Arg::with_name("from")
                                 .long("from")
                                 .short("f")
                                 .takes_value(true)
                                 .required(true)
                                 .help("Remove Link from this entry"))
                            .arg(Arg::with_name("to")
                                 .long("to")
                                 .short("t")
                                 .takes_value(true)
                                 .required(true)
                                 .multiple(true)
                                 .help("Remove links to these entries"))
                            )

                        .arg(Arg::with_name("list")
                             .long("list")
                             .short("l")
                             .takes_value(false)
                             .required(false)
                             .help("List links to this entry"))
                    )
        .subcommand(SubCommand::with_name("external")
                    .about("Add and remove external links")
                    .version("0.1")

                    .arg(Arg::with_name("id")
                         .long("id")
                         .short("i")
                         .takes_value(true)
                         .required(true)
                         .help("Modify external link of this entry"))

                    .arg(Arg::with_name("set")
                         .long("set")
                         .short("s")
                         .takes_value(true)
                         .required(false)
                         .help("Set this URI as external link"))

                    .arg(Arg::with_name("remove")
                         .long("remove")
                         .short("r")
                         .takes_value(false)
                         .required(false)
                         .help("Remove external link"))

                    .arg(Arg::with_name("list")
                         .long("list")
                         .short("l")
                         .takes_value(false)
                         .required(false)
                         .help("List external link"))

                    .arg(Arg::with_name("show")
                         .long("show")
                         .short("s")
                         .takes_value(false)
                         .required(false)
                         .help("List external link (alias for --list)"))
                    )
}

<commit_msg>Remove "show" argument, as the short key "s" was duplicated<commit_after>use clap::{Arg, App, SubCommand};

pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
    app
        .subcommand(SubCommand::with_name("internal")
                    .about("Add, remove and list internal links")
                    .version("0.1")
                    .subcommand(SubCommand::with_name("add")
                                .about("Add link from one entry to another (and vice-versa)")
                                .version("0.1")
                                .arg(Arg::with_name("from")
                                     .long("from")
                                     .short("f")
                                     .takes_value(true)
                                     .required(true)
                                     .help("Link from this entry"))
                                .arg(Arg::with_name("to")
                                     .long("to")
                                     .short("t")
                                     .takes_value(true)
                                     .required(true)
                                     .multiple(true)
                                     .help("Link to this entries"))
                                )

                    .subcommand(SubCommand::with_name("remove")
                            .about("Remove a link between two or more entries")
                            .version("0.1")
                            .arg(Arg::with_name("from")
                                 .long("from")
                                 .short("f")
                                 .takes_value(true)
                                 .required(true)
                                 .help("Remove Link from this entry"))
                            .arg(Arg::with_name("to")
                                 .long("to")
                                 .short("t")
                                 .takes_value(true)
                                 .required(true)
                                 .multiple(true)
                                 .help("Remove links to these entries"))
                            )

                        .arg(Arg::with_name("list")
                             .long("list")
                             .short("l")
                             .takes_value(false)
                             .required(false)
                             .help("List links to this entry"))
                    )
        .subcommand(SubCommand::with_name("external")
                    .about("Add and remove external links")
                    .version("0.1")

                    .arg(Arg::with_name("id")
                         .long("id")
                         .short("i")
                         .takes_value(true)
                         .required(true)
                         .help("Modify external link of this entry"))

                    .arg(Arg::with_name("set")
                         .long("set")
                         .short("s")
                         .takes_value(true)
                         .required(false)
                         .help("Set this URI as external link"))

                    .arg(Arg::with_name("remove")
                         .long("remove")
                         .short("r")
                         .takes_value(false)
                         .required(false)
                         .help("Remove external link"))

                    .arg(Arg::with_name("list")
                         .long("list")
                         .short("l")
                         .takes_value(false)
                         .required(false)
                         .help("List external link"))

                    )
}

<|endoftext|>


<commit_before>use super::*;
use crate::test;
use ci_info;
use envmnt;
use rust_info::types::RustChannel;

#[test]
#[ignore]
fn get_cargo_home_env() {
    let directory_path = env::current_dir().unwrap();
    let directory = directory_path.to_str().unwrap();
    envmnt::set("CARGO_HOME", directory);

    let output = get_cargo_home().unwrap();
    envmnt::remove("CARGO_HOME");

    assert_eq!(output, directory);
}

#[test]
#[ignore]
fn get_cargo_home_no_env() {
    envmnt::remove("CARGO_HOME");

    let output = get_cargo_home().unwrap();

    assert!(output.contains(".cargo"));
}

#[test]
#[ignore]
fn get_cargo_home_file() {
    let directory_path = env::current_dir().unwrap();
    let directory = directory_path.to_str().unwrap();
    let file_path = Path::new(directory).join("Cargo.toml");
    envmnt::set("CARGO_HOME", file_path);

    let output = get_cargo_home();
    envmnt::remove("CARGO_HOME");

    assert!(output.is_none());
}

#[test]
fn load_crates_toml_valid() {
    let cwd = envmnt::get_or_panic("CARGO_MAKE_WORKING_DIRECTORY");
    let file_path = Path::new(&cwd).join("src/lib/test/test_files");
    let directory = file_path.to_str().unwrap();
    let info_option = load_crates_toml(&directory);

    assert!(info_option.is_some());
    let info = info_option.unwrap();
    assert!(info.v1.is_some());
    assert_eq!(info.v1.unwrap().len(), 3);
}

#[test]
fn load_crates_toml_file_not_found() {
    let cwd = envmnt::get_or_panic("CARGO_MAKE_WORKING_DIRECTORY");
    let info_option = load_crates_toml(&cwd);

    assert!(info_option.is_none());
}

#[test]
fn get_crate_version_from_info_no_info() {
    let info = CratesRegistryInfo { v1: None };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_empty_info() {
    let info = CratesRegistryInfo {
        v1: Some(HashMap::new()),
    };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_not_found() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_found_invalid_line() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("cargo-make".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_found_invalid_version() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("cargo-make abc 2".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_valid() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("cargo-make 1.2.3 2".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    let semver_version = version.unwrap();
    assert_eq!(semver_version.major, 1);
    assert_eq!(semver_version.minor, 2);
    assert_eq!(semver_version.patch, 3);
}

#[test]
#[cfg(target_os = "linux")]
fn get_crate_version_for_rustup_component() {
    if test::is_rust_channel(RustChannel::Stable) || !ci_info::is_ci() {
        let mut version = get_crate_version("rustfmt", None);
        assert!(version.is_none());

        version = get_crate_version("rustfmt", Some("rustfmt"));
        assert!(version.is_some());
    }
}

#[test]
fn is_min_version_valid_for_versions_equal() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_for_versions_false_major() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("2.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_min_version_valid_for_versions_false_minor() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.3.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_min_version_valid_for_versions_false_patch() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.4").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_min_version_valid_for_versions_true_major() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("2.2.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_for_versions_true_minor() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.3.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_for_versions_true_patch() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.2.4").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_not_found() {
    let valid = is_min_version_valid("bad_crate", "1.2.3", None);

    assert!(valid);
}

#[test]
fn is_min_version_valid_invalid_version() {
    let valid = is_min_version_valid("cargo-make", "bad_version", None);

    assert!(valid);
}

#[test]
fn is_min_version_valid_old_version() {
    let valid = is_min_version_valid("cargo-make", "0.0.1", None);

    assert!(valid);
}

#[test]
fn is_min_version_valid_newer_version() {
    let valid = is_min_version_valid("cargo-make", "10000.0.0", None);

    assert!(!valid);
}

#[test]
fn is_min_version_valid_same_version() {
    let version = get_crate_version("cargo-make", None).unwrap();
    let mut version_string = String::new();
    version_string.push_str(&version.major.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.minor.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.patch.to_string());

    let valid = is_min_version_valid("cargo-make", &version_string, None);

    assert!(valid);
}

#[test]
fn is_version_valid_for_versions_equal() {
    let valid = is_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_version_valid_for_versions_false_major() {
    let valid = is_version_valid_for_versions(
        &Version::parse("2.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_version_valid_for_versions_false_minor() {
    let valid = is_version_valid_for_versions(
        &Version::parse("1.3.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_version_valid_for_versions_false_patch() {
    let valid = is_version_valid_for_versions(
        &Version::parse("1.2.4").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_version_valid_not_found() {
    let valid = is_version_valid("bad_crate", "1.2.3", None);

    assert!(valid);
}

#[test]
fn is_version_valid_invalid_version() {
    let valid = is_version_valid("cargo-make", "bad_version", None);

    assert!(valid);
}

#[test]
fn is_version_valid_old_version() {
    let valid = is_version_valid("cargo-make", "0.0.1", None);

    assert!(!valid);
}

#[test]
fn is_version_valid_newer_version() {
    let valid = is_version_valid("cargo-make", "10000.0.0", None);

    assert!(!valid);
}

#[test]
fn is_version_valid_same_version() {
    let version = get_crate_version("cargo-make", None).unwrap();
    let mut version_string = String::new();
    version_string.push_str(&version.major.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.minor.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.patch.to_string());

    let valid = is_min_version_valid("cargo-make", &version_string, None);

    assert!(valid);
}
<commit_msg>fix tests<commit_after>use super::*;
use crate::test;
use ci_info;
use envmnt;
#[cfg(target_os = "linux")]
use rust_info::types::RustChannel;

#[test]
#[ignore]
fn get_cargo_home_env() {
    let directory_path = env::current_dir().unwrap();
    let directory = directory_path.to_str().unwrap();
    envmnt::set("CARGO_HOME", directory);

    let output = get_cargo_home().unwrap();
    envmnt::remove("CARGO_HOME");

    assert_eq!(output, directory);
}

#[test]
#[ignore]
fn get_cargo_home_no_env() {
    envmnt::remove("CARGO_HOME");

    let output = get_cargo_home().unwrap();

    assert!(output.contains(".cargo"));
}

#[test]
#[ignore]
fn get_cargo_home_file() {
    let directory_path = env::current_dir().unwrap();
    let directory = directory_path.to_str().unwrap();
    let file_path = Path::new(directory).join("Cargo.toml");
    envmnt::set("CARGO_HOME", file_path);

    let output = get_cargo_home();
    envmnt::remove("CARGO_HOME");

    assert!(output.is_none());
}

#[test]
fn load_crates_toml_valid() {
    let cwd = envmnt::get_or_panic("CARGO_MAKE_WORKING_DIRECTORY");
    let file_path = Path::new(&cwd).join("src/lib/test/test_files");
    let directory = file_path.to_str().unwrap();
    let info_option = load_crates_toml(&directory);

    assert!(info_option.is_some());
    let info = info_option.unwrap();
    assert!(info.v1.is_some());
    assert_eq!(info.v1.unwrap().len(), 3);
}

#[test]
fn load_crates_toml_file_not_found() {
    let cwd = envmnt::get_or_panic("CARGO_MAKE_WORKING_DIRECTORY");
    let info_option = load_crates_toml(&cwd);

    assert!(info_option.is_none());
}

#[test]
fn get_crate_version_from_info_no_info() {
    let info = CratesRegistryInfo { v1: None };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_empty_info() {
    let info = CratesRegistryInfo {
        v1: Some(HashMap::new()),
    };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_not_found() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_found_invalid_line() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("cargo-make".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_found_invalid_version() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("cargo-make abc 2".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    assert!(version.is_none());
}

#[test]
fn get_crate_version_from_info_valid() {
    let mut map = HashMap::new();
    map.insert("1 1 1".to_string(), vec![]);
    map.insert("cargo-make 1.2.3 2".to_string(), vec![]);
    map.insert("2 2 2".to_string(), vec![]);
    let info = CratesRegistryInfo { v1: Some(map) };
    let version = get_crate_version_from_info("cargo-make", &info);

    let semver_version = version.unwrap();
    assert_eq!(semver_version.major, 1);
    assert_eq!(semver_version.minor, 2);
    assert_eq!(semver_version.patch, 3);
}

#[test]
#[cfg(target_os = "linux")]
fn get_crate_version_for_rustup_component() {
    if test::is_rust_channel(RustChannel::Stable) || !ci_info::is_ci() {
        let mut version = get_crate_version("rustfmt", None);
        assert!(version.is_none());

        version = get_crate_version("rustfmt", Some("rustfmt"));
        assert!(version.is_some());
    }
}

#[test]
fn is_min_version_valid_for_versions_equal() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_for_versions_false_major() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("2.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_min_version_valid_for_versions_false_minor() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.3.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_min_version_valid_for_versions_false_patch() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.4").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_min_version_valid_for_versions_true_major() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("2.2.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_for_versions_true_minor() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.3.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_for_versions_true_patch() {
    let valid = is_min_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.2.4").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_min_version_valid_not_found() {
    let valid = is_min_version_valid("bad_crate", "1.2.3", None);

    assert!(valid);
}

#[test]
fn is_min_version_valid_invalid_version() {
    let valid = is_min_version_valid("cargo-make", "bad_version", None);

    assert!(valid);
}

#[test]
fn is_min_version_valid_old_version() {
    let valid = is_min_version_valid("cargo-make", "0.0.1", None);

    assert!(valid);
}

#[test]
fn is_min_version_valid_newer_version() {
    let valid = is_min_version_valid("cargo-make", "10000.0.0", None);

    assert!(!valid);
}

#[test]
fn is_min_version_valid_same_version() {
    let version = get_crate_version("cargo-make", None).unwrap();
    let mut version_string = String::new();
    version_string.push_str(&version.major.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.minor.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.patch.to_string());

    let valid = is_min_version_valid("cargo-make", &version_string, None);

    assert!(valid);
}

#[test]
fn is_version_valid_for_versions_equal() {
    let valid = is_version_valid_for_versions(
        &Version::parse("1.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(valid);
}

#[test]
fn is_version_valid_for_versions_false_major() {
    let valid = is_version_valid_for_versions(
        &Version::parse("2.2.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_version_valid_for_versions_false_minor() {
    let valid = is_version_valid_for_versions(
        &Version::parse("1.3.3").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_version_valid_for_versions_false_patch() {
    let valid = is_version_valid_for_versions(
        &Version::parse("1.2.4").unwrap(),
        &Version::parse("1.2.3").unwrap(),
    );

    assert!(!valid);
}

#[test]
fn is_version_valid_not_found() {
    let valid = is_version_valid("bad_crate", "1.2.3", None);

    assert!(valid);
}

#[test]
fn is_version_valid_invalid_version() {
    let valid = is_version_valid("cargo-make", "bad_version", None);

    assert!(valid);
}

#[test]
fn is_version_valid_old_version() {
    let valid = is_version_valid("cargo-make", "0.0.1", None);

    assert!(!valid);
}

#[test]
fn is_version_valid_newer_version() {
    let valid = is_version_valid("cargo-make", "10000.0.0", None);

    assert!(!valid);
}

#[test]
fn is_version_valid_same_version() {
    let version = get_crate_version("cargo-make", None).unwrap();
    let mut version_string = String::new();
    version_string.push_str(&version.major.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.minor.to_string());
    version_string.push_str(".");
    version_string.push_str(&version.patch.to_string());

    let valid = is_min_version_valid("cargo-make", &version_string, None);

    assert!(valid);
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::Write;

use rustc_serialize::json::as_json;

use external_data::*;
use data::{VariableKind, Visibility};
use dump::Dump;
use id_from_def_id;

use rls_data::{Analysis, Import, ImportKind, Def, DefKind, CratePreludeData};

// A dumper to dump a restricted set of JSON information, designed for use with
// libraries distributed without their source. Clients are likely to use type
// information here, and (for example) generate Rustdoc URLs, but don't need
// information for navigating the source of the crate.
// Relative to the regular JSON save-analysis info, this form is filtered to
// remove non-visible items.
pub struct JsonApiDumper<'b, W: Write + 'b> {
    output: &'b mut W,
    result: Analysis,
}

impl<'b, W: Write> JsonApiDumper<'b, W> {
    pub fn new(writer: &'b mut W) -> JsonApiDumper<'b, W> {
        JsonApiDumper { output: writer, result: Analysis::new() }
    }
}

impl<'b, W: Write> Drop for JsonApiDumper<'b, W> {
    fn drop(&mut self) {
        if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
            error!("Error writing output");
        }
    }
}

macro_rules! impl_fn {
    ($fn_name: ident, $data_type: ident, $bucket: ident) => {
        fn $fn_name(&mut self, data: $data_type) {
            if let Some(datum) = data.into() {
                self.result.$bucket.push(datum);
            }
        }
    }
}

impl<'b, W: Write + 'b> Dump for JsonApiDumper<'b, W> {
    fn crate_prelude(&mut self, data: CratePreludeData) {
        self.result.prelude = Some(data)
    }

    impl_fn!(use_data, UseData, imports);
    impl_fn!(use_glob, UseGlobData, imports);

    impl_fn!(enum_data, EnumData, defs);
    impl_fn!(tuple_variant, TupleVariantData, defs);
    impl_fn!(struct_variant, StructVariantData, defs);
    impl_fn!(struct_data, StructData, defs);
    impl_fn!(trait_data, TraitData, defs);
    impl_fn!(function, FunctionData, defs);
    impl_fn!(method, MethodData, defs);
    impl_fn!(macro_data, MacroData, defs);
    impl_fn!(mod_data, ModData, defs);
    impl_fn!(typedef, TypeDefData, defs);
    impl_fn!(variable, VariableData, defs);

    fn impl_data(&mut self, data: ImplData) {
        if data.self_ref.is_some() {
            self.result.relations.push(data.into());
        }
    }
    fn inheritance(&mut self, data: InheritanceData) {
        self.result.relations.push(data.into());
    }
}

// FIXME methods. The defs have information about possible overriding and the
// refs have decl information (e.g., a trait method where we know the required
// method, but not the supplied method). In both cases, we are currently
// ignoring it.

impl Into<Option<Import>> for UseData {
    fn into(self) -> Option<Import> {
        match self.visibility {
            Visibility::Public => Some(Import {
                kind: ImportKind::Use,
                ref_id: self.mod_id.map(|id| id_from_def_id(id)),
                span: self.span,
                name: self.name,
                value: String::new(),
            }),
            _ => None,
        }
    }
}
impl Into<Option<Import>> for UseGlobData {
    fn into(self) -> Option<Import> {
        match self.visibility {
            Visibility::Public => Some(Import {
                kind: ImportKind::GlobUse,
                ref_id: None,
                span: self.span,
                name: "*".to_owned(),
                value: self.names.join(", "),
            }),
            _ => None,
        }
    }
}

impl Into<Option<Def>> for EnumData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Enum,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                parent: None,
                children: self.variants.into_iter().map(|id| id_from_def_id(id)).collect(),
                decl_id: None,
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}

impl Into<Option<Def>> for TupleVariantData {
    fn into(self) -> Option<Def> {
        Some(Def {
            kind: DefKind::Tuple,
            id: id_from_def_id(self.id),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: self.value,
            parent: self.parent.map(|id| id_from_def_id(id)),
            children: vec![],
            decl_id: None,
            docs: self.docs,
            sig: self.sig,
            attributes: vec![],
        })
    }
}
impl Into<Option<Def>> for StructVariantData {
    fn into(self) -> Option<Def> {
        Some(Def {
            kind: DefKind::Struct,
            id: id_from_def_id(self.id),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: self.value,
            parent: self.parent.map(|id| id_from_def_id(id)),
            children: vec![],
            decl_id: None,
            docs: self.docs,
            sig: self.sig,
            attributes: vec![],
        })
    }
}
impl Into<Option<Def>> for StructData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
            kind: DefKind::Struct,
            id: id_from_def_id(self.id),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: self.value,
            parent: None,
            children: self.fields.into_iter().map(|id| id_from_def_id(id)).collect(),
            decl_id: None,
            docs: self.docs,
            sig: self.sig,
            attributes: vec![],
        }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for TraitData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Trait,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: self.items.into_iter().map(|id| id_from_def_id(id)).collect(),
                parent: None,
                decl_id: None,
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for FunctionData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Function,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: None,
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for MethodData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Method,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: self.decl_id.map(|id| id_from_def_id(id)),
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for MacroData {
    fn into(self) -> Option<Def> {
        Some(Def {
            kind: DefKind::Macro,
            id: id_from_def_id(null_def_id()),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: String::new(),
            children: vec![],
            parent: None,
            decl_id: None,
            docs: self.docs,
            sig: None,
            attributes: vec![],
        })
    }
}
impl Into<Option<Def>> for ModData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Mod,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.filename,
                children: self.items.into_iter().map(|id| id_from_def_id(id)).collect(),
                parent: None,
                decl_id: None,
                docs: self.docs,
                sig: self.sig.map(|s| s.into()),
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for TypeDefData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Type,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: None,
                docs: String::new(),
                sig: self.sig.map(|s| s.into()),
                attributes: vec![],
            }),
            _ => None,
        }
    }
}

impl Into<Option<Def>> for VariableData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: match self.kind {
                    VariableKind::Static => DefKind::Static,
                    VariableKind::Const => DefKind::Const,
                    VariableKind::Local => { return None }
                    VariableKind::Field => DefKind::Field,
                },
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: None,
                docs: self.docs,
                sig: self.sig.map(|s| s.into()),
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
<commit_msg>Fix a bug where Json API save-analysis was not labelled as such<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::Write;

use rustc_serialize::json::as_json;

use external_data::*;
use data::{VariableKind, Visibility};
use dump::Dump;
use id_from_def_id;

use rls_data::{Analysis, Import, ImportKind, Def, DefKind, CratePreludeData, Format};

// A dumper to dump a restricted set of JSON information, designed for use with
// libraries distributed without their source. Clients are likely to use type
// information here, and (for example) generate Rustdoc URLs, but don't need
// information for navigating the source of the crate.
// Relative to the regular JSON save-analysis info, this form is filtered to
// remove non-visible items.
pub struct JsonApiDumper<'b, W: Write + 'b> {
    output: &'b mut W,
    result: Analysis,
}

impl<'b, W: Write> JsonApiDumper<'b, W> {
    pub fn new(writer: &'b mut W) -> JsonApiDumper<'b, W> {
        let mut result = Analysis::new();
        result.kind = Format::JsonApi;
        JsonApiDumper { output: writer, result }
    }
}

impl<'b, W: Write> Drop for JsonApiDumper<'b, W> {
    fn drop(&mut self) {
        if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
            error!("Error writing output");
        }
    }
}

macro_rules! impl_fn {
    ($fn_name: ident, $data_type: ident, $bucket: ident) => {
        fn $fn_name(&mut self, data: $data_type) {
            if let Some(datum) = data.into() {
                self.result.$bucket.push(datum);
            }
        }
    }
}

impl<'b, W: Write + 'b> Dump for JsonApiDumper<'b, W> {
    fn crate_prelude(&mut self, data: CratePreludeData) {
        self.result.prelude = Some(data)
    }

    impl_fn!(use_data, UseData, imports);
    impl_fn!(use_glob, UseGlobData, imports);

    impl_fn!(enum_data, EnumData, defs);
    impl_fn!(tuple_variant, TupleVariantData, defs);
    impl_fn!(struct_variant, StructVariantData, defs);
    impl_fn!(struct_data, StructData, defs);
    impl_fn!(trait_data, TraitData, defs);
    impl_fn!(function, FunctionData, defs);
    impl_fn!(method, MethodData, defs);
    impl_fn!(macro_data, MacroData, defs);
    impl_fn!(mod_data, ModData, defs);
    impl_fn!(typedef, TypeDefData, defs);
    impl_fn!(variable, VariableData, defs);

    fn impl_data(&mut self, data: ImplData) {
        if data.self_ref.is_some() {
            self.result.relations.push(data.into());
        }
    }
    fn inheritance(&mut self, data: InheritanceData) {
        self.result.relations.push(data.into());
    }
}

// FIXME methods. The defs have information about possible overriding and the
// refs have decl information (e.g., a trait method where we know the required
// method, but not the supplied method). In both cases, we are currently
// ignoring it.

impl Into<Option<Import>> for UseData {
    fn into(self) -> Option<Import> {
        match self.visibility {
            Visibility::Public => Some(Import {
                kind: ImportKind::Use,
                ref_id: self.mod_id.map(|id| id_from_def_id(id)),
                span: self.span,
                name: self.name,
                value: String::new(),
            }),
            _ => None,
        }
    }
}
impl Into<Option<Import>> for UseGlobData {
    fn into(self) -> Option<Import> {
        match self.visibility {
            Visibility::Public => Some(Import {
                kind: ImportKind::GlobUse,
                ref_id: None,
                span: self.span,
                name: "*".to_owned(),
                value: self.names.join(", "),
            }),
            _ => None,
        }
    }
}

impl Into<Option<Def>> for EnumData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Enum,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                parent: None,
                children: self.variants.into_iter().map(|id| id_from_def_id(id)).collect(),
                decl_id: None,
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}

impl Into<Option<Def>> for TupleVariantData {
    fn into(self) -> Option<Def> {
        Some(Def {
            kind: DefKind::Tuple,
            id: id_from_def_id(self.id),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: self.value,
            parent: self.parent.map(|id| id_from_def_id(id)),
            children: vec![],
            decl_id: None,
            docs: self.docs,
            sig: self.sig,
            attributes: vec![],
        })
    }
}
impl Into<Option<Def>> for StructVariantData {
    fn into(self) -> Option<Def> {
        Some(Def {
            kind: DefKind::Struct,
            id: id_from_def_id(self.id),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: self.value,
            parent: self.parent.map(|id| id_from_def_id(id)),
            children: vec![],
            decl_id: None,
            docs: self.docs,
            sig: self.sig,
            attributes: vec![],
        })
    }
}
impl Into<Option<Def>> for StructData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
            kind: DefKind::Struct,
            id: id_from_def_id(self.id),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: self.value,
            parent: None,
            children: self.fields.into_iter().map(|id| id_from_def_id(id)).collect(),
            decl_id: None,
            docs: self.docs,
            sig: self.sig,
            attributes: vec![],
        }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for TraitData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Trait,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: self.items.into_iter().map(|id| id_from_def_id(id)).collect(),
                parent: None,
                decl_id: None,
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for FunctionData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Function,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: None,
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for MethodData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Method,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: self.decl_id.map(|id| id_from_def_id(id)),
                docs: self.docs,
                sig: self.sig,
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for MacroData {
    fn into(self) -> Option<Def> {
        Some(Def {
            kind: DefKind::Macro,
            id: id_from_def_id(null_def_id()),
            span: self.span,
            name: self.name,
            qualname: self.qualname,
            value: String::new(),
            children: vec![],
            parent: None,
            decl_id: None,
            docs: self.docs,
            sig: None,
            attributes: vec![],
        })
    }
}
impl Into<Option<Def>> for ModData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Mod,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.filename,
                children: self.items.into_iter().map(|id| id_from_def_id(id)).collect(),
                parent: None,
                decl_id: None,
                docs: self.docs,
                sig: self.sig.map(|s| s.into()),
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
impl Into<Option<Def>> for TypeDefData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: DefKind::Type,
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: None,
                docs: String::new(),
                sig: self.sig.map(|s| s.into()),
                attributes: vec![],
            }),
            _ => None,
        }
    }
}

impl Into<Option<Def>> for VariableData {
    fn into(self) -> Option<Def> {
        match self.visibility {
            Visibility::Public => Some(Def {
                kind: match self.kind {
                    VariableKind::Static => DefKind::Static,
                    VariableKind::Const => DefKind::Const,
                    VariableKind::Local => { return None }
                    VariableKind::Field => DefKind::Field,
                },
                id: id_from_def_id(self.id),
                span: self.span,
                name: self.name,
                qualname: self.qualname,
                value: self.value,
                children: vec![],
                parent: self.parent.map(|id| id_from_def_id(id)),
                decl_id: None,
                docs: self.docs,
                sig: self.sig.map(|s| s.into()),
                attributes: vec![],
            }),
            _ => None,
        }
    }
}
<|endoftext|>