<commit_before>use map::{RoomId, Map};
use combat::CombatState;
use inventory::Inventory;
use direction::Direction;
use items::Item::*;
use platform;

const PAUSE_MS: u64 = 2500;
const CHEATING_FOOD_DIVISOR: i32 = 4;
const INITIAL_STRENGTH: i32 = 100;
const INITIAL_WEALTH: i32 = 75;
const STRENGTH_PER_FOOD: i32 = 10;
const TALLY_PER_MOVE: i32 = 1;
const STRENGTH_LOSS_PER_MOVE: i32 = 5;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum GameMode {
  AskName,
  Primary,
  Inventory,
  EatFood,
  Combat(CombatState),
  Finished,

  #[cfg(debug_assertions)]
  Debug,
}

// We have our input callbacks have a GameState explicitly passed
// into them instead of expecting 'self' to be captured into their
// closure, because the latter leads to all kinds of lifetime
// headaches.
//
// Note also that this doesn't need to be a FnOnce, since it can
// request that it be called again, e.g. if the user input was invalid.
type InputCallback = Fn(&mut GameState, String);

pub struct GameState {
  pub map: Map,
  pub curr_mode: GameMode,
  pub shown_hint: bool,
  pub player_name: String,
  pub strength: i32,
  pub wealth: i32,
  pub tally: i32,
  pub monsters_killed: i32,
  pub curr_room: RoomId,
  pub show_desc: bool,
  pub items: Inventory,
  input_callback: Option<Box<InputCallback>>,
  is_processing_input: bool,
  last_input_prompt: String,
  read_input_again: bool,
}

impl GameState {
  pub fn new() -> Self {
    Self {
      map: Map::new(),
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: RoomId::Entrance,
      shown_hint: false,
      strength: INITIAL_STRENGTH,
      wealth: INITIAL_WEALTH,
      tally: 0,
      monsters_killed: 0,
      show_desc: true,
      items: Inventory::new(),
      input_callback: None,
      is_processing_input: false,
      last_input_prompt: String::from(""),
      read_input_again: false,
    }
  }

  pub fn show_prompt(&mut self, prompt: &str) {
    self.last_input_prompt = String::from(prompt);
    platform::show_prompt(prompt);
  }

  pub fn ask_again(&mut self) {
    assert_eq!(self.is_processing_input, true,
               "This method must be called from an input callback");
    self.read_input_again = true;
  }

  pub fn ask_i32<F>(&mut self, question: &str, cb: F)
      where F: 'static + Fn(&mut GameState, i32) {
    self.show_prompt(question);
    self.read_input(move |state, input| {
      match input.parse::<i32>() {
        Ok(amount) => {
          cb(state, amount);
        },
        Err(_) => {
          println!("That does not even look like a number, {}.",
                   state.player_name);
          state.ask_again();
        }
      }
    });
  }

  pub fn ask<F>(&mut self, question: &str, cb: F)
      where F: 'static + Fn(&mut GameState, String) {
    self.show_prompt(question);
    self.read_input(cb);
  }

  pub fn read_input<F>(&mut self, cb: F)
      where F: 'static + Fn(&mut GameState, String) {
    assert!(self.input_callback.is_none(),
            "Program must not already be waiting for input");
    self.read_input_again = false;
    self.input_callback = Some(Box::new(cb));
  }

  pub fn can_player_see(&self) -> bool {
    self.curr_room == RoomId::Entrance || self.items.owns(Torch)
  }

  pub fn set_mode(&mut self, mode: GameMode) {
    self.show_desc = true;
    self.curr_mode = mode;
  }

  pub fn print_wealth(&self) {
    print!("You have ");
    if self.wealth > 0 {
      println!("${}.", self.wealth);
    } else {
      println!("no money.");
    }
  }

  pub fn print_food(&self) {
    let food = self.items.get_quantity(Food);
    println!("Your provisions sack holds {} unit{} of food.",
             food, if food == 1 { "" } else { "s" });
  }

  fn get_score(&self) -> i32 {
    3  * self.tally +
    5  * self.strength +
    2  * self.wealth +
    1  * self.items.get_quantity(Food) +
    30 * self.monsters_killed
  }

  pub fn finish_game(&mut self) {
    self.curr_mode = GameMode::Finished;
    println!("Your score is {}.\n", self.get_score());
    println!("Farewell.");
  }

  pub fn is_finished(&self) -> bool {
    self.curr_mode == GameMode::Finished
  }

  pub fn accuse_player_of_cheating(&mut self) {
    println!("YOU HAVE TRIED TO CHEAT ME!");
    self.wealth = 0;
    self.items.lose(Torch);
    self.items.lose(Axe);
    self.items.lose(Sword);

    let food = self.items.get_quantity(Food);
    self.items.set_quantity(Food, food / CHEATING_FOOD_DIVISOR);

    self.items.lose(Amulet);
    self.items.lose(Armor);
    Self::pause();
  }

  pub fn pause() {
    platform::sleep(PAUSE_MS);
  }

  fn die(&mut self) {
    println!("You have died.........");
    Self::pause();
    self.finish_game();
  }

  fn tick_ask_name_mode(&mut self) {
    self.ask("What is your name, explorer? ", |state, input| {
      if input.len() == 0 {
        println!("Pardon me?");
      } else {
        state.player_name = input;
        state.set_mode(GameMode::Primary);
      }
    });
  }

  fn tick_eat_food_mode(&mut self) {
    if self.show_desc {
      self.print_food();
      println!("");
      self.show_desc = false;
    }

    self.ask_i32("How many do you want to eat? ", |state, amount| {
      if amount < 0 {
        println!("GIVE ME A POSITIVE INTEGER.");
        state.ask_again();
      } else if amount == 0 {
        println!("Fine, be that way.");
        Self::pause();
        state.set_mode(GameMode::Primary);
      } else if amount > state.items.get_quantity(Food) {
        state.accuse_player_of_cheating();
        state.set_mode(GameMode::Primary);
      } else {
        println!("After some munching, you feel stronger.");
        state.items.decrease(Food, amount);
        state.strength += amount * STRENGTH_PER_FOOD;
        state.set_mode(GameMode::Primary);
        Self::pause();
      }
    });
  }

  pub fn process_move(&mut self) {
    self.tally += TALLY_PER_MOVE;
    self.strength -= STRENGTH_LOSS_PER_MOVE;
  }

  pub fn try_to_move(&mut self, dir: Direction) -> bool {
    if let Some(room) = self.map.room(self.curr_room).get_exit(dir) {
      self.curr_room = room;
      self.show_desc = true;
      self.process_move();
      true
    } else {
      println!("You can't go that way.");
      false
    }
  }

  pub fn tick(&mut self) {
    let mut input_processed = false;
    let mut input_cb: Option<Box<InputCallback>> = None;

    ::std::mem::swap(&mut input_cb, &mut self.input_callback);

    if let Some(ref cb) = input_cb {
      self.is_processing_input = true;
      match platform::read_input() {
        Some(input) => {
          platform::hide_prompt();
          cb(self, input);
          // Note that at this point, self.input_callback may be
          // set again, if the callback asked for input again.
        },
        None => {
          // We're probably running in the browser and there's currently
          // no input to process.
        }
      }
      self.is_processing_input = false;
      input_processed = true;
    }

    if input_processed {
      if self.read_input_again {
        assert!(self.input_callback.is_none(),
                "Program cannot ask to re-run last input callback \
                 *and* run a new input callback simultaneously");
        self.read_input_again = false;
        self.input_callback = input_cb;
        platform::show_prompt(self.last_input_prompt.as_str());
      }

      return;
    }

    if self.strength < 1 { self.die() }

    match self.curr_mode {
      GameMode::AskName => { self.tick_ask_name_mode() },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Inventory => { self.tick_inventory_mode() },
      GameMode::EatFood => { self.tick_eat_food_mode() },
      GameMode::Combat(state) => { self.tick_combat_mode(&state) },
      GameMode::Finished => {},

      #[cfg(debug_assertions)]
      GameMode::Debug => { self.tick_debug_mode() },
    }
  }
}
<commit_msg>Fix browser input bug.<commit_after>use map::{RoomId, Map};
use combat::CombatState;
use inventory::Inventory;
use direction::Direction;
use items::Item::*;
use platform;

const PAUSE_MS: u64 = 2500;
const CHEATING_FOOD_DIVISOR: i32 = 4;
const INITIAL_STRENGTH: i32 = 100;
const INITIAL_WEALTH: i32 = 75;
const STRENGTH_PER_FOOD: i32 = 10;
const TALLY_PER_MOVE: i32 = 1;
const STRENGTH_LOSS_PER_MOVE: i32 = 5;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum GameMode {
  AskName,
  Primary,
  Inventory,
  EatFood,
  Combat(CombatState),
  Finished,

  #[cfg(debug_assertions)]
  Debug,
}

// We have our input callbacks have a GameState explicitly passed
// into them instead of expecting 'self' to be captured into their
// closure, because the latter leads to all kinds of lifetime
// headaches.
//
// Note also that this doesn't need to be a FnOnce, since it can
// request that it be called again, e.g. if the user input was invalid.
type InputCallback = Fn(&mut GameState, String);

pub struct GameState {
  pub map: Map,
  pub curr_mode: GameMode,
  pub shown_hint: bool,
  pub player_name: String,
  pub strength: i32,
  pub wealth: i32,
  pub tally: i32,
  pub monsters_killed: i32,
  pub curr_room: RoomId,
  pub show_desc: bool,
  pub items: Inventory,
  input_callback: Option<Box<InputCallback>>,
  is_processing_input: bool,
  last_input_prompt: String,
  read_input_again: bool,
}

impl GameState {
  pub fn new() -> Self {
    Self {
      map: Map::new(),
      player_name: String::from(""),
      curr_mode: GameMode::AskName,
      curr_room: RoomId::Entrance,
      shown_hint: false,
      strength: INITIAL_STRENGTH,
      wealth: INITIAL_WEALTH,
      tally: 0,
      monsters_killed: 0,
      show_desc: true,
      items: Inventory::new(),
      input_callback: None,
      is_processing_input: false,
      last_input_prompt: String::from(""),
      read_input_again: false,
    }
  }

  pub fn show_prompt(&mut self, prompt: &str) {
    self.last_input_prompt = String::from(prompt);
    platform::show_prompt(prompt);
  }

  pub fn ask_again(&mut self) {
    assert_eq!(self.is_processing_input, true,
               "This method must be called from an input callback");
    self.read_input_again = true;
  }

