<commit_before>use std::{
    env::current_exe,
    path::PathBuf,
};
use piston_window::*;
use crate::{
    direction::Direction,
    game::*,
    settings::Settings,
};

pub struct App<'a> {
    window: PistonWindow,
    window_size: [u32; 2],
    game: Game<'a>,
    turn_active: bool,
    turn_end_timer: f64,
    cpu_turn_timer: f64,
    mouse_cursor: [f64; 2],
    grid_area: [u32; 4],
}

impl<'a> App<'a> {
    pub fn new(settings: &Settings) -> App {
        let grid_area = [
            settings.space_size,
            settings.space_size * 3,
            settings.spaces[0] as u32 * settings.space_size,
            settings.spaces[1] as u32 * settings.space_size
        ];

        let window_size = [
            grid_area[2] + settings.space_size * 2,
            grid_area[3] + settings.space_size * 4
        ];

        let window_title = "Battleship";
        let window: PistonWindow = WindowSettings::new(
            window_title,
            window_size,
            )
            .exit_on_esc(true)
            .resizable(false)
            .build()
            .unwrap();

        App {
            window: window,
            window_size: window_size,
            game: Game::new(&settings),
            turn_active: true,
            turn_end_timer: 0.0,
            cpu_turn_timer: 0.0,
            mouse_cursor: [0.0; 2],
            grid_area: grid_area,
        }
    }

    pub fn init(&mut self) {
        self.window.set_ups(60);
        self.window.set_max_fps(60);

        // TODO set textures in a not terrible way
        let assets_dir = Self::get_assets_dir(current_exe().unwrap()).unwrap();
        let images_dir: PathBuf = assets_dir.join("images");
        let mut space_textures = vec![];

        for state in 0..3 {
            let image_file = format!("gridspace-{}.png", state);
            space_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        space_textures.push(self.get_texture(images_dir.join("shipspace.png")));

        let grid_cursor_texture = self.get_texture(images_dir.join("grid-cursor.png"));

        let mut ship_textures = vec![];
        for ship_size in 2..6 {
            let image_file = format!("ship-{}.png", ship_size);
            ship_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        let player_text = [
            self.get_texture(images_dir.join("player-1.png")),
            self.get_texture(images_dir.join("player-2.png")),
        ];

        let game_over_text = [
            self.get_texture(images_dir.join("game-over.png")),
            self.get_texture(images_dir.join("wins.png")),
        ];

        while let Some(e) = self.window.next() {
            if let Some(p) = e.press_args() {
                match p {
                    Button::Mouse(mouse::MouseButton::Left) => self.mouse_left_click(),
                    Button::Mouse(mouse::MouseButton::Right) => self.button_secondary(),
                    Button::Keyboard(keyboard::Key::Left) => self.button_left(),
                    Button::Keyboard(keyboard::Key::Right) => self.button_right(),
                    Button::Keyboard(keyboard::Key::Up) => self.button_up(),
                    Button::Keyboard(keyboard::Key::Down) => self.button_down(),
                    Button::Keyboard(keyboard::Key::Return) => self.button_primary(),
                    Button::Keyboard(keyboard::Key::Space) => self.button_secondary(),
                    _ => {}
                }
            }

            if let Some(c) = e.mouse_cursor_args() {
                self.mouse_cursor_movement(&c);
            }

            if let Some(u) = e.update_args() {
                self.update(&u);
            }

            if e.render_args().is_some() {
                let current_player = self.game.active_player();
                let game_state = self.game.state();
                let shown_player = match game_state {
                    GameState::ShipPlacement => current_player,
                    _ => self.game.inactive_player()
                };

                let space_size_u32 = self.game.settings.space_size as u32;
                let grid_area = self.grid_area;
                let window_size = self.window_size;
                let turn_end_timer = self.turn_end_timer;
                let game_winner = self.game.get_winner();
                let game_turn = self.game.turn();
                let turn_active = self.turn_active;

                self.window.draw_2d(&e, |c, g| {
                    clear([0.6, 0.6, 1.0, 1.0], g);

                    // Ship icons above grid
                    for (i, ship) in shown_player.ships().iter().enumerate() {
                        if ship.is_active() {
                            let transform = c.transform.trans(
                                (space_size_u32 * 2 * i as u32 + grid_area[0] * 2) as f64,
                                30.0 as f64,
                            );
                            image(&ship_textures[i], transform, g);
                        }
                    }

                    // Grid spaces
                    for space in shown_player.spaces() {
                        let space_pos = space.pos();
                        let transform = c.transform.trans(
                            (space_size_u32 * space_pos[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * space_pos[1] as u32 + grid_area[1]) as f64,
                        );

                        // Only show ship locations during ship placement or if the
                        // current player is computer-controlled.
                        if shown_player.ship_is_in_space(space_pos) && (game_state == GameState::ShipPlacement || (space.is_unchecked() && current_player.is_cpu)) {
                            image(&space_textures[3], transform, g);
                        } else {
                            let space_state = if space.is_unchecked() {
                                0
                            } else if space.is_empty() {
                                1
                            } else {
                                2
                            };
                            image(&space_textures[space_state], transform, g);
                        }
                    }

                    // During ship placement, show the temporary position of the
                    // next ship to be placed.
                    if game_state == GameState::ShipPlacement {
                        for pos in &shown_player.temp_ship_pos {
                            let transform = c.transform.trans(
                                (space_size_u32 * pos[0] as u32 + grid_area[0]) as f64,
                                (space_size_u32 * pos[1] as u32 + grid_area[1]) as f64,
                            );
                            image(&space_textures[3], transform, g);
                        }
                    }

                    // During the game, show the player's grid cursor.
                    if game_state == GameState::Active && turn_end_timer == 0.0 && !current_player.is_cpu {
                        let grid_cursor = current_player.get_grid_cursor();
                        let transform = c.transform.trans(
                            (space_size_u32 * grid_cursor[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * grid_cursor[1] as u32 + grid_area[1]) as f64,
                        );
                        image(&grid_cursor_texture, transform, g);
                    }

                    // Current player text image
                    if game_winner.is_none() {
                        let turn = game_turn;
                        let player_text_size = player_text[turn].get_size();
                        let transform = c.transform.trans(
                            ((window_size[0] - player_text_size.0) / 2) as f64,
                            2.0
                        );
                        image(&player_text[turn], transform, g);
                    }

                    // During turn transitions / game over, cover the window with
                    // a black rectangle of increasing opacity.
                    if !turn_active && turn_end_timer >= 0.75 {
                        let alpha = if game_state != GameState::Over {
                            (turn_end_timer as f32 - 0.75) / 0.75
                        } else {
                            (turn_end_timer as f32 - 0.75) / 1.125
                        };
                        rectangle(
                            [0.0, 0.0, 0.0, alpha],
                            [0.0, 0.0, window_size[0] as f64, window_size[1] as f64],
                            c.transform,
                            g
                        );
                    }

                    // Game over content, to appear over the black rectangle.
                    if turn_end_timer >= 1.5 && game_winner.is_some() {
                        let winner = game_winner.unwrap();
                        let game_over_text_size = game_over_text[0].get_size();
                        let wins_text_size = game_over_text[1].get_size();
                        let player_text_size = player_text[winner].get_size();
                        image(&game_over_text[0], c.transform.trans(
                            ((window_size[0] - game_over_text_size.0) / 2) as f64,
                            2.0
                        ), g);
                        image(&player_text[winner], c.transform.trans(
                            ((window_size[0] - player_text_size.0 - wins_text_size.0 - 2) / 2) as f64,
                            22.0
                        ), g);
                        image(&game_over_text[1], c.transform.trans(
                            ((window_size[0] + player_text_size.0 - wins_text_size.0 + 2) / 2) as f64,
                            22.0
                        ), g);
                    }
                });
            }
        }
    }

    fn update(&mut self, u: &UpdateArgs) {
        let state = self.game.state();
        let active_player_is_cpu = self.game.is_active_player_cpu();

        if state == GameState::ShipPlacement {
            if active_player_is_cpu {
                self.game.active_player_mut().cpu_place_ships();
            }

            if self.game.active_player().ships().len() == 4 {
                self.game.switch_turn();
            }

            // All ships have been placed; start the game.
            if self.game.active_player().ships().len() == 4 && self.game.inactive_player().ships().len() == 4 {
                self.game.start();
            }
        } else if state == GameState::Active || state == GameState::Over {
            if !self.turn_active {
                // Continue/end the end-of-turn delay.
                if self.turn_end_timer < 1.5 {
                    self.turn_end_timer += u.dt;
                } else if state == GameState::Active {
                    self.game.switch_turn();
                    self.turn_end_timer = 0.0;
                    self.turn_active = true;
                }
            }

            // Continue/end the delay when CPU players take their turn.
            if self.turn_active && active_player_is_cpu {
                self.cpu_turn_timer += u.dt;

                if self.cpu_turn_timer >= 1.0 {
                    let cpu_space = self.game.inactive_player().cpu_select_space();
                    self.game.select_opponent_space(&cpu_space);
                    self.cpu_turn_timer = 0.0;
                    self.turn_active = false;
                }
            }
        }
    }

    fn primary_action(&mut self, grid_pos: &[u8; 2]) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().place_temp_ship();
        }

        if self.game.is_player_selecting_space() {
            self.game.select_opponent_space(grid_pos);
            self.turn_active = false;
        }
    }

    /// Processes left button presses according to the current program state.
    fn button_left(&mut self) {
        self.movement(Direction::West);
    }

    /// Processes right button presses according to the current program state.
    fn button_right(&mut self) {
        self.movement(Direction::East);
    }

    /// Processes up button presses according to the current program state.
    fn button_up(&mut self) {
        self.movement(Direction::North);
    }

    /// Processes down button presses according to the current program state.
    fn button_down(&mut self) {
        self.movement(Direction::South);
    }

    /// Processes primary button presses according to the current program state.
    fn button_primary(&mut self) {
        let grid_pos = self.game.active_player().get_grid_cursor();
        self.primary_action(&grid_pos);
    }

    /// Processes secondary button presses according to the current program state.
    fn button_secondary(&mut self) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().rotate_temp_ship();
        }
    }

