<commit_before>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use imgui_sys::{self, ImVec2, ImVec4};

use model::{Color, FieldCoord, HexCoord, Model};
use view::Event;

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

// #f3e4cf
const FIELD_WHITE: [f32; 4] = [0.9529, 0.8941, 0.8118, 1.0];
// #998578
const FIELD_BLACK: [f32; 4] = [0.6, 0.5216, 0.4706, 1.0];
// #ffffff
const PIECE_WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// #000000
const PIECE_OUTLINE: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
// #757575
const PIECE_BLACK: [f32; 4] = [0.4588, 0.4588, 0.4588, 1.0];
// #f7b102
const SELECT_HIGHLIGHT: [f32; 4] = [0.9686, 0.6941, 0.0078, 0.8];
// #ffff00
const LAST_MOVE_HIGHLIGHT: [f32; 4] = [1.0, 1.0, 0.0, 0.8];

pub fn board(model: &Model, size: &ImVec2) -> Option<Event> {
    let mouse_click;
    let mut mouse_pos = ImVec2::default();
    let mut cursor_pos = ImVec2::default();
    unsafe {
        mouse_click = imgui_sys::igIsMouseClicked(0, false);
        imgui_sys::igGetMousePos(&mut mouse_pos);
        imgui_sys::igGetCursorScreenPos(&mut cursor_pos);
    }

    let side_len_x = size.x / 8.0;
    let side_len_y = size.y / (5.0 * SQRT_3);
    let side_len = if side_len_x > side_len_y {
        side_len_y
    } else {
        side_len_x
    };
    let origin = ImVec2::new(cursor_pos.x + size.x / 2.0, cursor_pos.y + size.y / 2.0);
    for hex in model.board.extant_hexes() {
        draw_hex(&hex, &origin, side_len);
    }

    if let Some((ref from, ref to)) = model.last_move {
        if model.board.is_hex_extant(&from.to_hex()) {
            highlight_field(LAST_MOVE_HIGHLIGHT, from, &origin, side_len);
        }
        highlight_field(LAST_MOVE_HIGHLIGHT, to, &origin, side_len);
    }

    if let Some(ref coord) = model.selected_piece {
        highlight_field(SELECT_HIGHLIGHT, coord, &origin, side_len);
    }

    if let Some(ref coords) = model.available_moves {
        for coord in coords {
            highlight_field_dot(SELECT_HIGHLIGHT, coord, &origin, side_len);
        }
    }

    for hex in model.board.extant_hexes() {
        for f in 0..6 {
            let coord = hex.to_field(f);
            if model.board.is_piece_on_field(&coord) {
                draw_piece(&coord, &origin, side_len);
            }
        }
    }

    unsafe {
        imgui_sys::igDummy(size);
    }

    let board_min = cursor_pos;
    let board_max = add_vec(&cursor_pos, size);
    if mouse_click && board_min.x <= mouse_pos.x && board_min.y <= mouse_pos.y
        && board_max.x >= mouse_pos.x && board_max.y >= mouse_pos.y
    {
        pixel_to_field(&mouse_pos, &origin, side_len).map(Event::Click)
    } else {
        None
    }
}

macro_rules! im_color {
    ($v:expr) => (imgui_sys::igColorConvertFloat4ToU32(ImVec4::from($v)))
}

fn draw_hex(coord: &HexCoord, origin: &ImVec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(FIELD_WHITE),
            Color::Black => im_color!(FIELD_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, color);
    }
}

fn highlight_field(color: [f32; 4], coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, highlight);
    }
}

fn highlight_field_dot(color: [f32; 4], coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let center = field_center(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddCircleFilled(
            draw_list,
            center,
            size / (4.0 * SQRT_3),
            highlight,
            15,
        );
    }
}

fn draw_piece(coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center = field_center(coord, origin, size);

    const SCALE: f32 = 0.7;

    let v1 = add_vec(&center, &mul_vec(&sub_vec(&v1, &center), SCALE));
    let v2 = add_vec(&center, &mul_vec(&sub_vec(&v2, &center), SCALE));
    let v3 = add_vec(&center, &mul_vec(&sub_vec(&v3, &center), SCALE));

    // Linear equation derived by human testing and regression
    let outline_size = 0.032 * size - 0.335;

    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(PIECE_WHITE),
            Color::Black => im_color!(PIECE_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, color);
        imgui_sys::ImDrawList_AddTriangle(
            draw_list,
            v1,
            v2,
            v3,
            im_color!(PIECE_OUTLINE),
            outline_size,
        );
    }
}

fn field_center(coord: &FieldCoord, origin: &ImVec2, size: f32) -> ImVec2 {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center_x = (v1.x + v2.x + v3.x) / 3.0;
    let min_y = if v1.y < v2.y || v1.y < v3.y {
        v1.y
    } else if v2.y < v3.y {
        v2.y
    } else {
        v3.y
    };
    let center_y = match coord.color() {
        Color::Black => min_y + size / (2.0 * SQRT_3),
        Color::White => min_y + size / SQRT_3,
    };

    ImVec2::new(center_x, center_y)
}

