<commit_before>use card_strategy::CardStrategy;

use card::Card;
use card::Suit;
use deal::Deal;
use game_status::GameStatus;
use game_status::RoundParameters;
use game_status::GameParticipant;
use player::PlayerName;

use std::collections::BTreeMap;
use std::collections::BTreeSet;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

/*
    Play 2 of clubs if in hand.
*/
impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus) -> (i32, i32, i32, i32) {
        let remaining_cards = game_status.unplayed_cards();

        let potential_points = Self::potential_points(card, &game_status.game_players, &game_status.in_progress_deal, &remaining_cards, &game_status.round_parameters);
        let definite_points = if Self::will_win_deal(card, &game_status.game_players, &game_status.in_progress_deal, &remaining_cards) {
            potential_points
        } else {
            0
        };
        let later_potential_points = 0 - Self::later_potential_points(card, &remaining_cards, &game_status.round_parameters);
        let card_rank = 0 - (u32::from(card.rank) as i32);
        (definite_points, potential_points, later_potential_points, card_rank)
    }

    fn can_win_deal(card: &Card, in_progress_deal: &Option<Deal>) -> bool {
        in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }

    fn will_win_deal(card: &Card, game_players: &Vec<GameParticipant>, in_progress_deal: &Option<Deal>, remaining_cards: &BTreeSet<Card>) -> bool {
        Self::can_win_deal(card, in_progress_deal) && (Self::plays_left(game_players, in_progress_deal).len() == 0 ||
            Self::deal_suit(in_progress_deal)
                .and_then(|suit| remaining_cards.iter()
                    .filter(|card| card.suit == *suit)
                    .max()
                    .map(|winning_card| card.rank > winning_card.rank)).unwrap_or(true))
    }

    fn deal_suit(in_progress_deal: &Option<Deal>) -> Option<&Suit> {
        in_progress_deal.as_ref().and_then(|deal| deal.suit.as_ref())
    }

    fn plays_left<'a>(game_players: &'a Vec<GameParticipant>, in_progress_deal: &Option<Deal>) -> BTreeSet<&'a PlayerName> {
        let mut players: BTreeSet<&PlayerName> = game_players.iter()
            .filter(|player| !player.has_turn)
            .map(|player| &player.team_name)
            .collect();

        if let &Some(ref deal) = in_progress_deal {
            for deal_card in &deal.deal_cards {
                players.remove(&deal_card.player_name);
            }
        }

        players
    }

    fn dealt_cards(in_progress_deal: &Option<Deal>) -> BTreeSet<&Card> {
        in_progress_deal.as_ref()
            .map(|deal| deal.deal_cards.iter().map(|deal_card| &deal_card.card).collect())
            .unwrap_or_default()
    }

    fn potential_points(card: &Card, game_players: &Vec<GameParticipant>, in_progress_deal: &Option<Deal>, remaining_cards: &BTreeSet<Card>, round_parameters: &RoundParameters) -> i32 {
        if Self::can_win_deal(card, in_progress_deal) {
            let dealt_cards = Self::dealt_cards(in_progress_deal);

            let card_points = round_parameters.points(card);

            let dealt_points: i32 = dealt_cards.iter()
                .map(|other| round_parameters.points(other))
                .sum();

            let suit_points: i32 = remaining_cards.iter()
                .filter(|other| other.suit == card.suit)
                .filter(|other| other.rank < card.rank)
                .map(|other| round_parameters.points(other))
                .sum();

            let other_points: i32 = remaining_cards.iter()
                .filter(|other| other.suit != card.suit)
                .map(|other| round_parameters.points(other))
                .sum();

            let number_of_suit = remaining_cards.iter().filter(|other| other.suit == card.suit).map(|_| 1).sum::<u32>();
            let number_dealt = dealt_cards.len() as u32;

            let safe_target = 10 - number_dealt;

            let suit_win_points = if number_dealt < 3 {
                (Self::chance_of_win(card, game_players, in_progress_deal, remaining_cards) * (suit_points as f32)) as i32
            } else {
                0
            };

            let other_win_points = if number_of_suit < safe_target && number_dealt < 3 {
                (Self::chance_of_later_win(card, remaining_cards) * (other_points as f32)) as i32
            } else {
                0
            };

            card_points + dealt_points + suit_win_points + other_win_points
        } else {
            0
        }
    }

    fn later_potential_points(card: &Card, remaining_cards: &BTreeSet<Card>, round_parameters: &RoundParameters) -> i32 {
        let card_points = round_parameters.points(card);

        let suit_points: i32 = remaining_cards.iter()
            .filter(|other| other.suit == card.suit)
            .filter(|other| other.rank < card.rank)
            .map(|other| round_parameters.points(other))
            .sum();

        let other_points: i32 = remaining_cards.iter()
            .filter(|other| other.suit != card.suit)
            .map(|other| round_parameters.points(other))
            .sum();

        let other_win_points = (Self::chance_of_later_win(card, remaining_cards) * (other_points as f32)) as i32;

        card_points + suit_points + other_win_points
    }

    fn chance_of_later_win(card: &Card, remaining_cards: &BTreeSet<Card>) -> f32 {
        let suit_cards = remaining_cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
        let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
        if suit_cards.is_empty() {
            1.0
        } else {
            (will_win_count as f32) / (suit_cards.len() as f32)
        }
    }

    fn chance_of_win(card: &Card, game_players: &Vec<GameParticipant>, in_progress_deal: &Option<Deal>, remaining_cards: &BTreeSet<Card>) -> f32 {
        if Self::will_win_deal(card, game_players, in_progress_deal, remaining_cards) {
            1.0
        } else {
            if Self::deal_suit(in_progress_deal).map(|suit| suit == &card.suit).unwrap_or(true) && Self::plays_left(game_players, in_progress_deal).len() > 0 {
                let suit_cards = remaining_cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
                let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
                if suit_cards.is_empty() {
                    1.0
                } else {
                    (will_win_count as f32) / (suit_cards.len() as f32)
                }
            } else {
                0.0
            }
        }
    }

    fn pass_card<'a>(hand: &'a BTreeSet<Card>, remaining_cards: &BTreeSet<Card>, round_parameters: &RoundParameters) -> Option<&'a Card> {
        hand.iter()
            .filter(|card| !remaining_cards.contains(card))
            .map(|card| ((0 - Self::later_potential_points(card, &remaining_cards, round_parameters), card), card))
            .collect::<BTreeMap<_, &Card>>()
            .into_iter()
            .map(|kv| kv.1)
            .next()
    }

}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        let mut remaining_cards = game_status.unplayed_cards();

        let card1 = Self::pass_card(&game_status.my_initial_hand, &remaining_cards, &game_status.round_parameters);
        if let Some(card) = card1 { remaining_cards.insert(card.clone()); };
        let card2 = Self::pass_card(&game_status.my_initial_hand, &remaining_cards, &game_status.round_parameters);
        if let Some(card) = card2 { remaining_cards.insert(card.clone()); };
        let card3 = Self::pass_card(&game_status.my_initial_hand, &remaining_cards, &game_status.round_parameters);

        vec!(card1, card2, card3).into_iter().filter_map(|card| card).collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let current_suit = game_status.in_progress_deal.as_ref().and_then(|deal| deal.suit);
        let mut valid_cards = game_status.my_current_hand.iter()
            .filter(|card| Some(card.suit) == current_suit)
            .collect::<Vec<&Card>>();

        if valid_cards.is_empty() {
            valid_cards.extend(&game_status.my_current_hand);
        }

        let evaluation = valid_cards.into_iter()
            .map(|card| ((Self::score_card(card, game_status), card), card))
            .collect::<BTreeMap<_,&Card>>();

        println!("Remaining: {}", game_status.unplayed_cards().iter().map(|card| format!("{}", card)).collect::<Vec<_>>().join(", "));
        for item in &evaluation {
            println!("{}: {:?}", item.1, (item.0).0);
        }

        evaluation.values()
            .next()
            .expect("No valid cards to play!")
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use player::PlayerName;
    use card::Card;
    use card::Rank::*;
    use card::Suit::*;
    use game_status::GameStatus;
    use game_status::dto::GameStatusDto;
    use card_strategy::CardStrategy;

    use try_from::TryFrom;
    use error::Error;

    extern crate serde;
    extern crate serde_json;

    use std::fs::File;
    use std::io::Read;

    fn open_scenario(name: &str) -> GameStatus {
        let file_name = format!("samples/scenarios/{}.json", name.replace("_", " "));
        let mut game_status_file = File::open(file_name).unwrap();
        let mut game_status_string = String::new();
        game_status_file.read_to_string(&mut game_status_string).unwrap();
        let game_status_dto: GameStatusDto = serde_json::from_str(&game_status_string).map_err(Error::from).unwrap();
        GameStatus::try_from(game_status_dto).unwrap()
    }

    fn should_play(name: &str, expected_card: Card) {
        let player_name = PlayerName::new("Derek Williams");
        let game_status = open_scenario(name);
        let card = DefensiveCardStrategy.play_card(&game_status, &player_name).clone();
        assert_eq!(expected_card, card);
    }

    macro_rules! test_play {
        ($($name:ident => $card:expr)*) => {
            $(#[test]
            fn $name() {
                should_play(stringify!($name), $card);
            })*
        }
    }

    test_play! {
        normal_1 => Jack.of(Diamond)
        normal_2 => King.of(Club)
        normal_3 => Six.of(Spade)

        should_play_heart_1 => Seven.of(Heart)
        should_play_heart_2 => Four.of(Heart)
        should_play_high_rank_1 => King.of(Diamond)
        should_play_high_rank_2 => Ace.of(Spade)
        // should_play_high_rank_3 => Ace.of(Spade)
        should_not_crash_during_card_play => Ten.of(Heart)
        // should_play_low_negative_points_card_1 => Two.of(Diamond)
        should_play_high_negative_points_card_1 => Ace.of(Diamond)
        should_try_to_win_deal_1 => Ace.of(Club)

        // corrections to this game cause no difference to outcome
        normal_game_1_01_01 => Four.of(Club)
        normal_game_1_01_02 => Eight.of(Heart)
        normal_game_1_01_03 => Seven.of(Club) // Six.of(Club)
        normal_game_1_01_04 => Five.of(Spade)
        normal_game_1_01_05 => Ten.of(Club) // Seven.of(Club)
        normal_game_1_01_06 => Seven.of(Heart)
        normal_game_1_01_07 => Six.of(Heart)
        normal_game_1_01_08 => Four.of(Diamond)
        normal_game_1_01_09 => Ten.of(Club)
        normal_game_1_01_10 => Nine.of(Club)
        normal_game_1_01_11 => Three.of(Heart)
        normal_game_1_01_12 => Four.of(Spade)
        normal_game_1_01_13 => Two.of(Spade)

        // corrections to this game cause no difference to outcome
        normal_game_1_02_01 => Eight.of(Club) // Six of Club
        normal_game_1_02_02 => Queen.of(Diamond)
        normal_game_1_02_03 => Eight.of(Club)
        normal_game_1_02_04 => Ace.of(Heart)
        normal_game_1_02_05 => Three.of(Heart)
        normal_game_1_02_06 => Eight.of(Diamond)
        normal_game_1_02_07 => Five.of(Diamond)
        normal_game_1_02_08 => Nine.of(Spade)
        normal_game_1_02_09 => Eight.of(Spade)
        normal_game_1_02_10 => Seven.of(Spade)
        normal_game_1_02_11 => Six.of(Spade)
        normal_game_1_02_12 => Three.of(Spade)
        normal_game_1_02_13 => Two.of(Spade)
    }

}<commit_msg>improve negative point handling<commit_after>use card_strategy::CardStrategy;

use card::Card;
use card::Suit;
use card::Rank;
use deal::Deal;
use game_status::GameStatus;
use game_status::RoundParameters;
use game_status::GameParticipant;
use player::PlayerName;