    /// Processes left mouse clicks according to the current program state.
    fn mouse_left_click(&mut self) {
        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            self.primary_action(&grid_pos);
        }
    }

    /// Performs grid movement according to the current program state.
    fn movement(&mut self, direction: Direction) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().move_temp_ship(direction);
        }

        if self.game.is_player_selecting_space() {
            self.game.active_player_mut().move_grid_cursor(direction);
        }
    }

    /// Records the last known mouse cursor position.
    pub fn mouse_cursor_movement(&mut self, c: &[f64; 2]) {
        self.mouse_cursor = *c;

        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            let is_player_turn = self.game.is_player_turn();
            let game_state = self.game.state();
            let ref mut player = self.game.active_player_mut();

            if game_state == GameState::ShipPlacement {
                if let Some(ship) = player.get_ship_position(
                    grid_pos,
                    player.temp_ship_dir,
                    player.ships().len() as u8 + 2
                ) {
                    player.temp_ship_pos = ship;
                }
            }

            if game_state == GameState::Active && is_player_turn {
                player.set_grid_cursor(&grid_pos);
            }
        }
    }

    /// Returns the grid coordinates of the mouse cursor position.
    fn mouse_cursor_grid_position(&self) -> Option<[u8; 2]> {
        match self.mouse_over_grid() {
            true => Some([
                ((self.mouse_cursor[0] - self.grid_area[0] as f64) / self.grid_area[0] as f64) as u8,
                ((self.mouse_cursor[1] - self.grid_area[1] as f64) / self.grid_area[0] as f64) as u8,
            ]),
            false => None,
        }
    }

    fn mouse_over_grid(&self) -> bool {
        self.mouse_cursor[0] >= self.grid_area[0] as f64
        && self.mouse_cursor[1] >= self.grid_area[1] as f64
        && self.mouse_cursor[0] < (self.grid_area[0] + self.grid_area[2]) as f64
        && self.mouse_cursor[1] < (self.grid_area[1] + self.grid_area[3]) as f64
    }

    /// Returns the texture from the file at the given path.
    fn get_texture(&mut self, path: PathBuf) -> G2dTexture {
        Texture::from_path(
            &mut self.window.factory,
            path,
            Flip::None,
            &TextureSettings::new(),
        ).unwrap()
    }

    /// Returns the assets directory, if it could be found.
    fn get_assets_dir(mut dir: PathBuf) -> Result<PathBuf, &'static str> {
        let mut result = None;

        while dir.pop() {
            if dir.join("assets").exists() {
                result = Some(dir.join("assets"));
                break;
            }
        }

        result.ok_or("could not find assets directory")
    }
}

<commit_msg>Fix being able to move grid cursor after selecting space<commit_after>use std::{
    env::current_exe,
    path::PathBuf,
};
use piston_window::*;
use crate::{
    direction::Direction,
    game::*,
    settings::Settings,
};

pub struct App<'a> {
    window: PistonWindow,
    window_size: [u32; 2],
    game: Game<'a>,
    turn_active: bool,
    turn_end_timer: f64,
    cpu_turn_timer: f64,
    mouse_cursor: [f64; 2],
    grid_area: [u32; 4],
}

impl<'a> App<'a> {
    pub fn new(settings: &Settings) -> App {
        let grid_area = [
            settings.space_size,
            settings.space_size * 3,
            settings.spaces[0] as u32 * settings.space_size,
            settings.spaces[1] as u32 * settings.space_size
        ];

        let window_size = [
            grid_area[2] + settings.space_size * 2,
            grid_area[3] + settings.space_size * 4
        ];

        let window_title = "Battleship";
        let window: PistonWindow = WindowSettings::new(
            window_title,
            window_size,
            )
            .exit_on_esc(true)
            .resizable(false)
            .build()
            .unwrap();

        App {
            window: window,
            window_size: window_size,
            game: Game::new(&settings),
            turn_active: true,
            turn_end_timer: 0.0,
            cpu_turn_timer: 0.0,
            mouse_cursor: [0.0; 2],
            grid_area: grid_area,
        }
    }

    pub fn init(&mut self) {
        self.window.set_ups(60);
        self.window.set_max_fps(60);

        // TODO set textures in a not terrible way
        let assets_dir = Self::get_assets_dir(current_exe().unwrap()).unwrap();
        let images_dir: PathBuf = assets_dir.join("images");
        let mut space_textures = vec![];

        for state in 0..3 {
            let image_file = format!("gridspace-{}.png", state);
            space_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        space_textures.push(self.get_texture(images_dir.join("shipspace.png")));

        let grid_cursor_texture = self.get_texture(images_dir.join("grid-cursor.png"));

        let mut ship_textures = vec![];
        for ship_size in 2..6 {
            let image_file = format!("ship-{}.png", ship_size);
            ship_textures.push(self.get_texture(images_dir.join(&image_file)));
        }

        let player_text = [
            self.get_texture(images_dir.join("player-1.png")),
            self.get_texture(images_dir.join("player-2.png")),
        ];

        let game_over_text = [
            self.get_texture(images_dir.join("game-over.png")),
            self.get_texture(images_dir.join("wins.png")),
        ];

        while let Some(e) = self.window.next() {
            if let Some(p) = e.press_args() {
                match p {
                    Button::Mouse(mouse::MouseButton::Left) => self.mouse_left_click(),
                    Button::Mouse(mouse::MouseButton::Right) => self.button_secondary(),
                    Button::Keyboard(keyboard::Key::Left) => self.button_left(),
                    Button::Keyboard(keyboard::Key::Right) => self.button_right(),
                    Button::Keyboard(keyboard::Key::Up) => self.button_up(),
                    Button::Keyboard(keyboard::Key::Down) => self.button_down(),
                    Button::Keyboard(keyboard::Key::Return) => self.button_primary(),
                    Button::Keyboard(keyboard::Key::Space) => self.button_secondary(),
                    _ => {}
                }
            }

            if let Some(c) = e.mouse_cursor_args() {
                self.mouse_cursor_movement(&c);
            }

            if let Some(u) = e.update_args() {
                self.update(&u);
            }

            if e.render_args().is_some() {
                let current_player = self.game.active_player();
                let game_state = self.game.state();
                let shown_player = match game_state {
                    GameState::ShipPlacement => current_player,
                    _ => self.game.inactive_player()
                };

                let space_size_u32 = self.game.settings.space_size as u32;
                let grid_area = self.grid_area;
                let window_size = self.window_size;
                let turn_end_timer = self.turn_end_timer;
                let game_winner = self.game.get_winner();
                let game_turn = self.game.turn();
                let turn_active = self.turn_active;

                self.window.draw_2d(&e, |c, g| {
                    clear([0.6, 0.6, 1.0, 1.0], g);

                    // Ship icons above grid
                    for (i, ship) in shown_player.ships().iter().enumerate() {
                        if ship.is_active() {
                            let transform = c.transform.trans(
                                (space_size_u32 * 2 * i as u32 + grid_area[0] * 2) as f64,
                                30.0 as f64,
                            );
                            image(&ship_textures[i], transform, g);
                        }
                    }

                    // Grid spaces
                    for space in shown_player.spaces() {
                        let space_pos = space.pos();
                        let transform = c.transform.trans(
                            (space_size_u32 * space_pos[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * space_pos[1] as u32 + grid_area[1]) as f64,
                        );

                        // Only show ship locations during ship placement or if the
                        // current player is computer-controlled.
                        if shown_player.ship_is_in_space(space_pos) && (game_state == GameState::ShipPlacement || (space.is_unchecked() && current_player.is_cpu)) {
                            image(&space_textures[3], transform, g);
                        } else {
                            let space_state = if space.is_unchecked() {
                                0
                            } else if space.is_empty() {
                                1
                            } else {
                                2
                            };
                            image(&space_textures[space_state], transform, g);
                        }
                    }

                    // During ship placement, show the temporary position of the
                    // next ship to be placed.
                    if game_state == GameState::ShipPlacement {
                        for pos in &shown_player.temp_ship_pos {
                            let transform = c.transform.trans(
                                (space_size_u32 * pos[0] as u32 + grid_area[0]) as f64,
                                (space_size_u32 * pos[1] as u32 + grid_area[1]) as f64,
                            );
                            image(&space_textures[3], transform, g);
                        }
                    }

                    // During the game, show the player's grid cursor.
                    if game_state == GameState::Active && turn_end_timer == 0.0 && !current_player.is_cpu {
                        let grid_cursor = current_player.get_grid_cursor();
                        let transform = c.transform.trans(
                            (space_size_u32 * grid_cursor[0] as u32 + grid_area[0]) as f64,
                            (space_size_u32 * grid_cursor[1] as u32 + grid_area[1]) as f64,
                        );
                        image(&grid_cursor_texture, transform, g);
                    }

                    // Current player text image
                    if game_winner.is_none() {
                        let turn = game_turn;
                        let player_text_size = player_text[turn].get_size();
                        let transform = c.transform.trans(
                            ((window_size[0] - player_text_size.0) / 2) as f64,
                            2.0
                        );
                        image(&player_text[turn], transform, g);
                    }

                    // During turn transitions / game over, cover the window with
                    // a black rectangle of increasing opacity.
                    if !turn_active && turn_end_timer >= 0.75 {
                        let alpha = if game_state != GameState::Over {
                            (turn_end_timer as f32 - 0.75) / 0.75
                        } else {
                            (turn_end_timer as f32 - 0.75) / 1.125
                        };
                        rectangle(
                            [0.0, 0.0, 0.0, alpha],
                            [0.0, 0.0, window_size[0] as f64, window_size[1] as f64],
                            c.transform,
                            g
                        );
                    }

                    // Game over content, to appear over the black rectangle.
                    if turn_end_timer >= 1.5 && game_winner.is_some() {
                        let winner = game_winner.unwrap();
                        let game_over_text_size = game_over_text[0].get_size();
                        let wins_text_size = game_over_text[1].get_size();
                        let player_text_size = player_text[winner].get_size();
                        image(&game_over_text[0], c.transform.trans(
                            ((window_size[0] - game_over_text_size.0) / 2) as f64,
                            2.0
                        ), g);
                        image(&player_text[winner], c.transform.trans(
                            ((window_size[0] - player_text_size.0 - wins_text_size.0 - 2) / 2) as f64,
                            22.0
                        ), g);
                        image(&game_over_text[1], c.transform.trans(
                            ((window_size[0] + player_text_size.0 - wins_text_size.0 + 2) / 2) as f64,
                            22.0
                        ), g);
                    }
                });
            }
        }
    }

    fn update(&mut self, u: &UpdateArgs) {
        let state = self.game.state();
        let active_player_is_cpu = self.game.is_active_player_cpu();

        if state == GameState::ShipPlacement {
            if active_player_is_cpu {
                self.game.active_player_mut().cpu_place_ships();
            }

            if self.game.active_player().ships().len() == 4 {
                self.game.switch_turn();
            }

            // All ships have been placed; start the game.
            if self.game.active_player().ships().len() == 4 && self.game.inactive_player().ships().len() == 4 {
                self.game.start();
            }
        } else if state == GameState::Active || state == GameState::Over {
            if !self.turn_active {
                // Continue/end the end-of-turn delay.
                if self.turn_end_timer < 1.5 {
                    self.turn_end_timer += u.dt;
                } else if state == GameState::Active {
                    self.game.switch_turn();
                    self.turn_end_timer = 0.0;
                    self.turn_active = true;
                }
            }

            // Continue/end the delay when CPU players take their turn.
            if self.turn_active && active_player_is_cpu {
                self.cpu_turn_timer += u.dt;

                if self.cpu_turn_timer >= 1.0 {
                    let cpu_space = self.game.inactive_player().cpu_select_space();
                    self.game.select_opponent_space(&cpu_space);
                    self.cpu_turn_timer = 0.0;
                    self.turn_active = false;
                }
            }
        }
    }

    fn primary_action(&mut self, grid_pos: &[u8; 2]) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().place_temp_ship();
        }

        if self.game.is_player_selecting_space() {
            self.game.select_opponent_space(grid_pos);
            self.turn_active = false;
        }
    }