fn field_vertexes(coord: &FieldCoord, origin: &ImVec2, size: f32) -> (ImVec2, ImVec2, ImVec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let v1;
    let v2;
    match coord.f() {
        0 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size / 2.0, center.y - height);
        }
        1 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size, center.y);
        }
        2 => {
            v1 = ImVec2::new(center.x + size, center.y);
            v2 = ImVec2::new(center.x + size / 2.0, center.y + height);
        }
        3 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size / 2.0, center.y + height);
        }
        4 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size, center.y);
        }
        5 => {
            v1 = ImVec2::new(center.x - size, center.y);
            v2 = ImVec2::new(center.x - size / 2.0, center.y - height);
        }
        _ => panic!("You made the impossible possible."),
    };

    (center, v1, v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: &ImVec2, size: f32) -> ImVec2 {
    let x = coord.x() as f32;
    let y = coord.y() as f32;

    let px = size * (3.0 / 2.0) * x;
    let py = size * -SQRT_3 * (x / 2.0 + y);

    ImVec2::new(px + origin.x, py + origin.y)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#pixel-to-hex
fn pixel_to_field(p: &ImVec2, origin: &ImVec2, size: f32) -> Option<FieldCoord> {
    let x = p.x - origin.x;
    let y = p.y - origin.y;

    let q = x * (2.0 / 3.0) / size;
    let r = (-x - SQRT_3 * y) / (size * 3.0);

    if let Some(hex) = round_hex_coord(q, r) {
        /*
           To find the field, we subtract the converted hex coordinates (q, r) from the rounded hex
           coordinates (hex) to get the fractional part of the coordinates. Here is a diagram of a
           single hex, with the fractional coordinates of each of its vertexes in the hex coordinate
           system:

                (-1/3, 2/3) _______ (1/3, 1/3)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
            (-2/3, 1/3) (----(0,0)----) (2/3, -1/3)
                         \    / \    /
                          \  /   \  /
              (-1/3, -1/3) \/_____\/ (1/3, -2/3)

           Suppose our converted coordinates were (q, r) = (1.333, 0.583). Our rounded coordinates
           are hex = (1, 1), and so our fractional coordinates are (0.333, -0.417).

           We now define three linear equations that will split this hexagon into its six fields:

                            _______ y = x (/)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
                        (------X------) y = -x/2 (-)
                         \    / \    /
                          \  /   \  /
                           \/_____\/ y = -2x (\)

           Using these three equations as linear inequalities, we can check any pair of fractional
           coordinates and find which field it is in. (As a reminder, fields are numbered clockwise
           starting from 0 at the top.)

           For our example (0.333, -0.417), we see that:
               y + x/2 >= 0, so our field is above (-), and is either 5, 0, or 1.
               y +  2x >= 0, so our field is to the right of (\), and is either 0 or 1.
               y -   x <  0, so our field is to the right of (/), and is 1.
        */
        let x_diff = q - hex.x() as f32;
        let y_diff = r - hex.y() as f32;
        let mut i = 0;

        if y_diff + x_diff / 2.0 >= 0.0 {
            i |= 0b100;
        }
        if y_diff + x_diff * 2.0 >= 0.0 {
            i |= 0b010;
        }
        if y_diff - x_diff >= 0.0 {
            i |= 0b001;
        }

        // Using a lookup table because nested ifs are too confusing
        const INVALID: u32 = 6;
        let field_lookup = [3, 4, 2, INVALID, INVALID, 5, 1, 0];

        Some(hex.to_field(field_lookup[i]))
    } else {
        None
    }
}

// Algorithm from http://www.redblobgames.com/grids/hexagons/#rounding
fn round_hex_coord(x: f32, y: f32) -> Option<HexCoord> {
    let z = -x - y;

    let mut rx = x.round();
    let mut ry = y.round();
    let rz = z.round();

    let x_diff = (rx - x).abs();
    let y_diff = (ry - y).abs();
    let z_diff = (rz - z).abs();

    if x_diff > y_diff && x_diff > z_diff {
        rx = -ry - rz;
    } else if y_diff > z_diff {
        ry = -rx - rz;
    }

    let rx = rx as i32;
    let ry = ry as i32;

    HexCoord::try_new(rx, ry)
}

fn add_vec(lhs: &ImVec2, rhs: &ImVec2) -> ImVec2 {
    ImVec2::new(lhs.x + rhs.x, lhs.y + rhs.y)
}

fn sub_vec(lhs: &ImVec2, rhs: &ImVec2) -> ImVec2 {
    ImVec2::new(lhs.x - rhs.x, lhs.y - rhs.y)
}

fn mul_vec(v: &ImVec2, c: f32) -> ImVec2 {
    ImVec2::new(v.x * c, v.y * c)
}
<commit_msg>Replace if statements with f32::min<commit_after>/*
 * Copyright (C) 2017 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use imgui_sys::{self, ImVec2, ImVec4};

use model::{Color, FieldCoord, HexCoord, Model};
use view::Event;

const SQRT_3: f32 = 1.732_050_807_568_877_f32;

// #f3e4cf
const FIELD_WHITE: [f32; 4] = [0.9529, 0.8941, 0.8118, 1.0];
// #998578
const FIELD_BLACK: [f32; 4] = [0.6, 0.5216, 0.4706, 1.0];
// #ffffff
const PIECE_WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
// #000000
const PIECE_OUTLINE: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
// #757575
const PIECE_BLACK: [f32; 4] = [0.4588, 0.4588, 0.4588, 1.0];
// #f7b102
const SELECT_HIGHLIGHT: [f32; 4] = [0.9686, 0.6941, 0.0078, 0.8];
// #ffff00
const LAST_MOVE_HIGHLIGHT: [f32; 4] = [1.0, 1.0, 0.0, 0.8];

pub fn board(model: &Model, size: &ImVec2) -> Option<Event> {
    let mouse_click;
    let mut mouse_pos = ImVec2::default();
    let mut cursor_pos = ImVec2::default();
    unsafe {
        mouse_click = imgui_sys::igIsMouseClicked(0, false);
        imgui_sys::igGetMousePos(&mut mouse_pos);
        imgui_sys::igGetCursorScreenPos(&mut cursor_pos);
    }

    let side_len = (size.x / 8.0).min(size.y / (5.0 * SQRT_3));
    let origin = ImVec2::new(cursor_pos.x + size.x / 2.0, cursor_pos.y + size.y / 2.0);
    for hex in model.board.extant_hexes() {
        draw_hex(&hex, &origin, side_len);
    }

    if let Some((ref from, ref to)) = model.last_move {
        if model.board.is_hex_extant(&from.to_hex()) {
            highlight_field(LAST_MOVE_HIGHLIGHT, from, &origin, side_len);
        }
        highlight_field(LAST_MOVE_HIGHLIGHT, to, &origin, side_len);
    }

    if let Some(ref coord) = model.selected_piece {
        highlight_field(SELECT_HIGHLIGHT, coord, &origin, side_len);
    }

    if let Some(ref coords) = model.available_moves {
        for coord in coords {
            highlight_field_dot(SELECT_HIGHLIGHT, coord, &origin, side_len);
        }
    }

    for hex in model.board.extant_hexes() {
        for f in 0..6 {
            let coord = hex.to_field(f);
            if model.board.is_piece_on_field(&coord) {
                draw_piece(&coord, &origin, side_len);
            }
        }
    }

    unsafe {
        imgui_sys::igDummy(size);
    }

    let board_min = cursor_pos;
    let board_max = add_vec(&cursor_pos, size);
    if mouse_click && board_min.x <= mouse_pos.x && board_min.y <= mouse_pos.y
        && board_max.x >= mouse_pos.x && board_max.y >= mouse_pos.y
    {
        pixel_to_field(&mouse_pos, &origin, side_len).map(Event::Click)
    } else {
        None
    }
}

macro_rules! im_color {
    ($v:expr) => (imgui_sys::igColorConvertFloat4ToU32(ImVec4::from($v)))
}

fn draw_hex(coord: &HexCoord, origin: &ImVec2, size: f32) {
    for i in 0..6 {
        draw_field(&coord.to_field(i), origin, size);
    }
}

fn draw_field(coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(FIELD_WHITE),
            Color::Black => im_color!(FIELD_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, color);
    }
}

fn highlight_field(color: [f32; 4], coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, highlight);
    }
}

fn highlight_field_dot(color: [f32; 4], coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let center = field_center(coord, origin, size);
    unsafe {
        let highlight = im_color!(color);

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddCircleFilled(
            draw_list,
            center,
            size / (4.0 * SQRT_3),
            highlight,
            15,
        );
    }
}

fn draw_piece(coord: &FieldCoord, origin: &ImVec2, size: f32) {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center = field_center(coord, origin, size);

    const SCALE: f32 = 0.7;

    let v1 = add_vec(&center, &mul_vec(&sub_vec(&v1, &center), SCALE));
    let v2 = add_vec(&center, &mul_vec(&sub_vec(&v2, &center), SCALE));
    let v3 = add_vec(&center, &mul_vec(&sub_vec(&v3, &center), SCALE));

    // Linear equation derived by human testing and regression
    let outline_size = 0.032 * size - 0.335;

    unsafe {
        let color = match coord.color() {
            Color::White => im_color!(PIECE_WHITE),
            Color::Black => im_color!(PIECE_BLACK),
        };

        let draw_list = imgui_sys::igGetWindowDrawList();
        imgui_sys::ImDrawList_AddTriangleFilled(draw_list, v1, v2, v3, color);
        imgui_sys::ImDrawList_AddTriangle(
            draw_list,
            v1,
            v2,
            v3,
            im_color!(PIECE_OUTLINE),
            outline_size,
        );
    }
}

fn field_center(coord: &FieldCoord, origin: &ImVec2, size: f32) -> ImVec2 {
    let (v1, v2, v3) = field_vertexes(coord, origin, size);
    let center_x = (v1.x + v2.x + v3.x) / 3.0;
    let min_y = (v1.y).min(v2.y).min(v3.y);
    let center_y = match coord.color() {
        Color::Black => min_y + size / (2.0 * SQRT_3),
        Color::White => min_y + size / SQRT_3,
    };

    ImVec2::new(center_x, center_y)
}

fn field_vertexes(coord: &FieldCoord, origin: &ImVec2, size: f32) -> (ImVec2, ImVec2, ImVec2) {
    let center = hex_to_pixel(&coord.to_hex(), origin, size);
    let height = size * SQRT_3 / 2.0;

    let v1;
    let v2;
    match coord.f() {
        0 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size / 2.0, center.y - height);
        }
        1 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y - height);
            v2 = ImVec2::new(center.x + size, center.y);
        }
        2 => {
            v1 = ImVec2::new(center.x + size, center.y);
            v2 = ImVec2::new(center.x + size / 2.0, center.y + height);
        }
        3 => {
            v1 = ImVec2::new(center.x + size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size / 2.0, center.y + height);
        }
        4 => {
            v1 = ImVec2::new(center.x - size / 2.0, center.y + height);
            v2 = ImVec2::new(center.x - size, center.y);
        }
        5 => {
            v1 = ImVec2::new(center.x - size, center.y);
            v2 = ImVec2::new(center.x - size / 2.0, center.y - height);
        }
        _ => panic!("You made the impossible possible."),
    };

    (center, v1, v2)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#hex-to-pixel
fn hex_to_pixel(coord: &HexCoord, origin: &ImVec2, size: f32) -> ImVec2 {
    let x = coord.x() as f32;
    let y = coord.y() as f32;

    let px = size * (3.0 / 2.0) * x;
    let py = size * -SQRT_3 * (x / 2.0 + y);

    ImVec2::new(px + origin.x, py + origin.y)
}

// Algorithm based on http://www.redblobgames.com/grids/hexagons/#pixel-to-hex
fn pixel_to_field(p: &ImVec2, origin: &ImVec2, size: f32) -> Option<FieldCoord> {
    let x = p.x - origin.x;
    let y = p.y - origin.y;

    let q = x * (2.0 / 3.0) / size;
    let r = (-x - SQRT_3 * y) / (size * 3.0);

    if let Some(hex) = round_hex_coord(q, r) {
        /*
           To find the field, we subtract the converted hex coordinates (q, r) from the rounded hex
           coordinates (hex) to get the fractional part of the coordinates. Here is a diagram of a
           single hex, with the fractional coordinates of each of its vertexes in the hex coordinate
           system:

                (-1/3, 2/3) _______ (1/3, 1/3)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
            (-2/3, 1/3) (----(0,0)----) (2/3, -1/3)
                         \    / \    /
                          \  /   \  /
              (-1/3, -1/3) \/_____\/ (1/3, -2/3)

           Suppose our converted coordinates were (q, r) = (1.333, 0.583). Our rounded coordinates
           are hex = (1, 1), and so our fractional coordinates are (0.333, -0.417).

           We now define three linear equations that will split this hexagon into its six fields:

                            _______ y = x (/)
                           /\     /\
                          /  \   /  \
                         /    \ /    \
                        (------X------) y = -x/2 (-)
                         \    / \    /
                          \  /   \  /
                           \/_____\/ y = -2x (\)

           Using these three equations as linear inequalities, we can check any pair of fractional
           coordinates and find which field it is in. (As a reminder, fields are numbered clockwise
           starting from 0 at the top.)

           For our example (0.333, -0.417), we see that:
               y + x/2 >= 0, so our field is above (-), and is either 5, 0, or 1.
               y +  2x >= 0, so our field is to the right of (\), and is either 0 or 1.
               y -   x <  0, so our field is to the right of (/), and is 1.
        */
        let x_diff = q - hex.x() as f32;
        let y_diff = r - hex.y() as f32;
        let mut i = 0;

        if y_diff + x_diff / 2.0 >= 0.0 {
            i |= 0b100;
        }
        if y_diff + x_diff * 2.0 >= 0.0 {
            i |= 0b010;
        }
        if y_diff - x_diff >= 0.0 {
            i |= 0b001;
        }

        // Using a lookup table because nested ifs are too confusing
        const INVALID: u32 = 6;
        let field_lookup = [3, 4, 2, INVALID, INVALID, 5, 1, 0];

        Some(hex.to_field(field_lookup[i]))
    } else {
        None
    }
}