  pub fn ask_i32<F>(&mut self, question: &str, cb: F)
      where F: 'static + Fn(&mut GameState, i32) {
    self.show_prompt(question);
    self.read_input(move |state, input| {
      match input.parse::<i32>() {
        Ok(amount) => {
          cb(state, amount);
        },
        Err(_) => {
          println!("That does not even look like a number, {}.",
                   state.player_name);
          state.ask_again();
        }
      }
    });
  }

  pub fn ask<F>(&mut self, question: &str, cb: F)
      where F: 'static + Fn(&mut GameState, String) {
    self.show_prompt(question);
    self.read_input(cb);
  }

  pub fn read_input<F>(&mut self, cb: F)
      where F: 'static + Fn(&mut GameState, String) {
    assert!(self.input_callback.is_none(),
            "Program must not already be waiting for input");
    self.read_input_again = false;
    self.input_callback = Some(Box::new(cb));
  }

  pub fn can_player_see(&self) -> bool {
    self.curr_room == RoomId::Entrance || self.items.owns(Torch)
  }

  pub fn set_mode(&mut self, mode: GameMode) {
    self.show_desc = true;
    self.curr_mode = mode;
  }

  pub fn print_wealth(&self) {
    print!("You have ");
    if self.wealth > 0 {
      println!("${}.", self.wealth);
    } else {
      println!("no money.");
    }
  }

  pub fn print_food(&self) {
    let food = self.items.get_quantity(Food);
    println!("Your provisions sack holds {} unit{} of food.",
             food, if food == 1 { "" } else { "s" });
  }

  fn get_score(&self) -> i32 {
    3  * self.tally +
    5  * self.strength +
    2  * self.wealth +
    1  * self.items.get_quantity(Food) +
    30 * self.monsters_killed
  }

  pub fn finish_game(&mut self) {
    self.curr_mode = GameMode::Finished;
    println!("Your score is {}.\n", self.get_score());
    println!("Farewell.");
  }

  pub fn is_finished(&self) -> bool {
    self.curr_mode == GameMode::Finished
  }

  pub fn accuse_player_of_cheating(&mut self) {
    println!("YOU HAVE TRIED TO CHEAT ME!");
    self.wealth = 0;
    self.items.lose(Torch);
    self.items.lose(Axe);
    self.items.lose(Sword);

    let food = self.items.get_quantity(Food);
    self.items.set_quantity(Food, food / CHEATING_FOOD_DIVISOR);

    self.items.lose(Amulet);
    self.items.lose(Armor);
    Self::pause();
  }

  pub fn pause() {
    platform::sleep(PAUSE_MS);
  }

  fn die(&mut self) {
    println!("You have died.........");
    Self::pause();
    self.finish_game();
  }

  fn tick_ask_name_mode(&mut self) {
    self.ask("What is your name, explorer? ", |state, input| {
      if input.len() == 0 {
        println!("Pardon me?");
      } else {
        state.player_name = input;
        state.set_mode(GameMode::Primary);
      }
    });
  }

  fn tick_eat_food_mode(&mut self) {
    if self.show_desc {
      self.print_food();
      println!("");
      self.show_desc = false;
    }

    self.ask_i32("How many do you want to eat? ", |state, amount| {
      if amount < 0 {
        println!("GIVE ME A POSITIVE INTEGER.");
        state.ask_again();
      } else if amount == 0 {
        println!("Fine, be that way.");
        Self::pause();
        state.set_mode(GameMode::Primary);
      } else if amount > state.items.get_quantity(Food) {
        state.accuse_player_of_cheating();
        state.set_mode(GameMode::Primary);
      } else {
        println!("After some munching, you feel stronger.");
        state.items.decrease(Food, amount);
        state.strength += amount * STRENGTH_PER_FOOD;
        state.set_mode(GameMode::Primary);
        Self::pause();
      }
    });
  }

  pub fn process_move(&mut self) {
    self.tally += TALLY_PER_MOVE;
    self.strength -= STRENGTH_LOSS_PER_MOVE;
  }

  pub fn try_to_move(&mut self, dir: Direction) -> bool {
    if let Some(room) = self.map.room(self.curr_room).get_exit(dir) {
      self.curr_room = room;
      self.show_desc = true;
      self.process_move();
      true
    } else {
      println!("You can't go that way.");
      false
    }
  }

  pub fn tick(&mut self) {
    let mut input_processed = false;
    let mut input_cb: Option<Box<InputCallback>> = None;

    ::std::mem::swap(&mut input_cb, &mut self.input_callback);

    if let Some(ref cb) = input_cb {
      self.is_processing_input = true;
      match platform::read_input() {
        Some(input) => {
          platform::hide_prompt();
          cb(self, input);
          // Note that at this point, self.input_callback may be
          // set again, if the callback asked for input again.
        },
        None => {
          // We're probably running in the browser and there's currently
          // no input to process. Re-schedule the input callback to be
          // called next tick.
          self.ask_again();
        }
      }
      self.is_processing_input = false;
      input_processed = true;
    }

    if input_processed {
      if self.read_input_again {
        assert!(self.input_callback.is_none(),
                "Program cannot ask to re-run last input callback \
                 *and* run a new input callback simultaneously");
        self.read_input_again = false;
        self.input_callback = input_cb;
        platform::show_prompt(self.last_input_prompt.as_str());
      }

      return;
    }

    if self.strength < 1 { self.die() }

    match self.curr_mode {
      GameMode::AskName => { self.tick_ask_name_mode() },
      GameMode::Primary => { self.tick_primary_mode() },
      GameMode::Inventory => { self.tick_inventory_mode() },
      GameMode::EatFood => { self.tick_eat_food_mode() },
      GameMode::Combat(state) => { self.tick_combat_mode(&state) },
      GameMode::Finished => {},

      #[cfg(debug_assertions)]
      GameMode::Debug => { self.tick_debug_mode() },
    }
  }
}
<|endoftext|>


<commit_before>extern crate rand;

const ROWS: i8 = 9;
const COLS: i8 = 12;
const TILES: i8 = 108;
const PLAYERS: i8 = 4;

#[derive(RustcDecodable, RustcEncodable)]
pub struct Game {
    players: Vec<Player>,
    board: Board,
    turn: PlayerId,
    merge_decision: Option<PlayerId>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Player {
    id: PlayerId,
    money: i32,
    shares: PlayerShares,
    tiles: Vec<Tile>
}

type PlayerId = i8;

#[derive(RustcDecodable, RustcEncodable)]
struct PlayerShares {
    luxor: i8,
    tower: i8,
    american: i8,
    festival: i8,
    worldwide: i8,
    continental: i8,
    imperial: i8
}

#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct Tile { row: i8, col: i8 }

#[derive(RustcDecodable, RustcEncodable)]
struct Board {
    slots: Vec<Slot>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Slot {
    row: i8,
    col: i8,
    has_tile: bool,
    hotel: Option<Hotel>
}

enum Action {
    PlaceTile,
    HandleMergeStocks,
    BuyStocks,
    DrawTile,
    EndGame
}

pub struct PlaceTile {
    player: PlayerId,
    tile: Tile
}

pub struct HandleMergeStocks {
    hold: i8,
    sell: i8,
    trade: i8
}

type BuyStocks = PlayerShares;

#[derive(RustcDecodable, RustcEncodable)]
enum Hotel { Luxor, Tower, American, Festival, Worldwide, Continental, Imperial }

fn empty_shares() -> PlayerShares {
    PlayerShares { luxor: 0, tower: 0, american: 0, festival: 0, worldwide: 0, continental: 0, imperial: 0 }
}

fn new_player(id: PlayerId, tiles: Vec<Tile>) -> Player {
    Player { id: id, money: 6000, shares: empty_shares(), tiles: tiles }
}

fn choose_tiles(tiles: Vec<Tile>, count: usize) -> Vec<Tile> {
    let mut remaining_tiles = tiles;
    let mut random_tiles = Vec::new();
    for _ in 0..count {
        let random_index = (rand::random::<i8>() as usize) % remaining_tiles.len();
        random_tiles.push(remaining_tiles.remove(random_index));
    }
    random_tiles
}

pub fn initial_state() -> Game {
    let slots: Vec<Slot> = (0..COLS)
        .flat_map(|col| {
            let rows = 0..ROWS;
            let res: Vec<Slot> = rows.map(|row| Slot { row: row, col: col, hotel: None, has_tile: false } ).collect();
            res
        }).collect();
    let tiles = (0..TILES).map(|i| Tile { row: i / COLS, col: i % ROWS }).collect();
    let mut chosen_tiles = choose_tiles(tiles, 6*PLAYERS as usize);
    let tiles1 = chosen_tiles.split_off(18);
    let tiles2 = chosen_tiles.split_off(12);
    let tiles3 = chosen_tiles.split_off(6);
    let tiles4 = chosen_tiles;
    let players = vec![
        new_player(1, tiles1),
        new_player(2, tiles2),
        new_player(3, tiles3),
        new_player(4, tiles4)];
    Game {
        board: Board { slots: slots },
        players: players,
        turn: 1,
        merge_decision: None
    }
}

#[test]
fn players_start_with_six_tiles() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.tiles.len(), 6);
    }
}

#[test]
fn players_start_with_6000_in_cash() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.money, 6000);
    }
}

#[test]
fn players_start_with_zero_shares() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.shares.luxor, 0);
        assert_eq!(player.shares.tower, 0);
        assert_eq!(player.shares.american, 0);
        assert_eq!(player.shares.festival, 0);
        assert_eq!(player.shares.worldwide, 0);
        assert_eq!(player.shares.continental, 0);
        assert_eq!(player.shares.imperial, 0);
    }
}

#[test]
fn game_starts_with_four_placed_tiles() {
    let state = initial_state();
    let board_tiles = state.board.slots.iter().filter(|s| s.has_tile).count();
    assert_eq!(board_tiles, 4)
}
<commit_msg>Pick random starting tiles<commit_after>extern crate rand;

const ROWS: i8 = 9;
const COLS: i8 = 12;
const TILES: i8 = 108;
const PLAYERS: i8 = 4;

#[derive(RustcDecodable, RustcEncodable)]
pub struct Game {
    players: Vec<Player>,
    board: Board,
    turn: PlayerId,
    merge_decision: Option<PlayerId>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Player {
    id: PlayerId,
    money: i32,
    shares: PlayerShares,
    tiles: Vec<Tile>
}

type PlayerId = i8;

#[derive(RustcDecodable, RustcEncodable)]
struct PlayerShares {
    luxor: i8,
    tower: i8,
    american: i8,
    festival: i8,
    worldwide: i8,
    continental: i8,
    imperial: i8
}

#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
struct Tile { row: i8, col: i8 }

#[derive(RustcDecodable, RustcEncodable)]
struct Board {
    slots: Vec<Slot>
}

#[derive(RustcDecodable, RustcEncodable)]
struct Slot {
    row: i8,
    col: i8,
    has_tile: bool,
    hotel: Option<Hotel>
}

enum Action {
    PlaceTile,
    HandleMergeStocks,
    BuyStocks,
    DrawTile,
    EndGame
}

pub struct PlaceTile {
    player: PlayerId,
    tile: Tile
}

pub struct HandleMergeStocks {
    hold: i8,
    sell: i8,
    trade: i8
}

type BuyStocks = PlayerShares;

#[derive(RustcDecodable, RustcEncodable)]
enum Hotel { Luxor, Tower, American, Festival, Worldwide, Continental, Imperial }

fn empty_shares() -> PlayerShares {
    PlayerShares { luxor: 0, tower: 0, american: 0, festival: 0, worldwide: 0, continental: 0, imperial: 0 }
}

fn new_player(id: PlayerId, tiles: Vec<Tile>) -> Player {
    Player { id: id, money: 6000, shares: empty_shares(), tiles: tiles }
}

fn choose_tiles(tiles: Vec<Tile>, count: usize) -> (Vec<Tile>, Vec<Tile>) {
    let mut remaining_tiles = tiles;
    let mut random_tiles = Vec::new();
    for _ in 0..count {
        let random_index = (rand::random::<i8>() as usize) % remaining_tiles.len();
        random_tiles.push(remaining_tiles.remove(random_index));
    }
    (random_tiles, remaining_tiles)
}

pub fn initial_state() -> Game {
    let mut slots: Vec<Slot> = (0..COLS)
        .flat_map(|col| {
            let rows = 0..ROWS;
            let res: Vec<Slot> = rows.map(|row| Slot { row: row, col: col, hotel: None, has_tile: false } ).collect();
            res
        }).collect();
    let tiles = (0..TILES).map(|i| Tile { row: i / COLS, col: i % ROWS }).collect();
    let (starting_tiles, remaining_tiles) = choose_tiles(tiles, PLAYERS as usize);
    let (tiles1, remaining_tiles1) = choose_tiles(remaining_tiles, 6 as usize);
    let (tiles2, remaining_tiles2) = choose_tiles(remaining_tiles1, 6 as usize);
    let (tiles3, remaining_tiles3) = choose_tiles(remaining_tiles2, 6 as usize);
    let (tiles4, _) = choose_tiles(remaining_tiles3, 6 as usize);
    let players = vec![
        new_player(1, tiles1),
        new_player(2, tiles2),
        new_player(3, tiles3),
        new_player(4, tiles4)];
    for tile in starting_tiles {
        let found_slot = slots.iter_mut().find(|slot| slot.row == tile.row && slot.col == tile.col);
        match found_slot {
            Some(slot) => {
                slot.has_tile = true;
            }
            None => panic!("No slot found!")
        }
    }
    Game {
        board: Board { slots: slots },
        players: players,
        turn: 1,
        merge_decision: None
    }
}

#[test]
fn players_start_with_six_tiles() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.tiles.len(), 6);
    }
}