use std::collections::BTreeMap;
use std::collections::BTreeSet;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus) -> (i32, i32, i32, i32) {
        let remaining_cards = game_status.unplayed_cards();

        let potential_points = Self::potential_points(card, &game_status.game_players, &game_status.in_progress_deal, &remaining_cards, &game_status.round_parameters);
        let definite_points = if Self::will_win_deal(card, &game_status.game_players, &game_status.in_progress_deal, &remaining_cards) {
            potential_points
        } else {
            0
        };
        let later_potential_points = 0 - Self::later_potential_points(card, &remaining_cards, &game_status.round_parameters);
        let rank_modifier = if game_status.round_parameters.points(card) < 0 {
            -1
        } else {
            1
        };
        let card_rank =  0 - (u32::from(card.rank) as i32) * rank_modifier;
        (definite_points, potential_points, later_potential_points, card_rank)
    }

    fn can_win_deal(card: &Card, in_progress_deal: &Option<Deal>) -> bool {
        in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }

    fn will_win_deal(card: &Card, game_players: &Vec<GameParticipant>, in_progress_deal: &Option<Deal>, remaining_cards: &BTreeSet<Card>) -> bool {
        Self::can_win_deal(card, in_progress_deal) && (Self::plays_left(game_players, in_progress_deal).len() == 0 ||
            Self::deal_suit(in_progress_deal)
                .and_then(|suit| remaining_cards.iter()
                    .filter(|card| card.suit == *suit)
                    .max()
                    .map(|winning_card| card.rank > winning_card.rank)).unwrap_or(true))
    }

    fn deal_suit(in_progress_deal: &Option<Deal>) -> Option<&Suit> {
        in_progress_deal.as_ref().and_then(|deal| deal.suit.as_ref())
    }

    fn plays_left<'a>(game_players: &'a Vec<GameParticipant>, in_progress_deal: &Option<Deal>) -> BTreeSet<&'a PlayerName> {
        let mut players: BTreeSet<&PlayerName> = game_players.iter()
            .filter(|player| !player.has_turn)
            .map(|player| &player.team_name)
            .collect();

        if let &Some(ref deal) = in_progress_deal {
            for deal_card in &deal.deal_cards {
                players.remove(&deal_card.player_name);
            }
        }

        players
    }

    fn dealt_cards(in_progress_deal: &Option<Deal>) -> BTreeSet<&Card> {
        in_progress_deal.as_ref()
            .map(|deal| deal.deal_cards.iter().map(|deal_card| &deal_card.card).collect())
            .unwrap_or_default()
    }

    fn potential_points(card: &Card, game_players: &Vec<GameParticipant>, in_progress_deal: &Option<Deal>, remaining_cards: &BTreeSet<Card>, round_parameters: &RoundParameters) -> i32 {
        if Self::can_win_deal(card, in_progress_deal) {
            let dealt_cards = Self::dealt_cards(in_progress_deal);

            let card_points = round_parameters.points(card);

            let dealt_points: i32 = dealt_cards.iter()
                .map(|other| round_parameters.points(other))
                .sum();

            let suit_points: i32 = remaining_cards.iter()
                .filter(|other| other.suit == card.suit)
                .filter(|other| other.rank < card.rank)
                .map(|other| round_parameters.points(other))
                .sum();

            let other_points: i32 = remaining_cards.iter()
                .filter(|other| other.suit != card.suit)
                .map(|other| round_parameters.points(other))
                .sum();

            let number_of_suit = remaining_cards.iter().filter(|other| other.suit == card.suit).map(|_| 1).sum::<u32>();
            let number_dealt = dealt_cards.len() as u32;

            let safe_target = 10 - number_dealt;

            let suit_win_points = if number_dealt < 3 {
                (Self::chance_of_win(card, game_players, in_progress_deal, remaining_cards) * (suit_points as f32)) as i32
            } else {
                0
            };

            let other_win_points = if number_of_suit < safe_target && number_dealt < 3 {
                (Self::chance_of_later_win(card, remaining_cards) * (other_points as f32)) as i32
            } else {
                0
            };

            card_points + dealt_points + suit_win_points + other_win_points
        } else {
            0
        }
    }

    fn later_potential_points(card: &Card, remaining_cards: &BTreeSet<Card>, round_parameters: &RoundParameters) -> i32 {
        let card_points = round_parameters.points(card);

        let suit_points: i32 = remaining_cards.iter()
            .filter(|other| other.suit == card.suit)
            .filter(|other| other.rank < card.rank)
            .map(|other| round_parameters.points(other))
            .sum();

        let other_points: i32 = remaining_cards.iter()
            .filter(|other| other.suit != card.suit)
            .map(|other| round_parameters.points(other))
            .sum();

        let other_win_points = (Self::chance_of_later_win(card, remaining_cards) * (other_points as f32)) as i32;

        card_points + suit_points + other_win_points
    }

    fn chance_of_later_win(card: &Card, remaining_cards: &BTreeSet<Card>) -> f32 {
        let suit_cards = remaining_cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
        let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
        if suit_cards.is_empty() {
            1.0
        } else {
            (will_win_count as f32) / (suit_cards.len() as f32)
        }
    }

    fn chance_of_win(card: &Card, game_players: &Vec<GameParticipant>, in_progress_deal: &Option<Deal>, remaining_cards: &BTreeSet<Card>) -> f32 {
        if Self::will_win_deal(card, game_players, in_progress_deal, remaining_cards) {
            1.0
        } else {
            if Self::deal_suit(in_progress_deal).map(|suit| suit == &card.suit).unwrap_or(true) && Self::plays_left(game_players, in_progress_deal).len() > 0 {
                let suit_cards = remaining_cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
                let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
                if suit_cards.is_empty() {
                    1.0
                } else {
                    (will_win_count as f32) / (suit_cards.len() as f32)
                }
            } else {
                0.0
            }
        }
    }

    fn pass_card<'a>(hand: &'a BTreeSet<Card>, remaining_cards: &BTreeSet<Card>, round_parameters: &RoundParameters) -> Option<&'a Card> {
        hand.iter()
            .filter(|card| !remaining_cards.contains(card))
            .map(|card| ((0 - Self::later_potential_points(card, &remaining_cards, round_parameters), card), card))
            .collect::<BTreeMap<_, &Card>>()
            .into_iter()
            .map(|kv| kv.1)
            .next()
    }

}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        let mut remaining_cards = game_status.unplayed_cards();

        let card1 = Self::pass_card(&game_status.my_initial_hand, &remaining_cards, &game_status.round_parameters);
        if let Some(card) = card1 { remaining_cards.insert(card.clone()); };
        let card2 = Self::pass_card(&game_status.my_initial_hand, &remaining_cards, &game_status.round_parameters);
        if let Some(card) = card2 { remaining_cards.insert(card.clone()); };
        let card3 = Self::pass_card(&game_status.my_initial_hand, &remaining_cards, &game_status.round_parameters);

        vec!(card1, card2, card3).into_iter().filter_map(|card| card).collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let two_of_clubs = Rank::Two.of(Suit::Club);
        if let Some(card) = game_status.my_current_hand.iter().find(|&card| card == &two_of_clubs) {
            card
        } else {
            let current_suit = game_status.in_progress_deal.as_ref().and_then(|deal| deal.suit);
            let mut valid_cards = game_status.my_current_hand.iter()
                .filter(|card| Some(card.suit) == current_suit)
                .collect::<Vec<&Card>>();

            if valid_cards.is_empty() {
                valid_cards.extend(&game_status.my_current_hand);
            }

            let evaluation = valid_cards.into_iter()
                .map(|card| ((Self::score_card(card, game_status), card), card))
                .collect::<BTreeMap<_,&Card>>();

            // println!("Remaining: {}", game_status.unplayed_cards().iter().map(|card| format!("{}", card)).collect::<Vec<_>>().join(", "));
            // for item in &evaluation {
            //     println!("{}: {:?}", item.1, (item.0).0);
            // }

            evaluation.values()
                .next()
                .expect("No valid cards to play!")
        }
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use player::PlayerName;
    use card::Card;
    use card::Rank::*;
    use card::Suit::*;
    use game_status::GameStatus;
    use game_status::dto::GameStatusDto;
    use card_strategy::CardStrategy;

    use try_from::TryFrom;
    use error::Error;

    extern crate serde;
    extern crate serde_json;

    use std::fs::File;
    use std::io::Read;

    fn open_scenario(name: &str) -> GameStatus {
        let file_name = format!("samples/scenarios/{}.json", name.replace("_", " "));
        let mut game_status_file = File::open(file_name).unwrap();
        let mut game_status_string = String::new();
        game_status_file.read_to_string(&mut game_status_string).unwrap();
        let game_status_dto: GameStatusDto = serde_json::from_str(&game_status_string).map_err(Error::from).unwrap();
        GameStatus::try_from(game_status_dto).unwrap()
    }

    fn should_play(name: &str, expected_card: Card) {
        let player_name = PlayerName::new("Derek Williams");
        let game_status = open_scenario(name);
        let card = DefensiveCardStrategy.play_card(&game_status, &player_name).clone();
        assert_eq!(expected_card, card);
    }

    macro_rules! test_play {
        ($($name:ident => $card:expr)*) => {
            $(#[test]
            fn $name() {
                should_play(stringify!($name), $card);
            })*
        }
    }

    test_play! {
        normal_1 => Jack.of(Diamond)
        normal_2 => King.of(Club)
        normal_3 => Six.of(Spade)

        should_play_heart_1 => Seven.of(Heart)
        should_play_heart_2 => Four.of(Heart)
        should_play_high_rank_1 => King.of(Diamond)
        should_play_high_rank_2 => Ace.of(Spade)
        should_play_high_rank_3 => Ace.of(Spade)
        should_not_crash_during_card_play => Ten.of(Heart)
        // should_play_low_negative_points_card_1 => Two.of(Diamond)
        should_play_high_negative_points_card_1 => Ace.of(Diamond)
        should_try_to_win_deal_1 => Queen.of(Club)

        // corrections to this game cause no difference to outcome
        normal_game_1_01_01 => Four.of(Club)
        normal_game_1_01_02 => Eight.of(Heart)
        normal_game_1_01_03 => Seven.of(Club) // Six.of(Club)
        normal_game_1_01_04 => Five.of(Spade)
        normal_game_1_01_05 => Ten.of(Club) // Seven.of(Club)
        normal_game_1_01_06 => Seven.of(Heart)
        normal_game_1_01_07 => Six.of(Heart)
        normal_game_1_01_08 => Four.of(Diamond)
        normal_game_1_01_09 => Ten.of(Club)
        normal_game_1_01_10 => Nine.of(Club)
        normal_game_1_01_11 => Three.of(Heart)
        normal_game_1_01_12 => Four.of(Spade)
        normal_game_1_01_13 => Two.of(Spade)

        // corrections to this game cause no difference to outcome
        normal_game_1_02_01 => Eight.of(Club) // Six of Club
        normal_game_1_02_02 => Queen.of(Diamond)
        normal_game_1_02_03 => Eight.of(Club)
        normal_game_1_02_04 => Ace.of(Heart)
        normal_game_1_02_05 => Three.of(Heart)
        normal_game_1_02_06 => Eight.of(Diamond)
        normal_game_1_02_07 => Five.of(Diamond)
        normal_game_1_02_08 => Nine.of(Spade)
        normal_game_1_02_09 => Eight.of(Spade)
        normal_game_1_02_10 => Seven.of(Spade)
        normal_game_1_02_11 => Six.of(Spade)
        normal_game_1_02_12 => Three.of(Spade)
        normal_game_1_02_13 => Two.of(Spade)
    }

}<|endoftext|>