// Algorithm from http://www.redblobgames.com/grids/hexagons/#rounding
fn round_hex_coord(x: f32, y: f32) -> Option<HexCoord> {
    let z = -x - y;

    let mut rx = x.round();
    let mut ry = y.round();
    let rz = z.round();

    let x_diff = (rx - x).abs();
    let y_diff = (ry - y).abs();
    let z_diff = (rz - z).abs();

    if x_diff > y_diff && x_diff > z_diff {
        rx = -ry - rz;
    } else if y_diff > z_diff {
        ry = -rx - rz;
    }

    let rx = rx as i32;
    let ry = ry as i32;

    HexCoord::try_new(rx, ry)
}

fn add_vec(lhs: &ImVec2, rhs: &ImVec2) -> ImVec2 {
    ImVec2::new(lhs.x + rhs.x, lhs.y + rhs.y)
}

fn sub_vec(lhs: &ImVec2, rhs: &ImVec2) -> ImVec2 {
    ImVec2::new(lhs.x - rhs.x, lhs.y - rhs.y)
}

fn mul_vec(v: &ImVec2, c: f32) -> ImVec2 {
    ImVec2::new(v.x * c, v.y * c)
}
<|endoftext|>


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

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod ffi;

use std::path::Path;
use libc::{c_uint,c_ulong};

pub const DEMUX_FILTER_SIZE: usize = ffi::DMX_FILTER_SIZE as usize;

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod enums {
    use super::ffi as ffi;

    include!(concat!(env!("OUT_DIR"), "/demux-enums.rs"));
}

pub use self::enums::{Input,Output,PesType};

pub struct Demux {
    device: DeviceFileDescriptor
}

use super::device::{DeviceFileDescriptor,BlockMode,ReadWriteMode,DeviceResult};

impl Demux {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Demux> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Demux { device: device })
    }

    pub fn read(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.read(buffer)
    }

    pub fn start(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_START as c_ulong)
    }

    pub fn stop(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_STOP as c_ulong)
    }

    pub fn set_filter(&self, filter_params: &SectionFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_sct_filter_params {
            pid: filter_params.pid,
            filter: ffi::Struct_dmx_filter {
                filter: filter_params.filter.filter,
                mask: filter_params.filter.mask,
                mode: filter_params.filter.mode,
            },
            timeout: filter_params.timeout,
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_pes_filter(&self, filter_params: &PesFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_pes_filter_params {
            pid: filter_params.pid,
            input: filter_params.input.into(),
            output: filter_params.output.into(),
            pes_type: filter_params.pes_type.into(),
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_PES_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }

    pub fn get_system_time_counter(&self, num: u32) -> DeviceResult<SystemTimeCounter> {
        let mut ffi_stc = ffi::Struct_dmx_stc { num: num as c_uint, base: 0, stc: 0 };
        try!(self.device.ioctl_pointer(ffi::DMX_GET_STC as c_ulong, &mut ffi_stc));
        Ok(SystemTimeCounter { base: ffi_stc.base, counter: ffi_stc.stc })
    }

    pub fn get_pes_pids(&self) -> DeviceResult<[u16; 5]> {
        let mut ffi_pids = [0u16; 5];
        try!(self.device.ioctl_pointer(ffi::DMX_GET_PES_PIDS as c_ulong, ffi_pids.as_mut_ptr()));
        Ok(ffi_pids)
    }

    /// This function is undocumented in the official documentation, and doesn't seem to be
    /// actually in use. It is still nevertheless included here, if it is used by out-of-tree DVB
    /// drivers.
    pub fn get_caps(&self) -> DeviceResult<DemuxCaps> {
        let mut ffi_caps = ffi::Struct_dmx_caps { caps: 0, num_decoders: 0 };
        try!(self.device.ioctl_pointer(ffi::DMX_GET_CAPS as c_ulong, &mut ffi_caps));
        Ok(DemuxCaps { caps: ffi_caps.caps, num_decoders: ffi_caps.num_decoders as i32 })
    }

    /// Like get_caps(), this doesn't seem to be actively in use in Linux, but is still implemented.
    pub fn set_source(&self, source: enums::DemuxSource) -> DeviceResult<()> {
        let mut ffi_source: ffi::dmx_source_t =  source.into();
        self.device.ioctl_pointer(ffi::DMX_SET_SOURCE as c_ulong, &mut ffi_source)
    }

    pub fn add_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_ADD_PID as c_ulong, &mut ffi_pid)
    }

    pub fn remove_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_REMOVE_PID as c_ulong, &mut ffi_pid)
    }
}

pub struct DemuxCaps {
    pub caps: u32,
    pub num_decoders: i32
}

pub struct SystemTimeCounter {
    pub base: u32,
    pub counter: u64
}

pub struct DemuxFilter {
    pub filter: [u8; DEMUX_FILTER_SIZE],
    pub mask: [u8; DEMUX_FILTER_SIZE],
    pub mode: [u8; DEMUX_FILTER_SIZE],
}

impl Default for DemuxFilter {
    fn default() -> DemuxFilter {
        DemuxFilter {
            filter: [0u8; DEMUX_FILTER_SIZE],
            mask: [0u8; DEMUX_FILTER_SIZE],
            mode: [0u8; DEMUX_FILTER_SIZE],
        }
    }
}

pub struct SectionFilterParams {
    pub pid: u16,
    pub filter: DemuxFilter,
    pub timeout: u32,
    pub flags: SectionFilterFlags
}

pub struct PesFilterParams {
    pub pid: u16,
    pub input: Input,
    pub output: Output,
    pub pes_type: PesType,
    pub flags: PesFilterFlags
}

bitflags!{
    pub struct SectionFilterFlags: u32 {
        const CHECK_CRC = ffi::DMX_CHECK_CRC;
        const ONESHOT = ffi::DMX_ONESHOT;
        const IMMEDIATE_START = ffi::DMX_IMMEDIATE_START;
    }
}

bitflags!{
    pub struct PesFilterFlags: u32 {
        const IMMEDIATE_START = ffi::DMX_IMMEDIATE_START;
    }
}

pub struct Dvr {
    device: DeviceFileDescriptor
}

impl Dvr {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Dvr> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Dvr { device: device })
    }

    pub fn write(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.write(buffer)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }
}

fn set_buffer_size(device: &DeviceFileDescriptor, buffer_size: u32) -> DeviceResult<()> {
    device.ioctl_value(ffi::DMX_SET_BUFFER_SIZE as c_ulong, buffer_size as c_ulong)
}
<commit_msg>Remove methods that have been removed from Linux itself as unused<commit_after>// Copyright 2015 Ilkka Rauta
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod ffi;

use std::path::Path;
use libc::{c_uint,c_ulong};

pub const DEMUX_FILTER_SIZE: usize = ffi::DMX_FILTER_SIZE as usize;

#[allow(dead_code,non_camel_case_types,non_snake_case)]
mod enums {
    use super::ffi as ffi;

    include!(concat!(env!("OUT_DIR"), "/demux-enums.rs"));
}

pub use self::enums::{Input,Output,PesType};

pub struct Demux {
    device: DeviceFileDescriptor
}

use super::device::{DeviceFileDescriptor,BlockMode,ReadWriteMode,DeviceResult};