    /// Processes left button presses according to the current program state.
    fn button_left(&mut self) {
        self.movement(Direction::West);
    }

    /// Processes right button presses according to the current program state.
    fn button_right(&mut self) {
        self.movement(Direction::East);
    }

    /// Processes up button presses according to the current program state.
    fn button_up(&mut self) {
        self.movement(Direction::North);
    }

    /// Processes down button presses according to the current program state.
    fn button_down(&mut self) {
        self.movement(Direction::South);
    }

    /// Processes primary button presses according to the current program state.
    fn button_primary(&mut self) {
        let grid_pos = self.game.active_player().get_grid_cursor();
        self.primary_action(&grid_pos);
    }

    /// Processes secondary button presses according to the current program state.
    fn button_secondary(&mut self) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().rotate_temp_ship();
        }
    }

    /// Processes left mouse clicks according to the current program state.
    fn mouse_left_click(&mut self) {
        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            self.primary_action(&grid_pos);
        }
    }

    /// Performs grid movement according to the current program state.
    fn movement(&mut self, direction: Direction) {
        if self.game.is_player_placing_ship() {
            self.game.active_player_mut().move_temp_ship(direction);
        }

        if self.game.is_player_selecting_space() && self.turn_active {
            self.game.active_player_mut().move_grid_cursor(direction);
        }
    }

    /// Records the last known mouse cursor position.
    pub fn mouse_cursor_movement(&mut self, c: &[f64; 2]) {
        self.mouse_cursor = *c;

        if let Some(grid_pos) = self.mouse_cursor_grid_position() {
            let is_player_turn = self.game.is_player_turn();
            let game_state = self.game.state();
            let ref mut player = self.game.active_player_mut();

            if game_state == GameState::ShipPlacement {
                if let Some(ship) = player.get_ship_position(
                    grid_pos,
                    player.temp_ship_dir,
                    player.ships().len() as u8 + 2
                ) {
                    player.temp_ship_pos = ship;
                }
            }

            if game_state == GameState::Active && is_player_turn {
                player.set_grid_cursor(&grid_pos);
            }
        }
    }

    /// Returns the grid coordinates of the mouse cursor position.
    fn mouse_cursor_grid_position(&self) -> Option<[u8; 2]> {
        match self.mouse_over_grid() {
            true => Some([
                ((self.mouse_cursor[0] - self.grid_area[0] as f64) / self.grid_area[0] as f64) as u8,
                ((self.mouse_cursor[1] - self.grid_area[1] as f64) / self.grid_area[0] as f64) as u8,
            ]),
            false => None,
        }
    }

    fn mouse_over_grid(&self) -> bool {
        self.mouse_cursor[0] >= self.grid_area[0] as f64
        && self.mouse_cursor[1] >= self.grid_area[1] as f64
        && self.mouse_cursor[0] < (self.grid_area[0] + self.grid_area[2]) as f64
        && self.mouse_cursor[1] < (self.grid_area[1] + self.grid_area[3]) as f64
    }

    /// Returns the texture from the file at the given path.
    fn get_texture(&mut self, path: PathBuf) -> G2dTexture {
        Texture::from_path(
            &mut self.window.factory,
            path,
            Flip::None,
            &TextureSettings::new(),
        ).unwrap()
    }

    /// Returns the assets directory, if it could be found.
    fn get_assets_dir(mut dir: PathBuf) -> Result<PathBuf, &'static str> {
        let mut result = None;

        while dir.pop() {
            if dir.join("assets").exists() {
                result = Some(dir.join("assets"));
                break;
            }
        }

        result.ok_or("could not find assets directory")
    }
}

<|endoftext|>


<commit_before>use std::str;
use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::sync::Arc;
use std::clone::Clone;

use hyper::status::StatusCode;
use hyper::server::{Handler, Server, Request, Response};
use mime_types::Types as MimeTypes;

pub use typemap::Key;
pub use hyper::header::Headers;
pub use hyper::header;
pub use hyper::mime;
pub use hyper::client::Client;
pub use request::SapperRequest;
pub use response::SapperResponse;
pub use router_m::Router;
pub use router::SapperRouter;
pub use handler::SapperHandler;

/// Path parameter type
#[derive(Clone)]
pub struct PathParams;

/// Re-export Status Codes
pub mod status {
    pub use hyper::status::StatusCode as Status;
    pub use hyper::status::StatusCode::*;
}

/// Sapper error enum
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    InvalidConfig,
    InvalidRouterConfig,
    FileNotExist,
    NotFound,
    Break,          // 400
    Unauthorized,   // 401
    Forbidden,      // 403
    TemporaryRedirect(String),     // 307
    Custom(String),
    CustomHtml(String),
    CustomJson(String),
}

/// Sapper result struct
pub type Result<T> = ::std::result::Result<T, Error>;

/// Sapper module trait
/// 3 methods: before, after, router
pub trait SapperModule: Sync + Send {

    /// module before filter, will be executed before handler
    fn before(&self, req: &mut SapperRequest) -> Result<()> {
        Ok(())
    }

    /// module after filter, will be executed after handler
    fn after(&self, req: &SapperRequest, res: &mut SapperResponse) -> Result<()> {
        Ok(())
    }

    /// module router method, used to write router collection of this module here
    fn router(&self, &mut SapperRouter) -> Result<()>;

}

/// Sapper smock trait, used to place global before and after middlewares
pub trait SapperSmock {
    fn before(&self, &mut SapperRequest) -> Result<()>;
    fn after(&self, &SapperRequest, &mut SapperResponse) -> Result<()>;
}

type GlobalInitClosure = Box<Fn(&mut SapperRequest) -> Result<()> + 'static + Send + Sync>;
type SapperSmockType = Box<SapperSmock + 'static + Send + Sync>;

/// Sapper app struct
pub struct SapperApp {
    // listen ip address
    pub address:        String,
    // listen port
    pub port:           u32,
    // for app entry, global middeware
    pub smock:          Option<Arc<SapperSmockType>>,
    // routers actually use to recognize
    pub routers:        Router,
    // do simple static file service
    pub static_service: bool,
    // if need init something, put them here
    pub init_closure:   Option<Arc<GlobalInitClosure>>,
    // 404 not found page
    pub not_found:      Option<String>
}

impl SapperApp {
    pub fn new() -> SapperApp {
        SapperApp {
            address: String::new(),
            port: 0,
            smock: None,
            routers: Router::new(),
            static_service: true,
            init_closure: None,
            not_found: None
        }
    }

    // listen ip address
    pub fn address(&mut self, address: &str) -> &mut Self {
        self.address = address.to_owned();
        self
    }

    // listen port
    pub fn port(&mut self, port: u32) -> &mut Self {
        self.port = port;
        self
    }

    // do simple static file service
    pub fn static_service(&mut self, open: bool) -> &mut Self {
        self.static_service = open;
        self
    }

    // with global middleware smock
    pub fn with_smock(&mut self, w: SapperSmockType) -> &mut Self {
        self.smock = Some(Arc::new(w));
        self
    }

    // init something, usually in global scope
    pub fn init_global(&mut self, clos: GlobalInitClosure) -> &mut Self {
        self.init_closure = Some(Arc::new(clos));
        self
    }

    // define 404 not found page here
    pub fn not_found_page(&mut self, page: String) -> &mut Self {
        self.not_found = Some(page);
        self
    }

    // add routers of one module to global routers
    pub fn add_module(&mut self, sm: Box<SapperModule>) -> &mut Self {

        let mut router = SapperRouter::new();
        // get the sm router
        sm.router(&mut router).unwrap();
        let sm = Arc::new(sm);

        for (method, handler_vec) in router.into_router() {
            // add to wrapped router
            for &(glob, ref handler) in handler_vec.iter() {
                let method = method.clone();
                let glob = glob.clone();
                let handler = handler.clone();
                let sm = sm.clone();
                let smock = self.smock.clone();
                let init_closure = self.init_closure.clone();

                self.routers.route(method, glob, Arc::new(Box::new(move |req: &mut SapperRequest| -> Result<SapperResponse> {
                    if let Some(ref c) = init_closure {
                        c(req)?;
                    }
                    if let Some(ref smock) = smock {
                        smock.before(req)?;
                    }
                    sm.before(req)?;
                    let mut response: SapperResponse = handler.handle(req)?;
                    sm.after(req, &mut response)?;
                    if let Some(ref smock) = smock {
                        smock.after(req, &mut response)?;
                    }
                    Ok(response)
                })));
            }
        }

        self
    }

    // run http server
    pub fn run_http(self) {

        let addr = self.address.clone() + ":" + &self.port.to_string();
        //let self_box = Arc::new(Box::new(self));

        Server::http(&addr[..]).unwrap()
                .handle(self).unwrap();

    }
}