#[test]
fn players_start_with_6000_in_cash() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.money, 6000);
    }
}

#[test]
fn players_start_with_zero_shares() {
    let state = initial_state();
    for player in state.players {
        assert_eq!(player.shares.luxor, 0);
        assert_eq!(player.shares.tower, 0);
        assert_eq!(player.shares.american, 0);
        assert_eq!(player.shares.festival, 0);
        assert_eq!(player.shares.worldwide, 0);
        assert_eq!(player.shares.continental, 0);
        assert_eq!(player.shares.imperial, 0);
    }
}

#[test]
fn game_starts_with_four_placed_tiles() {
    let state = initial_state();
    let board_tiles = state.board.slots.iter().filter(|s| s.has_tile).count();
    assert_eq!(board_tiles, 4)
}
<|endoftext|>


<commit_before>use repo::Workspace;

use std::str;
use std::process::{Command, ExitStatus};
use std::sync::Mutex;

fn benchmark(workdir: &str) {}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(wsl: &Mutex<Workspace>, id: &String) {
    println!("Tasting commit {}", id);
    let ws = wsl.lock().unwrap();
    ws.checkout_commit(id);

    // Try to build the commit
    if !build(&ws.path).success() {
        println!("Failed to build {}", id);
        return;
    }

    // Run benchmarks and collect results
    benchmark(&ws.path);
}
<commit_msg>Run `cargo update` before building<commit_after>use repo::Workspace;

use std::str;
use std::process::{Command, ExitStatus};
use std::sync::Mutex;

fn benchmark(workdir: &str) {}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(wsl: &Mutex<Workspace>, id: &String) {
    println!("Tasting commit {}", id);
    let ws = wsl.lock().unwrap();
    ws.checkout_commit(id);

    // Update dependencies
    if !update(&ws.path).success() {
        println!("Failed to update crates for {}", id);
        return;
    }

    // Try to build the commit
    if !build(&ws.path).success() {
        println!("Failed to build {}", id);
        return;
    }

    // Run benchmarks and collect results
    benchmark(&ws.path);
}

fn update(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .status()
        .expect("Failed to execute 'cargo update'!")
}
<|endoftext|>


<commit_before>use std::prelude::v1::*;
use std::mem::{transmute};
use libc;
use rand;
use std::mem::size_of;

// The types we want to test:
use super::{DList, ZoneAllocator, SlabPage, SlabAllocator, SlabPageAllocator};

#[cfg(target_arch="x86_64")]
use x86::paging::{CACHE_LINE_SIZE, BASE_PAGE_SIZE};

/// Page allocator based on mmap/munmap system calls for backing slab memory.
struct MmapSlabAllocator;

/// mmap/munmap page allocator implementation.
impl<'a> SlabPageAllocator<'a> for MmapSlabAllocator {

    fn allocate_slabpage(&self) -> Option<&'a mut SlabPage<'a>> {
        let mut addr: libc::c_void = libc::c_void::__variant1;
        let len: libc::size_t = BASE_PAGE_SIZE;
        let prot = libc::PROT_READ | libc::PROT_WRITE;
        let flags = libc::MAP_PRIVATE | libc::MAP_ANON;
        let fd = -1;
        let offset = 0;
        let r = unsafe { libc::mmap(&mut addr, len as libc::size_t, prot, flags, fd, offset) };
        if r == libc::MAP_FAILED {
            return None;
        }
        else {
            let mut slab_page: &'a mut SlabPage = unsafe { transmute(r as usize) };
            return Some(slab_page);
        }
    }

    fn release_slabpage(&self, p: &'a SlabPage<'a>) {
        let addr: *mut libc::c_void = unsafe { transmute(p) };
        let len: libc::size_t = BASE_PAGE_SIZE;
        let r = unsafe { libc::munmap(addr, len) };
        if r != 0 {
            panic!("munmap failed!");
        }
    }

}

#[test]
fn type_size() {
    assert!(BASE_PAGE_SIZE as usize == size_of::<SlabPage>(),
               "SlabPage should be exactly the size of a single page.");
}

#[test]
fn test_mmap_allocator() {
    let mmap = MmapSlabAllocator;
    match mmap.allocate_slabpage() {
        Some(sp) =>  {
            assert!(!sp.is_full(), "Got empty slab");
            mmap.release_slabpage(sp)
        },
        None => panic!("failed to allocate slabpage")
    }
}

macro_rules! test_slab_allocation {
    ( $test:ident, $size:expr, $alignment:expr, $allocations:expr  ) => {
        #[test]
        fn $test() {
            let mmap = MmapSlabAllocator;
            let mut sa: SlabAllocator = SlabAllocator{
                size: $size,
                pager: &mmap,
                allocateable: DList { head: None, head_elements: 0 },
            };
            let alignment = $alignment;

            let mut objects: Vec<*mut u8> = Vec::new();
            let mut vec: Vec<(usize, &mut [usize; $size / 8])> = Vec::new();

            for _ in 0..$allocations {
                match sa.allocate(alignment) {
                    None => panic!("OOM is unlikely."),
                    Some(obj) => {
                        unsafe { vec.push( (rand::random::<usize>(), transmute(obj)) ) };
                        objects.push(obj)
                    }
                }
            }

            // Write the objects with a random pattern
            for item in vec.iter_mut() {
                let (pattern, ref mut obj) = *item;
                for i in 0..obj.len() {
                    obj[i] = pattern;
                }
            }

            for item in vec.iter() {
                let (pattern, ref obj) = *item;
                //assert!(**obj as usize % alignment == 0, "Object allocation respects alignment.");
                for i in 0..obj.len() {
                    assert!( (obj[i]) == pattern, "No two allocations point to the same memory.");
                }
            }

            // Make sure we can correctly deallocate:
            let pages_allocated = sa.allocateable.head_elements;

            println!("Deallocate all the objects");
            for item in objects.iter_mut() {
                sa.deallocate(*item);
            }

            // then allocate everything again,
            for idx in 0..$allocations {
                println!("{:?}", idx);
                match sa.allocate(alignment) {
                    None => panic!("OOM is unlikely."),
                    Some(obj) => {
                        unsafe { vec.push( (rand::random::<usize>(), transmute(obj)) ) };
                        objects.push(obj)
                    }
                }
            }

            println!("{} {}", pages_allocated, sa.allocateable.head_elements);
            // and make sure we do not request more pages than what we had previously
            assert!(pages_allocated == sa.allocateable.head_elements,
                    "Did not use more memory for 2nd allocation run.");
        }

    };
}

macro_rules! test_slab_allocation_panic {
    ( $test:ident, $size:expr, $alignment:expr, $allocations:expr  ) => {
        test_slab_allocation!($test, $size, $alignment, $allocations);
    };
}

test_slab_allocation!(test_slab_allocation8192_size8_alignment1, 8, 1, 512);
//test_slab_allocation!(test_slab_allocation4096_size8_alignment8, 8, 8, 4096);
//test_slab_allocation!(test_slab_allocation500_size8_alignment64, 8, 64, 500);
//test_slab_allocation!(test_slab_allocation4096_size12_alignment1, 12, 1, 4096);
//test_slab_allocation!(test_slab_allocation4096_size13_alignment1, 13, 1, 4096);
//test_slab_allocation!(test_slab_allocation2000_size14_alignment1, 14, 1, 2000);
//test_slab_allocation!(test_slab_allocation4096_size15_alignment1, 15, 1, 4096);
//test_slab_allocation!(test_slab_allocation8000_size16_alignment1, 16, 1, 8000);
//test_slab_allocation!(test_slab_allocation1024_size24_alignment1, 24, 1, 1024);
//test_slab_allocation!(test_slab_allocation3090_size32_alignment1, 32, 1, 3090);
//test_slab_allocation!(test_slab_allocation4096_size64_alignment1, 64, 1, 4096);
//test_slab_allocation!(test_slab_allocation1000_size512_alignment1, 512, 1, 1000);
//test_slab_allocation!(test_slab_allocation4096_size1024_alignment1, 1024, 1, 4096);
//test_slab_allocation!(test_slab_allocation10_size2048_alignment1, 2048, 1, 10);

#[test]
#[should_panic]
fn allocation_too_big() {
    let mmap = MmapSlabAllocator;
    let mut sa: SlabAllocator = SlabAllocator{
        size: 10,
        pager: &mmap,
        allocateable: DList { head: None, head_elements: 0 },
    };

    sa.allocate(4096-CACHE_LINE_SIZE+1);
}

#[test]
#[should_panic]
fn invalid_alignment() {
    let mmap = MmapSlabAllocator;
    let mut sa: SlabAllocator = SlabAllocator{
        size: 10,
        pager: &mmap,
        allocateable: DList { head: None, head_elements: 0 },
    };

    sa.allocate(3);
}
<commit_msg>Enable tests.<commit_after>use std::prelude::v1::*;
use std::mem::{transmute};
use libc;
use rand;
use std::mem::size_of;

// The types we want to test:
use super::{DList, ZoneAllocator, SlabPage, SlabAllocator, SlabPageAllocator};

#[cfg(target_arch="x86_64")]
use x86::paging::{CACHE_LINE_SIZE, BASE_PAGE_SIZE};

/// Page allocator based on mmap/munmap system calls for backing slab memory.
struct MmapSlabAllocator;

/// mmap/munmap page allocator implementation.
impl<'a> SlabPageAllocator<'a> for MmapSlabAllocator {

    fn allocate_slabpage(&self) -> Option<&'a mut SlabPage<'a>> {
        let mut addr: libc::c_void = libc::c_void::__variant1;
        let len: libc::size_t = BASE_PAGE_SIZE;
        let prot = libc::PROT_READ | libc::PROT_WRITE;
        let flags = libc::MAP_PRIVATE | libc::MAP_ANON;
        let fd = -1;
        let offset = 0;
        let r = unsafe { libc::mmap(&mut addr, len as libc::size_t, prot, flags, fd, offset) };
        if r == libc::MAP_FAILED {
            return None;
        }
        else {
            let mut slab_page: &'a mut SlabPage = unsafe { transmute(r as usize) };
            return Some(slab_page);
        }
    }

    fn release_slabpage(&self, p: &'a SlabPage<'a>) {
        let addr: *mut libc::c_void = unsafe { transmute(p) };
        let len: libc::size_t = BASE_PAGE_SIZE;
        let r = unsafe { libc::munmap(addr, len) };
        if r != 0 {
            panic!("munmap failed!");
        }
    }

}

#[test]
fn type_size() {
    assert!(BASE_PAGE_SIZE as usize == size_of::<SlabPage>(),
               "SlabPage should be exactly the size of a single page.");
}