impl Demux {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Demux> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Demux { device: device })
    }

    pub fn read(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.read(buffer)
    }

    pub fn start(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_START as c_ulong)
    }

    pub fn stop(&self) -> DeviceResult<()> {
        self.device.ioctl_argumentless(ffi::DMX_STOP as c_ulong)
    }

    pub fn set_filter(&self, filter_params: &SectionFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_sct_filter_params {
            pid: filter_params.pid,
            filter: ffi::Struct_dmx_filter {
                filter: filter_params.filter.filter,
                mask: filter_params.filter.mask,
                mode: filter_params.filter.mode,
            },
            timeout: filter_params.timeout,
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_pes_filter(&self, filter_params: &PesFilterParams) -> DeviceResult<()> {
        let mut ffi_params = ffi::Struct_dmx_pes_filter_params {
            pid: filter_params.pid,
            input: filter_params.input.into(),
            output: filter_params.output.into(),
            pes_type: filter_params.pes_type.into(),
            flags: filter_params.flags.bits()
        };
        self.device.ioctl_pointer(ffi::DMX_SET_PES_FILTER as c_ulong, &mut ffi_params)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }

    pub fn get_system_time_counter(&self, num: u32) -> DeviceResult<SystemTimeCounter> {
        let mut ffi_stc = ffi::Struct_dmx_stc { num: num as c_uint, base: 0, stc: 0 };
        try!(self.device.ioctl_pointer(ffi::DMX_GET_STC as c_ulong, &mut ffi_stc));
        Ok(SystemTimeCounter { base: ffi_stc.base, counter: ffi_stc.stc })
    }

    pub fn get_pes_pids(&self) -> DeviceResult<[u16; 5]> {
        let mut ffi_pids = [0u16; 5];
        try!(self.device.ioctl_pointer(ffi::DMX_GET_PES_PIDS as c_ulong, ffi_pids.as_mut_ptr()));
        Ok(ffi_pids)
    }

// These two have been left out of Linux as unused, so not keeping them alive here either.
//    /// This function is undocumented in the official documentation, and doesn't seem to be
//    /// actually in use. It is still nevertheless included here, if it is used by out-of-tree DVB
//    /// drivers.
//    pub fn get_caps(&self) -> DeviceResult<DemuxCaps> {
//        let mut ffi_caps = ffi::Struct_dmx_caps { caps: 0, num_decoders: 0 };
//        try!(self.device.ioctl_pointer(ffi::DMX_GET_CAPS as c_ulong, &mut ffi_caps));
//        Ok(DemuxCaps { caps: ffi_caps.caps, num_decoders: ffi_caps.num_decoders as i32 })
//    }
//
//    /// Like get_caps(), this doesn't seem to be actively in use in Linux, but is still implemented.
//    pub fn set_source(&self, source: enums::DemuxSource) -> DeviceResult<()> {
//        let mut ffi_source: ffi::dmx_source_t =  source.into();
//        self.device.ioctl_pointer(ffi::DMX_SET_SOURCE as c_ulong, &mut ffi_source)
//    }

    pub fn add_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_ADD_PID as c_ulong, &mut ffi_pid)
    }

    pub fn remove_pid(&self, pid: u16) -> DeviceResult<()> {
        let mut ffi_pid = pid;
        self.device.ioctl_pointer(ffi::DMX_REMOVE_PID as c_ulong, &mut ffi_pid)
    }
}

// See comment for get_caps and set_source above.
//pub struct DemuxCaps {
//    pub caps: u32,
//    pub num_decoders: i32
//}

pub struct SystemTimeCounter {
    pub base: u32,
    pub counter: u64
}

pub struct DemuxFilter {
    pub filter: [u8; DEMUX_FILTER_SIZE],
    pub mask: [u8; DEMUX_FILTER_SIZE],
    pub mode: [u8; DEMUX_FILTER_SIZE],
}

impl Default for DemuxFilter {
    fn default() -> DemuxFilter {
        DemuxFilter {
            filter: [0u8; DEMUX_FILTER_SIZE],
            mask: [0u8; DEMUX_FILTER_SIZE],
            mode: [0u8; DEMUX_FILTER_SIZE],
        }
    }
}

pub struct SectionFilterParams {
    pub pid: u16,
    pub filter: DemuxFilter,
    pub timeout: u32,
    pub flags: SectionFilterFlags
}

pub struct PesFilterParams {
    pub pid: u16,
    pub input: Input,
    pub output: Output,
    pub pes_type: PesType,
    pub flags: PesFilterFlags
}

bitflags!{
    pub struct SectionFilterFlags: u32 {
        const CHECK_CRC = ffi::DMX_CHECK_CRC;
        const ONESHOT = ffi::DMX_ONESHOT;
        const IMMEDIATE_START = ffi::DMX_IMMEDIATE_START;
    }
}

bitflags!{
    pub struct PesFilterFlags: u32 {
        const IMMEDIATE_START = ffi::DMX_IMMEDIATE_START;
    }
}

pub struct Dvr {
    device: DeviceFileDescriptor
}

impl Dvr {
    pub fn open(file: &Path, rw_mode: ReadWriteMode, block_mode: BlockMode) -> DeviceResult<Dvr> {
        let device = try!(DeviceFileDescriptor::open(file, rw_mode, block_mode));
        Ok(Dvr { device: device })
    }

    pub fn write(&self, buffer: &mut [u8]) -> DeviceResult<usize> {
        self.device.write(buffer)
    }

    pub fn set_buffer_size(&self, buffer_size: u32) -> DeviceResult<()> {
        set_buffer_size(&self.device, buffer_size)
    }
}

fn set_buffer_size(device: &DeviceFileDescriptor, buffer_size: u32) -> DeviceResult<()> {
    device.ioctl_value(ffi::DMX_SET_BUFFER_SIZE as c_ulong, buffer_size as c_ulong)
}
<|endoftext|>


<commit_before><commit_msg>Add example of reading tags to the crate docs<commit_after><|endoftext|>


<commit_before><commit_msg>Ignore documented usage string format in doc-tests.<commit_after><|endoftext|>


<commit_before><commit_msg>fixup! Assert mock hits<commit_after><|endoftext|>


<commit_before><commit_msg>Added functions for searching for a target T rather than index<commit_after><|endoftext|>


<commit_before><commit_msg>lib: Add function blg2key for partial lookups<commit_after><|endoftext|>


<commit_before>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate serde_json;
extern crate hyper;

pub mod requests;

#[cfg(test)]
mod test {
    use super::requests;
    use hyper;
    use serde::Deserialize;