<commit_before>fn main() {
    // Statements here are executed when the compiled binary is called

    // Print text to the console
    println!("Hello World!");
}
<commit_msg>remove unrelated file<commit_after><|endoftext|>


<commit_before>#![allow(dead_code, non_camel_case_types)]

extern crate libc;

pub use self::os::{mdb_mode_t, mdb_filehandle_t};
use libc::{c_int, c_uint, c_void, c_char, size_t};

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux",
          target_os = "freebsd", target_os = "dragonfly",
          target_os = "android"))]
mod os {
    use libc;

    pub type mdb_mode_t = libc::mode_t;

    pub type mdb_filehandle_t = libc::c_int;
}

#[cfg(target_os = "windows")]
mod os {
    use libc;

    pub type mdb_mode_t = libc::c_int;

    pub type mdb_filehandle_t = libc::c_int;
}

pub type MDB_dbi = c_uint;

pub type MDB_rel_func = extern fn(*const MDB_val, *const c_void, *const c_void, *const c_void);
pub type MDB_msg_func = extern fn(*const c_char, *const c_void) -> c_int;
pub type MDB_cmp_func = extern fn(*const MDB_val, *const MDB_val) -> c_int;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct MDB_val {
    pub mv_size: size_t,
    pub mv_data: *const c_void,
}

#[allow(missing_copy_implementations)]
pub enum MDB_env {}

#[allow(missing_copy_implementations)]
pub enum MDB_txn {}

#[allow(missing_copy_implementations)]
pub enum MDB_cursor {}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct MDB_stat {
    pub ms_psize: c_uint,
    pub ms_depth: c_uint,
    pub ms_branch_pages: size_t,
    pub ms_leaf_pages: size_t,
    pub ms_overflow_pages: size_t,
    pub ms_entries: size_t
}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MDB_envinfo {
    pub me_mapaddr: *const c_void,
    pub me_mapsize: size_t,
    pub me_last_pgno: size_t,
    pub me_last_txnid: size_t,
    pub me_maxreaders: c_uint,
    pub me_numreaders: c_uint
}

#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum MDB_cursor_op {
    MDB_FIRST,
    MDB_FIRST_DUP,
    MDB_GET_BOTH,
    MDB_GET_BOTH_RANGE,
    MDB_GET_CURRENT,
    MDB_GET_MULTIPLE,
    MDB_LAST,
    MDB_LAST_DUP,
    MDB_NEXT,
    MDB_NEXT_DUP,
    MDB_NEXT_MULTIPLE,
    MDB_NEXT_NODUP,
    MDB_PREV,
    MDB_PREV_DUP,
    MDB_PREV_NODUP,
    MDB_SET,
    MDB_SET_KEY,
    MDB_SET_RANGE
}

// Return codes
pub const MDB_SUCCESS: c_int = 0;
pub const MDB_KEYEXIST: c_int = -30799;
pub const MDB_NOTFOUND: c_int = -30798;
pub const MDB_PAGE_NOTFOUND: c_int = -30797;
pub const MDB_CORRUPTED: c_int = -30796;
pub const MDB_PANIC: c_int = -30795;
pub const MDB_VERSION_MISMATCH: c_int = -30794;
pub const MDB_INVALID: c_int = -30793;
pub const MDB_MAP_FULL: c_int = -30792;
pub const MDB_DBS_FULL: c_int = -30791;
pub const MDB_READERS_FULL: c_int = -30790;
pub const MDB_TLS_FULL: c_int = -30789;
pub const MDB_TXN_FULL: c_int = -30788;
pub const MDB_CURSOR_FULL: c_int = -30787;
pub const MDB_PAGE_FULL: c_int = -30786;
pub const MDB_MAP_RESIZED: c_int = -30785;
pub const MDB_INCOMPATIBLE: c_int = -30784;
pub const MDB_BAD_RSLOT: c_int = -30783;
pub const MDB_BAD_TXN: c_int = -30782;
pub const MDB_BAD_VALSIZE: c_int = -30781;

// Write flags
pub const MDB_NOOVERWRITE: c_uint = 0x10;
pub const MDB_NODUPDATA: c_uint = 0x20;
pub const MDB_CURRENT: c_uint = 0x40;
pub const MDB_RESERVE: c_uint = 0x10000;
pub const MDB_APPEND: c_uint = 0x20000;
pub const MDB_APPENDDUP: c_uint = 0x40000;
pub const MDB_MULTIPLE: c_uint = 0x80000;

// Database flags
pub const MDB_REVERSEKEY: c_uint = 0x02;
pub const MDB_DUPSORT: c_uint = 0x04;
pub const MDB_INTEGERKEY: c_uint = 0x08;
pub const MDB_DUPFIXED: c_uint = 0x10;
pub const MDB_INTEGERDUP: c_uint = 0x20;
pub const MDB_REVERSEDUP: c_uint =  0x40;
pub const MDB_CREATE: c_uint = 0x40000;

// Environment flags
pub const MDB_FIXEDMAP: c_uint =  0x01;
pub const MDB_NOSUBDIR: c_uint = 0x4000;
pub const MDB_NOSYNC: c_uint = 0x10000;
pub const MDB_RDONLY: c_uint = 0x20000;
pub const MDB_NOMETASYNC: c_uint = 0x40000;
pub const MDB_WRITEMAP: c_uint = 0x80000;
pub const MDB_MAPASYNC: c_uint = 0x100000;
pub const MDB_NOTLS: c_uint = 0x200000;
pub const MDB_NOLOCK: c_uint =  0x400000;
pub const MDB_NORDAHEAD: c_uint = 0x800000;
pub const MDB_NOMEMINIT: c_uint =  0x1000000;