#[test]
fn test_mmap_allocator() {
    let mmap = MmapSlabAllocator;
    match mmap.allocate_slabpage() {
        Some(sp) =>  {
            assert!(!sp.is_full(), "Got empty slab");
            mmap.release_slabpage(sp)
        },
        None => panic!("failed to allocate slabpage")
    }
}

macro_rules! test_slab_allocation {
    ( $test:ident, $size:expr, $alignment:expr, $allocations:expr  ) => {
        #[test]
        fn $test() {
            let mmap = MmapSlabAllocator;
            let mut sa: SlabAllocator = SlabAllocator{
                size: $size,
                pager: &mmap,
                allocateable: DList { head: None, head_elements: 0 },
            };
            let alignment = $alignment;

            let mut objects: Vec<*mut u8> = Vec::new();
            let mut vec: Vec<(usize, &mut [usize; $size / 8])> = Vec::new();

            for _ in 0..$allocations {
                match sa.allocate(alignment) {
                    None => panic!("OOM is unlikely."),
                    Some(obj) => {
                        unsafe { vec.push( (rand::random::<usize>(), transmute(obj)) ) };
                        objects.push(obj)
                    }
                }
            }

            // Write the objects with a random pattern
            for item in vec.iter_mut() {
                let (pattern, ref mut obj) = *item;
                for i in 0..obj.len() {
                    obj[i] = pattern;
                }
            }

            for item in vec.iter() {
                let (pattern, ref obj) = *item;
                //assert!(**obj as usize % alignment == 0, "Object allocation respects alignment.");
                for i in 0..obj.len() {
                    assert!( (obj[i]) == pattern, "No two allocations point to the same memory.");
                }
            }

            // Make sure we can correctly deallocate:
            let pages_allocated = sa.allocateable.head_elements;

            //println!("Deallocate all the objects");
            for item in objects.iter_mut() {
                sa.deallocate(*item);
            }

            // then allocate everything again,
            for idx in 0..$allocations {
                //println!("{:?}", idx);
                match sa.allocate(alignment) {
                    None => panic!("OOM is unlikely."),
                    Some(obj) => {
                        unsafe { vec.push( (rand::random::<usize>(), transmute(obj)) ) };
                        objects.push(obj)
                    }
                }
            }

            println!("{} {}", pages_allocated, sa.allocateable.head_elements);
            // and make sure we do not request more pages than what we had previously
            assert!(pages_allocated == sa.allocateable.head_elements,
                    "Did not use more memory for 2nd allocation run.");
        }

    };
}

macro_rules! test_slab_allocation_panic {
    ( $test:ident, $size:expr, $alignment:expr, $allocations:expr  ) => {
        test_slab_allocation!($test, $size, $alignment, $allocations);
    };
}

test_slab_allocation!(test_slab_allocation8192_size8_alignment1, 8, 1, 512);
test_slab_allocation!(test_slab_allocation4096_size8_alignment8, 8, 8, 4096);
test_slab_allocation!(test_slab_allocation500_size8_alignment64, 8, 64, 500);
test_slab_allocation!(test_slab_allocation4096_size12_alignment1, 12, 1, 4096);
test_slab_allocation!(test_slab_allocation4096_size13_alignment1, 13, 1, 4096);
test_slab_allocation!(test_slab_allocation2000_size14_alignment1, 14, 1, 2000);
test_slab_allocation!(test_slab_allocation4096_size15_alignment1, 15, 1, 4096);
test_slab_allocation!(test_slab_allocation8000_size16_alignment1, 16, 1, 8000);
test_slab_allocation!(test_slab_allocation1024_size24_alignment1, 24, 1, 1024);
test_slab_allocation!(test_slab_allocation3090_size32_alignment1, 32, 1, 3090);
test_slab_allocation!(test_slab_allocation4096_size64_alignment1, 64, 1, 4096);
test_slab_allocation!(test_slab_allocation1000_size512_alignment1, 512, 1, 1000);
test_slab_allocation!(test_slab_allocation4096_size1024_alignment1, 1024, 1, 4096);
test_slab_allocation!(test_slab_allocation10_size2048_alignment1, 2048, 1, 10);

#[test]
#[should_panic]
fn allocation_too_big() {
    let mmap = MmapSlabAllocator;
    let mut sa: SlabAllocator = SlabAllocator{
        size: 10,
        pager: &mmap,
        allocateable: DList { head: None, head_elements: 0 },
    };

    sa.allocate(4096-CACHE_LINE_SIZE+1);
}

#[test]
#[should_panic]
fn invalid_alignment() {
    let mmap = MmapSlabAllocator;
    let mut sa: SlabAllocator = SlabAllocator{
        size: 10,
        pager: &mmap,
        allocateable: DList { head: None, head_elements: 0 },
    };

    sa.allocate(3);
}
<|endoftext|>


<commit_before>extern crate ncurses;

use self::ncurses::*;

// Window are actually implemented has ncurcesw subwin
// of the stdscr
pub struct Window {
    start_x: i32,
    start_y: i32,

    size_x: i32,
    size_y: i32,

    name: String,
    handle: WINDOW, // ncurses subwin handle
}

impl Window {
    pub fn new(name: String, start_y: i32, start_x: i32, size_y: i32, size_x: i32) -> Window {
        Window {
            name: name,
            start_x: start_x,
            start_y: start_y,
            size_x: size_x,
            size_y: size_y,
            handle: stdscr, // hack init the subwin to the whole window
        }
    }

    pub fn init(&mut self) {

        self.handle = ncurses::subwin(stdscr,
                                      self.start_y + self.size_y,
                                      self.start_x + self.size_x,
                                      self.start_y,
                                      self.start_x);
        ncurses::box_(self.handle, 0, 0);
        // ncurses::waddstr(self.handle, &self.name as str);
    }

    pub fn write(&self, text: String) {}

    pub fn refresh(&self) {}
}
<commit_msg>create a Render trait object<commit_after>extern crate ncurses;

use self::ncurses::*;

// Window are actually implemented has ncurcesw subwin
// of the stdscr
pub struct Window {
    start_x: i32,
    start_y: i32,

    size_x: i32,
    size_y: i32,

    handle: WINDOW, // ncurses subwin handle

    renderer: Box<Render>,
}

impl Window {
    pub fn new(start_y: i32,
               start_x: i32,
               size_y: i32,
               size_x: i32,
               renderer: Box<Render>)
               -> Window {
        Window {
            start_x: start_x,
            start_y: start_y,
            size_x: size_x,
            size_y: size_y,
            renderer: renderer,
            handle: stdscr, // hack init the subwin to the whole window
        }
    }

    pub fn init(&mut self) {

        self.handle = ncurses::subwin(stdscr,
                                      self.start_y + self.size_y,
                                      self.start_x + self.size_x,
                                      self.start_y,
                                      self.start_x);
        ncurses::box_(self.handle, 0, 0);
        // ncurses::waddstr(self.handle, &self.name as str);
    }

    pub fn write(&self, text: String) {}

    pub fn refresh(&self) {}
}

pub trait Render {
    fn render();
}

struct StoryWin;
struct MapWin;
struct StatusWin;

impl Render for StoryWin {
    fn render() {
        println!("I am story!");
    }
}

impl Render for MapWin {
    fn render() {
        println!("I'm map!");
    }
}

impl Render for StatusWin {
    fn render() {
        println!("I'm program");
    }
}
<|endoftext|>


<commit_before>#[cfg_attr(rustfmt, rustfmt_skip)]
static RUST_KEYWORDS: &'static [&'static str] = &[
    "as",
    "async",
    "await",
    "break",
    "crate",
    "dyn",
    "else",
    "enum",
    "extern",
    "false",
    "fn",
    "for",
    "if",
    "impl",
    "in",
    "let",
    "loop",
    "match",
    "mod",
    "move",
    "mut",
    "pub",
    "ref",
    "return",
    "static",
    "self",
    "Self",
    "struct",
    "super",
    "true",
    "trait",
    "type",
    "unsafe",
    "use",
    "while",
    "continue",
    "box",
    "const",
    "where",
    "virtual",
    "proc",
    "alignof",
    "become",
    "offsetof",
    "priv",
    "pure",
    "sizeof",
    "typeof",
    "unsized",
    "yield",
    "do",
    "abstract",
    "final",
    "override",
    "macro",
];

// https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/3
#[cfg_attr(rustfmt, rustfmt_skip)]
static RUST_KEYWORDS_WHICH_CANNOT_BE_RAW: &'static [&'static str] = &[
    "super",
    "self",
    "Self",
    "extern",
    "crate",
];

pub(crate) fn parse_rust_keyword(word: &str) -> Option<&'static str> {
    RUST_KEYWORDS.iter().cloned().find(|&kw| kw == word)
}

pub(crate) fn is_rust_keyword(ident: &str) -> bool {
    parse_rust_keyword(ident).is_some()
}

#[allow(dead_code)]
pub(crate) fn is_rust_keyword_which_cannot_be_raw(ident: &str) -> bool {
    RUST_KEYWORDS_WHICH_CANNOT_BE_RAW
        .iter()
        .cloned()
        .find(|&kw| kw == ident)
        .is_some()
}
<commit_msg>Undescore is also rust keyword<commit_after>#[cfg_attr(rustfmt, rustfmt_skip)]
static RUST_KEYWORDS: &'static [&'static str] = &[
    "_",
    "as",
    "async",
    "await",
    "break",
    "crate",
    "dyn",
    "else",
    "enum",
    "extern",
    "false",
    "fn",
    "for",
    "if",
    "impl",
    "in",
    "let",
    "loop",
    "match",
    "mod",
    "move",
    "mut",
    "pub",
    "ref",
    "return",
    "static",
    "self",
    "Self",
    "struct",
    "super",
    "true",
    "trait",
    "type",
    "unsafe",
    "use",
    "while",
    "continue",
    "box",
    "const",
    "where",
    "virtual",
    "proc",
    "alignof",
    "become",
    "offsetof",
    "priv",
    "pure",
    "sizeof",
    "typeof",
    "unsized",
    "yield",
    "do",
    "abstract",
    "final",
    "override",
    "macro",
];

// https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/3
#[cfg_attr(rustfmt, rustfmt_skip)]
static RUST_KEYWORDS_WHICH_CANNOT_BE_RAW: &'static [&'static str] = &[
    "super",
    "self",
    "Self",
    "extern",
    "crate",
];

pub(crate) fn parse_rust_keyword(word: &str) -> Option<&'static str> {
    RUST_KEYWORDS.iter().cloned().find(|&kw| kw == word)
}

pub(crate) fn is_rust_keyword(ident: &str) -> bool {
    parse_rust_keyword(ident).is_some()
}

#[allow(dead_code)]
pub(crate) fn is_rust_keyword_which_cannot_be_raw(ident: &str) -> bool {
    RUST_KEYWORDS_WHICH_CANNOT_BE_RAW
        .iter()
        .cloned()
        .find(|&kw| kw == ident)
        .is_some()
}
<|endoftext|>


<commit_before>//! Tracing and profiling functions. Error and warning log.

use libc::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::str;
use std::time::Duration;

use super::ffi;
use {SqliteError, SqliteResult, SqliteConnection};

/// Set up the process-wide SQLite error logging callback.
/// This function is marked unsafe for two reasons:
///
/// * The function is not threadsafe. No other SQLite calls may be made while
///   `config_log` is running, and multiple threads may not call `config_log`
///   simultaneously.
/// * The provided `callback` itself function has two requirements:
///     * It must not invoke any SQLite calls.
///     * It must be threadsafe if SQLite is used in a multithreaded way.
///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> SqliteResult<()> {
    extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) {
        let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
        let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };

        if let Ok(s) = str::from_utf8(c_slice) {
            callback(err, s);
        }
    }

    let rc = match callback {
        Some(f) => {
            let p_arg: *mut c_void = mem::transmute(f);
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, Some(log_callback), p_arg)
        },
        None => {
            let nullptr: *mut c_void = ptr::null_mut();
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, nullptr, nullptr)
        }
    };

    if rc != ffi::SQLITE_OK {
        return Err(SqliteError{ code: rc, message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string() });
    }

    Ok(())
}