impl Handler for SapperApp {
    /// do actual handling for a request
    fn handle(&self, req: Request, mut res: Response) {

        let mut sreq = SapperRequest::new(Box::new(req));
        let (path, query) = sreq.uri();

        // pass req to routers, execute matched biz handler
        let response_w = self.routers.handle_method(&mut sreq, &path);
        match response_w {
            Ok(sres) => {
                *res.status_mut() = sres.status();
                match sres.body() {
                    &Some(ref vec) => {
                        for header in sres.headers().iter() {
                            res.headers_mut()
                                .set_raw(header.name().to_owned(),
                                         vec![header.value_string().as_bytes().to_vec()]);
                        }
                        return res.send(&vec[..]).unwrap();
                    },
                    &None => {
                        return res.send(&"".as_bytes()).unwrap();
                    }
                }
            },
            Err(Error::NotFound) => {
                if self.static_service {
                    match simple_file_get(&path) {
                        Ok((file_u8vec, file_mime)) => {
                            res.headers_mut().set_raw("Content-Type", vec![file_mime.as_bytes().to_vec()]);
                            return res.send(&file_u8vec[..]).unwrap();
                        },
                        Err(_) => {
                            *res.status_mut() = StatusCode::NotFound;
                            return res.send(self.not_found.to_owned().unwrap_or(String::from("404 Not Found")).as_bytes()).unwrap();
                        }
                    }
                }

                // return 404 NotFound now
                *res.status_mut() = StatusCode::NotFound;
                return res.send(self.not_found.to_owned().unwrap_or(String::from("404 Not Found")).as_bytes()).unwrap();
            },
            Err(Error::Break) => {
                *res.status_mut() = StatusCode::BadRequest;
                return res.send(&"Bad Request".as_bytes()).unwrap();
            },
            Err(Error::Unauthorized) => {
                *res.status_mut() = StatusCode::Unauthorized;
                return res.send(&"Unauthorized".as_bytes()).unwrap();
            },
            Err(Error::Forbidden) => {
                *res.status_mut() = StatusCode::Forbidden;
                return res.send(&"Forbidden".as_bytes()).unwrap();
            },
            Err(Error::TemporaryRedirect(new_uri)) => {
                *res.status_mut() = StatusCode::TemporaryRedirect;
                res.headers_mut().set_raw("Location", vec![new_uri.as_bytes().to_vec()]);
                return res.send(&"Temporary Redirect".as_bytes()).unwrap();
            },
            Err(Error::Custom(ustr)) => {
                *res.status_mut() = StatusCode::Ok;
                return res.send(&ustr.as_bytes()).unwrap();
            },
            Err(Error::CustomHtml(html_str)) => {
                *res.status_mut() = StatusCode::Ok;
                res.headers_mut().set_raw("Content-Type", vec!["text/html".as_bytes().to_vec()]);
                return res.send(&html_str.as_bytes()).unwrap();
            },
            Err(Error::CustomJson(json_str)) => {
                *res.status_mut() = StatusCode::Ok;
                res.headers_mut().set_raw("Content-Type", vec!["application/x-javascript".as_bytes().to_vec()]);
                return res.send(&json_str.as_bytes()).unwrap();
            },
            Err(_) => {
                *res.status_mut() = StatusCode::InternalServerError;
                return res.send(&"InternalServerError".as_bytes()).unwrap();
            }

        }
    }
}

// this is very expensive in time
// should make it as global
lazy_static! {
    static ref MTYPES: MimeTypes = { MimeTypes::new().unwrap() };
}

// a simple static file service
fn simple_file_get(path: &str) -> Result<(Vec<u8>, String)> {
    let new_path;
    if &path[(path.len()-1)..] == "/" {
        new_path = "static/".to_owned() + path + "index.html";
    }
    else {
        new_path = "static/".to_owned() + path;
    }
    //println!("file path: {}", new_path);
    match File::open(&new_path) {
        Ok(ref mut file) => {
            let mut s: Vec<u8> = vec![];
            file.read_to_end(&mut s).unwrap_or(0);

            let mt_str = MTYPES.mime_for_path(Path::new(&new_path));

            Ok((s, mt_str.to_owned()))
        },
        Err(_) => Err(Error::FileNotExist)
    }
}

<commit_msg>rename static_service to static_file_service.<commit_after>use std::str;
use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::sync::Arc;
use std::clone::Clone;

use hyper::status::StatusCode;
use hyper::server::{Handler, Server, Request, Response};
use mime_types::Types as MimeTypes;

pub use typemap::Key;
pub use hyper::header::Headers;
pub use hyper::header;
pub use hyper::mime;
pub use hyper::client::Client;
pub use request::SapperRequest;
pub use response::SapperResponse;
pub use router_m::Router;
pub use router::SapperRouter;
pub use handler::SapperHandler;

/// Path parameter type
#[derive(Clone)]
pub struct PathParams;

/// Re-export Status Codes
pub mod status {
    pub use hyper::status::StatusCode as Status;
    pub use hyper::status::StatusCode::*;
}

/// Sapper error enum
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
    InvalidConfig,
    InvalidRouterConfig,
    FileNotExist,
    NotFound,
    Break,          // 400
    Unauthorized,   // 401
    Forbidden,      // 403
    TemporaryRedirect(String),     // 307
    Custom(String),
    CustomHtml(String),
    CustomJson(String),
}

/// Sapper result struct
pub type Result<T> = ::std::result::Result<T, Error>;

/// Sapper module trait
/// 3 methods: before, after, router
pub trait SapperModule: Sync + Send {

    /// module before filter, will be executed before handler
    fn before(&self, req: &mut SapperRequest) -> Result<()> {
        Ok(())
    }

    /// module after filter, will be executed after handler
    fn after(&self, req: &SapperRequest, res: &mut SapperResponse) -> Result<()> {
        Ok(())
    }

    /// module router method, used to write router collection of this module here
    fn router(&self, &mut SapperRouter) -> Result<()>;

}

/// Sapper smock trait, used to place global before and after middlewares
pub trait SapperSmock {
    fn before(&self, &mut SapperRequest) -> Result<()>;
    fn after(&self, &SapperRequest, &mut SapperResponse) -> Result<()>;
}

type GlobalInitClosure = Box<Fn(&mut SapperRequest) -> Result<()> + 'static + Send + Sync>;
type SapperSmockType = Box<SapperSmock + 'static + Send + Sync>;

/// Sapper app struct
pub struct SapperApp {
    // listen ip address
    pub address:        String,
    // listen port
    pub port:           u32,
    // for app entry, global middeware
    pub smock:          Option<Arc<SapperSmockType>>,
    // routers actually use to recognize
    pub routers:        Router,
    // do simple static file service
    pub static_file_service: bool,
    // if need init something, put them here
    pub init_closure:   Option<Arc<GlobalInitClosure>>,
    // 404 not found page
    pub not_found:      Option<String>
}

impl SapperApp {
    pub fn new() -> SapperApp {
        SapperApp {
            address: String::new(),
            port: 0,
            smock: None,
            routers: Router::new(),
            static_file_service: true,
            init_closure: None,
            not_found: None
        }
    }

    // listen ip address
    pub fn address(&mut self, address: &str) -> &mut Self {
        self.address = address.to_owned();
        self
    }

    // listen port
    pub fn port(&mut self, port: u32) -> &mut Self {
        self.port = port;
        self
    }

    // do simple static file service
    pub fn static_file_service(&mut self, open: bool) -> &mut Self {
        self.static_file_service = open;
        self
    }

    // with global middleware smock
    pub fn with_smock(&mut self, w: SapperSmockType) -> &mut Self {
        self.smock = Some(Arc::new(w));
        self
    }

    // init something, usually in global scope
    pub fn init_global(&mut self, clos: GlobalInitClosure) -> &mut Self {
        self.init_closure = Some(Arc::new(clos));
        self
    }

    // define 404 not found page here
    pub fn not_found_page(&mut self, page: String) -> &mut Self {
        self.not_found = Some(page);
        self
    }

    // add routers of one module to global routers
    pub fn add_module(&mut self, sm: Box<SapperModule>) -> &mut Self {

        let mut router = SapperRouter::new();
        // get the sm router
        sm.router(&mut router).unwrap();
        let sm = Arc::new(sm);

        for (method, handler_vec) in router.into_router() {
            // add to wrapped router
            for &(glob, ref handler) in handler_vec.iter() {
                let method = method.clone();
                let glob = glob.clone();
                let handler = handler.clone();
                let sm = sm.clone();
                let smock = self.smock.clone();
                let init_closure = self.init_closure.clone();

                self.routers.route(method, glob, Arc::new(Box::new(move |req: &mut SapperRequest| -> Result<SapperResponse> {
                    if let Some(ref c) = init_closure {
                        c(req)?;
                    }
                    if let Some(ref smock) = smock {
                        smock.before(req)?;
                    }
                    sm.before(req)?;
                    let mut response: SapperResponse = handler.handle(req)?;
                    sm.after(req, &mut response)?;
                    if let Some(ref smock) = smock {
                        smock.after(req, &mut response)?;
                    }
                    Ok(response)
                })));
            }
        }

        self
    }

    // run http server
    pub fn run_http(self) {

        let addr = self.address.clone() + ":" + &self.port.to_string();
        //let self_box = Arc::new(Box::new(self));

        Server::http(&addr[..]).unwrap()
                .handle(self).unwrap();

    }
}

impl Handler for SapperApp {
    /// do actual handling for a request
    fn handle(&self, req: Request, mut res: Response) {

        let mut sreq = SapperRequest::new(Box::new(req));
        let (path, query) = sreq.uri();

        // pass req to routers, execute matched biz handler
        let response_w = self.routers.handle_method(&mut sreq, &path);
        match response_w {
            Ok(sres) => {
                *res.status_mut() = sres.status();
                match sres.body() {
                    &Some(ref vec) => {
                        for header in sres.headers().iter() {
                            res.headers_mut()
                                .set_raw(header.name().to_owned(),
                                         vec![header.value_string().as_bytes().to_vec()]);
                        }
                        return res.send(&vec[..]).unwrap();
                    },
                    &None => {
                        return res.send(&"".as_bytes()).unwrap();
                    }
                }
            },
            Err(Error::NotFound) => {
                if self.static_file_service {
                    match simple_file_get(&path) {
                        Ok((file_u8vec, file_mime)) => {
                            res.headers_mut().set_raw("Content-Type", vec![file_mime.as_bytes().to_vec()]);
                            return res.send(&file_u8vec[..]).unwrap();
                        },
                        Err(_) => {
                            *res.status_mut() = StatusCode::NotFound;
                            return res.send(self.not_found.to_owned().unwrap_or(String::from("404 Not Found")).as_bytes()).unwrap();
                        }
                    }
                }

                // return 404 NotFound now
                *res.status_mut() = StatusCode::NotFound;
                return res.send(self.not_found.to_owned().unwrap_or(String::from("404 Not Found")).as_bytes()).unwrap();
            },
            Err(Error::Break) => {
                *res.status_mut() = StatusCode::BadRequest;
                return res.send(&"Bad Request".as_bytes()).unwrap();
            },
            Err(Error::Unauthorized) => {
                *res.status_mut() = StatusCode::Unauthorized;
                return res.send(&"Unauthorized".as_bytes()).unwrap();
            },
            Err(Error::Forbidden) => {
                *res.status_mut() = StatusCode::Forbidden;
                return res.send(&"Forbidden".as_bytes()).unwrap();
            },
            Err(Error::TemporaryRedirect(new_uri)) => {
                *res.status_mut() = StatusCode::TemporaryRedirect;
                res.headers_mut().set_raw("Location", vec![new_uri.as_bytes().to_vec()]);
                return res.send(&"Temporary Redirect".as_bytes()).unwrap();
            },
            Err(Error::Custom(ustr)) => {
                *res.status_mut() = StatusCode::Ok;
                return res.send(&ustr.as_bytes()).unwrap();
            },
            Err(Error::CustomHtml(html_str)) => {
                *res.status_mut() = StatusCode::Ok;
                res.headers_mut().set_raw("Content-Type", vec!["text/html".as_bytes().to_vec()]);
                return res.send(&html_str.as_bytes()).unwrap();
            },
            Err(Error::CustomJson(json_str)) => {
                *res.status_mut() = StatusCode::Ok;
                res.headers_mut().set_raw("Content-Type", vec!["application/x-javascript".as_bytes().to_vec()]);
                return res.send(&json_str.as_bytes()).unwrap();
            },
            Err(_) => {
                *res.status_mut() = StatusCode::InternalServerError;
                return res.send(&"InternalServerError".as_bytes()).unwrap();
            }

        }
    }
}