// Embedding should work better for now
extern "C" {
    pub fn mdb_version(major: *mut c_int, minor: *mut c_int, patch: *mut c_int) -> *const c_char;
    pub fn mdb_strerror(err: c_int) -> *const c_char;
    pub fn mdb_env_create(env: *mut *mut MDB_env) -> c_int;
    pub fn mdb_env_open(env: *mut MDB_env, path: *const c_char, flags: c_uint, mode: mdb_mode_t) -> c_int;
    pub fn mdb_env_copy(env: *mut MDB_env, path: *const c_char) -> c_int;
    pub fn mdb_env_copyfd(env: *mut MDB_env, fd: mdb_filehandle_t) -> c_int;
    pub fn mdb_env_stat(env: *mut MDB_env, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_env_info(env: *mut MDB_env, info: *mut MDB_envinfo) -> c_int;
    pub fn mdb_env_sync(env: *mut MDB_env, force: c_int) -> c_int;
    pub fn mdb_env_close(env: *mut MDB_env);
    pub fn mdb_env_set_flags(env: *mut MDB_env, flags: c_uint, onoff: c_int) -> c_int;
    pub fn mdb_env_get_flags(env: *mut MDB_env, flags: *mut c_uint) -> c_int;
    pub fn mdb_env_get_path(env: *mut MDB_env, path: *mut *mut c_char) -> c_int;
    pub fn mdb_env_get_fd(env: *mut MDB_env, fd: *mut mdb_filehandle_t) -> c_int;
    pub fn mdb_env_set_mapsize(env: *mut MDB_env, size: size_t) -> c_int;
    pub fn mdb_env_set_maxreaders(env: *mut MDB_env, readers: c_uint) -> c_int;
    pub fn mdb_env_get_maxreaders(env: *mut MDB_env, readers: *mut c_uint) -> c_int;
    pub fn mdb_env_set_maxdbs(env: *mut MDB_env, dbs: MDB_dbi) -> c_int;
    pub fn mdb_env_get_maxkeysize(env: *mut MDB_env) -> c_int;
    pub fn mdb_txn_begin(env: *mut MDB_env, parent: *mut MDB_txn, flags: c_uint, txn: *mut *mut MDB_txn) -> c_int;
    pub fn mdb_txn_env(txn: *mut MDB_txn) -> *mut MDB_env;
    pub fn mdb_txn_commit(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_txn_abort(txn: *mut MDB_txn);
    pub fn mdb_txn_reset(txn: *mut MDB_txn);
    pub fn mdb_txn_renew(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_dbi_open(txn: *mut MDB_txn, name: *const c_char, flags: c_uint, dbi: *mut MDB_dbi) -> c_int;
    pub fn mdb_stat(txn: *mut MDB_txn, dbi: MDB_dbi, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_dbi_flags(txn: *mut MDB_txn, dbi: MDB_dbi, flags: *mut c_uint) -> c_int;
    pub fn mdb_dbi_close(txn: *mut MDB_env, dbi: MDB_dbi);
    pub fn mdb_drop(txn: *mut MDB_txn, dbi: MDB_dbi, del: c_int) -> c_int;
    pub fn mdb_set_compare(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_dupsort(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_relfunc(txn: *mut MDB_txn, dbi: MDB_dbi, rel: MDB_rel_func) -> c_int;
    pub fn mdb_set_relctx(txn: *mut MDB_txn, dbi: MDB_dbi, ctx: *const c_void) -> c_int;
    pub fn mdb_get(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_put(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_del(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_cursor_open(txn: *mut MDB_txn, dbi: MDB_dbi, cursor: *mut *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_close(cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_renew(txn: *mut MDB_txn, cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_txn(cursor: *mut MDB_cursor) -> *mut MDB_txn;
    pub fn mdb_cursor_dbi(cursor: *mut MDB_cursor) -> *mut MDB_dbi;
    pub fn mdb_cursor_get(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, op: MDB_cursor_op) -> c_int;
    pub fn mdb_cursor_put(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_cursor_del(cursor: *mut MDB_cursor, flags: c_uint) -> c_int;
    pub fn mdb_cursor_count(cursor: *mut MDB_cursor, countp: *mut size_t) -> c_int;
    pub fn mdb_cmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_dcmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_reader_list(env: *mut MDB_env, func: MDB_msg_func, ctx: *const c_void) -> c_int;
    pub fn mdb_reader_check(env: *mut MDB_env, dead: *mut c_int) -> c_int;
}

impl std::fmt::Debug for MDB_val {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        unsafe {
            let buf: &[u8] = std::slice::from_raw_parts(std::mem::transmute(self.mv_data),
                                                        self.mv_size as usize);
            write!(fmt, "{:?}@{:?}", buf, self.mv_data)
        }
    }
}
<commit_msg>Add OpenBSD support<commit_after>#![allow(dead_code, non_camel_case_types)]

extern crate libc;

pub use self::os::{mdb_mode_t, mdb_filehandle_t};
use libc::{c_int, c_uint, c_void, c_char, size_t};

#[cfg(any(target_os = "macos", target_os = "ios", target_os = "linux",
          target_os = "freebsd", target_os = "dragonfly",
          target_os = "android", target_os = "openbsd"))]
mod os {
    use libc;

    pub type mdb_mode_t = libc::mode_t;

    pub type mdb_filehandle_t = libc::c_int;
}

#[cfg(target_os = "windows")]
mod os {
    use libc;

    pub type mdb_mode_t = libc::c_int;

    pub type mdb_filehandle_t = libc::c_int;
}

pub type MDB_dbi = c_uint;

pub type MDB_rel_func = extern fn(*const MDB_val, *const c_void, *const c_void, *const c_void);
pub type MDB_msg_func = extern fn(*const c_char, *const c_void) -> c_int;
pub type MDB_cmp_func = extern fn(*const MDB_val, *const MDB_val) -> c_int;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct MDB_val {
    pub mv_size: size_t,
    pub mv_data: *const c_void,
}

#[allow(missing_copy_implementations)]
pub enum MDB_env {}

#[allow(missing_copy_implementations)]
pub enum MDB_txn {}

#[allow(missing_copy_implementations)]
pub enum MDB_cursor {}

#[repr(C)]
#[derive(Copy, Clone)]
pub struct MDB_stat {
    pub ms_psize: c_uint,
    pub ms_depth: c_uint,
    pub ms_branch_pages: size_t,
    pub ms_leaf_pages: size_t,
    pub ms_overflow_pages: size_t,
    pub ms_entries: size_t
}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct MDB_envinfo {
    pub me_mapaddr: *const c_void,
    pub me_mapsize: size_t,
    pub me_last_pgno: size_t,
    pub me_last_txnid: size_t,
    pub me_maxreaders: c_uint,
    pub me_numreaders: c_uint
}

#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum MDB_cursor_op {
    MDB_FIRST,
    MDB_FIRST_DUP,
    MDB_GET_BOTH,
    MDB_GET_BOTH_RANGE,
    MDB_GET_CURRENT,
    MDB_GET_MULTIPLE,
    MDB_LAST,
    MDB_LAST_DUP,
    MDB_NEXT,
    MDB_NEXT_DUP,
    MDB_NEXT_MULTIPLE,
    MDB_NEXT_NODUP,
    MDB_PREV,
    MDB_PREV_DUP,
    MDB_PREV_NODUP,
    MDB_SET,
    MDB_SET_KEY,
    MDB_SET_RANGE
}

// Return codes
pub const MDB_SUCCESS: c_int = 0;
pub const MDB_KEYEXIST: c_int = -30799;
pub const MDB_NOTFOUND: c_int = -30798;
pub const MDB_PAGE_NOTFOUND: c_int = -30797;
pub const MDB_CORRUPTED: c_int = -30796;
pub const MDB_PANIC: c_int = -30795;
pub const MDB_VERSION_MISMATCH: c_int = -30794;
pub const MDB_INVALID: c_int = -30793;
pub const MDB_MAP_FULL: c_int = -30792;
pub const MDB_DBS_FULL: c_int = -30791;
pub const MDB_READERS_FULL: c_int = -30790;
pub const MDB_TLS_FULL: c_int = -30789;
pub const MDB_TXN_FULL: c_int = -30788;
pub const MDB_CURSOR_FULL: c_int = -30787;
pub const MDB_PAGE_FULL: c_int = -30786;
pub const MDB_MAP_RESIZED: c_int = -30785;
pub const MDB_INCOMPATIBLE: c_int = -30784;
pub const MDB_BAD_RSLOT: c_int = -30783;
pub const MDB_BAD_TXN: c_int = -30782;
pub const MDB_BAD_VALSIZE: c_int = -30781;

// Write flags
pub const MDB_NOOVERWRITE: c_uint = 0x10;
pub const MDB_NODUPDATA: c_uint = 0x20;
pub const MDB_CURRENT: c_uint = 0x40;
pub const MDB_RESERVE: c_uint = 0x10000;
pub const MDB_APPEND: c_uint = 0x20000;
pub const MDB_APPENDDUP: c_uint = 0x40000;
pub const MDB_MULTIPLE: c_uint = 0x80000;

// Database flags
pub const MDB_REVERSEKEY: c_uint = 0x02;
pub const MDB_DUPSORT: c_uint = 0x04;
pub const MDB_INTEGERKEY: c_uint = 0x08;
pub const MDB_DUPFIXED: c_uint = 0x10;
pub const MDB_INTEGERDUP: c_uint = 0x20;
pub const MDB_REVERSEDUP: c_uint =  0x40;
pub const MDB_CREATE: c_uint = 0x40000;

// Environment flags
pub const MDB_FIXEDMAP: c_uint =  0x01;
pub const MDB_NOSUBDIR: c_uint = 0x4000;
pub const MDB_NOSYNC: c_uint = 0x10000;
pub const MDB_RDONLY: c_uint = 0x20000;
pub const MDB_NOMETASYNC: c_uint = 0x40000;
pub const MDB_WRITEMAP: c_uint = 0x80000;
pub const MDB_MAPASYNC: c_uint = 0x100000;
pub const MDB_NOTLS: c_uint = 0x200000;
pub const MDB_NOLOCK: c_uint =  0x400000;
pub const MDB_NORDAHEAD: c_uint = 0x800000;
pub const MDB_NOMEMINIT: c_uint =  0x1000000;

// Embedding should work better for now
extern "C" {
    pub fn mdb_version(major: *mut c_int, minor: *mut c_int, patch: *mut c_int) -> *const c_char;
    pub fn mdb_strerror(err: c_int) -> *const c_char;
    pub fn mdb_env_create(env: *mut *mut MDB_env) -> c_int;
    pub fn mdb_env_open(env: *mut MDB_env, path: *const c_char, flags: c_uint, mode: mdb_mode_t) -> c_int;
    pub fn mdb_env_copy(env: *mut MDB_env, path: *const c_char) -> c_int;
    pub fn mdb_env_copyfd(env: *mut MDB_env, fd: mdb_filehandle_t) -> c_int;
    pub fn mdb_env_stat(env: *mut MDB_env, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_env_info(env: *mut MDB_env, info: *mut MDB_envinfo) -> c_int;
    pub fn mdb_env_sync(env: *mut MDB_env, force: c_int) -> c_int;
    pub fn mdb_env_close(env: *mut MDB_env);
    pub fn mdb_env_set_flags(env: *mut MDB_env, flags: c_uint, onoff: c_int) -> c_int;
    pub fn mdb_env_get_flags(env: *mut MDB_env, flags: *mut c_uint) -> c_int;
    pub fn mdb_env_get_path(env: *mut MDB_env, path: *mut *mut c_char) -> c_int;
    pub fn mdb_env_get_fd(env: *mut MDB_env, fd: *mut mdb_filehandle_t) -> c_int;
    pub fn mdb_env_set_mapsize(env: *mut MDB_env, size: size_t) -> c_int;
    pub fn mdb_env_set_maxreaders(env: *mut MDB_env, readers: c_uint) -> c_int;
    pub fn mdb_env_get_maxreaders(env: *mut MDB_env, readers: *mut c_uint) -> c_int;
    pub fn mdb_env_set_maxdbs(env: *mut MDB_env, dbs: MDB_dbi) -> c_int;
    pub fn mdb_env_get_maxkeysize(env: *mut MDB_env) -> c_int;
    pub fn mdb_txn_begin(env: *mut MDB_env, parent: *mut MDB_txn, flags: c_uint, txn: *mut *mut MDB_txn) -> c_int;
    pub fn mdb_txn_env(txn: *mut MDB_txn) -> *mut MDB_env;
    pub fn mdb_txn_commit(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_txn_abort(txn: *mut MDB_txn);
    pub fn mdb_txn_reset(txn: *mut MDB_txn);
    pub fn mdb_txn_renew(txn: *mut MDB_txn) -> c_int;
    pub fn mdb_dbi_open(txn: *mut MDB_txn, name: *const c_char, flags: c_uint, dbi: *mut MDB_dbi) -> c_int;
    pub fn mdb_stat(txn: *mut MDB_txn, dbi: MDB_dbi, stat: *mut MDB_stat) -> c_int;
    pub fn mdb_dbi_flags(txn: *mut MDB_txn, dbi: MDB_dbi, flags: *mut c_uint) -> c_int;
    pub fn mdb_dbi_close(txn: *mut MDB_env, dbi: MDB_dbi);
    pub fn mdb_drop(txn: *mut MDB_txn, dbi: MDB_dbi, del: c_int) -> c_int;
    pub fn mdb_set_compare(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_dupsort(txn: *mut MDB_txn, dbi: MDB_dbi, cmp: MDB_cmp_func) -> c_int;
    pub fn mdb_set_relfunc(txn: *mut MDB_txn, dbi: MDB_dbi, rel: MDB_rel_func) -> c_int;
    pub fn mdb_set_relctx(txn: *mut MDB_txn, dbi: MDB_dbi, ctx: *const c_void) -> c_int;
    pub fn mdb_get(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_put(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_del(txn: *mut MDB_txn, dbi: MDB_dbi, key: *mut MDB_val, data: *mut MDB_val) -> c_int;
    pub fn mdb_cursor_open(txn: *mut MDB_txn, dbi: MDB_dbi, cursor: *mut *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_close(cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_renew(txn: *mut MDB_txn, cursor: *mut MDB_cursor) -> c_int;
    pub fn mdb_cursor_txn(cursor: *mut MDB_cursor) -> *mut MDB_txn;
    pub fn mdb_cursor_dbi(cursor: *mut MDB_cursor) -> *mut MDB_dbi;
    pub fn mdb_cursor_get(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, op: MDB_cursor_op) -> c_int;
    pub fn mdb_cursor_put(cursor: *mut MDB_cursor, key: *mut MDB_val, data: *mut MDB_val, flags: c_uint) -> c_int;
    pub fn mdb_cursor_del(cursor: *mut MDB_cursor, flags: c_uint) -> c_int;
    pub fn mdb_cursor_count(cursor: *mut MDB_cursor, countp: *mut size_t) -> c_int;
    pub fn mdb_cmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_dcmp(txn: *mut MDB_txn, dbi: MDB_dbi, a: *mut MDB_val, b: *mut MDB_val) -> c_int;
    pub fn mdb_reader_list(env: *mut MDB_env, func: MDB_msg_func, ctx: *const c_void) -> c_int;
    pub fn mdb_reader_check(env: *mut MDB_env, dead: *mut c_int) -> c_int;
}

impl std::fmt::Debug for MDB_val {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        unsafe {
            let buf: &[u8] = std::slice::from_raw_parts(std::mem::transmute(self.mv_data),
                                                        self.mv_size as usize);
            write!(fmt, "{:?}@{:?}", buf, self.mv_data)
        }
    }
}
<|endoftext|>


<commit_before>extern crate sdl2;

use sdl2::audio::{AudioCallback, AudioSpecDesired};
use std::time::Duration;

fn gen_wave(bytes_to_write: i32) -> Vec<i16> {
    // Generate a square wave
    let tone_volume = 1000i16;
    let period = 48000 / 256;
    let sample_count = bytes_to_write / 4;
    let mut result = Vec::new();

    for x in 0..sample_count {
        result.push(
            if (x / period) % 2 == 0 {
                tone_volume
            }
            else {
                -tone_volume
            }
        );
    }
    result
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let audio_subsystem = sdl_context.audio().unwrap();

    let desired_spec = AudioSpecDesired {
        freq: Some(48000),
        channels: Some(2),  // mono
        samples: Some(4)    // default sample size
    };

    let device = audio_subsystem.open_queue::<i16>(None, &desired_spec).unwrap();

    let target_bytes = 48000 * 4;
    let wave = gen_wave(target_bytes);
    device.queue(&wave);

    // Start playback
    device.resume();

    // Play for 2 seconds
    std::thread::sleep(Duration::from_millis(2000));

    // Device is automatically closed when dropped
}
<commit_msg>adjusted new example code<commit_after>extern crate sdl2;

use sdl2::audio::{AudioCallback, AudioSpecDesired};
use std::time::Duration;

fn gen_wave(bytes_to_write: i32) -> Vec<i16> {
    // Generate a square wave
    let tone_volume = 1000i16;
    let period = 48000 / 256;
    let sample_count = bytes_to_write;
    let mut result = Vec::new();

    for x in 0..sample_count {
        result.push(
            if (x / period) % 2 == 0 {
                tone_volume
            }
            else {
                -tone_volume
            }
        );
    }
    result
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let audio_subsystem = sdl_context.audio().unwrap();

    let desired_spec = AudioSpecDesired {
        freq: Some(48000),
        channels: Some(2),  // mono
        samples: Some(4)    // default sample size
    };

    let device = audio_subsystem.open_queue::<i16>(None, &desired_spec).unwrap();

    let target_bytes = 48000 * 4;
    let wave = gen_wave(target_bytes);
    device.queue(&wave);

    // Start playback
    device.resume();

    // Play for 2 seconds
    std::thread::sleep(Duration::from_millis(2000));

    // Device is automatically closed when dropped
}
<|endoftext|>


<commit_before>// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

pub mod async_io;
pub mod sync_io;

pub use self::async_io::AsyncFileEngine;
pub use self::sync_io::SyncFileEngine;
use std::fs::File;
use vm_memory::{GuestAddress, GuestMemoryMmap};

#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct UserDataOk<T> {
    pub user_data: T,
    pub count: u32,
}

#[cfg_attr(test, derive(Debug, PartialEq))]
pub enum FileEngineOk<T> {
    Submitted,
    Executed(UserDataOk<T>),
}

#[derive(Debug)]
pub enum Error {
    Sync(sync_io::Error),
    Async(async_io::Error),
}

#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct UserDataError<T, E> {
    pub user_data: T,
    pub error: E,
}

pub enum FileEngine<T> {
    #[allow(unused)]
    Async(AsyncFileEngine<T>),
    Sync(SyncFileEngine),
}

impl<T> FileEngine<T> {
    pub fn from_file_sync(file: File) -> Result<FileEngine<T>, Error> {
        Ok(FileEngine::Sync(SyncFileEngine::from_file(file)))
    }

    #[allow(unused)]
    pub fn from_file_async(file: File) -> Result<FileEngine<T>, Error> {
        Ok(FileEngine::Async(
            AsyncFileEngine::from_file(file).map_err(Error::Async)?,
        ))
    }

    pub fn file(&self) -> &File {
        match self {
            FileEngine::Async(engine) => engine.file(),
            FileEngine::Sync(engine) => engine.file(),
        }
    }

    pub fn read(
        &mut self,
        offset: u64,
        mem: &GuestMemoryMmap,
        addr: GuestAddress,
        count: u32,
        user_data: T,
    ) -> Result<FileEngineOk<T>, UserDataError<T, Error>> {
        match self {
            FileEngine::Async(engine) => {
                match engine.push_read(offset, mem, addr, count, user_data) {
                    Ok(_) => Ok(FileEngineOk::Submitted),
                    Err(e) => Err(UserDataError {
                        user_data: e.user_data,
                        error: Error::Async(e.error),
                    }),
                }
            }
            FileEngine::Sync(engine) => match engine.read(offset, mem, addr, count) {
                Ok(count) => Ok(FileEngineOk::Executed(UserDataOk { user_data, count })),
                Err(e) => Err(UserDataError {
                    user_data,
                    error: Error::Sync(e),
                }),
            },
        }
    }

    pub fn write(
        &mut self,
        offset: u64,
        mem: &GuestMemoryMmap,
        addr: GuestAddress,
        count: u32,
        user_data: T,
    ) -> Result<FileEngineOk<T>, UserDataError<T, Error>> {
        match self {
            FileEngine::Async(engine) => {
                match engine.push_write(offset, mem, addr, count, user_data) {
                    Ok(_) => Ok(FileEngineOk::Submitted),
                    Err(e) => Err(UserDataError {
                        user_data: e.user_data,
                        error: Error::Async(e.error),
                    }),
                }
            }
            FileEngine::Sync(engine) => match engine.write(offset, mem, addr, count) {
                Ok(count) => Ok(FileEngineOk::Executed(UserDataOk { user_data, count })),
                Err(e) => Err(UserDataError {
                    user_data,
                    error: Error::Sync(e),
                }),
            },
        }
    }

    pub fn flush(&mut self, user_data: T) -> Result<FileEngineOk<T>, UserDataError<T, Error>> {
        match self {
            FileEngine::Async(engine) => match engine.push_flush(user_data) {
                Ok(_) => Ok(FileEngineOk::Submitted),
                Err(e) => Err(UserDataError {
                    user_data: e.user_data,
                    error: Error::Async(e.error),
                }),
            },
            FileEngine::Sync(engine) => match engine.flush() {
                Ok(_) => Ok(FileEngineOk::Executed(UserDataOk {
                    user_data,
                    count: 0,
                })),
                Err(e) => Err(UserDataError {
                    user_data,
                    error: Error::Sync(e),
                }),
            },
        }
    }

    pub fn drain(&mut self, flush: bool) -> Result<(), Error> {
        match self {
            FileEngine::Async(engine) => engine.drain(flush).map_err(Error::Async),
            FileEngine::Sync(engine) => {
                if !flush {
                    return Ok(());
                }
                engine.flush().map_err(Error::Sync)
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::os::unix::ffi::OsStrExt;
    use std::os::unix::io::FromRawFd;

    use super::*;
    use utils::kernel_version::KernelVersion;
    use utils::tempfile::TempFile;
    use vm_memory::{Bytes, GuestMemory, GuestMemoryRegion, MemoryRegionAddress};

    const FILE_LEN: u32 = 1024;
    const MEM_LEN: usize = 4096;

    macro_rules! assert_err {
        ($expression:expr, $($pattern:tt)+) => {
            match $expression {
                Err(UserDataError {
                    user_data: _,
                    error: $($pattern)+,
                }) => (),
                ref e => {
                    println!("expected `{}` but got `{:?}`", stringify!($($pattern)+), e);
                    assert!(false)
                }
            }
        };
    }

    macro_rules! assert_sync_execution {
        ($expression:expr, $count:expr) => {
            if let Ok(FileEngineOk::Executed(UserDataOk {
                user_data: _,
                count,
            })) = $expression
            {
                assert_eq!(count, $count);
            } else {
                println!("expected Ok, received Err");
                assert!(false);
            }
        };
    }

    macro_rules! assert_queued {
        ($expression:expr) => {
            assert!(matches!($expression, Ok(FileEngineOk::Submitted)))
        };
    }

    macro_rules! assert_async_execution {
        ($engine:expr, $count:expr) => {
            if let FileEngine::Async(ref mut engine) = $engine {
                engine.kick_submission_queue_and_wait(1).unwrap();
                assert_eq!(engine.pop().unwrap().unwrap().result().unwrap(), $count);
            }
        };
    }

    fn clear_mem(mem: &GuestMemoryMmap) {
        for region in mem.iter() {
            let empty_data = vec![0u8; region.len() as usize];
            region
                .write_slice(&empty_data, MemoryRegionAddress(0))
                .unwrap();
        }
    }

    #[test]
    fn test_sync() {
        // Create guest memory
        let mem =
            vm_memory::test_utils::create_anon_guest_memory(&[(GuestAddress(0), MEM_LEN)], false)
                .unwrap();

        // Check invalid file
        let file = unsafe { File::from_raw_fd(-2) };
        let mut engine = FileEngine::from_file_sync(file).unwrap();
        let res = engine.read(0, &mem, GuestAddress(0), 0, ());
        assert_err!(res, Error::Sync(sync_io::Error::Seek(_e)));
        let res = engine.write(0, &mem, GuestAddress(0), 0, ());
        assert_err!(res, Error::Sync(sync_io::Error::Seek(_e)));
        let res = engine.flush(());
        assert_err!(res, Error::Sync(sync_io::Error::SyncAll(_e)));

        // Create backing file.
        let file = TempFile::new().unwrap().into_file();
        let mut engine = FileEngine::from_file_sync(file).unwrap();

        let data = utils::rand::rand_alphanumerics(FILE_LEN as usize)
            .as_bytes()
            .to_vec();

        // Partial write
        let partial_len = 50;
        let addr = GuestAddress(MEM_LEN as u64 - partial_len);
        mem.write(&data, addr).unwrap();
        assert_sync_execution!(
            engine.write(0, &mem, addr, FILE_LEN, ()),
            partial_len as u32
        );
        // Partial read
        clear_mem(&mem);
        assert_sync_execution!(engine.read(0, &mem, addr, FILE_LEN, ()), partial_len as u32);
        // Check data
        let mut buf = vec![0u8; partial_len as usize];
        mem.read_slice(&mut buf, addr).unwrap();
        assert_eq!(buf, data[..partial_len as usize]);

        // Offset write
        let offset = 100;
        let partial_len = 50;
        let addr = GuestAddress(0);
        mem.write(&data, addr).unwrap();
        assert_sync_execution!(
            engine.write(offset, &mem, addr, partial_len, ()),
            partial_len as u32
        );
        // Offset read
        clear_mem(&mem);
        assert_sync_execution!(
            engine.read(offset, &mem, addr, partial_len, ()),
            partial_len as u32
        );
        // Check data
        let mut buf = vec![0u8; partial_len as usize];
        mem.read_slice(&mut buf, addr).unwrap();
        assert_eq!(buf, data[..partial_len as usize]);

        // Full write
        mem.write(&data, GuestAddress(0)).unwrap();
        assert_sync_execution!(
            engine.write(0, &mem, GuestAddress(0), FILE_LEN, ()),
            FILE_LEN
        );
        // Full read
        clear_mem(&mem);
        assert_sync_execution!(
            engine.read(0, &mem, GuestAddress(0), FILE_LEN, ()),
            FILE_LEN
        );
        // Check data
        let mut buf = vec![0u8; FILE_LEN as usize];
        mem.read_slice(&mut buf, GuestAddress(0)).unwrap();
        assert_eq!(buf, data.as_slice());

        // Check other ops
        assert!(engine.flush(()).is_ok());
        assert!(engine.drain(true).is_ok());
        assert!(engine.drain(false).is_ok());
    }

    #[test]
    fn test_async() {
        if KernelVersion::get().unwrap() < KernelVersion::new(5, 10, 0) {
            return;
        }

        // Create guest memory
        let mem =
            vm_memory::test_utils::create_anon_guest_memory(&[(GuestAddress(0), MEM_LEN)], false)
                .unwrap();
        // Check invalid file
        let file = unsafe { File::from_raw_fd(-2) };
        assert!(FileEngine::<()>::from_file_async(file).is_err());

        // Create backing file.
        let file = TempFile::new().unwrap().into_file();
        let mut engine = FileEngine::<()>::from_file_async(file).unwrap();

        let data = utils::rand::rand_alphanumerics(FILE_LEN as usize)
            .as_bytes()
            .to_vec();

        // Partial reads and writes cannot really be tested because io_uring will return an error
        // code for trying to write to unmapped memory.

        // Offset write
        let offset = 100;
        let partial_len = 50;
        let addr = GuestAddress(0);
        mem.write(&data, addr).unwrap();
        assert_queued!(engine.write(offset, &mem, addr, partial_len, ()));
        assert_async_execution!(engine, partial_len as u32);
        // Offset read
        clear_mem(&mem);
        assert_queued!(engine.read(offset, &mem, addr, partial_len, ()));
        assert_async_execution!(engine, partial_len as u32);
        // Check data
        let mut buf = vec![0u8; partial_len as usize];
        mem.read_slice(&mut buf, addr).unwrap();
        assert_eq!(buf, data[..partial_len as usize]);

        // Full write
        mem.write(&data, GuestAddress(0)).unwrap();
        assert_queued!(engine.write(0, &mem, addr, FILE_LEN, ()));
        assert_async_execution!(engine, FILE_LEN as u32);

        // Full read
        clear_mem(&mem);
        assert_queued!(engine.read(0, &mem, addr, FILE_LEN, ()));
        assert_async_execution!(engine, FILE_LEN as u32);
        // Check data
        let mut buf = vec![0u8; FILE_LEN as usize];
        mem.read_slice(&mut buf, GuestAddress(0)).unwrap();
        assert_eq!(buf, data.as_slice());

        // Check other ops
        assert_queued!(engine.flush(()));
        assert_async_execution!(engine, 0);

        assert!(engine.drain(true).is_ok());
        assert!(engine.drain(false).is_ok());
    }
}
<commit_msg>[block] FileEngine unit tests adjustments<commit_after>// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

pub mod async_io;
pub mod sync_io;

pub use self::async_io::AsyncFileEngine;
pub use self::sync_io::SyncFileEngine;
use std::fs::File;
use vm_memory::{GuestAddress, GuestMemoryMmap};

#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct UserDataOk<T> {
    pub user_data: T,
    pub count: u32,
}

#[cfg_attr(test, derive(Debug, PartialEq))]
pub enum FileEngineOk<T> {
    Submitted,
    Executed(UserDataOk<T>),
}

#[derive(Debug)]
pub enum Error {
    Sync(sync_io::Error),
    Async(async_io::Error),
}

#[cfg_attr(test, derive(Debug, PartialEq))]
pub struct UserDataError<T, E> {
    pub user_data: T,
    pub error: E,
}

pub enum FileEngine<T> {
    #[allow(unused)]
    Async(AsyncFileEngine<T>),
    Sync(SyncFileEngine),
}

impl<T> FileEngine<T> {
    pub fn from_file_sync(file: File) -> Result<FileEngine<T>, Error> {
        Ok(FileEngine::Sync(SyncFileEngine::from_file(file)))
    }

    #[allow(unused)]
    pub fn from_file_async(file: File) -> Result<FileEngine<T>, Error> {
        Ok(FileEngine::Async(
            AsyncFileEngine::from_file(file).map_err(Error::Async)?,
        ))
    }

    pub fn file(&self) -> &File {
        match self {
            FileEngine::Async(engine) => engine.file(),
            FileEngine::Sync(engine) => engine.file(),
        }
    }

    pub fn read(
        &mut self,
        offset: u64,
        mem: &GuestMemoryMmap,
        addr: GuestAddress,
        count: u32,
        user_data: T,
    ) -> Result<FileEngineOk<T>, UserDataError<T, Error>> {
        match self {
            FileEngine::Async(engine) => {
                match engine.push_read(offset, mem, addr, count, user_data) {
                    Ok(_) => Ok(FileEngineOk::Submitted),
                    Err(e) => Err(UserDataError {
                        user_data: e.user_data,
                        error: Error::Async(e.error),
                    }),
                }
            }
            FileEngine::Sync(engine) => match engine.read(offset, mem, addr, count) {
                Ok(count) => Ok(FileEngineOk::Executed(UserDataOk { user_data, count })),
                Err(e) => Err(UserDataError {
                    user_data,
                    error: Error::Sync(e),
                }),
            },
        }
    }

    pub fn write(
        &mut self,
        offset: u64,
        mem: &GuestMemoryMmap,
        addr: GuestAddress,
        count: u32,
        user_data: T,
    ) -> Result<FileEngineOk<T>, UserDataError<T, Error>> {
        match self {
            FileEngine::Async(engine) => {
                match engine.push_write(offset, mem, addr, count, user_data) {
                    Ok(_) => Ok(FileEngineOk::Submitted),
                    Err(e) => Err(UserDataError {
                        user_data: e.user_data,
                        error: Error::Async(e.error),
                    }),
                }
            }
            FileEngine::Sync(engine) => match engine.write(offset, mem, addr, count) {
                Ok(count) => Ok(FileEngineOk::Executed(UserDataOk { user_data, count })),
                Err(e) => Err(UserDataError {
                    user_data,
                    error: Error::Sync(e),
                }),
            },
        }
    }

    pub fn flush(&mut self, user_data: T) -> Result<FileEngineOk<T>, UserDataError<T, Error>> {
        match self {
            FileEngine::Async(engine) => match engine.push_flush(user_data) {
                Ok(_) => Ok(FileEngineOk::Submitted),
                Err(e) => Err(UserDataError {
                    user_data: e.user_data,
                    error: Error::Async(e.error),
                }),
            },
            FileEngine::Sync(engine) => match engine.flush() {
                Ok(_) => Ok(FileEngineOk::Executed(UserDataOk {
                    user_data,
                    count: 0,
                })),
                Err(e) => Err(UserDataError {
                    user_data,
                    error: Error::Sync(e),
                }),
            },
        }
    }

    pub fn drain(&mut self, flush: bool) -> Result<(), Error> {
        match self {
            FileEngine::Async(engine) => engine.drain(flush).map_err(Error::Async),
            FileEngine::Sync(engine) => {
                if !flush {
                    return Ok(());
                }
                engine.flush().map_err(Error::Sync)
            }
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::os::unix::ffi::OsStrExt;
    use std::os::unix::io::FromRawFd;

    use super::*;
    use utils::kernel_version::KernelVersion;
    use utils::tempfile::TempFile;
    use vm_memory::Bytes;

    const FILE_LEN: u32 = 1024;
    const MEM_LEN: usize = 4096;

    macro_rules! assert_err {
        ($expression:expr, $($pattern:tt)+) => {
            match $expression {
                Err(UserDataError {
                    user_data: _,
                    error: $($pattern)+,
                }) => (),
                ref e => {
                    println!("expected `{}` but got `{:?}`", stringify!($($pattern)+), e);
                    assert!(false)
                }
            }
        };
    }

    macro_rules! assert_sync_execution {
        ($expression:expr, $count:expr) => {
            if let Ok(FileEngineOk::Executed(UserDataOk {
                user_data: _,
                count,
            })) = $expression
            {
                assert_eq!(count, $count);
            } else {
                println!("expected Ok, received Err");
                assert!(false);
            }
        };
    }

    macro_rules! assert_queued {
        ($expression:expr) => {
            assert!(matches!($expression, Ok(FileEngineOk::Submitted)))
        };
    }

    fn assert_async_execution(engine: &mut FileEngine<()>, count: u32) {
        if let FileEngine::Async(ref mut engine) = engine {
            engine.kick_submission_queue_and_wait(1).unwrap();
            assert_eq!(engine.pop().unwrap().unwrap().result().unwrap(), count);
        }
    }

    fn create_mem() -> GuestMemoryMmap {
        vm_memory::test_utils::create_anon_guest_memory(&[(GuestAddress(0), MEM_LEN)], true)
            .unwrap()
    }

    #[test]
    fn test_sync() {
        // Check invalid file
        let mem = create_mem();
        let file = unsafe { File::from_raw_fd(-2) };
        let mut engine = FileEngine::from_file_sync(file).unwrap();
        let res = engine.read(0, &mem, GuestAddress(0), 0, ());
        assert_err!(res, Error::Sync(sync_io::Error::Seek(_e)));
        let res = engine.write(0, &mem, GuestAddress(0), 0, ());
        assert_err!(res, Error::Sync(sync_io::Error::Seek(_e)));
        let res = engine.flush(());
        assert_err!(res, Error::Sync(sync_io::Error::SyncAll(_e)));

        // Create backing file.
        let file = TempFile::new().unwrap().into_file();
        let mut engine = FileEngine::from_file_sync(file).unwrap();

        let data = utils::rand::rand_alphanumerics(FILE_LEN as usize)
            .as_bytes()
            .to_vec();

        // Partial write
        let partial_len = 50;
        let addr = GuestAddress(MEM_LEN as u64 - partial_len);
        mem.write(&data, addr).unwrap();
        assert_sync_execution!(
            engine.write(0, &mem, addr, FILE_LEN, ()),
            partial_len as u32
        );
        // Partial read
        let mem = create_mem();
        assert_sync_execution!(engine.read(0, &mem, addr, FILE_LEN, ()), partial_len as u32);
        // Check data
        let mut buf = vec![0u8; partial_len as usize];
        mem.read_slice(&mut buf, addr).unwrap();
        assert_eq!(buf, data[..partial_len as usize]);

        // Offset write
        let offset = 100;
        let partial_len = 50;
        let addr = GuestAddress(0);
        mem.write(&data, addr).unwrap();
        assert_sync_execution!(
            engine.write(offset, &mem, addr, partial_len, ()),
            partial_len as u32
        );
        // Offset read
        let mem = create_mem();
        assert_sync_execution!(
            engine.read(offset, &mem, addr, partial_len, ()),
            partial_len as u32
        );
        // Check data
        let mut buf = vec![0u8; partial_len as usize];
        mem.read_slice(&mut buf, addr).unwrap();
        assert_eq!(buf, data[..partial_len as usize]);

        // Full write
        mem.write(&data, GuestAddress(0)).unwrap();
        assert_sync_execution!(
            engine.write(0, &mem, GuestAddress(0), FILE_LEN, ()),
            FILE_LEN
        );
        // Full read
        let mem = create_mem();
        assert_sync_execution!(
            engine.read(0, &mem, GuestAddress(0), FILE_LEN, ()),
            FILE_LEN
        );
        // Check data
        let mut buf = vec![0u8; FILE_LEN as usize];
        mem.read_slice(&mut buf, GuestAddress(0)).unwrap();
        assert_eq!(buf, data.as_slice());

        // Check other ops
        assert!(engine.flush(()).is_ok());
        assert!(engine.drain(true).is_ok());
        assert!(engine.drain(false).is_ok());
    }

    #[test]
    fn test_async() {
        if KernelVersion::get().unwrap() < KernelVersion::new(5, 10, 0) {
            return;
        }

        // Check invalid file
        let file = unsafe { File::from_raw_fd(-2) };
        assert!(FileEngine::<()>::from_file_async(file).is_err());

        // Create backing file.
        let file = TempFile::new().unwrap().into_file();
        let mut engine = FileEngine::<()>::from_file_async(file).unwrap();

        let data = utils::rand::rand_alphanumerics(FILE_LEN as usize)
            .as_bytes()
            .to_vec();

        // Partial reads and writes cannot really be tested because io_uring will return an error
        // code for trying to write to unmapped memory.

        // Offset write
        let mem = create_mem();
        let offset = 100;
        let partial_len = 50;
        let addr = GuestAddress(0);
        mem.write(&data, addr).unwrap();
        assert_queued!(engine.write(offset, &mem, addr, partial_len, ()));
        assert_async_execution(&mut engine, partial_len as u32);
        // Offset read
        let mem = create_mem();
        assert_queued!(engine.read(offset, &mem, addr, partial_len, ()));
        assert_async_execution(&mut engine, partial_len as u32);
        // Check data
        let mut buf = vec![0u8; partial_len as usize];
        mem.read_slice(&mut buf, addr).unwrap();
        assert_eq!(buf, data[..partial_len as usize]);

        // Full write
        mem.write(&data, GuestAddress(0)).unwrap();
        assert_queued!(engine.write(0, &mem, addr, FILE_LEN, ()));
        assert_async_execution(&mut engine, FILE_LEN as u32);

        // Full read
        let mem = create_mem();
        assert_queued!(engine.read(0, &mem, addr, FILE_LEN, ()));
        assert_async_execution(&mut engine, FILE_LEN as u32);
        // Check data
        let mut buf = vec![0u8; FILE_LEN as usize];
        mem.read_slice(&mut buf, GuestAddress(0)).unwrap();
        assert_eq!(buf, data.as_slice());

        // Check other ops
        assert_queued!(engine.flush(()));
        assert_async_execution(&mut engine, 0);

        assert!(engine.drain(true).is_ok());
        assert!(engine.drain(false).is_ok());
    }
}
<|endoftext|>


<commit_before>use cgmath::*;
use crossbeam;
use rand;
use rand::Rng;
use utils::v3;
use ncollide as nc;
use ncollide::broad_phase::BroadPhase;
use ncollide::bounding_volume;
use nalgebra as na;

pub type V3 = Vector3<f32>;

#[derive(Clone)]
pub struct CubeState {
    pub pos: V3,
    pub rot: Basis3<f32>,
    pub spd: V3,
    pub color: V3,
    pub blink: bool,
    pub rot_axe: V3,
    pub rot_spd: f32,
}

#[derive(Clone)]
pub struct State {
    pub tick: f64,
    pub cubes: Vec<CubeState>,
    pub light_pos: V3,
}

const CUBE_SPAN : f32 = 200.;

fn fmin(a: (f32, f32), b: (f32, f32)) -> (f32, f32) {
    if a.0 < b.0 { a } else { b }
}

impl State {
    pub fn new(object_count: u32) -> Self {
        let mut rng = rand::XorShiftRng::new_unseeded();
        let mut cubes = Vec::with_capacity(object_count as usize);
        for _ in 0 .. object_count {
            let x = rng.next_f32();
            let y = rng.next_f32();
            let z = rng.next_f32();
            let cube_state = CubeState {
                pos: v3((x - 0.5) * CUBE_SPAN,
                             (y - 0.5) * CUBE_SPAN,
                             (z - 1.) * CUBE_SPAN),
                color: v3(x, y, z),
                blink: false,
                rot: <Basis3<f32> as Rotation<_>>::one(),
                spd: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5).normalize()*rng.next_f32()*1.,
                rot_axe: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5)
                              .normalize(),
                rot_spd: rng.next_f32()*30.0,
            };
            cubes.push(cube_state);
        };
        State {
            tick: 0.0,
            cubes: cubes,
            light_pos: v3(0., 0., 0.),
        }
    }

    fn update_one(cube: &CubeState, dt: f32) -> CubeState {
        let mut new_spd = cube.spd;
        let mut new_pos = cube.pos + cube.spd * dt;
        let (dist, sprj) = fmin(((new_pos.x - (-CUBE_SPAN/2.)).abs(), new_spd.x), fmin(((new_pos.x - CUBE_SPAN/2.).abs(), new_spd.x),
                    fmin(((new_pos.y - (-CUBE_SPAN/2.)).abs(), new_spd.y), fmin(((new_pos.y - (CUBE_SPAN/2.)).abs(), new_spd.y),
                    fmin(((new_pos.z - (-CUBE_SPAN)).abs(), new_spd.z), ((new_pos.z - 0.).abs(), new_spd.z))))));
        if new_pos.x < -CUBE_SPAN/2. {
            new_pos.x = -CUBE_SPAN/2.;
            new_spd.x = -new_spd.x;
        }
        if new_pos.x > CUBE_SPAN/2. {
            new_pos.x = CUBE_SPAN/2.;
            new_spd.x = -new_spd.x;
        }
        if new_pos.y < -CUBE_SPAN/2. {
            new_pos.y = -CUBE_SPAN/2.;
            new_spd.y = -new_spd.y;
        }
        if new_pos.y > CUBE_SPAN/2. {
            new_pos.y = CUBE_SPAN/2.;
            new_spd.y = -new_spd.y;
        }
        if new_pos.z < -CUBE_SPAN {
            new_pos.z = -CUBE_SPAN;
            new_spd.z = -new_spd.z;
        }
        if new_pos.z > 0. {
            new_pos.z = 0.;
            new_spd.z = -new_spd.z;
        }
        CubeState {
            pos: new_pos,
            spd: new_spd,
            rot: Basis3::from_axis_angle(cube.rot_axe, deg(cube.rot_spd * dt).into()).concat(&cube.rot),
            blink: dist.abs() < sprj.abs(),
            .. *cube
        }
    }

    pub fn update(&self, dt: f32, thread_cnt: u32) -> Self {
        if dt == 0.0 {
          return (*self).clone();
        }
        // advance position of cubes
        let mut cubes = Vec::with_capacity(self.cubes.len());
        if thread_cnt == 1 {
            for cube in &self.cubes[..] {
                cubes.push(State::update_one(cube, dt));
            }
        } else {
            // Uninitialized content of cubes will be immediately overwritten
            unsafe{ cubes.set_len(self.cubes.len()) };
            crossbeam::scope(|scope| {
                let chunk_len = (cubes.len()/thread_cnt as usize)+1;
                for (chunk, chunk_src) in cubes[..].chunks_mut(chunk_len).zip(self.cubes[..].chunks(chunk_len)) {
                    scope.spawn(move || {
                        for (c, cube) in chunk.iter_mut().zip(chunk_src) {
                            *c = State::update_one(cube, dt);
                        };
                    });
                };
            });
        }
        let mut broad_phase = nc::broad_phase::DBVTBroadPhase::new(10.0f32, true);
        for (i, c) in self.cubes.iter().enumerate() {
            let bv = bounding_volume::bounding_sphere(&nc::shape::Cuboid::new(na::Vector3::new(0.5, 0.5, 0.5)), &na::Vector3::new(c.pos.x, c.pos.y, c.pos.z));
            //println!("{:?}", bv);
            broad_phase.deferred_add(i, bv, i);
        }
        let mut ccount = 0;
        broad_phase.update(&mut |&i1, &i2| {(self.cubes[i1].pos - self.cubes[i2].pos).magnitude2() < 36.0}, &mut |&i1, &i2, c|{
            if c {
                let dp = (cubes[i2].pos - cubes[i1].pos).normalize();
                let ds = (cubes[i2].spd - cubes[i1].spd).normalize();
                let ci_spd = ds.dot(dp);
                if ci_spd < 0. {
                    // closing in
                    cubes[i1].spd += dp*(ci_spd/2.0);
                    cubes[i1].rot_spd /= 2.;
                    cubes[i1].blink = true;
                    cubes[i2].spd -= dp*(ci_spd/2.0);
                    cubes[i2].rot_spd /= 2.;
                    cubes[i2].blink = true;
                }
            }
            //ccount += 1;
        });
        //println!("Comparisons: {}", ccount);

        let (lx, ly) = f64::sin_cos(self.tick*0.05);
        let (lx, ly) = (lx as f32, ly as f32);
        let light_pos = v3(lx*50., ly*50., -0.);//v3(0., 0., -100.);
        State {
            tick: self.tick + dt as f64,
            cubes: cubes,
            light_pos: light_pos,
        }
    }
}

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};

fn state_update_agent(thread_cnt: u32, rx: Receiver<(Arc<State>, f32)>, tx: Sender<State>) {
    loop {
        if let Ok((state_ref, dt)) = rx.recv() {
            if let Err(_) = tx.send(state_ref.update(dt, thread_cnt)) {
                return ();
            }
        } else {
            return ();
        }
    }
}

pub struct StateUpdateAgent {
    tx: Sender<(Arc<State>, f32)>,
    rx: Receiver<State>,
}

impl StateUpdateAgent {
    pub fn new(thread_cnt: u32) -> Self {
        let (otx, orx) = channel();
        let (itx, irx) = channel();
        thread::spawn(move || state_update_agent(thread_cnt, orx, itx));
        StateUpdateAgent {
            tx: otx,
            rx: irx,
        }
    }

    pub fn start_update<'a>(&'a self, state: Arc<State>, dt: f32) -> UpdateResult<'a> {
        self.tx.send((state, dt)).unwrap();
        UpdateResult{
            agent: self,
            result_aquired: false,
        }
    }
}

pub struct UpdateResult<'a> {
    agent: &'a StateUpdateAgent,
    result_aquired: bool,
}

impl<'a> UpdateResult<'a> {
    pub fn get(&mut self) -> State {
        self.result_aquired = true;
        self.agent.rx.recv().unwrap()
    }
}

impl<'a> Drop for UpdateResult<'a> {
    fn drop(&mut self) {
        if !self.result_aquired {
            error!("Update result is not retrieved before drop");
        }
    }
}
<commit_msg>bugfix, fooling around<commit_after>use cgmath::*;
use crossbeam;
use rand;
use rand::Rng;
use utils::v3;
use ncollide as nc;
use ncollide::broad_phase::BroadPhase;
use ncollide::bounding_volume;
use nalgebra as na;

pub type V3 = Vector3<f32>;

#[derive(Clone)]
pub struct CubeState {
    pub pos: V3,
    pub rot: Basis3<f32>,
    pub spd: V3,
    pub color: V3,
    pub blink: bool,
    pub rot_axe: V3,
    pub rot_spd: f32,
}

#[derive(Clone)]
pub struct State {
    pub tick: f64,
    pub cubes: Vec<CubeState>,
    pub light_pos: V3,
}

const CUBE_SPAN : f32 = 200.;

fn fmin(a: (f32, f32), b: (f32, f32)) -> (f32, f32) {
    if a.0 < b.0 { a } else { b }
}

impl State {
    pub fn new(object_count: u32) -> Self {
        let mut rng = rand::XorShiftRng::new_unseeded();
        let mut cubes = Vec::with_capacity(object_count as usize);
        for _ in 0 .. object_count {
            let x = rng.next_f32();
            let y = rng.next_f32();
            let z = rng.next_f32();
            let cube_state = CubeState {
                pos: v3((x - 0.5) * CUBE_SPAN,
                             (y - 0.5) * CUBE_SPAN,
                             (z - 1.) * CUBE_SPAN),
                color: v3(x, y, z),
                blink: false,
                rot: <Basis3<f32> as Rotation<_>>::one(),
                spd: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5).normalize()*rng.next_f32()*1.,
                rot_axe: v3(rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5,
                             rng.next_f32() - 0.5)
                              .normalize(),
                rot_spd: rng.next_f32()*30.0,
            };
            cubes.push(cube_state);
        };
        State {
            tick: 0.0,
            cubes: cubes,
            light_pos: v3(0., 0., 0.),
        }
    }

    fn update_one(cube: &CubeState, dt: f32) -> CubeState {
        let a = v3(0., -9.8, 0.);
        let mut new_spd = cube.spd + a*dt;
        let dp = cube.spd * dt + a * dt * dt / 2.;
        let mut new_pos = cube.pos + dp;
        let mut bounce = false;
        let (dist, sprj) = fmin(((new_pos.x - (-CUBE_SPAN/2.)).abs(), new_spd.x), fmin(((new_pos.x - CUBE_SPAN/2.).abs(), new_spd.x),
                    fmin(((new_pos.y - (-CUBE_SPAN/2.)).abs(), new_spd.y), fmin(((new_pos.y - (CUBE_SPAN/2.)).abs(), new_spd.y),
                    fmin(((new_pos.z - (-CUBE_SPAN)).abs(), new_spd.z), ((new_pos.z - 0.).abs(), new_spd.z))))));
        if new_pos.x < -CUBE_SPAN/2. {
            new_pos.x = -CUBE_SPAN - new_pos.x;
            new_spd.x = -new_spd.x;
            bounce = true;
        }
        if new_pos.x > CUBE_SPAN/2. {
            new_pos.x = CUBE_SPAN - new_pos.x;
            new_spd.x = -new_spd.x;
            bounce = true;
        }
        if new_pos.y < -CUBE_SPAN/2. {
            new_pos.y = -CUBE_SPAN - new_pos.y;
            new_spd.y = -new_spd.y;
            bounce = true;
        }
        if new_pos.y > CUBE_SPAN/2. {
            new_pos.y = CUBE_SPAN - new_pos.y;
            new_spd.y = -new_spd.y;
            bounce = true;
        }
        if new_pos.z < -CUBE_SPAN {
            new_pos.z = -CUBE_SPAN*2. - new_pos.z;
            new_spd.z = -new_spd.z;
            bounce = true;
        }
        if new_pos.z > 0. {
            new_pos.z = -new_pos.z;
            new_spd.z = -new_spd.z;
            bounce = true;
        }
        if bounce { new_spd *= 0.95 };
        if bounce && new_spd.magnitude2() < 0.8 {
            new_spd = v3(0., 0., 0.);
        }
        CubeState {
            pos: new_pos,
            spd: new_spd,
            rot: Basis3::from_axis_angle(cube.rot_axe, deg(cube.rot_spd * dt).into()).concat(&cube.rot),
            blink: dist.abs() < sprj.abs()/4. && new_spd.magnitude2()>4.,
            .. *cube
        }
    }

    pub fn update(&self, dt: f32, thread_cnt: u32) -> Self {
        if dt == 0.0 {
          return (*self).clone();
        }
        // advance position of cubes
        let mut cubes = Vec::with_capacity(self.cubes.len());
        if thread_cnt == 1 {
            for cube in &self.cubes[..] {
                cubes.push(State::update_one(cube, dt));
            }
        } else {
            // Uninitialized content of cubes will be immediately overwritten
            unsafe{ cubes.set_len(self.cubes.len()) };
            crossbeam::scope(|scope| {
                let chunk_len = (cubes.len()/thread_cnt as usize)+1;
                for (chunk, chunk_src) in cubes[..].chunks_mut(chunk_len).zip(self.cubes[..].chunks(chunk_len)) {
                    scope.spawn(move || {
                        for (c, cube) in chunk.iter_mut().zip(chunk_src) {
                            *c = State::update_one(cube, dt);
                        };
                    });
                };
            });
        }
        let mut broad_phase = nc::broad_phase::DBVTBroadPhase::new(0.1f32, true);
        for (i, c) in self.cubes.iter().enumerate() {
            let bv = bounding_volume::bounding_sphere(&nc::shape::Cuboid::new(na::Vector3::new(0.5, 0.5, 0.5)), &na::Vector3::new(c.pos.x, c.pos.y, c.pos.z));
            //println!("{:?}", bv);
            broad_phase.deferred_add(i, bv, i);
        }
        let mut ccount = 0;
        broad_phase.update(&mut |&i1, &i2| {(self.cubes[i1].pos - self.cubes[i2].pos).magnitude2() < 4.0}, &mut |&i1, &i2, c|{
            if c {
                let dp = (cubes[i2].pos - cubes[i1].pos).normalize();
                let ds = (cubes[i2].spd - cubes[i1].spd);
                let ci_spd = ds.dot(dp);
                if ci_spd < 0. {
                    // closing in
                    cubes[i1].spd += dp*(ci_spd/2.01);
                    cubes[i1].rot_spd /= 2.;
                    cubes[i2].spd -= dp*(ci_spd/2.01);
                    cubes[i2].rot_spd /= 2.;
                }
            }
            //ccount += 1;
        });
        //println!("Comparisons: {}", ccount);

        let (lx, ly) = f64::sin_cos(self.tick*0.05);
        let (lx, ly) = (lx as f32, ly as f32);
        let light_pos = v3(lx*50., ly*50., -0.);//v3(0., 0., -100.);
        State {
            tick: self.tick + dt as f64,
            cubes: cubes,
            light_pos: light_pos,
        }
    }
}

use std::thread;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};

fn state_update_agent(thread_cnt: u32, rx: Receiver<(Arc<State>, f32)>, tx: Sender<State>) {
    loop {
        if let Ok((state_ref, dt)) = rx.recv() {
            if let Err(_) = tx.send(state_ref.update(dt, thread_cnt)) {
                return ();
            }
        } else {
            return ();
        }
    }
}

pub struct StateUpdateAgent {
    tx: Sender<(Arc<State>, f32)>,
    rx: Receiver<State>,
}

impl StateUpdateAgent {
    pub fn new(thread_cnt: u32) -> Self {
        let (otx, orx) = channel();
        let (itx, irx) = channel();
        thread::spawn(move || state_update_agent(thread_cnt, orx, itx));
        StateUpdateAgent {
            tx: otx,
            rx: irx,
        }
    }

    pub fn start_update<'a>(&'a self, state: Arc<State>, dt: f32) -> UpdateResult<'a> {
        self.tx.send((state, dt)).unwrap();
        UpdateResult{
            agent: self,
            result_aquired: false,
        }
    }
}

pub struct UpdateResult<'a> {
    agent: &'a StateUpdateAgent,
    result_aquired: bool,
}

impl<'a> UpdateResult<'a> {
    pub fn get(&mut self) -> State {
        self.result_aquired = true;
        self.agent.rx.recv().unwrap()
    }
}

impl<'a> Drop for UpdateResult<'a> {
    fn drop(&mut self) {
        if !self.result_aquired {
            error!("Update result is not retrieved before drop");
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! CSS Multi-column layout http://dev.w3.org/csswg/css-multicol/

#![deny(unsafe_code)]

use app_units::Au;
use block::BlockFlow;
use context::LayoutContext;
use euclid::{Point2D, Rect};
use floats::FloatKind;
use flow::{Flow, FlowClass, OpaqueFlow, mut_base};
use fragment::{Fragment, FragmentBorderBoxIterator};
use std::fmt;
use std::sync::Arc;
use style::properties::ComputedValues;
use util::logical_geometry::LogicalSize;
use util::print_tree::PrintTree;

pub struct MulticolFlow {
    pub block_flow: BlockFlow,
}

impl MulticolFlow {
    pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
        MulticolFlow {
            block_flow: BlockFlow::from_fragment(fragment, float_kind)
        }
    }
}

impl Flow for MulticolFlow {
    fn class(&self) -> FlowClass {
        FlowClass::Multicol
    }

    fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
        self
    }

    fn as_mut_block(&mut self) -> &mut BlockFlow {
        &mut self.block_flow
    }

    fn as_block(&self) -> &BlockFlow {
        &self.block_flow
    }

    fn bubble_inline_sizes(&mut self) {
        // FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic
        self.block_flow.bubble_inline_sizes();
    }

    fn assign_inline_sizes(&mut self, ctx: &LayoutContext) {
        debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol");
        self.block_flow.assign_inline_sizes(ctx);
    }

    fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
        debug!("assign_block_size: assigning block_size for multicol");
        let available_block_size = Au(0);
        for child in mut_base(self).children.iter_mut() {
            child.fragment(ctx, available_block_size);
        }
        self.block_flow.assign_block_size(ctx);
    }

    fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
        self.block_flow.compute_absolute_position(layout_context)
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
        self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
    }

    fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
        self.block_flow.update_late_computed_block_position_if_necessary(block_position)
    }

    fn build_display_list(&mut self, layout_context: &LayoutContext) {
        debug!("build_display_list_multicol: same process as block flow");
        self.block_flow.build_display_list(layout_context)
    }

    fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
        self.block_flow.repair_style(new_style)
    }

    fn compute_overflow(&self) -> Rect<Au> {
        self.block_flow.compute_overflow()
    }

    fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
        self.block_flow.generated_containing_block_size(flow)
    }

    fn iterate_through_fragment_border_boxes(&self,
                                             iterator: &mut FragmentBorderBoxIterator,
                                             level: i32,
                                             stacking_context_position: &Point2D<Au>) {
        self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position)
    }

    fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
        self.block_flow.mutate_fragments(mutator)
    }

    fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
        self.block_flow.print_extra_flow_children(print_tree);
    }
}