/// Write a message into the error log established by `config_log`.
pub fn log(err_code: c_int, msg: &str) {
    let msg = CString::new(msg).expect("SQLite log messages cannot contain embedded zeroes");
    unsafe {
        ffi::sqlite3_log(err_code, msg.as_ptr());
    }
}

impl SqliteConnection {
    /// Register or clear a callback function that can be used for tracing the execution of SQL statements.
    ///
    /// Prepared statement placeholders are replaced/logged with their assigned values.
    /// There can only be a single tracer defined for each database connection.
    /// Setting a new tracer clears the old one.
    pub fn trace(&mut self, trace_fn: Option<fn(&str)>) {
        extern "C" fn trace_callback (p_arg: *mut c_void, z_sql: *const c_char) {
            let trace_fn: fn(&str) = unsafe { mem::transmute(p_arg) };
            let c_slice = unsafe { CStr::from_ptr(z_sql).to_bytes() };
            if let Ok(s) = str::from_utf8(c_slice) {
                trace_fn(s);
            }
        }

        let c = self.db.borrow_mut();
        match trace_fn {
            Some(f) => unsafe { ffi::sqlite3_trace(c.db(), Some(trace_callback), mem::transmute(f)); },
            None    => unsafe { ffi::sqlite3_trace(c.db(), None, ptr::null_mut()); },
        }
    }

    /// Register or clear a callback function that can be used for profiling the execution of SQL statements.
    ///
    /// There can only be a single profiler defined for each database connection.
    /// Setting a new profiler clears the old one.
    pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
        extern "C" fn profile_callback(p_arg: *mut c_void, z_sql: *const c_char, nanoseconds: u64) {
            let profile_fn: fn(&str, Duration) = unsafe { mem::transmute(p_arg) };
            let c_slice = unsafe { CStr::from_ptr(z_sql).to_bytes() };
            if let Ok(s) = str::from_utf8(c_slice) {
                const NANOS_PER_SEC: u64 = 1_000_000_000;

                let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
                                             (nanoseconds % NANOS_PER_SEC) as u32);
                profile_fn(s, duration);
            }
        }

        let c = self.db.borrow_mut();
        match profile_fn {
            Some(f) => unsafe { ffi::sqlite3_profile(c.db(), Some(profile_callback), mem::transmute(f)) },
            None    => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) },
        };
    }
}

#[cfg(test)]
mod test {
    use std::sync::Mutex;
    use std::time::Duration;

    use SqliteConnection;

    #[test]
    fn test_trace() {
        lazy_static! {
            static ref TRACED_STMTS: Mutex<Vec<String>> = Mutex::new(Vec::new());
        }
        fn tracer(s: &str) {
            let mut traced_stmts = TRACED_STMTS.lock().unwrap();
            traced_stmts.push(s.to_owned());
        }

        let mut db = SqliteConnection::open_in_memory().unwrap();
        db.trace(Some(tracer));
        {
            let _ = db.query_row("SELECT ?", &[&1i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"hello"], |_| {});
        }
        db.trace(None);
        {
            let _ = db.query_row("SELECT ?", &[&2i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"goodbye"], |_| {});
        }

        let traced_stmts = TRACED_STMTS.lock().unwrap();
        assert_eq!(traced_stmts.len(), 2);
        assert_eq!(traced_stmts[0], "SELECT 1");
        assert_eq!(traced_stmts[1], "SELECT 'hello'");
    }

    #[test]
    fn test_profile() {
        lazy_static! {
            static ref PROFILED: Mutex<Vec<(String, Duration)>> = Mutex::new(Vec::new());
        }
        fn profiler(s: &str, d: Duration) {
            let mut profiled = PROFILED.lock().unwrap();
            profiled.push((s.to_owned(), d));
        }

        let mut db = SqliteConnection::open_in_memory().unwrap();
        db.profile(Some(profiler));
        db.execute_batch("PRAGMA application_id = 1").unwrap();
        db.profile(None);
        db.execute_batch("PRAGMA application_id = 2").unwrap();

        let profiled = PROFILED.lock().unwrap();
        assert_eq!(profiled.len(), 1);
        assert_eq!(profiled[0].0, "PRAGMA application_id = 1");
    }
}
<commit_msg>Fix build on nightly<commit_after>//! Tracing and profiling functions. Error and warning log.

use libc::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr;
use std::str;
use std::time::Duration;

use super::ffi;
use {SqliteError, SqliteResult, SqliteConnection};

/// Set up the process-wide SQLite error logging callback.
/// This function is marked unsafe for two reasons:
///
/// * The function is not threadsafe. No other SQLite calls may be made while
///   `config_log` is running, and multiple threads may not call `config_log`
///   simultaneously.
/// * The provided `callback` itself function has two requirements:
///     * It must not invoke any SQLite calls.
///     * It must be threadsafe if SQLite is used in a multithreaded way.
///
/// cf [The Error And Warning Log](http://sqlite.org/errlog.html).
pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> SqliteResult<()> {
    extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) {
        let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() };
        let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };

        if let Ok(s) = str::from_utf8(c_slice) {
            callback(err, s);
        }
    }

    let rc = match callback {
        Some(f) => {
            let p_arg: *mut c_void = mem::transmute(f);
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, Some(log_callback), p_arg)
        },
        None => {
            let nullptr: *mut c_void = ptr::null_mut();
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, nullptr, nullptr)
        }
    };

    if rc != ffi::SQLITE_OK {
        return Err(SqliteError{ code: rc, message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string() });
    }

    Ok(())
}

/// Write a message into the error log established by `config_log`.
pub fn log(err_code: c_int, msg: &str) {
    let msg = CString::new(msg).expect("SQLite log messages cannot contain embedded zeroes");
    unsafe {
        ffi::sqlite3_log(err_code, msg.as_ptr());
    }
}

impl SqliteConnection {
    /// Register or clear a callback function that can be used for tracing the execution of SQL statements.
    ///
    /// Prepared statement placeholders are replaced/logged with their assigned values.
    /// There can only be a single tracer defined for each database connection.
    /// Setting a new tracer clears the old one.
    pub fn trace(&mut self, trace_fn: Option<fn(&str)>) {
        unsafe extern "C" fn trace_callback (p_arg: *mut c_void, z_sql: *const c_char) {
            let trace_fn: fn(&str) = mem::transmute(p_arg);
            let c_slice = CStr::from_ptr(z_sql).to_bytes();
            if let Ok(s) = str::from_utf8(c_slice) {
                trace_fn(s);
            }
        }

        let c = self.db.borrow_mut();
        match trace_fn {
            Some(f) => unsafe { ffi::sqlite3_trace(c.db(), Some(trace_callback), mem::transmute(f)); },
            None    => unsafe { ffi::sqlite3_trace(c.db(), None, ptr::null_mut()); },
        }
    }

    /// Register or clear a callback function that can be used for profiling the execution of SQL statements.
    ///
    /// There can only be a single profiler defined for each database connection.
    /// Setting a new profiler clears the old one.
    pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
        unsafe extern "C" fn profile_callback(p_arg: *mut c_void, z_sql: *const c_char, nanoseconds: u64) {
            let profile_fn: fn(&str, Duration) = mem::transmute(p_arg);
            let c_slice = CStr::from_ptr(z_sql).to_bytes();
            if let Ok(s) = str::from_utf8(c_slice) {
                const NANOS_PER_SEC: u64 = 1_000_000_000;

                let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
                                             (nanoseconds % NANOS_PER_SEC) as u32);
                profile_fn(s, duration);
            }
        }

        let c = self.db.borrow_mut();
        match profile_fn {
            Some(f) => unsafe { ffi::sqlite3_profile(c.db(), Some(profile_callback), mem::transmute(f)) },
            None    => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) },
        };
    }
}

#[cfg(test)]
mod test {
    use std::sync::Mutex;
    use std::time::Duration;

    use SqliteConnection;

    #[test]
    fn test_trace() {
        lazy_static! {
            static ref TRACED_STMTS: Mutex<Vec<String>> = Mutex::new(Vec::new());
        }
        fn tracer(s: &str) {
            let mut traced_stmts = TRACED_STMTS.lock().unwrap();
            traced_stmts.push(s.to_owned());
        }

        let mut db = SqliteConnection::open_in_memory().unwrap();
        db.trace(Some(tracer));
        {
            let _ = db.query_row("SELECT ?", &[&1i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"hello"], |_| {});
        }
        db.trace(None);
        {
            let _ = db.query_row("SELECT ?", &[&2i32], |_| {});
            let _ = db.query_row("SELECT ?", &[&"goodbye"], |_| {});
        }

        let traced_stmts = TRACED_STMTS.lock().unwrap();
        assert_eq!(traced_stmts.len(), 2);
        assert_eq!(traced_stmts[0], "SELECT 1");
        assert_eq!(traced_stmts[1], "SELECT 'hello'");
    }

    #[test]
    fn test_profile() {
        lazy_static! {
            static ref PROFILED: Mutex<Vec<(String, Duration)>> = Mutex::new(Vec::new());
        }
        fn profiler(s: &str, d: Duration) {
            let mut profiled = PROFILED.lock().unwrap();
            profiled.push((s.to_owned(), d));
        }

        let mut db = SqliteConnection::open_in_memory().unwrap();
        db.profile(Some(profiler));
        db.execute_batch("PRAGMA application_id = 1").unwrap();
        db.profile(None);
        db.execute_batch("PRAGMA application_id = 2").unwrap();

        let profiled = PROFILED.lock().unwrap();
        assert_eq!(profiled.len(), 1);
        assert_eq!(profiled[0].0, "PRAGMA application_id = 1");
    }
}
<|endoftext|>


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

use hash;
use key;
use std::collections::VecDeque;

#[derive(Clone)]
pub struct FileEntry {
    pub hash_ref: hash::tree::HashRef,
    pub meta: key::Entry,
}

pub trait LikesFiles {
    fn include_file(&mut self, _file: &FileEntry) -> bool {
        true
    }
    fn include_dir(&mut self, _file: &FileEntry) -> bool {
        true
    }
}

pub trait HasFiles {
    fn files(&mut self) -> Vec<FileEntry> {
        vec![]
    }
}

enum StackItem {
    File(FileEntry),
    Dir(FileEntry),
}

enum Child<B> {
    File(hash::tree::Walker<B>),
    Dir(Box<Walker<B>>),
}

pub struct Walker<B> {
    backend: B,
    tree: hash::tree::Walker<B>,
    child: Option<Child<B>>,
    stack: VecDeque<StackItem>,
}