// this is very expensive in time
// should make it as global
lazy_static! {
    static ref MTYPES: MimeTypes = { MimeTypes::new().unwrap() };
}

// a simple static file service
fn simple_file_get(path: &str) -> Result<(Vec<u8>, String)> {
    let new_path;
    if &path[(path.len()-1)..] == "/" {
        new_path = "static/".to_owned() + path + "index.html";
    }
    else {
        new_path = "static/".to_owned() + path;
    }
    //println!("file path: {}", new_path);
    match File::open(&new_path) {
        Ok(ref mut file) => {
            let mut s: Vec<u8> = vec![];
            file.read_to_end(&mut s).unwrap_or(0);

            let mt_str = MTYPES.mime_for_path(Path::new(&new_path));

            Ok((s, mt_str.to_owned()))
        },
        Err(_) => Err(Error::FileNotExist)
    }
}

<|endoftext|>


<commit_before>#![allow(unsafe_code)]
/// We use this because we never use the weak count on the std
/// `Arc`, but we use a LOT of `Arc`'s, so the extra 8 bytes
/// turn into a huge overhead.
use std::{
    alloc::{alloc, dealloc, Layout},
    convert::TryFrom,
    fmt::{self, Debug},
    mem,
    ops::Deref,
    ptr,
    sync::atomic::{AtomicUsize, Ordering},
};

// we make this repr(C) because we do a raw
// write to the beginning where we expect
// the rc to be.
#[repr(C)]
struct ArcInner<T: ?Sized> {
    rc: AtomicUsize,
    inner: T,
}

pub struct Arc<T: ?Sized> {
    ptr: *mut ArcInner<T>,
}

unsafe impl<T: Send + Sync + ?Sized> Send for Arc<T> {}
unsafe impl<T: Send + Sync + ?Sized> Sync for Arc<T> {}

impl<T: Debug> Debug for Arc<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        f.debug_map().entry(&"inner", unsafe { &(*self.ptr).inner }).finish()
    }
}

impl<T> Arc<T> {
    pub fn new(inner: T) -> Arc<T> {
        let bx = Box::new(ArcInner { inner, rc: AtomicUsize::new(1) });
        let ptr = Box::into_raw(bx);
        Arc { ptr }
    }

    pub fn copy_from_slice(s: &[T]) -> Arc<[T]> {
        let align =
            std::cmp::max(mem::align_of::<T>(), mem::align_of::<AtomicUsize>());

        let rc_width = std::cmp::max(align, mem::size_of::<AtomicUsize>());
        let data_width = mem::size_of::<T>().checked_mul(s.len()).unwrap();

        let size = rc_width.checked_add(data_width).unwrap();

        let layout = Layout::from_size_align(size, align).unwrap();

        unsafe {
            let ptr = alloc(layout);

            assert!(!ptr.is_null(), "failed to allocate Arc");
            #[allow(clippy::cast_ptr_alignment)]
            ptr::write(ptr as _, AtomicUsize::new(1));

            let data_ptr = ptr.add(rc_width);
            ptr::copy_nonoverlapping(s.as_ptr(), data_ptr as _, s.len());

            let fat_ptr: *const ArcInner<[T]> = Arc::fatten(ptr, s.len());

            Arc { ptr: fat_ptr as *mut _ }
        }
    }

    /// <https://users.rust-lang.org/t/construct-fat-pointer-to-struct/29198/9>
    #[allow(trivial_casts)]
    fn fatten(data: *const u8, len: usize) -> *const ArcInner<[T]> {
        // Requirements of slice::from_raw_parts.
        assert!(!data.is_null());
        assert!(isize::try_from(len).is_ok());

        let slice =
            unsafe { core::slice::from_raw_parts(data as *const (), len) };
        slice as *const [()] as *const _
    }

    pub fn into_raw(arc: Arc<T>) -> *const T {
        let ptr = unsafe { &(*arc.ptr).inner };
        #[allow(clippy::mem_forget)]
        mem::forget(arc);
        ptr
    }

    pub unsafe fn from_raw(ptr: *const T) -> Arc<T> {
        let align =
            std::cmp::max(mem::align_of::<T>(), mem::align_of::<AtomicUsize>());

        let rc_width = std::cmp::max(align, mem::size_of::<AtomicUsize>());

        let sub_ptr = (ptr as *const u8).sub(rc_width) as *mut ArcInner<T>;

        Arc { ptr: sub_ptr }
    }
}

impl<T: ?Sized> Arc<T> {
    pub fn strong_count(arc: &Arc<T>) -> usize {
        unsafe { (*arc.ptr).rc.load(Ordering::Acquire) }
    }

    pub fn get_mut(arc: &mut Arc<T>) -> Option<&mut T> {
        if Arc::strong_count(arc) == 1 {
            Some(unsafe { &mut arc.ptr.as_mut().unwrap().inner })
        } else {
            None
        }
    }
}

impl<T: ?Sized + Clone> Arc<T> {
    pub fn make_mut(arc: &mut Arc<T>) -> &mut T {
        if Arc::strong_count(arc) != 1 {
            *arc = Arc::new((**arc).clone());
            assert_eq!(Arc::strong_count(arc), 1);
        }
        Arc::get_mut(arc).unwrap()
    }
}

impl<T: Default> Default for Arc<T> {
    fn default() -> Arc<T> {
        Arc::new(T::default())
    }
}

impl<T: ?Sized> Clone for Arc<T> {
    fn clone(&self) -> Arc<T> {
        // safe to use Relaxed ordering below because
        // of the required synchronization for passing
        // any objects to another thread.
        let last_count =
            unsafe { (*self.ptr).rc.fetch_add(1, Ordering::Relaxed) };

        if last_count == usize::max_value() {
            #[cold]
            std::process::abort();
        }

        Arc { ptr: self.ptr }
    }
}

impl<T: ?Sized> Drop for Arc<T> {
    fn drop(&mut self) {
        unsafe {
            let rc = (*self.ptr).rc.fetch_sub(1, Ordering::Release) - 1;
            if rc == 0 {
                std::sync::atomic::fence(Ordering::Acquire);
                Box::from_raw(self.ptr);
            }
        }
    }
}

impl<T: Copy> From<&[T]> for Arc<[T]> {
    #[inline]
    fn from(s: &[T]) -> Arc<[T]> {
        Arc::copy_from_slice(s)
    }
}

#[allow(clippy::fallible_impl_from)]
impl<T> From<Box<T>> for Arc<T> {
    #[inline]
    fn from(b: Box<T>) -> Arc<T> {
        let align =
            std::cmp::max(mem::align_of::<T>(), mem::align_of::<AtomicUsize>());

        let rc_width = std::cmp::max(align, mem::size_of::<AtomicUsize>());

        unsafe {
            let dst_layout = Layout::new::<ArcInner<T>>();
            let dst = alloc(dst_layout);
            assert!(!dst.is_null(), "failed to allocate Arc");

            let data_ptr = dst.add(rc_width);
            #[allow(clippy::cast_ptr_alignment)]
            ptr::write(dst as _, AtomicUsize::new(1));
            let src = Box::into_raw(b);
            ptr::copy(src, data_ptr as *mut T, 1);

            // free the old box memory without running Drop
            let src_layout = Layout::new::<T>();
            dealloc(src as *mut u8, src_layout);

            Arc { ptr: dst as *mut _ }
        }
    }
}

impl<T> From<Vec<T>> for Arc<[T]> {
    #[inline]
    fn from(mut v: Vec<T>) -> Arc<[T]> {
        unsafe {
            let arc = Arc::copy_from_slice(&v);

            // Allow the Vec to free its memory, but not destroy its contents
            v.set_len(0);

            arc
        }
    }
}

impl<T: ?Sized> Deref for Arc<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &(*self.ptr).inner }
    }
}

impl<T: ?Sized> std::borrow::Borrow<T> for Arc<T> {
    fn borrow(&self) -> &T {
        &**self
    }
}

impl<T: ?Sized> AsRef<T> for Arc<T> {
    fn as_ref(&self) -> &T {
        &**self
    }
}
<commit_msg>copy_from_slice: unsafe, private; pad allocation<commit_after>#![allow(unsafe_code)]
/// We use this because we never use the weak count on the std
/// `Arc`, but we use a LOT of `Arc`'s, so the extra 8 bytes
/// turn into a huge overhead.
use std::{
    alloc::{alloc, dealloc, Layout},
    convert::TryFrom,
    fmt::{self, Debug},
    mem,
    ops::Deref,
    ptr,
    sync::atomic::{AtomicUsize, Ordering},
};

// we make this repr(C) because we do a raw
// write to the beginning where we expect
// the rc to be.
#[repr(C)]
struct ArcInner<T: ?Sized> {
    rc: AtomicUsize,
    inner: T,
}

pub struct Arc<T: ?Sized> {
    ptr: *mut ArcInner<T>,
}

unsafe impl<T: Send + Sync + ?Sized> Send for Arc<T> {}
unsafe impl<T: Send + Sync + ?Sized> Sync for Arc<T> {}

impl<T: Debug> Debug for Arc<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        f.debug_map().entry(&"inner", unsafe { &(*self.ptr).inner }).finish()
    }
}

impl<T> Arc<T> {
    pub fn new(inner: T) -> Arc<T> {
        let bx = Box::new(ArcInner { inner, rc: AtomicUsize::new(1) });
        let ptr = Box::into_raw(bx);
        Arc { ptr }
    }