impl fmt::Debug for MulticolFlow {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MulticolFlow: {:?}", self.block_flow)
    }
}
<commit_msg>Multicol: compute the width of columns based on `column-width` and `column-count`.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! CSS Multi-column layout http://dev.w3.org/csswg/css-multicol/

#![deny(unsafe_code)]

use app_units::Au;
use block::BlockFlow;
use context::LayoutContext;
use euclid::{Point2D, Rect};
use floats::FloatKind;
use flow::{Flow, FlowClass, OpaqueFlow, mut_base};
use fragment::{Fragment, FragmentBorderBoxIterator};
use std::cmp::{min, max};
use std::fmt;
use std::sync::Arc;
use style::properties::ComputedValues;
use util::logical_geometry::LogicalSize;
use util::print_tree::PrintTree;

pub struct MulticolFlow {
    pub block_flow: BlockFlow,
}

impl MulticolFlow {
    pub fn from_fragment(fragment: Fragment, float_kind: Option<FloatKind>) -> MulticolFlow {
        MulticolFlow {
            block_flow: BlockFlow::from_fragment(fragment, float_kind)
        }
    }
}

impl Flow for MulticolFlow {
    fn class(&self) -> FlowClass {
        FlowClass::Multicol
    }

    fn as_mut_multicol(&mut self) -> &mut MulticolFlow {
        self
    }