impl<B> Walker<B>
    where B: hash::tree::HashTreeBackend
{
    pub fn new(backend: B, root_hash: hash::tree::HashRef) -> Result<Walker<B>, B::Err> {
        let tree = hash::tree::Walker::new(backend.clone(), root_hash)?.unwrap();
        Ok(Walker {
            backend: backend,
            tree: tree,
            child: None,
            stack: VecDeque::new(),
        })
    }

    pub fn resume_child<FV, DV>(&mut self,
                                mut file_v: &mut FV,
                                mut dir_v: &mut DV)
                                -> Result<bool, B::Err>
        where FV: LikesFiles,
              DV: HasFiles,
              FV: hash::tree::Visitor,
              DV: hash::tree::Visitor
    {
        Ok(match self.child.as_mut() {
            Some(&mut Child::File(ref mut tree)) => tree.resume(file_v)?,
            Some(&mut Child::Dir(ref mut walker)) => walker.resume(file_v, dir_v)?,
            None => false,
        })
    }

    pub fn resume<FV, DV>(&mut self,
                          mut file_v: &mut FV,
                          mut dir_v: &mut DV)
                          -> Result<bool, B::Err>
        where FV: LikesFiles,
              DV: HasFiles,
              FV: hash::tree::Visitor,
              DV: hash::tree::Visitor
    {
        if self.resume_child(file_v, dir_v)? {
            return Ok(true);
        }

        self.child = match self.stack.pop_front() {
            Some(StackItem::File(f)) => {
                Some(Child::File(hash::tree::Walker::new(self.backend.clone(), f.hash_ref)
                    ?
                    .unwrap()))
            }
            Some(StackItem::Dir(f)) => {
                Some(Child::Dir(Box::new(Walker::new(self.backend.clone(), f.hash_ref).unwrap())))
            }
            None => None,
        };

        if self.child.is_none() {
            if !self.tree.resume(dir_v)? {
                return Ok(false);
            }
            for file in dir_v.files() {
                if file_v.include_file(&file) {
                    self.stack.push_back(StackItem::File(file.clone()));
                } else if file_v.include_dir(&file) {
                    self.stack.push_back(StackItem::Dir(file));
                }
            }
        }
        self.resume(file_v, dir_v)
    }
}
<commit_msg>Progress can happen even if a dir did not find files.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use hash;
use key;
use std::collections::VecDeque;

#[derive(Clone)]
pub struct FileEntry {
    pub hash_ref: hash::tree::HashRef,
    pub meta: key::Entry,
}

pub trait LikesFiles {
    fn include_file(&mut self, _file: &FileEntry) -> bool {
        true
    }
    fn include_dir(&mut self, _file: &FileEntry) -> bool {
        true
    }
}

pub trait HasFiles {
    fn files(&mut self) -> Vec<FileEntry> {
        vec![]
    }
}

enum StackItem {
    File(FileEntry),
    Dir(FileEntry),
}

enum Child<B> {
    File(hash::tree::Walker<B>),
    Dir(Box<Walker<B>>),
}

pub struct Walker<B> {
    backend: B,
    tree: hash::tree::Walker<B>,
    child: Option<Child<B>>,
    stack: VecDeque<StackItem>,
}

impl<B> Walker<B>
    where B: hash::tree::HashTreeBackend
{
    pub fn new(backend: B, root_hash: hash::tree::HashRef) -> Result<Walker<B>, B::Err> {
        let tree = hash::tree::Walker::new(backend.clone(), root_hash)?.unwrap();
        Ok(Walker {
            backend: backend,
            tree: tree,
            child: None,
            stack: VecDeque::new(),
        })
    }

    pub fn resume_child<FV, DV>(&mut self,
                                mut file_v: &mut FV,
                                mut dir_v: &mut DV)
                                -> Result<bool, B::Err>
        where FV: LikesFiles,
              DV: HasFiles,
              FV: hash::tree::Visitor,
              DV: hash::tree::Visitor
    {
        Ok(match self.child.as_mut() {
            Some(&mut Child::File(ref mut tree)) => tree.resume(file_v)?,
            Some(&mut Child::Dir(ref mut walker)) => walker.resume(file_v, dir_v)?,
            None => false,
        })
    }

    pub fn resume<FV, DV>(&mut self,
                          mut file_v: &mut FV,
                          mut dir_v: &mut DV)
                          -> Result<bool, B::Err>
        where FV: LikesFiles,
              DV: HasFiles,
              FV: hash::tree::Visitor,
              DV: hash::tree::Visitor
    {
        if self.resume_child(file_v, dir_v)? {
            return Ok(true);
        }

        self.child = match self.stack.pop_front() {
            Some(StackItem::File(f)) => {
                Some(Child::File(hash::tree::Walker::new(self.backend.clone(), f.hash_ref)
                    ?
                    .unwrap()))
            }
            Some(StackItem::Dir(f)) => {
                Some(Child::Dir(Box::new(Walker::new(self.backend.clone(), f.hash_ref).unwrap())))
            }
            None => None,
        };

        if self.child.is_some() {
            return self.resume(file_v, dir_v);
        } else {
            if !self.tree.resume(dir_v)? {
                // No child and no tree. We can never do more work.
                return Ok(false);
            }
            for file in dir_v.files() {
                if file_v.include_file(&file) {
                    self.stack.push_back(StackItem::File(file.clone()));
                } else if file_v.include_dir(&file) {
                    self.stack.push_back(StackItem::Dir(file));
                }
            }
        }

        return Ok(true);
    }
}
<|endoftext|>


<commit_before>//! Abstractions for page tables and page table entries.

use core::fmt;
use core::ops::{Index, IndexMut};

use super::{PageSize, PhysFrame, Size4KiB};
use crate::addr::PhysAddr;

use bitflags::bitflags;

/// The error returned by the `PageTableEntry::frame` method.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FrameError {
    /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame.
    FrameNotPresent,
    /// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame
    /// as return type, so a huge frame can't be returned.
    HugeFrame,
}

/// A 64-bit page table entry.
#[derive(Clone)]
#[repr(transparent)]
pub struct PageTableEntry {
    entry: u64,
}

impl PageTableEntry {
    /// Creates an unused page table entry.
    pub fn new() -> Self {
        PageTableEntry { entry: 0 }
    }

    /// Returns whether this entry is zero.
    pub fn is_unused(&self) -> bool {
        self.entry == 0
    }

    /// Sets this entry to zero.
    pub fn set_unused(&mut self) {
        self.entry = 0;
    }

    /// Returns the flags of this entry.
    pub fn flags(&self) -> PageTableFlags {
        PageTableFlags::from_bits_truncate(self.entry)
    }

    /// Returns the physical address mapped by this entry, might be zero.
    pub fn addr(&self) -> PhysAddr {
        PhysAddr::new(self.entry & 0x000fffff_fffff000)
    }

    /// Returns the physical frame mapped by this entry.
    ///
    /// Returns the following errors:
    ///
    /// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set.
    /// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the
    ///    `addr` function must be used)
    pub fn frame(&self) -> Result<PhysFrame, FrameError> {
        if !self.flags().contains(PageTableFlags::PRESENT) {
            Err(FrameError::FrameNotPresent)
        } else if self.flags().contains(PageTableFlags::HUGE_PAGE) {
            Err(FrameError::HugeFrame)
        } else {
            Ok(PhysFrame::containing_address(self.addr()))
        }
    }

    /// Map the entry to the specified physical address with the specified flags.
    pub fn set_addr(&mut self, addr: PhysAddr, flags: PageTableFlags) {
        assert!(addr.is_aligned(Size4KiB::SIZE));
        self.entry = (addr.as_u64()) | flags.bits();
    }

    /// Map the entry to the specified physical frame with the specified flags.
    pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) {
        assert!(!flags.contains(PageTableFlags::HUGE_PAGE));
        self.set_addr(frame.start_address(), flags)
    }

    /// Sets the flags of this entry.
    pub fn set_flags(&mut self, flags: PageTableFlags) {
        self.entry = self.addr().as_u64() | flags.bits();
    }
}

impl fmt::Debug for PageTableEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut f = f.debug_struct("PageTableEntry");
        f.field("addr", &self.addr());
        f.field("flags", &self.flags());
        f.finish()
    }
}

bitflags! {
    /// Possible flags for a page table entry.
    pub struct PageTableFlags: u64 {
        /// Specifies whether the mapped frame or page table is loaded in memory.
        const PRESENT =         1 << 0;
        /// Controls whether writes to the mapped frames are allowed.
        ///
        /// If this bit is unset in a level 1 page table entry, the mapped frame is read-only.
        /// If this bit is unset in a higher level page table entry the complete range of mapped
        /// pages is read-only.
        const WRITABLE =        1 << 1;
        /// Controls whether accesses from userspace (i.e. ring 3) are permitted.
        const USER_ACCESSIBLE = 1 << 2;
        /// If this bit is set, a “write-through” policy is used for the cache, else a “write-back”
        /// policy is used.
        const WRITE_THROUGH =   1 << 3;
        /// Disables caching for the pointed entry is cacheable.
        const NO_CACHE =        1 << 4;
        /// Set by the CPU when the mapped frame or page table is accessed.
        const ACCESSED =        1 << 5;
        /// Set by the CPU on a write to the mapped frame.
        const DIRTY =           1 << 6;
        /// Specifies that the entry maps a huge frame instead of a page table. Only allowed in
        /// P2 or P3 tables.
        const HUGE_PAGE =       1 << 7;
        /// Indicates that the mapping is present in all address spaces, so it isn't flushed from
        /// the TLB on an address space switch.
        const GLOBAL =          1 << 8;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_9 =           1 << 9;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_10 =          1 << 10;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_11 =          1 << 11;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_52 =          1 << 52;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_53 =          1 << 53;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_54 =          1 << 54;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_55 =          1 << 55;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_56 =          1 << 56;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_57 =          1 << 57;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_58 =          1 << 58;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_59 =          1 << 59;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_60 =          1 << 60;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_61 =          1 << 61;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_62 =          1 << 62;
        /// Forbid code execution from the mapped frames.
        ///
        /// Can be only used when the no-execute page protection feature is enabled in the EFER
        /// register.
        const NO_EXECUTE =      1 << 63;
    }
}

/// The number of entries in a page table.
const ENTRY_COUNT: usize = 512;

/// Represents a page table.
///
/// Always page-sized.
///
/// This struct implements the `Index` and `IndexMut` traits, so the entries can be accessed
/// through index operations. For example, `page_table[15]` returns the 15th page table entry.
#[repr(align(4096))]
#[repr(C)]
pub struct PageTable {
    entries: [PageTableEntry; ENTRY_COUNT],
}

impl PageTable {
    /// Creates an empty page table.
    pub fn new() -> Self {
        use array_init::array_init;

        PageTable {
            entries: array_init(|_| PageTableEntry::new()),
        }
    }

    /// Clears all entries.
    pub fn zero(&mut self) {
        for entry in self.entries.iter_mut() {
            entry.set_unused();
        }
    }

    /// Returns an iterator over the entries of the page table.
    pub fn iter(&self) -> impl Iterator<Item = &PageTableEntry> {
        self.entries.iter()
    }

    /// Returns an iterator that allows modifying the entries of the page table.
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut PageTableEntry> {
        self.entries.iter_mut()
    }
}

impl Index<usize> for PageTable {
    type Output = PageTableEntry;

    fn index(&self, index: usize) -> &Self::Output {
        &self.entries[index]
    }
}

impl IndexMut<usize> for PageTable {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.entries[index]
    }
}

impl Index<PageTableIndex> for PageTable {
    type Output = PageTableEntry;

    fn index(&self, index: PageTableIndex) -> &Self::Output {
        &self.entries[cast::usize(u16::from(index))]
    }
}

impl IndexMut<PageTableIndex> for PageTable {
    fn index_mut(&mut self, index: PageTableIndex) -> &mut Self::Output {
        &mut self.entries[cast::usize(u16::from(index))]
    }
}