    // See std::sync::arc::Arc::copy_from_slice,
    // "Unsafe because the caller must either take ownership or bind `T: Copy`"
    unsafe fn copy_from_slice(s: &[T]) -> Arc<[T]> {
        let align =
            std::cmp::max(mem::align_of::<T>(), mem::align_of::<AtomicUsize>());

        let rc_width = std::cmp::max(align, mem::size_of::<AtomicUsize>());
        let data_width = mem::size_of::<T>().checked_mul(s.len()).unwrap();

        let size = rc_width.checked_add(data_width).unwrap();

        let layout = Layout::from_size_align(size, align).unwrap();
        let layout = layout.pad_to_align();

        let ptr = alloc(layout);

        assert!(!ptr.is_null(), "failed to allocate Arc");
        #[allow(clippy::cast_ptr_alignment)]
        ptr::write(ptr as _, AtomicUsize::new(1));

        let data_ptr = ptr.add(rc_width);
        ptr::copy_nonoverlapping(s.as_ptr(), data_ptr as _, s.len());

        let fat_ptr: *const ArcInner<[T]> = Arc::fatten(ptr, s.len());

        Arc { ptr: fat_ptr as *mut _ }
    }

    /// <https://users.rust-lang.org/t/construct-fat-pointer-to-struct/29198/9>
    #[allow(trivial_casts)]
    fn fatten(data: *const u8, len: usize) -> *const ArcInner<[T]> {
        // Requirements of slice::from_raw_parts.
        assert!(!data.is_null());
        assert!(isize::try_from(len).is_ok());

        let slice =
            unsafe { core::slice::from_raw_parts(data as *const (), len) };
        slice as *const [()] as *const _
    }

    pub fn into_raw(arc: Arc<T>) -> *const T {
        let ptr = unsafe { &(*arc.ptr).inner };
        #[allow(clippy::mem_forget)]
        mem::forget(arc);
        ptr
    }

    pub unsafe fn from_raw(ptr: *const T) -> Arc<T> {
        let align =
            std::cmp::max(mem::align_of::<T>(), mem::align_of::<AtomicUsize>());

        let rc_width = std::cmp::max(align, mem::size_of::<AtomicUsize>());

        let sub_ptr = (ptr as *const u8).sub(rc_width) as *mut ArcInner<T>;

        Arc { ptr: sub_ptr }
    }
}

impl<T: ?Sized> Arc<T> {
    pub fn strong_count(arc: &Arc<T>) -> usize {
        unsafe { (*arc.ptr).rc.load(Ordering::Acquire) }
    }

    pub fn get_mut(arc: &mut Arc<T>) -> Option<&mut T> {
        if Arc::strong_count(arc) == 1 {
            Some(unsafe { &mut arc.ptr.as_mut().unwrap().inner })
        } else {
            None
        }
    }
}

impl<T: ?Sized + Clone> Arc<T> {
    pub fn make_mut(arc: &mut Arc<T>) -> &mut T {
        if Arc::strong_count(arc) != 1 {
            *arc = Arc::new((**arc).clone());
            assert_eq!(Arc::strong_count(arc), 1);
        }
        Arc::get_mut(arc).unwrap()
    }
}

impl<T: Default> Default for Arc<T> {
    fn default() -> Arc<T> {
        Arc::new(T::default())
    }
}

impl<T: ?Sized> Clone for Arc<T> {
    fn clone(&self) -> Arc<T> {
        // safe to use Relaxed ordering below because
        // of the required synchronization for passing
        // any objects to another thread.
        let last_count =
            unsafe { (*self.ptr).rc.fetch_add(1, Ordering::Relaxed) };

        if last_count == usize::max_value() {
            #[cold]
            std::process::abort();
        }

        Arc { ptr: self.ptr }
    }
}

impl<T: ?Sized> Drop for Arc<T> {
    fn drop(&mut self) {
        unsafe {
            let rc = (*self.ptr).rc.fetch_sub(1, Ordering::Release) - 1;
            if rc == 0 {
                std::sync::atomic::fence(Ordering::Acquire);
                Box::from_raw(self.ptr);
            }
        }
    }
}

impl<T: Copy> From<&[T]> for Arc<[T]> {
    #[inline]
    fn from(s: &[T]) -> Arc<[T]> {
        unsafe { Arc::copy_from_slice(s) }
    }
}

#[allow(clippy::fallible_impl_from)]
impl<T> From<Box<T>> for Arc<T> {
    #[inline]
    fn from(b: Box<T>) -> Arc<T> {
        let align =
            std::cmp::max(mem::align_of::<T>(), mem::align_of::<AtomicUsize>());

        let rc_width = std::cmp::max(align, mem::size_of::<AtomicUsize>());

        unsafe {
            let dst_layout = Layout::new::<ArcInner<T>>();
            let dst = alloc(dst_layout);
            assert!(!dst.is_null(), "failed to allocate Arc");

            let data_ptr = dst.add(rc_width);
            #[allow(clippy::cast_ptr_alignment)]
            ptr::write(dst as _, AtomicUsize::new(1));
            let src = Box::into_raw(b);
            ptr::copy(src, data_ptr as *mut T, 1);

            // free the old box memory without running Drop
            let src_layout = Layout::new::<T>();
            dealloc(src as *mut u8, src_layout);

            Arc { ptr: dst as *mut _ }
        }
    }
}

impl<T> From<Vec<T>> for Arc<[T]> {
    #[inline]
    fn from(mut v: Vec<T>) -> Arc<[T]> {
        unsafe {
            let arc = Arc::copy_from_slice(&v);

            // Allow the Vec to free its memory, but not destroy its contents
            v.set_len(0);

            arc
        }
    }
}

impl<T: ?Sized> Deref for Arc<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &(*self.ptr).inner }
    }
}

impl<T: ?Sized> std::borrow::Borrow<T> for Arc<T> {
    fn borrow(&self) -> &T {
        &**self
    }
}

impl<T: ?Sized> AsRef<T> for Arc<T> {
    fn as_ref(&self) -> &T {
        &**self
    }
}
<|endoftext|>


<commit_before>use core::intrinsics;

// NOTE This function and the ones below are implemented using assembly because they using a custom
// calling convention which can't be implemented using a normal Rust function
#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_uidivmod() {
    asm!("push {lr}
          sub sp, sp, #4
          mov r2, sp
          bl __udivmodsi4
          ldr r1, [sp]
          add sp, sp, #4
          pop {pc}");
    intrinsics::unreachable();
}

#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_uldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __udivmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}");
    intrinsics::unreachable();
}

#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_idivmod() {
    asm!("push {r0, r1, r4, lr}
          bl __divsi3
          pop {r1, r2}
          muls r2, r2, r0
          subs r1, r1, r2
          pop {r4, pc}");
    intrinsics::unreachable();
}

#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_ldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __divmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}");
    intrinsics::unreachable();
}

// TODO: These two functions should be defined as aliases
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __aeabi_uidiv(a: u32, b: u32) -> u32 {
    ::int::udiv::__udivsi3(a, b)
}

#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __aeabi_idiv(a: i32, b: i32) -> i32 {
    ::int::sdiv::__divsi3(a, b)
}

extern "C" {
    fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
    fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
    fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8;
}

// FIXME: The `*4` and `*8` variants should be defined as aliases.