    fn as_mut_block(&mut self) -> &mut BlockFlow {
        &mut self.block_flow
    }

    fn as_block(&self) -> &BlockFlow {
        &self.block_flow
    }

    fn bubble_inline_sizes(&mut self) {
        // FIXME(SimonSapin) http://dev.w3.org/csswg/css-sizing/#multicol-intrinsic
        self.block_flow.bubble_inline_sizes();
    }

    fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
        debug!("assign_inline_sizes({}): assigning inline_size for flow", "multicol");
        self.block_flow.compute_inline_sizes(layout_context);

        // Move in from the inline-start border edge.
        let inline_start_content_edge = self.block_flow.fragment.border_box.start.i +
            self.block_flow.fragment.border_padding.inline_start;

        // Distance from the inline-end margin edge to the inline-end content edge.
        let inline_end_content_edge =
            self.block_flow.fragment.margin.inline_end +
            self.block_flow.fragment.border_padding.inline_end;

        self.block_flow.assign_inline_sizes(layout_context);
        let padding_and_borders = self.block_flow.fragment.border_padding.inline_start_end();
        let content_inline_size =
            self.block_flow.fragment.border_box.size.inline - padding_and_borders;
        let column_width;
        {
            let column_style = self.block_flow.fragment.style.get_column();

            // `None` is 'normal': "UA-specified length. A value of 1em is suggested."
            let column_gap = column_style.column_gap.0.unwrap_or_else(||
                self.block_flow.fragment.style.get_font().font_size);
            let mut column_count;
            if let Some(column_width) = column_style.column_width.0 {
                column_count =
                    max(1, (content_inline_size + column_gap).0 / (column_width + column_gap).0);
                if let Some(specified_column_count) = column_style.column_count.0 {
                    column_count = min(column_count, specified_column_count as i32);
                }
            } else {
                column_count = column_style.column_count.0.unwrap() as i32;
            }
            column_width =
                max(Au(0), (content_inline_size + column_gap) / column_count - column_gap);
        }