    #[test]
    fn get() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::get(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn post() {
        const URL: &'static str = "http://httpbin.org/post";
        let res = requests::post(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn put() {
        const URL: &'static str = "http://httpbin.org/put";
        let res = requests::put(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn head() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::head(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn delete() {
        const URL: &'static str = "http://httpbin.org/delete";
        let res = requests::delete(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn user_agent() {
        let useragent = concat!("{\n  \"user-agent\": \"requests-rs/",
                                env!("CARGO_PKG_VERSION"),
                                "\"\n}\n");
        const URL: &'static str = "http://httpbin.org/user-agent";
        let res = requests::get(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
        assert_eq!(res.text(), Some(useragent));
    }

    #[test]
    fn user_agent_json() {

        #[derive(Debug, Deserialize)]
        struct UserAgent {
            #[serde(rename="user-agent")]
            user_agent: String,
        }

        const URL: &'static str = "http://httpbin.org/user-agent";
        let res = requests::get(URL).unwrap();
        assert!(res.is_json());

        let ua: UserAgent = res.from_json().unwrap();
        assert_eq!(ua.user_agent, concat!("requests-rs/", env!("CARGO_PKG_VERSION")));
    }
}
<commit_msg>Test GET data payload<commit_after>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate serde_json;
extern crate hyper;

pub mod requests;

#[cfg(test)]
mod test {
    use super::requests;
    use hyper;

    #[derive(Debug, Deserialize)]
    struct Args;

    #[derive(Debug, Deserialize)]
    struct Headers {
        #[serde(rename="Host")]
        host: String,
        #[serde(rename="User-Agent")]
        user_agent: String,
    }

    #[derive(Debug, Deserialize)]
    struct HttpBinData {
        // args: Args,
        headers: Headers,
        origin: String,
        url: String,
    }

    #[test]
    fn get() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::get(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
        let data: HttpBinData = res.from_json().unwrap();
        assert_eq!(data.url, URL);
        assert_eq!(data.headers.host, "httpbin.org");
        assert_eq!(data.headers.user_agent, concat!("requests-rs/", env!("CARGO_PKG_VERSION")));
    }

    #[test]
    fn post() {
        const URL: &'static str = "http://httpbin.org/post";
        let res = requests::post(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn put() {
        const URL: &'static str = "http://httpbin.org/put";
        let res = requests::put(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn head() {
        const URL: &'static str = "http://httpbin.org/get";
        let res = requests::head(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn delete() {
        const URL: &'static str = "http://httpbin.org/delete";
        let res = requests::delete(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
    }

    #[test]
    fn user_agent() {
        let useragent = concat!("{\n  \"user-agent\": \"requests-rs/",
                                env!("CARGO_PKG_VERSION"),
                                "\"\n}\n");
        const URL: &'static str = "http://httpbin.org/user-agent";
        let res = requests::get(URL).unwrap();
        assert_eq!(res.url(), URL);
        assert_eq!(res.status_code(), hyper::Ok);
        assert_eq!(res.reason(), "OK");
        assert_eq!(res.text(), Some(useragent));
    }

    #[test]
    fn user_agent_json() {

        #[derive(Debug, Deserialize)]
        struct UserAgent {
            #[serde(rename="user-agent")]
            user_agent: String,
        }

        const URL: &'static str = "http://httpbin.org/user-agent";
        let res = requests::get(URL).unwrap();
        assert!(res.is_json());

        let ua: UserAgent = res.from_json().unwrap();
        assert_eq!(ua.user_agent, concat!("requests-rs/", env!("CARGO_PKG_VERSION")));
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;

const LF: u8 = 10;
const CR: u8 = 13;
const QUOTE: u8 = 34;
const COMMA: u8 = 44;

#[derive(PartialEq)]
enum CSVState {
    Start,
    NonQuotedValue,
    NonQuotedQuote,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

type CSVResult = Result<CSVState, ()>;

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: i32,
    pub col: i32,
}

fn parse_start(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        _ => Ok(CSVState::NonQuotedValue)
    }
}

fn parse_non_quoted(byte: u8) -> CSVResult {
    match byte {
        COMMA => Ok(CSVState::Start),
        CR    => Ok(CSVState::ExpectLF),
        LF    => Ok(CSVState::Start),
        QUOTE => Ok(CSVState::NonQuotedQuote),
        _     => Ok(CSVState::NonQuotedValue),
    }
}

fn parse_non_quoted_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::NonQuotedValue),
        COMMA => Err(()),
        CR    => Err(()),
        LF    => Err(()),
        _     => Ok(CSVState::NonQuotedQuote),
    }
}

fn parse_quoted(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuoteQuote),
        CR    => Err(()),
        LF    => Err(()),
        _     => Ok(CSVState::QuotedValue),
    }
}

fn parse_quote_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        CR    => Ok(CSVState::ExpectLF),
        LF    => Ok(CSVState::Start),
        _     => Err(()),
    }
}

fn parse_cr(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _  => Err(()),
    }
}

fn parse_err(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _  => Ok(CSVState::Error),
    }
}

pub fn errors_for_csv(file: File) -> Vec<CSVError> {
    let mut state = CSVState::Start;
    let mut line = 1;
    let mut col = 0;

    let mut errors: Vec<CSVError> = Vec::new();

    for byte in file.bytes() {
        let b = byte.unwrap();

        let next_state = match state {
            CSVState::Start          => parse_start(b),
            CSVState::NonQuotedValue => parse_non_quoted(b),
            CSVState::NonQuotedQuote => parse_non_quoted_quote(b),
            CSVState::QuotedValue    => parse_quoted(b),
            CSVState::QuoteQuote     => parse_quote_quote(b),
            CSVState::ExpectLF       => parse_cr(b),
            CSVState::Error          => parse_err(b),
        };

        state = match next_state {
            Ok(new_state) => new_state,
            Err(_)        => {
                errors.push(CSVError { line: line, col: col });
                CSVState::Error
            },
        };

        if b == LF {
            line = line + 1;
            col = 0;
        } else {
            col = col + 1;
        }
    }

    errors
}
<commit_msg>Didn't need that PartialEq after all<commit_after>use std::fs::File;
use std::io::Read;

const LF: u8 = 10;
const CR: u8 = 13;
const QUOTE: u8 = 34;
const COMMA: u8 = 44;

enum CSVState {
    Start,
    NonQuotedValue,
    NonQuotedQuote,
    QuotedValue,
    QuoteQuote,
    ExpectLF,
    Error,
}

type CSVResult = Result<CSVState, ()>;

#[derive(Debug, PartialEq, Eq)]
pub struct CSVError {
    pub line: i32,
    pub col: i32,
}

fn parse_start(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        _ => Ok(CSVState::NonQuotedValue)
    }
}

fn parse_non_quoted(byte: u8) -> CSVResult {
    match byte {
        COMMA => Ok(CSVState::Start),
        CR    => Ok(CSVState::ExpectLF),
        LF    => Ok(CSVState::Start),
        QUOTE => Ok(CSVState::NonQuotedQuote),
        _     => Ok(CSVState::NonQuotedValue),
    }
}

fn parse_non_quoted_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::NonQuotedValue),
        COMMA => Err(()),
        CR    => Err(()),
        LF    => Err(()),
        _     => Ok(CSVState::NonQuotedQuote),
    }
}

fn parse_quoted(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuoteQuote),
        CR    => Err(()),
        LF    => Err(()),
        _     => Ok(CSVState::QuotedValue),
    }
}

fn parse_quote_quote(byte: u8) -> CSVResult {
    match byte {
        QUOTE => Ok(CSVState::QuotedValue),
        COMMA => Ok(CSVState::Start),
        CR    => Ok(CSVState::ExpectLF),
        LF    => Ok(CSVState::Start),
        _     => Err(()),
    }
}

fn parse_cr(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _  => Err(()),
    }
}

fn parse_err(byte: u8) -> CSVResult {
    match byte {
        LF => Ok(CSVState::Start),
        _  => Ok(CSVState::Error),
    }
}

pub fn errors_for_csv(file: File) -> Vec<CSVError> {
    let mut state = CSVState::Start;
    let mut line = 1;
    let mut col = 0;

    let mut errors: Vec<CSVError> = Vec::new();

    for byte in file.bytes() {
        let b = byte.unwrap();

        let next_state = match state {
            CSVState::Start          => parse_start(b),
            CSVState::NonQuotedValue => parse_non_quoted(b),
            CSVState::NonQuotedQuote => parse_non_quoted_quote(b),
            CSVState::QuotedValue    => parse_quoted(b),
            CSVState::QuoteQuote     => parse_quote_quote(b),
            CSVState::ExpectLF       => parse_cr(b),
            CSVState::Error          => parse_err(b),
        };

        state = match next_state {
            Ok(new_state) => new_state,
            Err(_)        => {
                errors.push(CSVError { line: line, col: col });
                CSVState::Error
            },
        };

        if b == LF {
            line = line + 1;
            col = 0;
        } else {
            col = col + 1;
        }
    }

    errors
}
<|endoftext|>


<commit_before>#![allow(unsafe_code)]

use std::{
    borrow::{Borrow, BorrowMut},
    convert::TryFrom,
    hash::{Hash, Hasher},
    mem::MaybeUninit,
    sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
};

use crate::{
    atomic_shim::AtomicU64,
    debug_delay,
    dll::{DoublyLinkedList, Node},
    fastlock::FastLock,
    FastSet8, Guard, PageId,
};

#[cfg(any(test, feature = "lock_free_delays"))]
const MAX_QUEUE_ITEMS: usize = 4;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const MAX_QUEUE_ITEMS: usize = 64;

#[cfg(any(test, feature = "lock_free_delays"))]
const N_SHARDS: usize = 2;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const N_SHARDS: usize = 256;

struct AccessBlock {
    len: AtomicUsize,
    block: [AtomicU64; MAX_QUEUE_ITEMS],
    next: AtomicPtr<AccessBlock>,
}

impl Default for AccessBlock {
    fn default() -> AccessBlock {
        AccessBlock {
            len: AtomicUsize::new(0),
            block: unsafe { MaybeUninit::zeroed().assume_init() },
            next: AtomicPtr::default(),
        }
    }
}

struct AccessQueue {
    writing: AtomicPtr<AccessBlock>,
    full_list: AtomicPtr<AccessBlock>,
}

impl Default for AccessQueue {
    fn default() -> AccessQueue {
        AccessQueue {
            writing: AtomicPtr::new(Box::into_raw(Box::new(
                AccessBlock::default(),
            ))),
            full_list: AtomicPtr::default(),
        }
    }
}

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        let mut filled = false;
        let item_u64: u64 = item.into();
        assert_ne!(item_u64, 0);
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

            debug_delay();
            let offset = block.len.fetch_add(1, Ordering::Release);

            if offset < MAX_QUEUE_ITEMS {
                debug_delay();
                unsafe {
                    block
                        .block
                        .get_unchecked(offset)
                        .store(item_u64, Ordering::Release);
                }
                return filled;
            } else {
                // install new writer
                let new = Box::into_raw(Box::new(AccessBlock::default()));
                debug_delay();
                let res = self.writing.compare_exchange_weak(
                    head,
                    new,
                    Ordering::Release,
                    Ordering::Relaxed,
                );
                if res.is_err() {
                    // we lost the CAS, free the new item that was
                    // never published to other threads
                    unsafe {
                        drop(Box::from_raw(new));
                    }
                    continue;
                }

                // push the now-full item to the full list for future
                // consumption
                let mut ret;
                let mut full_list_ptr = self.full_list.load(Ordering::Acquire);
                while {
                    // we loop because maybe other threads are pushing stuff too
                    block.next.store(full_list_ptr, Ordering::Release);
                    debug_delay();
                    ret = self.full_list.compare_exchange_weak(
                        full_list_ptr,
                        head,
                        Ordering::Release,
                        Ordering::Relaxed,
                    );
                    ret.is_err()
                } {
                    full_list_ptr = ret.unwrap_err();
                }
                filled = true;
            }
        }
    }

    fn take<'a>(&self, guard: &'a Guard) -> CacheAccessIter<'a> {
        debug_delay();
        let ptr = self.full_list.swap(std::ptr::null_mut(), Ordering::AcqRel);

        CacheAccessIter { guard, current_offset: 0, current_block: ptr }
    }
}

impl Drop for AccessQueue {
    fn drop(&mut self) {
        debug_delay();
        let writing = self.writing.load(Ordering::Acquire);
        unsafe {
            Box::from_raw(writing);
        }
        debug_delay();
        let mut head = self.full_list.load(Ordering::Acquire);
        while !head.is_null() {
            unsafe {
                debug_delay();
                let next =
                    (*head).next.swap(std::ptr::null_mut(), Ordering::Release);
                Box::from_raw(head);
                head = next;
            }
        }
    }
}

struct CacheAccessIter<'a> {
    guard: &'a Guard,
    current_offset: usize,
    current_block: *mut AccessBlock,
}

impl<'a> Iterator for CacheAccessIter<'a> {
    type Item = CacheAccess;