impl fmt::Debug for PageTable {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.entries[..].fmt(f)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct PageTableIndex(u16);

impl PageTableIndex {
    pub fn new(index: u16) -> Self {
        assert!(usize::from(index) < ENTRY_COUNT);
        Self(index)
    }
}

impl From<PageTableIndex> for u16 {
    fn from(index: PageTableIndex) -> Self {
        index.0
    }
}

impl From<PageTableIndex> for u32 {
    fn from(index: PageTableIndex) -> Self {
        u32::from(index.0)
    }
}

impl From<PageTableIndex> for u64 {
    fn from(index: PageTableIndex) -> Self {
        u64::from(index.0)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct PageOffset(u16);

impl PageOffset {
    pub fn new(offset: u16) -> Self {
        assert!(offset < (1 << 12));
        Self(offset)
    }
}

impl From<PageOffset> for u16 {
    fn from(offset: PageOffset) -> Self {
        offset.0
    }
}

impl From<PageOffset> for u32 {
    fn from(offset: PageOffset) -> Self {
        u32::from(offset.0)
    }
}

impl From<PageOffset> for u64 {
    fn from(offset: PageOffset) -> Self {
        u64::from(offset.0)
    }
}
<commit_msg>Add documentation for new types and functions<commit_after>//! Abstractions for page tables and page table entries.

use core::fmt;
use core::ops::{Index, IndexMut};

use super::{PageSize, PhysFrame, Size4KiB};
use crate::addr::PhysAddr;

use bitflags::bitflags;

/// The error returned by the `PageTableEntry::frame` method.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FrameError {
    /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame.
    FrameNotPresent,
    /// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame
    /// as return type, so a huge frame can't be returned.
    HugeFrame,
}

/// A 64-bit page table entry.
#[derive(Clone)]
#[repr(transparent)]
pub struct PageTableEntry {
    entry: u64,
}

impl PageTableEntry {
    /// Creates an unused page table entry.
    pub fn new() -> Self {
        PageTableEntry { entry: 0 }
    }

    /// Returns whether this entry is zero.
    pub fn is_unused(&self) -> bool {
        self.entry == 0
    }

    /// Sets this entry to zero.
    pub fn set_unused(&mut self) {
        self.entry = 0;
    }

    /// Returns the flags of this entry.
    pub fn flags(&self) -> PageTableFlags {
        PageTableFlags::from_bits_truncate(self.entry)
    }

    /// Returns the physical address mapped by this entry, might be zero.
    pub fn addr(&self) -> PhysAddr {
        PhysAddr::new(self.entry & 0x000fffff_fffff000)
    }

    /// Returns the physical frame mapped by this entry.
    ///
    /// Returns the following errors:
    ///
    /// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set.
    /// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the
    ///    `addr` function must be used)
    pub fn frame(&self) -> Result<PhysFrame, FrameError> {
        if !self.flags().contains(PageTableFlags::PRESENT) {
            Err(FrameError::FrameNotPresent)
        } else if self.flags().contains(PageTableFlags::HUGE_PAGE) {
            Err(FrameError::HugeFrame)
        } else {
            Ok(PhysFrame::containing_address(self.addr()))
        }
    }

    /// Map the entry to the specified physical address with the specified flags.
    pub fn set_addr(&mut self, addr: PhysAddr, flags: PageTableFlags) {
        assert!(addr.is_aligned(Size4KiB::SIZE));
        self.entry = (addr.as_u64()) | flags.bits();
    }

    /// Map the entry to the specified physical frame with the specified flags.
    pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) {
        assert!(!flags.contains(PageTableFlags::HUGE_PAGE));
        self.set_addr(frame.start_address(), flags)
    }

    /// Sets the flags of this entry.
    pub fn set_flags(&mut self, flags: PageTableFlags) {
        self.entry = self.addr().as_u64() | flags.bits();
    }
}

impl fmt::Debug for PageTableEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut f = f.debug_struct("PageTableEntry");
        f.field("addr", &self.addr());
        f.field("flags", &self.flags());
        f.finish()
    }
}

bitflags! {
    /// Possible flags for a page table entry.
    pub struct PageTableFlags: u64 {
        /// Specifies whether the mapped frame or page table is loaded in memory.
        const PRESENT =         1 << 0;
        /// Controls whether writes to the mapped frames are allowed.
        ///
        /// If this bit is unset in a level 1 page table entry, the mapped frame is read-only.
        /// If this bit is unset in a higher level page table entry the complete range of mapped
        /// pages is read-only.
        const WRITABLE =        1 << 1;
        /// Controls whether accesses from userspace (i.e. ring 3) are permitted.
        const USER_ACCESSIBLE = 1 << 2;
        /// If this bit is set, a “write-through” policy is used for the cache, else a “write-back”
        /// policy is used.
        const WRITE_THROUGH =   1 << 3;
        /// Disables caching for the pointed entry is cacheable.
        const NO_CACHE =        1 << 4;
        /// Set by the CPU when the mapped frame or page table is accessed.
        const ACCESSED =        1 << 5;
        /// Set by the CPU on a write to the mapped frame.
        const DIRTY =           1 << 6;
        /// Specifies that the entry maps a huge frame instead of a page table. Only allowed in
        /// P2 or P3 tables.
        const HUGE_PAGE =       1 << 7;
        /// Indicates that the mapping is present in all address spaces, so it isn't flushed from
        /// the TLB on an address space switch.
        const GLOBAL =          1 << 8;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_9 =           1 << 9;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_10 =          1 << 10;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_11 =          1 << 11;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_52 =          1 << 52;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_53 =          1 << 53;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_54 =          1 << 54;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_55 =          1 << 55;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_56 =          1 << 56;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_57 =          1 << 57;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_58 =          1 << 58;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_59 =          1 << 59;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_60 =          1 << 60;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_61 =          1 << 61;
        /// Available to the OS, can be used to store additional data, e.g. custom flags.
        const BIT_62 =          1 << 62;
        /// Forbid code execution from the mapped frames.
        ///
        /// Can be only used when the no-execute page protection feature is enabled in the EFER
        /// register.
        const NO_EXECUTE =      1 << 63;
    }
}

/// The number of entries in a page table.
const ENTRY_COUNT: usize = 512;

/// Represents a page table.
///
/// Always page-sized.
///
/// This struct implements the `Index` and `IndexMut` traits, so the entries can be accessed
/// through index operations. For example, `page_table[15]` returns the 15th page table entry.
#[repr(align(4096))]
#[repr(C)]
pub struct PageTable {
    entries: [PageTableEntry; ENTRY_COUNT],
}

impl PageTable {
    /// Creates an empty page table.
    pub fn new() -> Self {
        use array_init::array_init;

        PageTable {
            entries: array_init(|_| PageTableEntry::new()),
        }
    }

    /// Clears all entries.
    pub fn zero(&mut self) {
        for entry in self.entries.iter_mut() {
            entry.set_unused();
        }
    }

    /// Returns an iterator over the entries of the page table.
    pub fn iter(&self) -> impl Iterator<Item = &PageTableEntry> {
        self.entries.iter()
    }

    /// Returns an iterator that allows modifying the entries of the page table.
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut PageTableEntry> {
        self.entries.iter_mut()
    }
}

impl Index<usize> for PageTable {
    type Output = PageTableEntry;

    fn index(&self, index: usize) -> &Self::Output {
        &self.entries[index]
    }
}

impl IndexMut<usize> for PageTable {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.entries[index]
    }
}

impl Index<PageTableIndex> for PageTable {
    type Output = PageTableEntry;

    fn index(&self, index: PageTableIndex) -> &Self::Output {
        &self.entries[cast::usize(u16::from(index))]
    }
}

impl IndexMut<PageTableIndex> for PageTable {
    fn index_mut(&mut self, index: PageTableIndex) -> &mut Self::Output {
        &mut self.entries[cast::usize(u16::from(index))]
    }
}

impl fmt::Debug for PageTable {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.entries[..].fmt(f)
    }
}

/// A 9-bit index into a page table.
///
/// Can be used to select one of the 512 entries of a page table.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct PageTableIndex(u16);

impl PageTableIndex {
    /// Creates a new index from the given `u16`. Panics if the given value is >=512.
    pub fn new(index: u16) -> Self {
        assert!(usize::from(index) < ENTRY_COUNT);
        Self(index)
    }
}

impl From<PageTableIndex> for u16 {
    fn from(index: PageTableIndex) -> Self {
        index.0
    }
}

impl From<PageTableIndex> for u32 {
    fn from(index: PageTableIndex) -> Self {
        u32::from(index.0)
    }
}

impl From<PageTableIndex> for u64 {
    fn from(index: PageTableIndex) -> Self {
        u64::from(index.0)
    }
}

/// A 12-bit offset into a 4KiB Page.
///
/// This type is returned by the `VirtAddr::page_offset` method.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct PageOffset(u16);

impl PageOffset {
    /// Creates a new offset from the given `u16`. Panics if the passed value is >=4096.
    pub fn new(offset: u16) -> Self {
        assert!(offset < (1 << 12));
        Self(offset)
    }
}

impl From<PageOffset> for u16 {
    fn from(offset: PageOffset) -> Self {
        offset.0
    }
}

impl From<PageOffset> for u32 {
    fn from(offset: PageOffset) -> Self {
        u32::from(offset.0)
    }
}

impl From<PageOffset> for u64 {
    fn from(offset: PageOffset) -> Self {
        u64::from(offset.0)
    }
}
<|endoftext|>


<commit_before>//! An implementation of something like dual contouring.
//! Dual contouring examines the voxel grid and looks for edges that cross between different material types.
//! If at least one of the materials is non-opaque, polygons are generated by using vertex data from the adjacent voxels.
//! See the SIGGRAPH paper for more details, but note that this probably isn't precisely a dual contouring implementation.

mod voxel {
  pub use voxel_data::impls::surface_vertex::T::*;
  pub use voxel_data::impls::surface_vertex::*;
}

#[allow(missing_docs)]
pub mod material {
  /// This trait provides the functionality required from materials for this algorithm.
  pub trait T : Eq {
    /// Is this material opaque?
    fn is_opaque(&self) -> bool;
  }
}

#[allow(missing_docs)]
pub mod polygon {
  use cgmath::{Point3, Vector3};

  /// The polygon type produced by this algorithm.
  pub struct T<Material> {
    #[allow(missing_docs)]
    pub vertices: [Point3<f32>; 3],
    #[allow(missing_docs)]
    pub normals: [Vector3<f32>; 3],
    #[allow(missing_docs)]
    pub material: Material,
  }
}

#[allow(missing_docs)]
pub mod voxel_storage {
  use cgmath::{Point3, Vector3};
  use voxel_data;

  use super::material;

  #[allow(missing_docs)]
  pub struct VoxelData {
    pub bounds: voxel_data::bounds::T,
    pub vertex: Point3<f32>,
    pub normal: Vector3<f32>,
  }

  /// The voxel storage interface required by dual contouring.
  pub trait T<Material> where Material: material::T {
    #[allow(missing_docs)]
    fn get_material(&mut self, voxel: &voxel_data::bounds::T) -> Material;
    /// Get the data for the given voxel. This function may also return data from a larger encompassing voxel.
    fn get_voxel_data(&mut self, voxel: &voxel_data::bounds::T) -> VoxelData;
  }
}

#[allow(missing_docs)]
pub mod edge {
  use cgmath::{Aabb, Point, Point3, Vector, Vector3};
  use voxel_data;

  use super::{voxel_storage, polygon, material};

  #[derive(Debug, Clone, Copy)]
  pub enum Direction { X, Y, Z }

  #[allow(missing_docs)]
  pub struct T {
    low_corner: Point3<i32>,
    lg_size: i16,
    direction: Direction,
  }