#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) {
    memcpy(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) {
    memcpy(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memcpy8(dest: *mut u8, src: *const u8, n: usize) {
    memcpy(dest, src, n);
}

#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memmove(dest: *mut u8, src: *const u8, n: usize) {
    memmove(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memmove4(dest: *mut u8, src: *const u8, n: usize) {
    memmove(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memmove8(dest: *mut u8, src: *const u8, n: usize) {
    memmove(dest, src, n);
}

// Note the different argument order
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) {
    memset(dest, c, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) {
    memset(dest, c, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memset8(dest: *mut u8, n: usize, c: i32) {
    memset(dest, c, n);
}

#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memclr(dest: *mut u8, n: usize) {
    memset(dest, 0, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memclr4(dest: *mut u8, n: usize) {
    memset(dest, 0, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memclr8(dest: *mut u8, n: usize) {
    memset(dest, 0, n);
}

#[cfg(test)]
mod tests {
    use quickcheck::TestResult;
    use qc::{U32, U64};

    quickcheck!{
        fn uldivmod(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q: u64;
                let r: u64;
                unsafe {
                    // The inline asm is a bit tricky here, LLVM will allocate
                    // both r0 and r1 when we specify a 64-bit value for {r0}.
                    asm!("bl __aeabi_uldivmod"
                         : "={r0}" (q), "={r2}" (r)
                         : "{r0}" (n), "{r2}" (d)
                         : "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn uidivmod(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q: u32;
                let r: u32;
                unsafe {
                    asm!("bl __aeabi_uidivmod"
                         : "={r0}" (q), "={r1}" (r)
                         : "{r0}" (n), "{r1}" (d)
                         : "r2", "r3", "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn ldivmod(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0 as i64, d.0 as i64);
            if d == 0 {
                TestResult::discard()
            } else {
                let q: i64;
                let r: i64;
                unsafe {
                    // The inline asm is a bit tricky here, LLVM will allocate
                    // both r0 and r1 when we specify a 64-bit value for {r0}.
                    asm!("bl __aeabi_ldivmod"
                         : "={r0}" (q), "={r2}" (r)
                         : "{r0}" (n), "{r2}" (d)
                         : "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn idivmod(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0 as i32, d.0 as i32);
            if d == 0 {
                TestResult::discard()
            } else {
                let q: i32;
                let r: i32;
                unsafe {
                    asm!("bl __aeabi_idivmod"
                         : "={r0}" (q), "={r1}" (r)
                         : "{r0}" (n), "{r1}" (d)
                         : "r2", "r3", "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }
    }
}
<commit_msg>avoid division overflow in idivmod test<commit_after>use core::intrinsics;

// NOTE This function and the ones below are implemented using assembly because they using a custom
// calling convention which can't be implemented using a normal Rust function
#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_uidivmod() {
    asm!("push {lr}
          sub sp, sp, #4
          mov r2, sp
          bl __udivmodsi4
          ldr r1, [sp]
          add sp, sp, #4
          pop {pc}");
    intrinsics::unreachable();
}

#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_uldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __udivmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}");
    intrinsics::unreachable();
}

#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_idivmod() {
    asm!("push {r0, r1, r4, lr}
          bl __divsi3
          pop {r1, r2}
          muls r2, r2, r0
          subs r1, r1, r2
          pop {r4, pc}");
    intrinsics::unreachable();
}

#[naked]
#[cfg_attr(not(test), no_mangle)]
pub unsafe fn __aeabi_ldivmod() {
    asm!("push {r4, lr}
          sub sp, sp, #16
          add r4, sp, #8
          str r4, [sp]
          bl __divmoddi4
          ldr r2, [sp, #8]
          ldr r3, [sp, #12]
          add sp, sp, #16
          pop {r4, pc}");
    intrinsics::unreachable();
}

// TODO: These two functions should be defined as aliases
#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __aeabi_uidiv(a: u32, b: u32) -> u32 {
    ::int::udiv::__udivsi3(a, b)
}

#[cfg_attr(not(test), no_mangle)]
pub extern "C" fn __aeabi_idiv(a: i32, b: i32) -> i32 {
    ::int::sdiv::__divsi3(a, b)
}

extern "C" {
    fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
    fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8;
    fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8;
}

// FIXME: The `*4` and `*8` variants should be defined as aliases.

#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memcpy(dest: *mut u8, src: *const u8, n: usize) {
    memcpy(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memcpy4(dest: *mut u8, src: *const u8, n: usize) {
    memcpy(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memcpy8(dest: *mut u8, src: *const u8, n: usize) {
    memcpy(dest, src, n);
}

#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memmove(dest: *mut u8, src: *const u8, n: usize) {
    memmove(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memmove4(dest: *mut u8, src: *const u8, n: usize) {
    memmove(dest, src, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memmove8(dest: *mut u8, src: *const u8, n: usize) {
    memmove(dest, src, n);
}

// Note the different argument order
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memset(dest: *mut u8, n: usize, c: i32) {
    memset(dest, c, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memset4(dest: *mut u8, n: usize, c: i32) {
    memset(dest, c, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memset8(dest: *mut u8, n: usize, c: i32) {
    memset(dest, c, n);
}

#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memclr(dest: *mut u8, n: usize) {
    memset(dest, 0, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memclr4(dest: *mut u8, n: usize) {
    memset(dest, 0, n);
}
#[cfg_attr(not(test), no_mangle)]
pub unsafe extern "C" fn __aeabi_memclr8(dest: *mut u8, n: usize) {
    memset(dest, 0, n);
}

#[cfg(test)]
mod tests {
    use quickcheck::TestResult;
    use qc::{U32, U64};

    quickcheck!{
        fn uldivmod(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q: u64;
                let r: u64;
                unsafe {
                    // The inline asm is a bit tricky here, LLVM will allocate
                    // both r0 and r1 when we specify a 64-bit value for {r0}.
                    asm!("bl __aeabi_uldivmod"
                         : "={r0}" (q), "={r2}" (r)
                         : "{r0}" (n), "{r2}" (d)
                         : "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn uidivmod(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0, d.0);
            if d == 0 {
                TestResult::discard()
            } else {
                let q: u32;
                let r: u32;
                unsafe {
                    asm!("bl __aeabi_uidivmod"
                         : "={r0}" (q), "={r1}" (r)
                         : "{r0}" (n), "{r1}" (d)
                         : "r2", "r3", "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn ldivmod(n: U64, d: U64) -> TestResult {
            let (n, d) = (n.0 as i64, d.0 as i64);
            if d == 0 {
                TestResult::discard()
            } else {
                let q: i64;
                let r: i64;
                unsafe {
                    // The inline asm is a bit tricky here, LLVM will allocate
                    // both r0 and r1 when we specify a 64-bit value for {r0}.
                    asm!("bl __aeabi_ldivmod"
                         : "={r0}" (q), "={r2}" (r)
                         : "{r0}" (n), "{r2}" (d)
                         : "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }

        fn idivmod(n: U32, d: U32) -> TestResult {
            let (n, d) = (n.0 as i32, d.0 as i32);
            if d == 0 || (n == i32::min_value() && d == -1) {
                TestResult::discard()
            } else {
                let q: i32;
                let r: i32;
                unsafe {
                    asm!("bl __aeabi_idivmod"
                         : "={r0}" (q), "={r1}" (r)
                         : "{r0}" (n), "{r1}" (d)
                         : "r2", "r3", "r12", "lr", "flags");
                }
                TestResult::from_bool(q == n / d && r == n % d)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::hash;
use std::thunk::Invoke;

use name::*;
// pub use lazy::single::Thunk;

pub type UAr<'x,S,T> = Box<Fn<S,T> + 'x>;
// arrow-typed value (in CBPV speak),
// with explicit lifetime 'x, input tuple S, and return value of type T.
//
// Read in CBPV notation has "U(S -> T) with lifetime 'x".
//
// In rust, can be introduced by:
//   box () (move |&: args:S |{ $body }))

pub type UF<'x,T> = Box<Invoke<(),T> + 'x>;
// value-producing value (in CBPV speak).
// with explicit lifetime 'x and return value of type T.
//
// Read in CBPV notation has "U(F(T)) with lifetime 'x".
//
// In Rust, can be introduced by:
//   box () (move |: () |{ $body }))

/// rustc says that I need an explicit lifetime parameter; I name it `'x`.
struct ArtThunk<'x,T> {
    // TODO: Memo table identity, for comparing thunks with same names
    // TODO: Access to args, for equality check, hashing
    thunk : UF<'x,T>,
    value : Option<T>
}

impl<'x,T> fmt::Show for ArtThunk<'x,T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(ArtThunk)")
    }
}

#[derive(Show)]
struct ArtCon<'x,T> {
    name : Name,
    thunk : ArtThunk<'x,T>
}

impl<'x,T> PartialEq for ArtCon<'x,T> {
    fn eq(&self, other: &ArtCon<T>) -> bool {
        self.name.eq( &other.name )
    }
    fn ne(&self, other: &ArtCon<T>) -> bool {
        !(self.eq(other))
    }
}

impl<'x,T> Eq for ArtCon<'x,T> {
    fn assert_receiver_is_total_eq(&self) {  }
}

impl<'x,S: hash::Writer, T> hash::Hash<S> for ArtCon<'x,T> {
    fn hash(&self, state: &mut S) {
        self.name.hash( state )
    }
}

pub type Art<'x,T> = ArtCon<'x,T>;

// Create a named cell
pub fn cell<'x,T:'x> (n:Name, x:T) -> Art<'x,T> {
    //! TODO: Cache the art based on the name n
    ArtCon { name  : n,
             thunk : ArtThunk{
                 value:None,
                 thunk:box () (move|:()|{
                     x
                 } ) as Box<Invoke()->T>
             }
    }
}

// Create a named thunk
pub fn nart<'x,T:'x> (n:Name, body:Box<Invoke()->T+'x>) -> Art<'x,T> {
    //! TODO: Cache the art based on the name n
    ArtCon { name  : n,
             thunk : ArtThunk{
                 value:None,
                 thunk:body
             }
    }
}

#[macro_export]
macro_rules! nart (
    ($name:expr, $body:expr) => {
        nart($name,
             box () (move|:()|{ $body }))
    }
);

pub fn force<'x,T> (art:Art<'x,T>) -> T {
    match art {
        ArtCon {name:_,thunk:t} => {
            let result = t.thunk.invoke(()) ;
            result
        }
    }
}

/// Needed this form to get List Iterator to borrow-check.
pub fn force_ref<'x,T> (art:&'x Art<'x,T>) -> &'x T {
   panic!("Oh no something bad has happened!")
}
<commit_msg>force_ref is broken, perhaps fundementally<commit_after>use std::fmt;
use std::hash;
use std::thunk::Invoke;

use name::*;
// pub use lazy::single::Thunk;

pub type UAr<'x,S,T> = Box<Fn<S,T> + 'x>;
// arrow-typed value (in CBPV speak),
// with explicit lifetime 'x, input tuple S, and return value of type T.
//
// Read in CBPV notation has "U(S -> T) with lifetime 'x".
//
// In rust, can be introduced by:
//   box () (move |&: args:S |{ $body }))

pub type UF<'x,T> = Box<Invoke<(),T> + 'x>;
// value-producing value (in CBPV speak).
// with explicit lifetime 'x and return value of type T.
//
// Read in CBPV notation has "U(F(T)) with lifetime 'x".
//
// In Rust, can be introduced by:
//   box () (move |: () |{ $body }))

/// rustc says that I need an explicit lifetime parameter; I name it `'x`.
struct ArtThunk<'x,T> {
    // TODO: Memo table identity, for comparing thunks with same names
    // TODO: Access to args, for equality check, hashing
    thunk : UF<'x,T>,
    value : Option<T>
}

impl<'x,T> fmt::Show for ArtThunk<'x,T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(ArtThunk)")
    }
}

#[derive(Show)]
struct ArtCon<'x,T> {
    name : Name,
    thunk : ArtThunk<'x,T>
}

impl<'x,T> PartialEq for ArtCon<'x,T> {
    fn eq(&self, other: &ArtCon<T>) -> bool {
        self.name.eq( &other.name )
    }
    fn ne(&self, other: &ArtCon<T>) -> bool {
        !(self.eq(other))
    }
}

impl<'x,T> Eq for ArtCon<'x,T> {
    fn assert_receiver_is_total_eq(&self) {  }
}

impl<'x,S: hash::Writer, T> hash::Hash<S> for ArtCon<'x,T> {
    fn hash(&self, state: &mut S) {
        self.name.hash( state )
    }
}

pub type Art<'x,T> = ArtCon<'x,T>;

// Create a named cell
pub fn cell<'x,T:'x> (n:Name, x:T) -> Art<'x,T> {
    //! TODO: Cache the art based on the name n
    ArtCon { name  : n,
             thunk : ArtThunk{
                 value:None,
                 thunk:box () (move|:()|{
                     x
                 } ) as Box<Invoke()->T>
             }
    }
}

// Create a named thunk
pub fn nart<'x,T:'x> (n:Name, body:Box<Invoke()->T+'x>) -> Art<'x,T> {
    //! TODO: Cache the art based on the name n
    ArtCon { name  : n,
             thunk : ArtThunk{
                 value:None,
                 thunk:body
             }
    }
}

#[macro_export]
macro_rules! nart (
    ($name:expr, $body:expr) => {
        nart($name,
             box () (move|:()|{ $body }))
    }
);

pub fn force<'x,T> (art:Art<'x,T>) -> T {
    match art {
        ArtCon {name:_,thunk:t} => {
            let result = t.thunk.invoke(()) ;
            result
        }
    }
}

/// Needed this form to get List Iterator to borrow-check.
pub fn force_ref<'x,T> (art:&'x Art<'x,T>) -> &'x T {
    match art {
        & ArtCon {name:_,thunk:ref t} => {
            // & t.thunk.invoke(())
            panic!("this is broken");
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeSet, BTreeMap};
use std::fmt;

type Terminal = char;
type TerminalSet = BTreeSet<Terminal>;
type NonTerminal = char;
type NonTerminalSet = BTreeSet<NonTerminal>;

#[derive(Clone, Debug)]
enum TNT {
    T(Terminal),
    NT(NonTerminal),
    Lambda,
}

type Derivation = Vec<TNT>;
type DerivationVec = Vec<Derivation>;

type Productions<T> = Vec<(NonTerminal, T)>;
type ProductionsMap = BTreeMap<NonTerminal, DerivationVec>;

#[derive(Clone, Debug)]
struct CFG {
    vn: NonTerminalSet,
    vt: TerminalSet,
    p: ProductionsMap,
    s: NonTerminal,
}

// TODO:
// - handy function to print trees!
// - handy function to go from TNT -> Char
// - handy function to go from Derivation -> String

impl CFG {
    pub fn new<T: Into<String> + fmt::Debug>(vn: NonTerminalSet, vt: TerminalSet, p: Productions<T>, s: NonTerminal) -> CFG {
        let mut p_map: ProductionsMap = BTreeMap::new();

        if !vn.is_disjoint(&vt) {
            panic!("VN and VT must be disjoint.\nVN: {:?} \nVT: {:?}", vn, vt);
        }

        for (nt, der_str) in p {
            if !vn.contains(&nt) {
                panic!("NonTerminal in production rule does not belong to VN {:?} -> {:?} \n {:?}", nt, der_str, vn);
            }

            let dervec = p_map.entry(nt).or_insert(vec!());

            let mut der: Derivation = vec!();

            let der_string = der_str.into();

            if der_string.len() == 0 {
                der.push(TNT::Lambda);
            }

            for c in der_string.chars() {
                if vn.contains(&c) {
                    der.push( TNT::NT(c.clone()) );
                    continue
                }

                if vt.contains(&c) {
                    der.push( TNT::T(c.clone()) );
                    continue
                }

                panic!("Char in derivation {:?} -> {:?} does not belong to VN or VT {:?}", nt, der_string, c);
            }

            dervec.push(der);
        }

        CFG {
            vn: vn,
            vt: vt,
            p: p_map,
            s: s,
        }
    }

    pub fn get_nt_derivations(&self, nt: &NonTerminal) -> DerivationVec {
        //TODO: optimize the case where the nt is not found
        self.p.get(nt).unwrap().clone()
    }
}

impl fmt::Display for CFG {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\n\n").unwrap();
        write!(f, "Context Free Grammar \n").unwrap();
        write!(f, "==================== \n").unwrap();
        write!(f, "VN: {:?} \n", self.vn).unwrap();
        write!(f, "VT: {:?} \n", self.vt).unwrap();
        write!(f, "S: {:?} \n", self.s).unwrap();
        write!(f, "Productions: \n").unwrap();
        for (nt, dervec) in &self.p {
            for der in dervec {
                let mut der_string = String::new();
                for e in der {
                    match *e {
                        TNT::T(t) => der_string.push(t),
                        TNT::NT(nt) => der_string.push(nt),
                        _ => {},
                    }
                }
                write!(f, "{:?} -> {:?} \n", nt, der_string).unwrap();
            }
        }

        write!(f, "\n")
    }
}

#[derive(Debug, Clone)]
struct TNode {
    val: TNT,
    children: Vec<TNode>
}

impl TNode {
    pub fn new(val: TNT, children: Vec<TNode>) -> TNode {
        TNode {
            val: val,
            children: children,
        }
    }

}

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

// Need to create a single derivation tree for... a single production rule like this
// A -> (A)
//TODO
//Creates a single level derivation tree for the given nonterminal
//in the given cfg
fn tree(nt: &NonTerminal, der: &Derivation) -> TNode {
    let mut children: Vec<TNode> = vec!();
    for e in der {
        let node = TNode::new(e.clone(), vec!());
        children.push(node);
    }
    // TODO: check if the nt is effectively a nonterminal or not
    let root = TNode::new(TNT::NT(nt.clone()), children);
    root
}

#[cfg(test)]
mod tests {
    #[test]
    fn cfg_new_test() {
        use super::{CFG, NonTerminalSet, TerminalSet, NonTerminal, Productions};

        let vn: NonTerminalSet = charset!('S');
        let vt: TerminalSet = charset!('a', '(', ')');
        let s: NonTerminal = 'S';
        let p: Productions<&'static str> = vec!(
            ('S', "(S)" ),
            ('S', "a" )
        );

        let g = CFG::new(vn, vt, p, s);
        println!("Resulted grammar {}", g);
    }

    #[test]
    fn tree_test() {
        use super::{TNT, tree};

        let der = vec!( TNT::T('('), TNT::NT('S'), TNT::T(')') );
        let t = tree(&'S', &der);
        println!("Resulted tree {}", t);
    }
}
<commit_msg>cleanup, pseudo pretty print for trees, add handy functions<commit_after>use std::collections::{BTreeSet, BTreeMap};
use std::fmt;

type Terminal = char;
type TerminalSet = BTreeSet<Terminal>;
type NonTerminal = char;
type NonTerminalSet = BTreeSet<NonTerminal>;

#[derive(Clone, Debug)]
enum TNT {
    T(Terminal),
    NT(NonTerminal),
    Lambda,
}

type Derivation = Vec<TNT>;
type DerivationVec = Vec<Derivation>;

type Productions<T> = Vec<(NonTerminal, T)>;
type ProductionsMap = BTreeMap<NonTerminal, DerivationVec>;

#[derive(Clone, Debug)]
struct CFG {
    vn: NonTerminalSet,
    vt: TerminalSet,
    p: ProductionsMap,
    s: NonTerminal,
}

impl CFG {
    pub fn new<T: Into<String> + fmt::Debug>(vn: NonTerminalSet, vt: TerminalSet, p: Productions<T>, s: NonTerminal) -> CFG {
        let mut p_map: ProductionsMap = BTreeMap::new();

        if !vn.is_disjoint(&vt) {
            panic!("VN and VT must be disjoint.\nVN: {:?} \nVT: {:?}", vn, vt);
        }

        for (nt, der_str) in p {
            if !vn.contains(&nt) {
                panic!("NonTerminal in production rule does not belong to VN {:?} -> {:?} \n {:?}", nt, der_str, vn);
            }

            let dervec = p_map.entry(nt).or_insert(vec!());

            let mut der: Derivation = vec!();

            let der_string = der_str.into();

            if der_string.len() == 0 {
                der.push(TNT::Lambda);
            }

            for c in der_string.chars() {
                if vn.contains(&c) {
                    der.push( TNT::NT(c.clone()) );
                    continue
                }

                if vt.contains(&c) {
                    der.push( TNT::T(c.clone()) );
                    continue
                }

                panic!("Char in derivation {:?} -> {:?} does not belong to VN or VT {:?}", nt, der_string, c);
            }

            dervec.push(der);
        }

        CFG {
            vn: vn,
            vt: vt,
            p: p_map,
            s: s,
        }
    }

    //pub fn get_nt_derivations(&self, nt: &NonTerminal) -> DerivationVec {
        ////TODO: optimize the case where the nt is not found
        //self.p.get(nt).unwrap().clone()
    //}
}

impl fmt::Display for CFG {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\n\n").unwrap();
        write!(f, "Context Free Grammar \n").unwrap();
        write!(f, "==================== \n").unwrap();
        write!(f, "VN: {:?} \n", self.vn).unwrap();
        write!(f, "VT: {:?} \n", self.vt).unwrap();
        write!(f, "S: {:?} \n", self.s).unwrap();
        write!(f, "Productions: \n").unwrap();
        for (nt, dervec) in &self.p {
            for der in dervec {
                let der_string = derivation_to_string(der);
                write!(f, "{:?} -> {:?} \n", nt, der_string).unwrap();
            }
        }

        write!(f, "\n")
    }
}

#[derive(Debug, Clone)]
struct TNode {
    val: TNT,
    children: Vec<TNode>
}

impl TNode {
    pub fn new(val: TNT, children: Vec<TNode>) -> TNode {
        TNode {
            val: val,
            children: children,
        }
    }

}

impl fmt::Display for TNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let width = 90;
        let cwidth = width / self.children.len();
        write!(f, "\n").unwrap();
        write!(f, "{:^width$}", self.val.to_string(), width = width).unwrap();
        write!(f, "\n").unwrap();

        write!(f, "\n").unwrap();
        write!(f, "\n").unwrap();

        for c in &self.children {
            write!(f, "{:^width$}", c.val.to_string(), width = cwidth).unwrap();
        }

        write!(f, "\n")
    }
}

impl TNT {
    fn to_char(&self) -> char {
        match *self {
            TNT::T(t) => t,
            TNT::NT(nt) => nt,
            // TODO: proper lambda symbol
            TNT::Lambda =>  '&',
        }
    }

    fn to_string(&self) -> String {
        match *self {
            TNT::T(t) => format!("T('{}')", t),
            TNT::NT(nt) => format!("NT('{}')", nt),
            TNT::Lambda =>  "Lambda".to_string(),
        }
    }
}

fn derivation_to_string(der: &Derivation) -> String {
    let mut der_string = String::new();
    for e in der {
        match *e {
            TNT::T(t) => der_string.push(t),
            TNT::NT(nt) => der_string.push(nt),
            _ => {},
        }
    }

    der_string
}

//Creates a single level derivation tree for the given Derivation
fn tree(nt: &NonTerminal, der: &Derivation) -> TNode {
    let mut children: Vec<TNode> = vec!();
    for e in der {
        let node = TNode::new(e.clone(), vec!());
        children.push(node);
    }
    // TODO: check if the nt is effectively a nonterminal or not
    let root = TNode::new(TNT::NT(nt.clone()), children);
    root
}

#[cfg(test)]
mod tests {
    #[test]
    fn cfg_new_test() {
        use super::{CFG, NonTerminalSet, TerminalSet, NonTerminal, Productions};

        let vn: NonTerminalSet = charset!('S');
        let vt: TerminalSet = charset!('a', '(', ')');
        let s: NonTerminal = 'S';
        let p: Productions<&'static str> = vec!(
            ('S', "(S)" ),
            ('S', "a" )
        );

        let g = CFG::new(vn, vt, p, s);
        println!("Resulted grammar {}", g);
    }

    #[test]
    fn tree_test() {
        use super::{TNT, tree};

        let der = vec!( TNT::T('('), TNT::NT('S'), TNT::T(')') );
        let t = tree(&'S', &der);
        println!("Resulted tree {}", t);
    }
}
<|endoftext|>