    fn next(&mut self) -> Option<CacheAccess> {
        while !self.current_block.is_null() {
            let current_block = unsafe { &*self.current_block };

            debug_delay();
            if self.current_offset >= MAX_QUEUE_ITEMS {
                let to_drop_ptr = self.current_block;
                debug_delay();
                self.current_block = current_block.next.load(Ordering::Acquire);
                self.current_offset = 0;
                debug_delay();
                let to_drop = unsafe { Box::from_raw(to_drop_ptr) };
                self.guard.defer(|| to_drop);
                continue;
            }

            let mut next = 0;
            while next == 0 {
                // we spin here because there's a race between bumping
                // the offset and setting the value to something other
                // than 0 (and 0 is an invalid value)
                debug_delay();
                next = current_block.block[self.current_offset]
                    .load(Ordering::Acquire);
            }
            self.current_offset += 1;
            return Some(CacheAccess::from(next));
        }

        None
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CacheAccess {
    // safe because MAX_PID_BITS / N_SHARDS < u32::MAX
    //                     2**37 /   2**8   < 2**32
    pub pid: u32,
    pub sz: u8,
}

impl From<CacheAccess> for u64 {
    fn from(ca: CacheAccess) -> u64 {
        (u64::from(ca.pid) << 8) | u64::from(ca.sz)
    }
}

#[allow(clippy::fallible_impl_from)]
impl From<u64> for CacheAccess {
    fn from(u: u64) -> CacheAccess {
        let sz = usize::try_from((u << 56) >> 56).unwrap();
        assert_ne!(sz, 0);
        let pid = u >> 8;
        assert!(pid < u64::from(std::u32::MAX));
        CacheAccess {
            pid: u32::try_from(pid).unwrap(),
            sz: u8::try_from(sz).unwrap(),
        }
    }
}

impl CacheAccess {
    fn size(&self) -> usize {
        1 << usize::from(self.sz)
    }

    fn new(pid: PageId, sz: usize) -> CacheAccess {
        let rounded_up_power_of_2 =
            u8::try_from(sz.next_power_of_two().trailing_zeros()).unwrap();

        CacheAccess {
            pid: u32::try_from(pid).expect("expected caller to shift pid down"),
            sz: rounded_up_power_of_2,
        }
    }
}

/// A simple LRU cache.
pub struct Lru {
    shards: Vec<(AccessQueue, FastLock<Shard>)>,
}

impl Lru {
    /// Instantiates a new `Lru` cache.
    pub(crate) fn new(cache_capacity: usize) -> Self {
        assert!(
            cache_capacity >= N_SHARDS,
            "Please configure the cache \
             capacity to be at least 256 bytes"
        );
        let shard_capacity = cache_capacity / N_SHARDS;

        let mut shards = Vec::with_capacity(N_SHARDS);
        shards.resize_with(N_SHARDS, || {
            (AccessQueue::default(), FastLock::new(Shard::new(shard_capacity)))
        });

        Self { shards }
    }

    /// Called when an item is accessed. Returns a Vec of items to be
    /// evicted. Uses flat-combining to avoid blocking on what can
    /// be an asynchronous operation.
    ///
    /// layout:
    ///   items:   1 2 3 4 5 6 7 8 9 10
    ///   shards:  1 0 1 0 1 0 1 0 1 0
    ///   shard 0:   2   4   6   8   10
    ///   shard 1: 1   3   5   7   9
    pub(crate) fn accessed(
        &self,
        id: PageId,
        item_size: usize,
        guard: &Guard,
    ) -> Vec<PageId> {
        const SHARD_BITS: usize = N_SHARDS.trailing_zeros() as usize;

        let mut ret = vec![];
        let shards = N_SHARDS as u64;
        let (shard_idx, shifted_pid) = (id % shards, id >> SHARD_BITS);
        let (access_queue, shard_mu) = &self.shards[safe_usize(shard_idx)];

        let cache_access = CacheAccess::new(shifted_pid, item_size);
        let filled = access_queue.push(cache_access);

        if filled {
            // only try to acquire this if the access queue has filled
            // an entire segment
            if let Some(mut shard) = shard_mu.try_lock() {
                let accesses = access_queue.take(guard);
                for item in accesses {
                    let to_evict = shard.accessed(item);
                    // map shard internal offsets to global items ids
                    for pos in to_evict {
                        let item =
                            (PageId::from(pos) << SHARD_BITS) + shard_idx;
                        ret.push(item);
                    }
                }
            }
        }
        ret
    }
}

#[derive(Eq)]
struct Entry(*mut Node);

unsafe impl Send for Entry {}

impl Ord for Entry {
    fn cmp(&self, other: &Entry) -> std::cmp::Ordering {
        let left_pid: u32 = *self.borrow();
        let right_pid: u32 = *other.borrow();
        left_pid.cmp(&right_pid)
    }
}

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

impl PartialEq for Entry {
    fn eq(&self, other: &Entry) -> bool {
        unsafe { (*self.0).pid == (*other.0).pid }
    }
}

impl BorrowMut<CacheAccess> for Entry {
    fn borrow_mut(&mut self) -> &mut CacheAccess {
        unsafe { &mut *self.0 }
    }
}

impl Borrow<CacheAccess> for Entry {
    fn borrow(&self) -> &CacheAccess {
        unsafe { &*self.0 }
    }
}

impl Borrow<u32> for Entry {
    fn borrow(&self) -> &u32 {
        unsafe { &(*self.0).pid }
    }
}

// we only hash on pid, since we will change
// sz sometimes and we access the item by pid
impl Hash for Entry {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        unsafe { (*self.0).pid.hash(hasher) }
    }
}

struct Shard {
    dll: DoublyLinkedList,
    entries: FastSet8<Entry>,
    capacity: usize,
    size: usize,
}

impl Shard {
    fn new(capacity: usize) -> Self {
        assert!(capacity > 0, "shard capacity must be non-zero");

        Self {
            dll: DoublyLinkedList::default(),
            entries: FastSet8::default(),
            capacity,
            size: 0,
        }
    }

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, cache_access: CacheAccess) -> Vec<u32> {
        if let Some(entry) = self.entries.get(&cache_access.pid) {
            let old_size = 1
                << usize::from(unsafe { (*entry.0).swap_sz(cache_access.sz) });

            self.size -= old_size;
            self.dll.promote(entry.0);
        } else {
            let ptr = self.dll.push_head(cache_access);
            self.entries.insert(Entry(ptr));
        };

        self.size += cache_access.size();

        let mut to_evict = vec![];

        while self.size > self.capacity {
            if self.dll.len() == 1 {
                // don't evict what we just added
                break;
            }

            let min_pid = self.dll.pop_tail().unwrap();

            self.entries.remove(&min_pid.pid);

            to_evict.push(min_pid.pid);

            self.size -= min_pid.size();
        }

        to_evict
    }
}

#[inline]
fn safe_usize(value: PageId) -> usize {
    usize::try_from(value).unwrap()
}

#[test]
fn lru_smoke_test() {
    use crate::pin;

    let lru = Lru::new(2);
    for i in 0..1000 {
        let guard = pin();
        lru.accessed(i, 16, &guard);
    }
}