  fn corner_bounds(edge: &T) -> [voxel_data::bounds::T; 2] {
    let p0 = edge.low_corner;
    let d =
      match edge.direction {
        Direction::X => Vector3::new(1, 0, 0),
        Direction::Y => Vector3::new(0, 1, 0),
        Direction::Z => Vector3::new(0, 0, 1),
      };
    let p1 = p0.add_v(&d);
    [
      voxel_data::bounds::new(p0.x, p0.y, p0.z, edge.lg_size),
      voxel_data::bounds::new(p1.x, p1.y, p1.z, edge.lg_size),
    ]
  }

  fn neighbors(edge: &T) -> [voxel_data::bounds::T; 4] {
    let (v1, v2) =
      match edge.direction {
        Direction::X => (Vector3::new(0, 0, -1), Vector3::new(0, -1, 0)),
        Direction::Y => (Vector3::new(-1, 0, 0), Vector3::new(0, 0, -1)),
        Direction::Z => (Vector3::new(0, -1, 0), Vector3::new(-1, 0, 0)),
      };
    let make_bounds = |p: Point3<i32>| voxel_data::bounds::new(p.x, p.y, p.z, edge.lg_size);
    [
      make_bounds(edge.low_corner),
      make_bounds(edge.low_corner.add_v(&v1)),
      make_bounds(edge.low_corner.add_v(&v1).add_v(&v2)),
      make_bounds(edge.low_corner.add_v(&v2)),
    ]
  }

  enum Crossing<Material> {
    None,
    LowInside(Material),
    HighInside(Material),
  }

  fn crossing<Material, Voxels>(
    voxels: &mut Voxels,
    edge: &T,
  ) -> Crossing<Material> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
  {
    let [b0, b1] = corner_bounds(edge);
    let material = voxels.get_material(&b0);
    let neighbor_material = voxels.get_material(&b1);
    if material == neighbor_material {
      Crossing::None
    } else if material.is_opaque() {
      Crossing::LowInside(material)
    } else {
      Crossing::HighInside(neighbor_material)
    }
  }

  fn resolve_voxels<'a, Material, Voxels, It>(
    voxels: &mut Voxels,
    bounds: It,
  ) -> Vec<(Point3<f32>, Vector3<f32>)> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
    It: Iterator<Item=&'a voxel_data::bounds::T>,
  {
    let mut resolved_voxel_data = Vec::new();
    let mut resolved_bounds: Vec<voxel_data::bounds::T> = Vec::new();

    'resolve_loop: for bounds in bounds {
      for resolved_bounds in &resolved_bounds {
        if resolved_bounds.contains(&bounds) {
          continue 'resolve_loop;
        }
      }

      let voxel_data = voxels.get_voxel_data(&bounds);
      resolved_bounds.push(voxel_data.bounds);
      resolved_voxel_data.push((voxel_data.vertex, voxel_data.normal));
    }

    resolved_voxel_data
  }

  /// Run dual contouring on a single edge
  // TODO: Return an iterator
  pub fn extract<Material, Voxels, OnPolygon>(
    voxels: &mut Voxels,
    edge: &T,
    mut poly: OnPolygon,
  ) where
    Material: material::T + Clone,
    Voxels: voxel_storage::T<Material>,
    OnPolygon: FnMut(polygon::T<Material>),
  {
    let (material, vertices_and_normals) =
      match crossing(voxels, edge) {
        Crossing::None => return,
        Crossing::HighInside(material) => {
          (material, resolve_voxels(voxels, neighbors(&edge).iter()))
        },
        Crossing::LowInside(material) => {
          (material, resolve_voxels(voxels, neighbors(&edge).iter().rev()))
        }
      };

    if vertices_and_normals.len() == 3 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      poly(
        polygon::T {
          vertices: [v0, v1, v2],
          normals: [n0, n1, n2],
          material: material,
        }
      );
    } else if vertices_and_normals.len() == 4 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      let (v3, n3) = vertices_and_normals[3];
      let v_center =
        v0.add_v(&v1.to_vec()).add_v(&v2.to_vec()).add_v(&v3.to_vec()).div_s(4.0);
      let n_center =
        n0.add_v(&n1).add_v(&n2).add_v(&n3).div_s(4.0);
      poly(
        polygon::T {
          vertices: [v0, v1, v_center],
          normals: [n0, n1, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v1, v2, v_center],
          normals: [n1, n2, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v2, v3, v_center],
          normals: [n2, n3, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v3, v0, v_center],
          normals: [n3, n0, n_center],
          material: material.clone(),
        }
      );
    } else {
      panic!("Edge has an unexpected number of neighbors: {}", vertices_and_normals.len());
    }
  }
}
<commit_msg>make edge fields public<commit_after>//! An implementation of something like dual contouring.
//! Dual contouring examines the voxel grid and looks for edges that cross between different material types.
//! If at least one of the materials is non-opaque, polygons are generated by using vertex data from the adjacent voxels.
//! See the SIGGRAPH paper for more details, but note that this probably isn't precisely a dual contouring implementation.

mod voxel {
  pub use voxel_data::impls::surface_vertex::T::*;
  pub use voxel_data::impls::surface_vertex::*;
}

#[allow(missing_docs)]
pub mod material {
  /// This trait provides the functionality required from materials for this algorithm.
  pub trait T : Eq {
    /// Is this material opaque?
    fn is_opaque(&self) -> bool;
  }
}

#[allow(missing_docs)]
pub mod polygon {
  use cgmath::{Point3, Vector3};

  /// The polygon type produced by this algorithm.
  pub struct T<Material> {
    #[allow(missing_docs)]
    pub vertices: [Point3<f32>; 3],
    #[allow(missing_docs)]
    pub normals: [Vector3<f32>; 3],
    #[allow(missing_docs)]
    pub material: Material,
  }
}

#[allow(missing_docs)]
pub mod voxel_storage {
  use cgmath::{Point3, Vector3};
  use voxel_data;

  use super::material;

  #[allow(missing_docs)]
  pub struct VoxelData {
    pub bounds: voxel_data::bounds::T,
    pub vertex: Point3<f32>,
    pub normal: Vector3<f32>,
  }

  /// The voxel storage interface required by dual contouring.
  pub trait T<Material> where Material: material::T {
    #[allow(missing_docs)]
    fn get_material(&mut self, voxel: &voxel_data::bounds::T) -> Material;
    /// Get the data for the given voxel. This function may also return data from a larger encompassing voxel.
    fn get_voxel_data(&mut self, voxel: &voxel_data::bounds::T) -> VoxelData;
  }
}

#[allow(missing_docs)]
pub mod edge {
  use cgmath::{Aabb, Point, Point3, Vector, Vector3};
  use voxel_data;

  use super::{voxel_storage, polygon, material};

  #[derive(Debug, Clone, Copy)]
  pub enum Direction { X, Y, Z }

  #[allow(missing_docs)]
  pub struct T {
    pub low_corner: Point3<i32>,
    pub lg_size: i16,
    pub direction: Direction,
  }

  fn corner_bounds(edge: &T) -> [voxel_data::bounds::T; 2] {
    let p0 = edge.low_corner;
    let d =
      match edge.direction {
        Direction::X => Vector3::new(1, 0, 0),
        Direction::Y => Vector3::new(0, 1, 0),
        Direction::Z => Vector3::new(0, 0, 1),
      };
    let p1 = p0.add_v(&d);
    [
      voxel_data::bounds::new(p0.x, p0.y, p0.z, edge.lg_size),
      voxel_data::bounds::new(p1.x, p1.y, p1.z, edge.lg_size),
    ]
  }

  fn neighbors(edge: &T) -> [voxel_data::bounds::T; 4] {
    let (v1, v2) =
      match edge.direction {
        Direction::X => (Vector3::new(0, 0, -1), Vector3::new(0, -1, 0)),
        Direction::Y => (Vector3::new(-1, 0, 0), Vector3::new(0, 0, -1)),
        Direction::Z => (Vector3::new(0, -1, 0), Vector3::new(-1, 0, 0)),
      };
    let make_bounds = |p: Point3<i32>| voxel_data::bounds::new(p.x, p.y, p.z, edge.lg_size);
    [
      make_bounds(edge.low_corner),
      make_bounds(edge.low_corner.add_v(&v1)),
      make_bounds(edge.low_corner.add_v(&v1).add_v(&v2)),
      make_bounds(edge.low_corner.add_v(&v2)),
    ]
  }

  enum Crossing<Material> {
    None,
    LowInside(Material),
    HighInside(Material),
  }

  fn crossing<Material, Voxels>(
    voxels: &mut Voxels,
    edge: &T,
  ) -> Crossing<Material> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
  {
    let [b0, b1] = corner_bounds(edge);
    let material = voxels.get_material(&b0);
    let neighbor_material = voxels.get_material(&b1);
    if material == neighbor_material {
      Crossing::None
    } else if material.is_opaque() {
      Crossing::LowInside(material)
    } else {
      Crossing::HighInside(neighbor_material)
    }
  }

  fn resolve_voxels<'a, Material, Voxels, It>(
    voxels: &mut Voxels,
    bounds: It,
  ) -> Vec<(Point3<f32>, Vector3<f32>)> where
    Material: material::T,
    Voxels: voxel_storage::T<Material>,
    It: Iterator<Item=&'a voxel_data::bounds::T>,
  {
    let mut resolved_voxel_data = Vec::new();
    let mut resolved_bounds: Vec<voxel_data::bounds::T> = Vec::new();

    'resolve_loop: for bounds in bounds {
      for resolved_bounds in &resolved_bounds {
        if resolved_bounds.contains(&bounds) {
          continue 'resolve_loop;
        }
      }

      let voxel_data = voxels.get_voxel_data(&bounds);
      resolved_bounds.push(voxel_data.bounds);
      resolved_voxel_data.push((voxel_data.vertex, voxel_data.normal));
    }

    resolved_voxel_data
  }

  /// Run dual contouring on a single edge
  // TODO: Return an iterator
  pub fn extract<Material, Voxels, OnPolygon>(
    voxels: &mut Voxels,
    edge: &T,
    mut poly: OnPolygon,
  ) where
    Material: material::T + Clone,
    Voxels: voxel_storage::T<Material>,
    OnPolygon: FnMut(polygon::T<Material>),
  {
    let (material, vertices_and_normals) =
      match crossing(voxels, edge) {
        Crossing::None => return,
        Crossing::HighInside(material) => {
          (material, resolve_voxels(voxels, neighbors(&edge).iter()))
        },
        Crossing::LowInside(material) => {
          (material, resolve_voxels(voxels, neighbors(&edge).iter().rev()))
        }
      };

    if vertices_and_normals.len() == 3 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      poly(
        polygon::T {
          vertices: [v0, v1, v2],
          normals: [n0, n1, n2],
          material: material,
        }
      );
    } else if vertices_and_normals.len() == 4 {
      let (v0, n0) = vertices_and_normals[0];
      let (v1, n1) = vertices_and_normals[1];
      let (v2, n2) = vertices_and_normals[2];
      let (v3, n3) = vertices_and_normals[3];
      let v_center =
        v0.add_v(&v1.to_vec()).add_v(&v2.to_vec()).add_v(&v3.to_vec()).div_s(4.0);
      let n_center =
        n0.add_v(&n1).add_v(&n2).add_v(&n3).div_s(4.0);
      poly(
        polygon::T {
          vertices: [v0, v1, v_center],
          normals: [n0, n1, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v1, v2, v_center],
          normals: [n1, n2, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v2, v3, v_center],
          normals: [n2, n3, n_center],
          material: material.clone(),
        }
      );
      poly(
        polygon::T {
          vertices: [v3, v0, v_center],
          normals: [n3, n0, n_center],
          material: material.clone(),
        }
      );
    } else {
      panic!("Edge has an unexpected number of neighbors: {}", vertices_and_normals.len());
    }
  }
}
<|endoftext|>