        self.block_flow.fragment.border_box.size.inline = content_inline_size + padding_and_borders;

        self.block_flow.propagate_assigned_inline_size_to_children(
            layout_context, inline_start_content_edge, inline_end_content_edge, column_width,
            |_, _, _, _, _, _| {});
    }

    fn assign_block_size<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
        debug!("assign_block_size: assigning block_size for multicol");
        let available_block_size = Au(0);
        for child in mut_base(self).children.iter_mut() {
            child.fragment(ctx, available_block_size);
        }
        self.block_flow.assign_block_size(ctx);
    }

    fn compute_absolute_position(&mut self, layout_context: &LayoutContext) {
        self.block_flow.compute_absolute_position(layout_context)
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
        self.block_flow.update_late_computed_inline_position_if_necessary(inline_position)
    }

    fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
        self.block_flow.update_late_computed_block_position_if_necessary(block_position)
    }

    fn build_display_list(&mut self, layout_context: &LayoutContext) {
        debug!("build_display_list_multicol: same process as block flow");
        self.block_flow.build_display_list(layout_context)
    }

    fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
        self.block_flow.repair_style(new_style)
    }

    fn compute_overflow(&self) -> Rect<Au> {
        self.block_flow.compute_overflow()
    }

    fn generated_containing_block_size(&self, flow: OpaqueFlow) -> LogicalSize<Au> {
        self.block_flow.generated_containing_block_size(flow)
    }

    fn iterate_through_fragment_border_boxes(&self,
                                             iterator: &mut FragmentBorderBoxIterator,
                                             level: i32,
                                             stacking_context_position: &Point2D<Au>) {
        self.block_flow.iterate_through_fragment_border_boxes(iterator, level, stacking_context_position)
    }

    fn mutate_fragments(&mut self, mutator: &mut FnMut(&mut Fragment)) {
        self.block_flow.mutate_fragments(mutator)
    }

    fn print_extra_flow_children(&self, print_tree: &mut PrintTree) {
        self.block_flow.print_extra_flow_children(print_tree);
    }
}

impl fmt::Debug for MulticolFlow {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MulticolFlow: {:?}", self.block_flow)
    }
}
<|endoftext|>