#[test]
fn lru_access_test() {
    use crate::pin;

    let ci = CacheAccess::new(6, 20667);
    assert_eq!(ci.size(), 32 * 1024);

    let lru = Lru::new(4096);

    let guard = pin();

    assert_eq!(lru.accessed(0, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(2, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(4, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(6, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(8, 20667, &guard), vec![0, 2, 4]);
    assert_eq!(lru.accessed(10, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(12, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(14, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(16, 20667, &guard), vec![6, 8, 10, 12]);
    assert_eq!(lru.accessed(18, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(20, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(22, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(24, 20667, &guard), vec![14, 16, 18, 20]);
}
<commit_msg>Small cleanup<commit_after>#![allow(unsafe_code)]

use std::{
    borrow::{Borrow, BorrowMut},
    convert::TryFrom,
    hash::{Hash, Hasher},
    mem::MaybeUninit,
    sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
};

use crate::{
    atomic_shim::AtomicU64,
    debug_delay,
    dll::{DoublyLinkedList, Node},
    fastlock::FastLock,
    FastSet8, Guard, PageId,
};

#[cfg(any(test, feature = "lock_free_delays"))]
const MAX_QUEUE_ITEMS: usize = 4;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const MAX_QUEUE_ITEMS: usize = 64;

#[cfg(any(test, feature = "lock_free_delays"))]
const N_SHARDS: usize = 2;

#[cfg(not(any(test, feature = "lock_free_delays")))]
const N_SHARDS: usize = 256;

struct AccessBlock {
    len: AtomicUsize,
    block: [AtomicU64; MAX_QUEUE_ITEMS],
    next: AtomicPtr<AccessBlock>,
}

impl Default for AccessBlock {
    fn default() -> AccessBlock {
        AccessBlock {
            len: AtomicUsize::new(0),
            block: unsafe { MaybeUninit::zeroed().assume_init() },
            next: AtomicPtr::default(),
        }
    }
}

struct AccessQueue {
    writing: AtomicPtr<AccessBlock>,
    full_list: AtomicPtr<AccessBlock>,
}

impl Default for AccessQueue {
    fn default() -> AccessQueue {
        AccessQueue {
            writing: AtomicPtr::new(Box::into_raw(Box::new(
                AccessBlock::default(),
            ))),
            full_list: AtomicPtr::default(),
        }
    }
}

impl AccessQueue {
    fn push(&self, item: CacheAccess) -> bool {
        let mut filled = false;
        let item_u64: u64 = item.into();
        assert_ne!(item_u64, 0);
        loop {
            debug_delay();
            let head = self.writing.load(Ordering::Acquire);
            let block = unsafe { &*head };

            debug_delay();
            let offset = block.len.fetch_add(1, Ordering::Release);

            if offset < MAX_QUEUE_ITEMS {
                debug_delay();
                unsafe {
                    block
                        .block
                        .get_unchecked(offset)
                        .store(item_u64, Ordering::Release);
                }
                return filled;
            } else {
                // install new writer
                let new = Box::into_raw(Box::new(AccessBlock::default()));
                debug_delay();
                let res = self.writing.compare_exchange_weak(
                    head,
                    new,
                    Ordering::Release,
                    Ordering::Relaxed,
                );
                if res.is_err() {
                    // we lost the CAS, free the new item that was
                    // never published to other threads
                    unsafe {
                        drop(Box::from_raw(new));
                    }
                    continue;
                }

                // push the now-full item to the full list for future
                // consumption
                let mut ret;
                let mut full_list_ptr = self.full_list.load(Ordering::Acquire);
                while {
                    // we loop because maybe other threads are pushing stuff too
                    block.next.store(full_list_ptr, Ordering::Release);
                    debug_delay();
                    ret = self.full_list.compare_exchange_weak(
                        full_list_ptr,
                        head,
                        Ordering::Release,
                        Ordering::Relaxed,
                    );
                    ret.is_err()
                } {
                    full_list_ptr = ret.unwrap_err();
                }
                filled = true;
            }
        }
    }

    fn take<'a>(&self, guard: &'a Guard) -> CacheAccessIter<'a> {
        debug_delay();
        let ptr = self.full_list.swap(std::ptr::null_mut(), Ordering::AcqRel);

        CacheAccessIter { guard, current_offset: 0, current_block: ptr }
    }
}

impl Drop for AccessQueue {
    fn drop(&mut self) {
        debug_delay();
        let writing = self.writing.load(Ordering::Acquire);
        unsafe {
            Box::from_raw(writing);
        }
        debug_delay();
        let mut head = self.full_list.load(Ordering::Acquire);
        while !head.is_null() {
            unsafe {
                debug_delay();
                let next =
                    (*head).next.swap(std::ptr::null_mut(), Ordering::Release);
                Box::from_raw(head);
                head = next;
            }
        }
    }
}

struct CacheAccessIter<'a> {
    guard: &'a Guard,
    current_offset: usize,
    current_block: *mut AccessBlock,
}

impl<'a> Iterator for CacheAccessIter<'a> {
    type Item = CacheAccess;

    fn next(&mut self) -> Option<CacheAccess> {
        while !self.current_block.is_null() {
            let current_block = unsafe { &*self.current_block };

            debug_delay();
            if self.current_offset >= MAX_QUEUE_ITEMS {
                let to_drop_ptr = self.current_block;
                debug_delay();
                self.current_block = current_block.next.load(Ordering::Acquire);
                self.current_offset = 0;
                debug_delay();
                let to_drop = unsafe { Box::from_raw(to_drop_ptr) };
                self.guard.defer(|| to_drop);
                continue;
            }

            let mut next = 0;
            while next == 0 {
                // we spin here because there's a race between bumping
                // the offset and setting the value to something other
                // than 0 (and 0 is an invalid value)
                debug_delay();
                next = current_block.block[self.current_offset]
                    .load(Ordering::Acquire);
            }
            self.current_offset += 1;
            return Some(CacheAccess::from(next));
        }

        None
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct CacheAccess {
    // safe because MAX_PID_BITS / N_SHARDS < u32::MAX
    //                     2**37 /   2**8   < 2**32
    pub pid: u32,
    pub sz: u8,
}

impl From<CacheAccess> for u64 {
    fn from(ca: CacheAccess) -> u64 {
        (u64::from(ca.pid) << 8) | u64::from(ca.sz)
    }
}

#[allow(clippy::fallible_impl_from)]
impl From<u64> for CacheAccess {
    fn from(u: u64) -> CacheAccess {
        let sz = usize::try_from((u << 56) >> 56).unwrap();
        assert_ne!(sz, 0);
        let pid = u >> 8;
        assert!(pid < u64::from(std::u32::MAX));
        CacheAccess {
            pid: u32::try_from(pid).unwrap(),
            sz: u8::try_from(sz).unwrap(),
        }
    }
}

impl CacheAccess {
    fn size(&self) -> usize {
        1 << usize::from(self.sz)
    }

    fn new(pid: PageId, sz: usize) -> CacheAccess {
        let rounded_up_power_of_2 =
            u8::try_from(sz.next_power_of_two().trailing_zeros()).unwrap();

        CacheAccess {
            pid: u32::try_from(pid).expect("expected caller to shift pid down"),
            sz: rounded_up_power_of_2,
        }
    }
}

/// A simple LRU cache.
pub struct Lru {
    shards: Vec<(AccessQueue, FastLock<Shard>)>,
}

impl Lru {
    /// Instantiates a new `Lru` cache.
    pub(crate) fn new(cache_capacity: usize) -> Self {
        assert!(
            cache_capacity >= N_SHARDS,
            "Please configure the cache \
             capacity to be at least 256 bytes"
        );
        let shard_capacity = cache_capacity / N_SHARDS;

        let mut shards = Vec::with_capacity(N_SHARDS);
        shards.resize_with(N_SHARDS, || {
            (AccessQueue::default(), FastLock::new(Shard::new(shard_capacity)))
        });

        Self { shards }
    }

    /// Called when an item is accessed. Returns a Vec of items to be
    /// evicted. Uses flat-combining to avoid blocking on what can
    /// be an asynchronous operation.
    ///
    /// layout:
    ///   items:   1 2 3 4 5 6 7 8 9 10
    ///   shards:  1 0 1 0 1 0 1 0 1 0
    ///   shard 0:   2   4   6   8   10
    ///   shard 1: 1   3   5   7   9
    pub(crate) fn accessed(
        &self,
        id: PageId,
        item_size: usize,
        guard: &Guard,
    ) -> Vec<PageId> {
        const SHARD_BITS: usize = N_SHARDS.trailing_zeros() as usize;

        let mut ret = vec![];
        let shards = N_SHARDS as u64;
        let (shard_idx, shifted_pid) = (id % shards, id >> SHARD_BITS);
        let (access_queue, shard_mu) = &self.shards[safe_usize(shard_idx)];

        let cache_access = CacheAccess::new(shifted_pid, item_size);
        let filled = access_queue.push(cache_access);

        if filled {
            // only try to acquire this if the access queue has filled
            // an entire segment
            if let Some(mut shard) = shard_mu.try_lock() {
                let accesses = access_queue.take(guard);
                for item in accesses {
                    let to_evict = shard.accessed(item);
                    // map shard internal offsets to global items ids
                    for pos in to_evict {
                        let item =
                            (PageId::from(pos) << SHARD_BITS) + shard_idx;
                        ret.push(item);
                    }
                }
            }
        }
        ret
    }
}

#[derive(Eq)]
struct Entry(*mut Node);

unsafe impl Send for Entry {}

impl Ord for Entry {
    fn cmp(&self, other: &Entry) -> std::cmp::Ordering {
        let left_pid: u32 = *self.borrow();
        let right_pid: u32 = *other.borrow();
        left_pid.cmp(&right_pid)
    }
}

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

impl PartialEq for Entry {
    fn eq(&self, other: &Entry) -> bool {
        unsafe { (*self.0).pid == (*other.0).pid }
    }
}

impl BorrowMut<CacheAccess> for Entry {
    fn borrow_mut(&mut self) -> &mut CacheAccess {
        unsafe { &mut *self.0 }
    }
}

impl Borrow<CacheAccess> for Entry {
    fn borrow(&self) -> &CacheAccess {
        unsafe { &*self.0 }
    }
}

impl Borrow<u32> for Entry {
    fn borrow(&self) -> &u32 {
        unsafe { &(*self.0).pid }
    }
}

// we only hash on pid, since we will change
// sz sometimes and we access the item by pid
impl Hash for Entry {
    fn hash<H: Hasher>(&self, hasher: &mut H) {
        unsafe { (*self.0).pid.hash(hasher) }
    }
}

struct Shard {
    dll: DoublyLinkedList,
    entries: FastSet8<Entry>,
    capacity: usize,
    size: usize,
}

impl Shard {
    fn new(capacity: usize) -> Self {
        assert!(capacity > 0, "shard capacity must be non-zero");

        Self {
            dll: DoublyLinkedList::default(),
            entries: FastSet8::default(),
            capacity,
            size: 0,
        }
    }

    /// `PageId`s in the shard list are indexes of the entries.
    fn accessed(&mut self, cache_access: CacheAccess) -> Vec<u32> {
        if let Some(entry) = self.entries.get(&cache_access.pid) {
            let old_sz_po2 = unsafe { (*entry.0).swap_sz(cache_access.sz) };
            let old_size = 1 << usize::from(old_sz_po2);

            self.size -= old_size;
            self.dll.promote(entry.0);
        } else {
            let ptr = self.dll.push_head(cache_access);
            self.entries.insert(Entry(ptr));
        };

        self.size += cache_access.size();

        let mut to_evict = vec![];

        while self.size > self.capacity {
            if self.dll.len() == 1 {
                // don't evict what we just added
                break;
            }

            let min_pid = self.dll.pop_tail().unwrap();

            self.entries.remove(&min_pid.pid);

            to_evict.push(min_pid.pid);

            self.size -= min_pid.size();
        }

        to_evict
    }
}

#[inline]
fn safe_usize(value: PageId) -> usize {
    usize::try_from(value).unwrap()
}

#[test]
fn lru_smoke_test() {
    use crate::pin;

    let lru = Lru::new(2);
    for i in 0..1000 {
        let guard = pin();
        lru.accessed(i, 16, &guard);
    }
}

#[test]
fn lru_access_test() {
    use crate::pin;

    let ci = CacheAccess::new(6, 20667);
    assert_eq!(ci.size(), 32 * 1024);

    let lru = Lru::new(4096);

    let guard = pin();

    assert_eq!(lru.accessed(0, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(2, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(4, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(6, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(8, 20667, &guard), vec![0, 2, 4]);
    assert_eq!(lru.accessed(10, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(12, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(14, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(16, 20667, &guard), vec![6, 8, 10, 12]);
    assert_eq!(lru.accessed(18, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(20, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(22, 20667, &guard), vec![]);
    assert_eq!(lru.accessed(24, 20667, &guard), vec![14, 16, 18, 20]);
}
<|endoftext|>


<commit_before>use std::io::{Cursor, Result, SeekFrom, Seek, Error, ErrorKind};
use std::fs::{File};
use std::path::Path;
use byteorder::{LittleEndian, ReadBytesExt};
use mul_reader::MulReader;

pub const BLOCK_SIZE: usize = 196;
pub const OFFSET: u32 = 4;
pub const MAP0_SIZE: u32 = 393216;

#[derive(Clone, Copy)]
pub struct Cell {
    pub graphic: u16,
    pub altitude: i8,
}

pub struct Block {
    pub header: u32,
    pub cells: [Cell; 64]
}

pub struct StaticLocation {
    pub object_id: u16,
    pub x: u8,
    pub y: u8,
    pub altitude: i8,
    pub unknown: u16
}

impl StaticLocation {
    pub fn color_idx(&self) -> u16 {
        self.object_id + 16384
    }
}

pub struct MapReader {
    data_reader: File,
    width: u32,
    height: u32
}

impl MapReader {

    pub fn new(map_path: &Path, width: u32, height: u32) -> Result<MapReader> {
        let data_reader = try!(File::open(map_path));

        Ok(MapReader {
            data_reader: data_reader,
            width: width,
            height: height
        })
    }

    /**
     * Read a specific block from a map
     */
    pub fn read_block(&mut self, id: u32) -> Result<Block> {
        //Cycle to id * 192
        try!(self.data_reader.seek(SeekFrom::Start((id * BLOCK_SIZE as u32) as u64)));
        //Read the header
        let mut block = Block {
            header: try!(self.data_reader.read_u32::<LittleEndian>()),
            cells: [Cell {graphic: 0, altitude: 0}; 64]
        };
        //Read 64 cells
        for i in 0..64 {
            block.cells[i] = Cell{
                graphic: try!(self.data_reader.read_u16::<LittleEndian>()),
                altitude: try!(self.data_reader.read_i8())
            };
        }
        Ok(block)
    }

    pub fn read_block_from_coordinates(&mut self, x: u32, y: u32) -> Result<Block> {
        let width = self.width;
        let height = self.height;
        if x < width && y < height {
            self.read_block(y + (x * height))
        } else {
            Err(Error::new(
                ErrorKind::Other,
                format!("{} {} is outside of valid map coordinates", x, y)
            ))
        }
    }
}

pub struct StaticReader {
    mul_reader: MulReader,
    width: u32,
    height: u32
}

impl StaticReader {

    pub fn new(index_path: &Path, mul_path: &Path, width: u32, height: u32) -> Result<StaticReader> {
        let mul_reader = try!(MulReader::new(index_path, mul_path));

        Ok(StaticReader {
            mul_reader: mul_reader,
            width: width,
            height: height
        })
    }

    pub fn read_block(&mut self, id: u32) -> Result<Vec<StaticLocation>> {
        let raw = try!(self.mul_reader.read(id));
        let len = raw.data.len();
        assert!(len % 7 == 0);
        let mut reader = Cursor::new(raw.data);
        let mut statics = vec![];
        for _i in 0..(len / 7) {
            let object_id = try!(reader.read_u16::<LittleEndian>());
            let x = try!(reader.read_u8());
            let y = try!(reader.read_u8());
            let altitude = try!(reader.read_i8());
            let unknown = try!(reader.read_u16::<LittleEndian>());
            statics.push(StaticLocation{
                object_id: object_id,
                x: x,
                y: y,
                altitude: altitude,
                unknown: unknown
            });
        };
        Ok(statics)
    }

    pub fn read_block_from_coordinates(&mut self, x: u32, y: u32) -> Result<Vec<StaticLocation>> {
        let width = self.width;
        let height = self.height;
        if x < width && y < height {
            self.read_block(y + (x * height))
        } else {
            Err(Error::new(
                ErrorKind::Other,
                format!("{} {} is outside of valid map coordinates", x, y)
            ))
        }
    }
}
<commit_msg>Radar colour reader<commit_after>use std::io::{Cursor, Result, SeekFrom, Seek, Error, ErrorKind};
use std::fs::{File};
use std::path::Path;
use byteorder::{LittleEndian, ReadBytesExt};
use mul_reader::MulReader;
use color::Color16;

pub const BLOCK_SIZE: usize = 196;
pub const OFFSET: u32 = 4;
pub const MAP0_SIZE: u32 = 393216;

#[derive(Clone, Copy)]
pub struct Cell {
    pub graphic: u16,
    pub altitude: i8,
}

pub struct Block {
    pub header: u32,
    pub cells: [Cell; 64]
}

pub struct StaticLocation {
    pub object_id: u16,
    pub x: u8,
    pub y: u8,
    pub altitude: i8,
    pub unknown: u16
}

impl StaticLocation {
    pub fn color_idx(&self) -> u16 {
        self.object_id + 16384
    }
}

pub struct MapReader {
    data_reader: File,
    width: u32,
    height: u32
}

impl MapReader {

    pub fn new(map_path: &Path, width: u32, height: u32) -> Result<MapReader> {
        let data_reader = try!(File::open(map_path));

        Ok(MapReader {
            data_reader: data_reader,
            width: width,
            height: height
        })
    }

    /**
     * Read a specific block from a map
     */
    pub fn read_block(&mut self, id: u32) -> Result<Block> {
        //Cycle to id * 192
        try!(self.data_reader.seek(SeekFrom::Start((id * BLOCK_SIZE as u32) as u64)));
        //Read the header
        let mut block = Block {
            header: try!(self.data_reader.read_u32::<LittleEndian>()),
            cells: [Cell {graphic: 0, altitude: 0}; 64]
        };
        //Read 64 cells
        for i in 0..64 {
            block.cells[i] = Cell{
                graphic: try!(self.data_reader.read_u16::<LittleEndian>()),
                altitude: try!(self.data_reader.read_i8())
            };
        }
        Ok(block)
    }

    pub fn read_block_from_coordinates(&mut self, x: u32, y: u32) -> Result<Block> {
        let width = self.width;
        let height = self.height;
        if x < width && y < height {
            self.read_block(y + (x * height))
        } else {
            Err(Error::new(
                ErrorKind::Other,
                format!("{} {} is outside of valid map coordinates", x, y)
            ))
        }
    }
}

pub struct StaticReader {
    mul_reader: MulReader,
    width: u32,
    height: u32
}

impl StaticReader {

    pub fn new(index_path: &Path, mul_path: &Path, width: u32, height: u32) -> Result<StaticReader> {
        let mul_reader = try!(MulReader::new(index_path, mul_path));

        Ok(StaticReader {
            mul_reader: mul_reader,
            width: width,
            height: height
        })
    }

    pub fn read_block(&mut self, id: u32) -> Result<Vec<StaticLocation>> {
        let raw = try!(self.mul_reader.read(id));
        let len = raw.data.len();
        assert!(len % 7 == 0);
        let mut reader = Cursor::new(raw.data);
        let mut statics = vec![];
        for _i in 0..(len / 7) {
            let object_id = try!(reader.read_u16::<LittleEndian>());
            let x = try!(reader.read_u8());
            let y = try!(reader.read_u8());
            let altitude = try!(reader.read_i8());
            let unknown = try!(reader.read_u16::<LittleEndian>());
            statics.push(StaticLocation{
                object_id: object_id,
                x: x,
                y: y,
                altitude: altitude,
                unknown: unknown
            });
        };
        Ok(statics)
    }

    pub fn read_block_from_coordinates(&mut self, x: u32, y: u32) -> Result<Vec<StaticLocation>> {
        let width = self.width;
        let height = self.height;
        if x < width && y < height {
            self.read_block(y + (x * height))
        } else {
            Err(Error::new(
                ErrorKind::Other,
                format!("{} {} is outside of valid map coordinates", x, y)
            ))
        }
    }
}

pub struct RadarColReader {
    data_reader: File
}

impl RadarColReader {
    pub fn new(radar_col_path: &Path) -> Result<RadarColReader> {
        let data_reader = try!(File::open(radar_col_path));

        Ok(RadarColReader {
            data_reader: data_reader,
        })
    }

    pub fn read_color(&mut self, id: u32) -> Result<Color16> {
        try!(self.data_reader.seek(SeekFrom::Start((id * 2) as u64)));
        let data = try!(self.data_reader.read_u16::<LittleEndian>());
        Ok(data)
    }

    pub fn read_colors(&mut self) -> Result<Vec<Color16>> {
        try!(self.data_reader.seek(SeekFrom::Start(0)));
        let meta = try!(self.data_reader.metadata());
        let mut output = vec![];
        for _i in 0..(meta.len() / 2) {
            output.push(try!(self.data_reader.read_u16::<LittleEndian>()));
        }
        Ok(output)
    }
}
<|endoftext|>