<commit_before>use conrod::{CharacterCache, Circle, CommonBuilder, Dimensions, Frameable, FramedRectangle,
             IndexSlot, Mouse, Point, Positionable, Scalar, UpdateArgs, Widget, WidgetKind};
use conrod::color::{self, Color, Colorable};
use vecmath;

use Side;

#[derive(Debug)]
pub struct OthelloDisk<F> {
    common: CommonBuilder,
    maybe_react: Option<F>,
    style: Style,
    disk: Option<Side>,
    flow_disk: Option<Side>,
}

pub const KIND: WidgetKind = "OthelloDisk";

widget_style!{
    KIND;
    style Style {
        - white_color: Color { color::WHITE },
        - black_color: Color { color::BLACK },
        - background_color: Color { theme.background_color },
        - frame: Scalar { theme.frame_width },
        - frame_color: Color { theme.frame_color },
        - radius_ratio: Scalar { 0.5 }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct State {
    interaction: Interaction,
    circle_idx: IndexSlot,
    rectangle_idx: IndexSlot,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Interaction {
    Normal,
    Highlighted,
    Clicked,
}

impl Interaction {
    fn color(&self, color: Color) -> Color {
        match *self {
            Interaction::Normal => color,
            Interaction::Highlighted => color.highlighted(),
            Interaction::Clicked => color.clicked(),
        }
    }
}

fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction {
    use conrod::MouseButtonPosition::{Down, Up};
    use self::Interaction::{Clicked, Highlighted, Normal};
    match (is_over, prev, mouse.left.position) {
        // LMB is down over the button. But the button wasn't Highlighted last
        // update. This means the user clicked somewhere outside the button and
        // moved over the button holding LMB down. We do nothing in this case.
        (true, Normal, Down) => Normal,

        // LMB is down over the button. The button was either Highlighted or Clicked
        // last update. If it was highlighted before, that means the user clicked
        // just now, and we transition to the Clicked state. If it was clicked
        // before, that means the user is still holding LMB down from a previous
        // click, in which case the state remains Clicked.
        (true, _, Down) => Clicked,

        // LMB is up. The mouse is hovering over the button. Regardless of what the
        // state was last update, the state should definitely be Highlighted now.
        (true, _, Up) => Highlighted,

        // LMB is down, the mouse is not over the button, but the previous state was
        // Clicked. That means the user clicked the button and then moved the mouse
        // outside the button while holding LMB down. The button stays Clicked.
        (false, Clicked, Down) => Clicked,

        // If none of the above applies, then nothing interesting is happening with
        // this button.
        _ => Normal,
    }
}

fn is_over_circ(circ_center: Point, mouse_point: Point, dim: Dimensions) -> bool {
    let offset = vecmath::vec2_sub(mouse_point, circ_center);
    vecmath::vec2_len(offset) <= dim[0] / 2.0
}

impl<F> OthelloDisk<F> {
    pub fn new() -> Self {
        OthelloDisk {
            common: CommonBuilder::new(),
            maybe_react: None,
            style: Style::new(),
            disk: None,
            flow_disk: None,
        }
    }

    builder_methods!{
        pub react { maybe_react = Some(F) }
        pub white_color { style.white_color = Some(Color) }
        pub black_color { style.black_color = Some(Color) }
        pub background_color { style.background_color = Some(Color) }
        pub radius_ratio { style.radius_ratio = Some(Scalar) }
        pub disk { disk = Some(Side) }
        pub flow_disk { flow_disk = Some(Side) }
    }
}

impl<F> Widget for OthelloDisk<F> where F: FnMut()
{
    type State = State;
    type Style = Style;

    fn common(&self) -> &CommonBuilder {
        &self.common
    }

    fn common_mut(&mut self) -> &mut CommonBuilder {
        &mut self.common
    }

    fn unique_kind(&self) -> &'static str {
        KIND
    }

    fn init_state(&self) -> State {
        State {
            interaction: Interaction::Normal,
            circle_idx: IndexSlot::new(),
            rectangle_idx: IndexSlot::new(),
        }
    }

    fn style(&self) -> Style {
        self.style.clone()
    }

    fn update<C: CharacterCache>(mut self, args: UpdateArgs<Self, C>) {
        let UpdateArgs { idx, state, rect, mut ui, style, .. } = args;
        let (xy, dim) = rect.xy_dim();
        let maybe_mouse = ui.input().maybe_mouse.map(|mouse| mouse.relative_to(xy));

        let new_interaction = match (self.disk.is_none(), maybe_mouse) {
            (false, _) | (true, None) => Interaction::Normal,
            (true, Some(mouse)) => {
                let is_over = is_over_circ([0.0, 0.0], mouse.xy, dim);

                get_new_interaction(is_over, state.view().interaction, mouse)
            }
        };

        if let (Interaction::Clicked, Interaction::Highlighted) = (state.view().interaction,
                                                                   new_interaction) {
            if let Some(ref mut react) = self.maybe_react {
                react();
            }
        }

        match (state.view().interaction, new_interaction) {
            (Interaction::Highlighted, Interaction::Clicked) => {
                ui.capture_mouse();
            }
            (Interaction::Clicked, Interaction::Highlighted) |
            (Interaction::Clicked, Interaction::Normal) => {
                ui.uncapture_mouse();
            }
            _ => {}
        }

        if state.view().interaction != new_interaction {
            state.update(|state| state.interaction = new_interaction);
        }

        let rectangle_idx = state.view().rectangle_idx.get(&mut ui);
        let dim = rect.dim();
        let background_color = style.background_color(ui.theme());
        let frame = style.frame(ui.theme());
        let frame_color = style.frame_color(ui.theme());
        FramedRectangle::new(dim)
            .middle_of(idx)
            .graphics_for(idx)
            .color(background_color)
            .frame(frame)
            .frame_color(frame_color)
            .set(rectangle_idx, &mut ui);

        if let Some(disk) = self.disk {
            let radius_ratio = style.radius_ratio(ui.theme());
            let radius = rect.w() * radius_ratio;
            let circle_color = match disk {
                Side::Black => new_interaction.color(style.black_color(ui.theme())),
                Side::White => new_interaction.color(style.white_color(ui.theme())),
            };
            let circle_idx = state.view().circle_idx.get(&mut ui);
            Circle::fill(radius)
                .middle_of(idx)
                .graphics_for(idx)
                .color(circle_color)
                .set(circle_idx, &mut ui);
        }

        if let Some(flow_disk) = self.flow_disk {
            let radius_ratio = style.radius_ratio(ui.theme());
            let radius = rect.w() * radius_ratio;
            let circle_color = match flow_disk {
                                   Side::Black => {
                                       new_interaction.color(style.black_color(ui.theme()))
                                   }
                                   Side::White => {
                                       new_interaction.color(style.white_color(ui.theme()))
                                   }
                               }
                               .alpha(0.5);
            let circle_idx = state.view().circle_idx.get(&mut ui);
            Circle::fill(radius)
                .middle_of(idx)
                .graphics_for(idx)
                .color(circle_color)
                .set(circle_idx, &mut ui);
        }
    }
}

impl<F> Frameable for OthelloDisk<F> {
    fn frame(mut self, width: f64) -> Self {
        self.style.frame = Some(width);
        self
    }

    fn frame_color(mut self, color: Color) -> Self {
        self.style.frame_color = Some(color);
        self
    }
}
<commit_msg>improve disk collision detection<commit_after>use conrod::{CharacterCache, Circle, CommonBuilder, Frameable, FramedRectangle, IndexSlot, Mouse,
             Point, Positionable, Scalar, UpdateArgs, Widget, WidgetKind};
use conrod::color::{self, Color, Colorable};
use vecmath;

use Side;

#[derive(Debug)]
pub struct OthelloDisk<F> {
    common: CommonBuilder,
    maybe_react: Option<F>,
    style: Style,
    disk: Option<Side>,
    flow_disk: Option<Side>,
}

pub const KIND: WidgetKind = "OthelloDisk";

widget_style!{
    KIND;
    style Style {
        - white_color: Color { color::WHITE },
        - black_color: Color { color::BLACK },
        - background_color: Color { theme.background_color },
        - frame: Scalar { theme.frame_width },
        - frame_color: Color { theme.frame_color },
        - radius_ratio: Scalar { 0.5 }
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct State {
    interaction: Interaction,
    circle_idx: IndexSlot,
    rectangle_idx: IndexSlot,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Interaction {
    Normal,
    Highlighted,
    Clicked,
}

impl Interaction {
    fn color(&self, color: Color) -> Color {
        match *self {
            Interaction::Normal => color,
            Interaction::Highlighted => color.highlighted(),
            Interaction::Clicked => color.clicked(),
        }
    }
}

fn get_new_interaction(is_over: bool, prev: Interaction, mouse: Mouse) -> Interaction {
    use conrod::MouseButtonPosition::{Down, Up};
    use self::Interaction::{Clicked, Highlighted, Normal};
    match (is_over, prev, mouse.left.position) {
        // LMB is down over the button. But the button wasn't Highlighted last
        // update. This means the user clicked somewhere outside the button and
        // moved over the button holding LMB down. We do nothing in this case.
        (true, Normal, Down) => Normal,

        // LMB is down over the button. The button was either Highlighted or Clicked
        // last update. If it was highlighted before, that means the user clicked
        // just now, and we transition to the Clicked state. If it was clicked
        // before, that means the user is still holding LMB down from a previous
        // click, in which case the state remains Clicked.
        (true, _, Down) => Clicked,

        // LMB is up. The mouse is hovering over the button. Regardless of what the
        // state was last update, the state should definitely be Highlighted now.
        (true, _, Up) => Highlighted,

        // LMB is down, the mouse is not over the button, but the previous state was
        // Clicked. That means the user clicked the button and then moved the mouse
        // outside the button while holding LMB down. The button stays Clicked.
        (false, Clicked, Down) => Clicked,

        // If none of the above applies, then nothing interesting is happening with
        // this button.
        _ => Normal,
    }
}

fn is_over_circ(circ_center: Point, mouse_point: Point, radius: Scalar) -> bool {
    let offset = vecmath::vec2_sub(mouse_point, circ_center);
    vecmath::vec2_len(offset) <= radius
}

impl<F> OthelloDisk<F> {
    pub fn new() -> Self {
        OthelloDisk {
            common: CommonBuilder::new(),
            maybe_react: None,
            style: Style::new(),
            disk: None,
            flow_disk: None,
        }
    }

    builder_methods!{
        pub react { maybe_react = Some(F) }
        pub white_color { style.white_color = Some(Color) }
        pub black_color { style.black_color = Some(Color) }
        pub background_color { style.background_color = Some(Color) }
        pub radius_ratio { style.radius_ratio = Some(Scalar) }
        pub disk { disk = Some(Side) }
        pub flow_disk { flow_disk = Some(Side) }
    }
}

impl<F> Widget for OthelloDisk<F> where F: FnMut()
{
    type State = State;
    type Style = Style;

    fn common(&self) -> &CommonBuilder {
        &self.common
    }

    fn common_mut(&mut self) -> &mut CommonBuilder {
        &mut self.common
    }

    fn unique_kind(&self) -> &'static str {
        KIND
    }

    fn init_state(&self) -> State {
        State {
            interaction: Interaction::Normal,
            circle_idx: IndexSlot::new(),
            rectangle_idx: IndexSlot::new(),
        }
    }

    fn style(&self) -> Style {
        self.style.clone()
    }

    fn update<C: CharacterCache>(mut self, args: UpdateArgs<Self, C>) {
        let UpdateArgs { idx, state, rect, mut ui, style, .. } = args;
        let (xy, dim) = rect.xy_dim();
        let maybe_mouse = ui.input().maybe_mouse.map(|mouse| mouse.relative_to(xy));
        let radius_ratio = style.radius_ratio(ui.theme());
        let radius = rect.w() * radius_ratio;

        let new_interaction = match (self.disk.is_none(), maybe_mouse) {
            (false, _) | (true, None) => Interaction::Normal,
            (true, Some(mouse)) => {
                let is_over = is_over_circ([0.0, 0.0], mouse.xy, radius);

                get_new_interaction(is_over, state.view().interaction, mouse)
            }
        };

        if let (Interaction::Clicked, Interaction::Highlighted) = (state.view().interaction,
                                                                   new_interaction) {
            if let Some(ref mut react) = self.maybe_react {
                react();
            }
        }

        match (state.view().interaction, new_interaction) {
            (Interaction::Highlighted, Interaction::Clicked) => {
                ui.capture_mouse();
            }
            (Interaction::Clicked, Interaction::Highlighted) |
            (Interaction::Clicked, Interaction::Normal) => {
                ui.uncapture_mouse();
            }
            _ => {}
        }

        if state.view().interaction != new_interaction {
            state.update(|state| state.interaction = new_interaction);
        }

        let rectangle_idx = state.view().rectangle_idx.get(&mut ui);
        let background_color = style.background_color(ui.theme());
        let frame = style.frame(ui.theme());
        let frame_color = style.frame_color(ui.theme());
        FramedRectangle::new(dim)
            .middle_of(idx)
            .graphics_for(idx)
            .color(background_color)
            .frame(frame)
            .frame_color(frame_color)
            .set(rectangle_idx, &mut ui);

        if let Some(disk) = self.disk {
            let circle_color = match disk {
                Side::Black => new_interaction.color(style.black_color(ui.theme())),
                Side::White => new_interaction.color(style.white_color(ui.theme())),
            };
            let circle_idx = state.view().circle_idx.get(&mut ui);
            Circle::fill(radius)
                .middle_of(idx)
                .graphics_for(idx)
                .color(circle_color)
                .set(circle_idx, &mut ui);
        }

        if let Some(flow_disk) = self.flow_disk {
            let circle_color = match flow_disk {
                                   Side::Black => {
                                       new_interaction.color(style.black_color(ui.theme()))
                                   }
                                   Side::White => {
                                       new_interaction.color(style.white_color(ui.theme()))
                                   }
                               }
                               .alpha(0.5);
            let circle_idx = state.view().circle_idx.get(&mut ui);
            Circle::fill(radius)
                .middle_of(idx)
                .graphics_for(idx)
                .color(circle_color)
                .set(circle_idx, &mut ui);
        }
    }
}

impl<F> Frameable for OthelloDisk<F> {
    fn frame(mut self, width: f64) -> Self {
        self.style.frame = Some(width);
        self
    }

    fn frame_color(mut self, color: Color) -> Self {
        self.style.frame_color = Some(color);
        self
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use libc;
use parking_lot::MutexGuard;

use super::*;
use super::mpv_err;
use super::super::raw::*;

use std::ffi::CStr;
use std::marker::PhantomData;
use std::panic;
use std::panic::RefUnwindSafe;
use std::ptr;

unsafe extern "C" fn get_proc_addr_wrapper<F>(cookie: *mut libc::c_void,
                                           name: *const libc::c_char)
                                           -> *mut libc::c_void
    where F: for<'a> Fn(&'a str) -> *const () + RefUnwindSafe
{
    let fun = cookie as *mut F;

    let ret = panic::catch_unwind(|| {
        let name = CStr::from_ptr(name).to_str().unwrap();
        (*fun)(name) as *mut () as *mut libc::c_void
    });
    if ret.is_ok() {
        ret.unwrap()
    } else {
        ptr::null_mut()
    }
}

#[allow(unused_must_use)]
unsafe extern "C" fn callback_update_wrapper<F, V>(cb_ctx: *mut libc::c_void)
    where F: for<'a> Fn(&'a V) + RefUnwindSafe + 'static, V: RefUnwindSafe
{
    let data = cb_ctx as *mut (*const F, *mut V);

    panic::catch_unwind(|| {
        (*(*data).0)(&*(*data).1);
    });
}

/// Holds all state relevant to opengl callback functions.
///
/// # Safety
/// Mpv relies on correct and initialized OpenGL state.
pub struct OpenGlState<'parent, V: RefUnwindSafe>
{
    api_ctx: *mut MpvOpenGlCbContext,
    update_callback_data: V,
    _guard: MutexGuard<'parent, ()>,
    _does_not_outlive: PhantomData<&'parent Parent>,
}

impl<'parent, V: RefUnwindSafe> Drop for OpenGlState<'parent, V> {
    fn drop(&mut self) {
        unsafe { mpv_opengl_cb_uninit_gl(self.api_ctx) };
    }
}

impl<'parent, V: RefUnwindSafe> OpenGlState<'parent, V> {
    pub(crate) fn new<F>(mpv_ctx: *mut MpvHandle,
                         mut proc_addr: F,
                         guard: MutexGuard<'parent, ()>,
                         parent: PhantomData<&'parent Parent>)
            -> Result<OpenGlState<'parent, V>>
        where F: for<'a> Fn(&'a str) -> *const () + RefUnwindSafe + 'static
    {
        let api_ctx = unsafe {
            mpv_get_sub_api(mpv_ctx, MpvSubApi::OpenglCb) as *mut MpvOpenGlCbContext
        };
        debug_assert!(!api_ctx.is_null());

        let proc_addr_ptr = &mut proc_addr as *mut F;

        mpv_err(
            OpenGlState {
                api_ctx: api_ctx,
                update_callback_data: unsafe{ mem::zeroed() },
                _guard: guard,
                _does_not_outlive: parent,
            },
            unsafe {
                    mpv_opengl_cb_init_gl(api_ctx, ptr::null(),
                                          get_proc_addr_wrapper::<F>,
                                          proc_addr_ptr as *mut libc::c_void)
            }
        )
    }

    #[inline]
    /// Set the fbo that mpv will draw on, and start rendering.
    /// Passing `0` as `w` or `h` will choose the size of the fbo.
    /// If `h` is negative, the coordinate system is flipped.
    pub unsafe fn draw(&self, fbo: libc::c_int, w: usize, h: isize)
        -> Result<()>
    {
        mpv_err((), mpv_opengl_cb_draw(self.api_ctx, fbo, w as _, h as _))
    }

    #[inline]
    /// Tell the renderer that a frame was flipped.
    ///
    /// Note that calling this at least once informs libmpv that you will use this
    /// function. If you use it inconsistently, expect bad video playback.
    // TODO: The time parameter is currently not given, because it is ignored.
    pub unsafe fn report_flip(&self) -> Result<()> {
        mpv_err((), mpv_opengl_cb_report_flip(self.api_ctx, 0))
    }

    #[inline]
    /// Set the callback that notifies you when a new video frame is available, or if the
    /// video display configuration somehow changed and requires a redraw.
    ///
    /// # Safety
    /// Do not call any mpv API during the callback.
    pub unsafe fn set_update_callback<F>(&mut self, mut data: V, callback: F)
        where F: for<'a> Fn(&'a V) + RefUnwindSafe + 'static, V: RefUnwindSafe
    {
        mpv_opengl_cb_set_update_callback(self.api_ctx,
                                          callback_update_wrapper::<F, V>,
                                          &mut (&callback as *const F, &mut data as *mut V)
                                            as *mut (*const F, *mut V)
                                            as *mut libc::c_void);

        self.update_callback_data = data;
    }
}

<commit_msg>[opengl] impl Send+Sync for OpenGlState<commit_after>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use libc;
use parking_lot::MutexGuard;

use super::*;
use super::mpv_err;
use super::super::raw::*;

use std::ffi::CStr;
use std::marker::PhantomData;
use std::panic;
use std::panic::RefUnwindSafe;
use std::ptr;

unsafe extern "C" fn get_proc_addr_wrapper<F>(cookie: *mut libc::c_void,
                                           name: *const libc::c_char)
                                           -> *mut libc::c_void
    where F: for<'a> Fn(&'a str) -> *const () + RefUnwindSafe
{
    let fun = cookie as *mut F;

    let ret = panic::catch_unwind(|| {
        let name = CStr::from_ptr(name).to_str().unwrap();
        (*fun)(name) as *mut () as *mut libc::c_void
    });
    if ret.is_ok() {
        ret.unwrap()
    } else {
        ptr::null_mut()
    }
}

#[allow(unused_must_use)]
unsafe extern "C" fn callback_update_wrapper<F, V>(cb_ctx: *mut libc::c_void)
    where F: for<'a> Fn(&'a V) + RefUnwindSafe + 'static, V: RefUnwindSafe
{
    let data = cb_ctx as *mut (*const F, *mut V);

    panic::catch_unwind(|| {
        (*(*data).0)(&*(*data).1);
    });
}

/// Holds all state relevant to opengl callback functions.
///
/// # Safety
/// Mpv relies on correct and initialized OpenGL state.
pub struct OpenGlState<'parent, V: RefUnwindSafe>
{
    api_ctx: *mut MpvOpenGlCbContext,
    update_callback_data: V,
    _guard: MutexGuard<'parent, ()>,
    _does_not_outlive: PhantomData<&'parent Parent>,
}

unsafe impl<'parent, V: RefUnwindSafe> Send for OpenGlState<'parent, V>{}
unsafe impl<'parent, V: RefUnwindSafe> Sync for OpenGlState<'parent, V>{}

impl<'parent, V: RefUnwindSafe> Drop for OpenGlState<'parent, V> {
    fn drop(&mut self) {
        unsafe { mpv_opengl_cb_uninit_gl(self.api_ctx) };
    }
}

impl<'parent, V: RefUnwindSafe> OpenGlState<'parent, V> {
    pub(crate) fn new<F>(mpv_ctx: *mut MpvHandle,
                         mut proc_addr: F,
                         guard: MutexGuard<'parent, ()>,
                         parent: PhantomData<&'parent Parent>)
            -> Result<OpenGlState<'parent, V>>
        where F: for<'a> Fn(&'a str) -> *const () + RefUnwindSafe + 'static
    {
        let api_ctx = unsafe {
            mpv_get_sub_api(mpv_ctx, MpvSubApi::OpenglCb) as *mut MpvOpenGlCbContext
        };
        debug_assert!(!api_ctx.is_null());

        let proc_addr_ptr = &mut proc_addr as *mut F;

        mpv_err(
            OpenGlState {
                api_ctx: api_ctx,
                update_callback_data: unsafe{ mem::zeroed() },
                _guard: guard,
                _does_not_outlive: parent,
            },
            unsafe {
                    mpv_opengl_cb_init_gl(api_ctx, ptr::null(),
                                          get_proc_addr_wrapper::<F>,
                                          proc_addr_ptr as *mut libc::c_void)
            }
        )
    }

    #[inline]
    /// Set the fbo that mpv will draw on, and start rendering.
    /// Passing `0` as `w` or `h` will choose the size of the fbo.
    /// If `h` is negative, the coordinate system is flipped.
    pub unsafe fn draw(&self, fbo: libc::c_int, w: usize, h: isize)
        -> Result<()>
    {
        mpv_err((), mpv_opengl_cb_draw(self.api_ctx, fbo, w as _, h as _))
    }

    #[inline]
    /// Tell the renderer that a frame was flipped.
    ///
    /// Note that calling this at least once informs libmpv that you will use this
    /// function. If you use it inconsistently, expect bad video playback.
    // TODO: The time parameter is currently not given, because it is ignored.
    pub unsafe fn report_flip(&self) -> Result<()> {
        mpv_err((), mpv_opengl_cb_report_flip(self.api_ctx, 0))
    }

    #[inline]
    /// Set the callback that notifies you when a new video frame is available, or if the
    /// video display configuration somehow changed and requires a redraw.
    ///
    /// # Safety
    /// Do not call any mpv API during the callback.
    pub unsafe fn set_update_callback<F>(&mut self, mut data: V, callback: F)
        where F: for<'a> Fn(&'a V) + RefUnwindSafe + 'static, V: RefUnwindSafe
    {
        mpv_opengl_cb_set_update_callback(self.api_ctx,
                                          callback_update_wrapper::<F, V>,
                                          &mut (&callback as *const F, &mut data as *mut V)
                                            as *mut (*const F, *mut V)
                                            as *mut libc::c_void);

        self.update_callback_data = data;
    }
}
<|endoftext|>


<commit_before>// https://leetcode.com/problems/largest-positive-integer-that-exists-with-its-negative/
pub fn find_max_k(nums: Vec<i32>) -> i32 {
    todo!()
}

fn main() {
    println!("{}", find_max_k(vec![-1, 2, -3, 3])); // 3
    println!("{}", find_max_k(vec![-1, 10, 6, 7, -7, 1])); // 7
    println!("{}", find_max_k(vec![-10, 8, 6, 7, -2, -3])); // -1
}
<commit_msg>Solve problem 2441<commit_after>use std::collections::HashSet;

// https://leetcode.com/problems/largest-positive-integer-that-exists-with-its-negative/
pub fn find_max_k(nums: Vec<i32>) -> i32 {
    let mut answer = -1;
    let positive_nums: HashSet<&i32> = nums.iter().filter(|a| **a > 0).collect();
    let negative_nums: HashSet<&i32> = nums.iter().filter(|a| **a < 0).collect();
    for positive_num in positive_nums.into_iter() {
        if negative_nums.contains(&-positive_num) {
            answer = answer.max(*positive_num);
        }
    }
    answer
}

fn main() {
    println!("{}", find_max_k(vec![-1, 2, -3, 3])); // 3
    println!("{}", find_max_k(vec![-1, 10, 6, 7, -7, 1])); // 7
    println!("{}", find_max_k(vec![-10, 8, 6, 7, -2, -3])); // -1
}
<|endoftext|>


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

use std::path::PathBuf;

use users;
use users::os::unix::UserExt;

use {Error, Result};

pub fn check_running_user_is_root() -> Result<()> {
    if users::get_effective_uid() != 0 {
        return Err(Error::RootUserRequired);
    }
    Ok(())
}

pub fn gid_for_groupname(groupname: &str) -> Result<u32> {
    Ok(
        users::get_group_by_name(groupname)
            .ok_or(Error::GroupnameNotFound(String::from(groupname)))?
            .gid(),
    )
}

pub fn uid_for_username(username: &str) -> Result<u32> {
    Ok(user_by_username(username)?.uid())
}

pub fn primary_gid_for_username(username: &str) -> Result<u32> {
    Ok(user_by_username(username)?.primary_group_id())
}

// pub fn primary_groupname_for(username: &str) -> Result<String> {
//     let gid = primary_gid_for_username(username)?;
//     let group = users::get_group_by_gid(gid).ok_or(Error::GidNotFound(gid))?;

//     Ok(String::from(group.name()))
// }

pub fn home_dir_for_username(username: &str) -> Result<PathBuf> {
    Ok(PathBuf::from(user_by_username(username)?.home_dir()))
}

pub fn my_username() -> Result<String> {
    users::get_effective_username().ok_or(Error::EffectiveUsernameNotFound)
}

pub fn my_groupname() -> Result<String> {
    users::get_effective_groupname().ok_or(Error::EffectiveGroupnameNotFound)
}

fn user_by_username(username: &str) -> Result<users::User> {
    users::get_user_by_name(username).ok_or(Error::UsernameNotFound(String::from(username)))
}
<commit_msg>[airlock] Remove commented out code.<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::path::PathBuf;

use users;
use users::os::unix::UserExt;

use {Error, Result};

pub fn check_running_user_is_root() -> Result<()> {
    if users::get_effective_uid() != 0 {
        return Err(Error::RootUserRequired);
    }
    Ok(())
}

pub fn gid_for_groupname(groupname: &str) -> Result<u32> {
    Ok(
        users::get_group_by_name(groupname)
            .ok_or(Error::GroupnameNotFound(String::from(groupname)))?
            .gid(),
    )
}

pub fn uid_for_username(username: &str) -> Result<u32> {
    Ok(user_by_username(username)?.uid())
}

pub fn primary_gid_for_username(username: &str) -> Result<u32> {
    Ok(user_by_username(username)?.primary_group_id())
}

pub fn home_dir_for_username(username: &str) -> Result<PathBuf> {
    Ok(PathBuf::from(user_by_username(username)?.home_dir()))
}

pub fn my_username() -> Result<String> {
    users::get_effective_username().ok_or(Error::EffectiveUsernameNotFound)
}

pub fn my_groupname() -> Result<String> {
    users::get_effective_groupname().ok_or(Error::EffectiveGroupnameNotFound)
}

fn user_by_username(username: &str) -> Result<users::User> {
    users::get_user_by_name(username).ok_or(Error::UsernameNotFound(String::from(username)))
}
<|endoftext|>


<commit_before>use std::{io::Cursor, path::Path};

use rocksdb::{
    merge_operator::MergeFn, BlockBasedIndexType, BlockBasedOptions, ColumnFamily,
    ColumnFamilyDescriptor, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform,
    WriteBatch, DB,
};
use tokio::task::block_in_place;

use crate::model::{
    GameId, Key, KeyPrefix, LichessEntry, LichessGame, MastersEntry, MastersGame, Month,
    PlayerEntry, PlayerStatus, UserId, Year,
};

#[derive(Debug)]
pub struct Database {
    pub inner: DB,
}

fn column_family(
    name: &str,
    merge: Option<&str>,
    merge_fn: impl MergeFn + Clone,
    prefix: Option<usize>,
    block_size: usize,
    bloom_filter: i32,
) -> ColumnFamilyDescriptor {
    let mut opts = Options::default();
    if let Some(merge) = merge {
        opts.set_merge_operator_associative(merge, merge_fn);
    }
    opts.set_prefix_extractor(match prefix {
        Some(prefix) => SliceTransform::create_fixed_prefix(prefix),
        None => SliceTransform::create_noop(),
    });
    let mut block_opts = BlockBasedOptions::default();
    block_opts.set_index_type(BlockBasedIndexType::HashSearch);
    block_opts.set_block_size(block_size);
    if bloom_filter > 0 {
        block_opts.set_bloom_filter(bloom_filter, true);
    }
    opts.set_block_based_table_factory(&block_opts);
    ColumnFamilyDescriptor::new(name, opts)
}

impl Database {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Database, rocksdb::Error> {
        let mut db_opts = Options::default();
        db_opts.create_if_missing(true);
        db_opts.create_missing_column_families(true);

        let inner = DB::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                // Masters database
                column_family(
                    "masters",
                    Some("masters_merge"),
                    masters_merge,
                    Some(KeyPrefix::SIZE),
                    8 * 1024,
                    3,
                ),
                column_family("masters_game", None, void_merge, None, 4 * 1024, 0),
                // Lichess database
                column_family(
                    "lichess",
                    Some("lichess_merge"),
                    lichess_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family(
                    "lichess_game",
                    Some("lichess_game_merge"),
                    lichess_game_merge,
                    None,
                    4 * 1024,
                    0,
                ),
                // Player database (also shares lichess_game)
                column_family(
                    "player",
                    Some("player_merge"),
                    player_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family("player_status", None, void_merge, None, 4 * 1024, 0),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn masters(&self) -> MastersDatabase<'_> {
        MastersDatabase {
            inner: &self.inner,
            cf_masters: self.inner.cf_handle("masters").expect("cf masters"),
            cf_masters_game: self
                .inner
                .cf_handle("masters_game")
                .expect("cf masters_game"),
        }
    }

    pub fn lichess(&self) -> LichessDatabase<'_> {
        LichessDatabase {
            inner: &self.inner,
            cf_lichess: self.inner.cf_handle("lichess").expect("cf lichess"),
            cf_lichess_game: self
                .inner
                .cf_handle("lichess_game")
                .expect("cf lichess_game"),

            cf_player: self.inner.cf_handle("player").expect("cf player"),
            cf_player_status: self
                .inner
                .cf_handle("player_status")
                .expect("cf player_status"),
        }
    }
}

pub struct MastersDatabase<'a> {
    inner: &'a DB,
    cf_masters: &'a ColumnFamily,
    cf_masters_game: &'a ColumnFamily,
}

impl MastersDatabase<'_> {
    pub fn has_game(&self, id: GameId) -> Result<bool, rocksdb::Error> {
        block_in_place(|| {
            self.inner
                .get_cf(self.cf_masters_game, id.to_bytes())
                .map(|maybe_entry| maybe_entry.is_some())
        })
    }

    pub fn game(&self, id: GameId) -> Result<Option<MastersGame>, rocksdb::Error> {
        block_in_place(|| {
            Ok(self
                .inner
                .get_cf(self.cf_masters_game, id.to_bytes())?
                .map(|buf| serde_json::from_slice(&buf).expect("deserialize masters game")))
        })
    }

    pub fn has(&self, key: Key) -> Result<bool, rocksdb::Error> {
        block_in_place(|| {
            self.inner
                .get_cf(self.cf_masters, key.into_bytes())
                .map(|maybe_entry| maybe_entry.is_some())
        })
    }

    pub fn read(
        &self,
        key: KeyPrefix,
        since: Year,
        until: Year,
    ) -> Result<MastersEntry, rocksdb::Error> {
        block_in_place(|| {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_year(since).into_bytes());
            opt.set_iterate_upper_bound(key.with_year(until.add_years_saturating(1)).into_bytes());

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_masters, opt, IteratorMode::Start);

            let mut entry = MastersEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize masters entry");
            }

            Ok(entry)
        })
    }

    pub fn batch(&self) -> MastersBatch<'_> {
        MastersBatch {
            db: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct MastersBatch<'a> {
    db: &'a MastersDatabase<'a>,
    batch: WriteBatch,
}

impl MastersBatch<'_> {
    pub fn merge(&mut self, key: Key, entry: MastersEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(MastersEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize masters entry");
        self.batch
            .merge_cf(self.db.cf_masters, key.into_bytes(), cursor.into_inner());
    }

    pub fn put_game(&mut self, id: GameId, game: &MastersGame) {
        self.batch.put_cf(
            self.db.cf_masters_game,
            id.to_bytes(),
            serde_json::to_vec(game).expect("serialize masters game"),
        );
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        block_in_place(|| self.db.inner.write(self.batch))
    }
}

pub struct LichessDatabase<'a> {
    inner: &'a DB,
    cf_lichess: &'a ColumnFamily,
    cf_lichess_game: &'a ColumnFamily,

    cf_player: &'a ColumnFamily,
    cf_player_status: &'a ColumnFamily,
}

impl LichessDatabase<'_> {
    pub fn game(&self, id: GameId) -> Result<Option<LichessGame>, rocksdb::Error> {
        block_in_place(|| {
            Ok(self
                .inner
                .get_cf(self.cf_lichess_game, id.to_bytes())?
                .map(|buf| {
                    let mut cursor = Cursor::new(buf);
                    LichessGame::read(&mut cursor).expect("deserialize game info")
                }))
        })
    }

    pub fn read_lichess(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<LichessEntry, rocksdb::Error> {
        block_in_place(|| {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
            opt.set_iterate_upper_bound(
                key.with_month(until.add_months_saturating(1)).into_bytes(),
            );

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_lichess, opt, IteratorMode::Start);

            let mut entry = LichessEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize lichess entry");
            }

            Ok(entry)
        })
    }

    pub fn read_player(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<PlayerEntry, rocksdb::Error> {
        block_in_place(|| {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
            opt.set_iterate_upper_bound(
                key.with_month(until.add_months_saturating(1)).into_bytes(),
            );

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_player, opt, IteratorMode::Start);

            let mut entry = PlayerEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize player entry");
            }

            Ok(entry)
        })
    }

    pub fn player_status(&self, id: &UserId) -> Result<Option<PlayerStatus>, rocksdb::Error> {
        block_in_place(|| {
            Ok(self
                .inner
                .get_cf(self.cf_player_status, id.as_lowercase_str())?
                .map(|buf| {
                    let mut cursor = Cursor::new(buf);
                    PlayerStatus::read(&mut cursor).expect("deserialize status")
                }))
        })
    }

    pub fn put_player_status(
        &self,
        id: &UserId,
        status: &PlayerStatus,
    ) -> Result<(), rocksdb::Error> {
        block_in_place(|| {
            let mut cursor = Cursor::new(Vec::with_capacity(PlayerStatus::SIZE_HINT));
            status.write(&mut cursor).expect("serialize status");
            self.inner.put_cf(
                self.cf_player_status,
                id.as_lowercase_str(),
                cursor.into_inner(),
            )
        })
    }

    pub fn batch(&self) -> LichessBatch<'_> {
        LichessBatch {
            inner: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct LichessBatch<'a> {
    inner: &'a LichessDatabase<'a>,
    batch: WriteBatch,
}

impl LichessBatch<'_> {
    pub fn merge_lichess(&mut self, key: Key, entry: LichessEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize lichess entry");
        self.batch
            .merge_cf(self.inner.cf_lichess, key.into_bytes(), cursor.into_inner());
    }

    pub fn merge_game(&mut self, id: GameId, info: LichessGame) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessGame::SIZE_HINT));
        info.write(&mut cursor).expect("serialize game info");
        self.batch.merge_cf(
            self.inner.cf_lichess_game,
            id.to_bytes(),
            cursor.into_inner(),
        );
    }

    pub fn merge_player(&mut self, key: Key, entry: PlayerEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize player entry");
        self.batch
            .merge_cf(self.inner.cf_player, key.into_bytes(), cursor.into_inner());
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        block_in_place(|| self.inner.inner.write(self.batch))
    }
}

fn lichess_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = LichessEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for lichess merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write lichess entry");
    Some(cursor.into_inner())
}

fn lichess_game_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    // Take latest game info, but merge index status.
    let mut info: Option<LichessGame> = None;
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = LichessGame::read(&mut cursor).expect("read for lichess game merge");
        if let Some(old_info) = info {
            new_info.indexed_player.white |= old_info.indexed_player.white;
            new_info.indexed_player.black |= old_info.indexed_player.black;
            new_info.indexed_lichess |= old_info.indexed_lichess;
        }
        info = Some(new_info);
        size_hint = op.len();
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
        info.write(&mut cursor).expect("write lichess game");
        cursor.into_inner()
    })
}

fn player_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PlayerEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for player merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write player entry");
    Some(cursor.into_inner())
}

fn masters_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = MastersEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for masters merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write masters entry");
    Some(cursor.into_inner())
}

fn void_merge(
    _key: &[u8],
    _existing: Option<&[u8]>,
    _operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    unreachable!("void merge")
}
<commit_msg>Revert "annotate all blocking database calls"<commit_after>use std::{io::Cursor, path::Path};

use rocksdb::{
    merge_operator::MergeFn, BlockBasedIndexType, BlockBasedOptions, ColumnFamily,
    ColumnFamilyDescriptor, IteratorMode, MergeOperands, Options, ReadOptions, SliceTransform,
    WriteBatch, DB,
};

use crate::model::{
    GameId, Key, KeyPrefix, LichessEntry, LichessGame, MastersEntry, MastersGame, Month,
    PlayerEntry, PlayerStatus, UserId, Year,
};

#[derive(Debug)]
pub struct Database {
    pub inner: DB,
}

fn column_family(
    name: &str,
    merge: Option<&str>,
    merge_fn: impl MergeFn + Clone,
    prefix: Option<usize>,
    block_size: usize,
    bloom_filter: i32,
) -> ColumnFamilyDescriptor {
    let mut opts = Options::default();
    if let Some(merge) = merge {
        opts.set_merge_operator_associative(merge, merge_fn);
    }
    opts.set_prefix_extractor(match prefix {
        Some(prefix) => SliceTransform::create_fixed_prefix(prefix),
        None => SliceTransform::create_noop(),
    });
    let mut block_opts = BlockBasedOptions::default();
    block_opts.set_index_type(BlockBasedIndexType::HashSearch);
    block_opts.set_block_size(block_size);
    if bloom_filter > 0 {
        block_opts.set_bloom_filter(bloom_filter, true);
    }
    opts.set_block_based_table_factory(&block_opts);
    ColumnFamilyDescriptor::new(name, opts)
}

impl Database {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Database, rocksdb::Error> {
        let mut db_opts = Options::default();
        db_opts.create_if_missing(true);
        db_opts.create_missing_column_families(true);

        let inner = DB::open_cf_descriptors(
            &db_opts,
            path,
            vec![
                // Masters database
                column_family(
                    "masters",
                    Some("masters_merge"),
                    masters_merge,
                    Some(KeyPrefix::SIZE),
                    8 * 1024,
                    3,
                ),
                column_family("masters_game", None, void_merge, None, 4 * 1024, 0),
                // Lichess database
                column_family(
                    "lichess",
                    Some("lichess_merge"),
                    lichess_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family(
                    "lichess_game",
                    Some("lichess_game_merge"),
                    lichess_game_merge,
                    None,
                    4 * 1024,
                    0,
                ),
                // Player database (also shares lichess_game)
                column_family(
                    "player",
                    Some("player_merge"),
                    player_merge,
                    Some(KeyPrefix::SIZE),
                    16 * 1024,
                    2,
                ),
                column_family("player_status", None, void_merge, None, 4 * 1024, 0),
            ],
        )?;

        Ok(Database { inner })
    }

    pub fn masters(&self) -> MastersDatabase<'_> {
        MastersDatabase {
            inner: &self.inner,
            cf_masters: self.inner.cf_handle("masters").expect("cf masters"),
            cf_masters_game: self
                .inner
                .cf_handle("masters_game")
                .expect("cf masters_game"),
        }
    }

    pub fn lichess(&self) -> LichessDatabase<'_> {
        LichessDatabase {
            inner: &self.inner,
            cf_lichess: self.inner.cf_handle("lichess").expect("cf lichess"),
            cf_lichess_game: self
                .inner
                .cf_handle("lichess_game")
                .expect("cf lichess_game"),

            cf_player: self.inner.cf_handle("player").expect("cf player"),
            cf_player_status: self
                .inner
                .cf_handle("player_status")
                .expect("cf player_status"),
        }
    }
}

pub struct MastersDatabase<'a> {
    inner: &'a DB,
    cf_masters: &'a ColumnFamily,
    cf_masters_game: &'a ColumnFamily,
}

impl MastersDatabase<'_> {
    pub fn has_game(&self, id: GameId) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters_game, id.to_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn game(&self, id: GameId) -> Result<Option<MastersGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_masters_game, id.to_bytes())?
            .map(|buf| serde_json::from_slice(&buf).expect("deserialize masters game")))
    }

    pub fn has(&self, key: Key) -> Result<bool, rocksdb::Error> {
        self.inner
            .get_cf(self.cf_masters, key.into_bytes())
            .map(|maybe_entry| maybe_entry.is_some())
    }

    pub fn read(
        &self,
        key: KeyPrefix,
        since: Year,
        until: Year,
    ) -> Result<MastersEntry, rocksdb::Error> {
        tokio::task::block_in_place(move || {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_year(since).into_bytes());
            opt.set_iterate_upper_bound(key.with_year(until.add_years_saturating(1)).into_bytes());

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_masters, opt, IteratorMode::Start);

            let mut entry = MastersEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize masters entry");
            }

            Ok(entry)
        })
    }

    pub fn batch(&self) -> MastersBatch<'_> {
        MastersBatch {
            db: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct MastersBatch<'a> {
    db: &'a MastersDatabase<'a>,
    batch: WriteBatch,
}

impl MastersBatch<'_> {
    pub fn merge(&mut self, key: Key, entry: MastersEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(MastersEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize masters entry");
        self.batch
            .merge_cf(self.db.cf_masters, key.into_bytes(), cursor.into_inner());
    }

    pub fn put_game(&mut self, id: GameId, game: &MastersGame) {
        self.batch.put_cf(
            self.db.cf_masters_game,
            id.to_bytes(),
            serde_json::to_vec(game).expect("serialize masters game"),
        );
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        tokio::task::block_in_place(move || self.db.inner.write(self.batch))
    }
}

pub struct LichessDatabase<'a> {
    inner: &'a DB,
    cf_lichess: &'a ColumnFamily,
    cf_lichess_game: &'a ColumnFamily,

    cf_player: &'a ColumnFamily,
    cf_player_status: &'a ColumnFamily,
}

impl LichessDatabase<'_> {
    pub fn game(&self, id: GameId) -> Result<Option<LichessGame>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_lichess_game, id.to_bytes())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                LichessGame::read(&mut cursor).expect("deserialize game info")
            }))
    }

    pub fn read_lichess(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<LichessEntry, rocksdb::Error> {
        tokio::task::block_in_place(move || {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
            opt.set_iterate_upper_bound(
                key.with_month(until.add_months_saturating(1)).into_bytes(),
            );

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_lichess, opt, IteratorMode::Start);

            let mut entry = LichessEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize lichess entry");
            }

            Ok(entry)
        })
    }

    pub fn read_player(
        &self,
        key: &KeyPrefix,
        since: Month,
        until: Month,
    ) -> Result<PlayerEntry, rocksdb::Error> {
        tokio::task::block_in_place(move || {
            let mut opt = ReadOptions::default();
            opt.set_prefix_same_as_start(true);
            opt.set_iterate_lower_bound(key.with_month(since).into_bytes());
            opt.set_iterate_upper_bound(
                key.with_month(until.add_months_saturating(1)).into_bytes(),
            );

            let iterator = self
                .inner
                .iterator_cf_opt(self.cf_player, opt, IteratorMode::Start);

            let mut entry = PlayerEntry::default();
            for (_key, value) in iterator {
                let mut cursor = Cursor::new(value);
                entry
                    .extend_from_reader(&mut cursor)
                    .expect("deserialize player entry");
            }

            Ok(entry)
        })
    }

    pub fn player_status(&self, id: &UserId) -> Result<Option<PlayerStatus>, rocksdb::Error> {
        Ok(self
            .inner
            .get_cf(self.cf_player_status, id.as_lowercase_str())?
            .map(|buf| {
                let mut cursor = Cursor::new(buf);
                PlayerStatus::read(&mut cursor).expect("deserialize status")
            }))
    }

    pub fn put_player_status(
        &self,
        id: &UserId,
        status: &PlayerStatus,
    ) -> Result<(), rocksdb::Error> {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerStatus::SIZE_HINT));
        status.write(&mut cursor).expect("serialize status");
        self.inner.put_cf(
            self.cf_player_status,
            id.as_lowercase_str(),
            cursor.into_inner(),
        )
    }

    pub fn batch(&self) -> LichessBatch<'_> {
        LichessBatch {
            inner: self,
            batch: WriteBatch::default(),
        }
    }
}

pub struct LichessBatch<'a> {
    inner: &'a LichessDatabase<'a>,
    batch: WriteBatch,
}

impl LichessBatch<'_> {
    pub fn merge_lichess(&mut self, key: Key, entry: LichessEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize lichess entry");
        self.batch
            .merge_cf(self.inner.cf_lichess, key.into_bytes(), cursor.into_inner());
    }

    pub fn merge_game(&mut self, id: GameId, info: LichessGame) {
        let mut cursor = Cursor::new(Vec::with_capacity(LichessGame::SIZE_HINT));
        info.write(&mut cursor).expect("serialize game info");
        self.batch.merge_cf(
            self.inner.cf_lichess_game,
            id.to_bytes(),
            cursor.into_inner(),
        );
    }

    pub fn merge_player(&mut self, key: Key, entry: PlayerEntry) {
        let mut cursor = Cursor::new(Vec::with_capacity(PlayerEntry::SIZE_HINT));
        entry.write(&mut cursor).expect("serialize player entry");
        self.batch
            .merge_cf(self.inner.cf_player, key.into_bytes(), cursor.into_inner());
    }

    pub fn commit(self) -> Result<(), rocksdb::Error> {
        tokio::task::block_in_place(|| self.inner.inner.write(self.batch))
    }
}

fn lichess_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = LichessEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for lichess merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write lichess entry");
    Some(cursor.into_inner())
}

fn lichess_game_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    // Take latest game info, but merge index status.
    let mut info: Option<LichessGame> = None;
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        let mut new_info = LichessGame::read(&mut cursor).expect("read for lichess game merge");
        if let Some(old_info) = info {
            new_info.indexed_player.white |= old_info.indexed_player.white;
            new_info.indexed_player.black |= old_info.indexed_player.black;
            new_info.indexed_lichess |= old_info.indexed_lichess;
        }
        info = Some(new_info);
        size_hint = op.len();
    }
    info.map(|info| {
        let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
        info.write(&mut cursor).expect("write lichess game");
        cursor.into_inner()
    })
}

fn player_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = PlayerEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for player merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write player entry");
    Some(cursor.into_inner())
}

fn masters_merge(
    _key: &[u8],
    existing: Option<&[u8]>,
    operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    let mut entry = MastersEntry::default();
    let mut size_hint = 0;
    for op in existing.into_iter().chain(operands.into_iter()) {
        let mut cursor = Cursor::new(op);
        entry
            .extend_from_reader(&mut cursor)
            .expect("deserialize for masters merge");
        size_hint += op.len();
    }
    let mut cursor = Cursor::new(Vec::with_capacity(size_hint));
    entry.write(&mut cursor).expect("write masters entry");
    Some(cursor.into_inner())
}

fn void_merge(
    _key: &[u8],
    _existing: Option<&[u8]>,
    _operands: &mut MergeOperands,
) -> Option<Vec<u8>> {
    unreachable!("void merge")
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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
//
//     https://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.
//
// Also licensed under MIT license, at your choice.

//! A simple compute shader example that draws into a window, based on wgpu.

use wgpu::util::DeviceExt;
use wgpu::{BufferUsages, Extent3d, SamplerBindingType};

use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::Window,
};

async fn run(event_loop: EventLoop<()>, window: Window) {
    let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
    let surface = unsafe { instance.create_surface(&window) };
    let adapter = instance
        .request_adapter(&wgpu::RequestAdapterOptions {
            power_preference: Default::default(),
            force_fallback_adapter: false,
            compatible_surface: Some(&surface),
        })
        .await
        .expect("error finding adapter");

    let (device, queue) = adapter
        .request_device(&Default::default(), None)
        .await
        .expect("error creating device");
    let size = window.inner_size();
    let format = surface.get_supported_formats(&adapter)[0];
    let sc = wgpu::SurfaceConfiguration {
        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
        format: format,
        width: size.width,
        height: size.height,
        present_mode: wgpu::PresentMode::Fifo,
        alpha_mode: surface.get_supported_alpha_modes(&adapter)[0],
    };
    surface.configure(&device, &sc);

    // We use a render pipeline just to copy the output buffer of the compute shader to the
    // swapchain. It would be nice if we could skip this, but swapchains with storage usage
    // are not fully portable.
    let copy_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
        label: None,
        source: wgpu::ShaderSource::Wgsl(include_str!("copy.wgsl").into()),
    });
    let copy_bind_group_layout =
        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: None,
            entries: &[
                wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        multisampled: false,
                        // Should filterable be false if we want nearest-neighbor?
                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
                        view_dimension: wgpu::TextureViewDimension::D2,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 1,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Sampler(SamplerBindingType::NonFiltering),
                    count: None,
                },
            ],
        });
    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
        label: None,
        bind_group_layouts: &[&copy_bind_group_layout],
        push_constant_ranges: &[],
    });
    let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
        label: None,
        layout: Some(&pipeline_layout),
        vertex: wgpu::VertexState {
            module: &copy_shader,
            entry_point: "vs_main",
            buffers: &[],
        },
        fragment: Some(wgpu::FragmentState {
            module: &copy_shader,
            entry_point: "fs_main",
            targets: &[Some(format.into())],
        }),
        primitive: wgpu::PrimitiveState::default(),
        depth_stencil: None,
        multisample: wgpu::MultisampleState::default(),
        multiview: None,
    });

    let img = device.create_texture(&wgpu::TextureDescriptor {
        label: None,
        size: Extent3d {
            width: size.width,
            height: size.height,
            depth_or_array_layers: 1,
        },
        mip_level_count: 1,
        sample_count: 1,
        dimension: wgpu::TextureDimension::D2,
        format: wgpu::TextureFormat::Rgba8Unorm,
        usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
    });
    let img_view = img.create_view(&Default::default());

    const CONFIG_SIZE: u64 = 12;

    let config_dev = device.create_buffer(&wgpu::BufferDescriptor {
        label: None,
        size: CONFIG_SIZE,
        usage: BufferUsages::COPY_DST | BufferUsages::STORAGE | BufferUsages::UNIFORM,
        mapped_at_creation: false,
    });
    let config_resource = config_dev.as_entire_binding();

    let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
        label: None,
        source: wgpu::ShaderSource::Wgsl(include_str!("paint.wgsl").into()),
    });
    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
        label: None,
        entries: &[
            wgpu::BindGroupLayoutEntry {
                binding: 0,
                visibility: wgpu::ShaderStages::COMPUTE,
                ty: wgpu::BindingType::Buffer {
                    ty: wgpu::BufferBindingType::Uniform,
                    has_dynamic_offset: false,
                    min_binding_size: None,
                },
                count: None,
            },
            wgpu::BindGroupLayoutEntry {
                binding: 1,
                visibility: wgpu::ShaderStages::COMPUTE,
                ty: wgpu::BindingType::StorageTexture {
                    access: wgpu::StorageTextureAccess::WriteOnly,
                    format: wgpu::TextureFormat::Rgba8Unorm,
                    view_dimension: wgpu::TextureViewDimension::D2,
                },
                count: None,
            },
        ],
    });
    let compute_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
        label: None,
        bind_group_layouts: &[&bind_group_layout],
        push_constant_ranges: &[],
    });
    let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
        label: None,
        layout: Some(&compute_pipeline_layout),
        module: &cs_module,
        entry_point: "main",
    });
    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
        label: None,
        layout: &bind_group_layout,
        entries: &[
            wgpu::BindGroupEntry {
                binding: 0,
                resource: config_resource,
            },
            wgpu::BindGroupEntry {
                binding: 1,
                resource: wgpu::BindingResource::TextureView(&img_view),
            },
        ],
    });
    let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
        address_mode_u: wgpu::AddressMode::ClampToEdge,
        address_mode_v: wgpu::AddressMode::ClampToEdge,
        address_mode_w: wgpu::AddressMode::ClampToEdge,
        mag_filter: wgpu::FilterMode::Nearest,
        min_filter: wgpu::FilterMode::Nearest,
        mipmap_filter: wgpu::FilterMode::Nearest,
        ..Default::default()
    });
    let copy_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
        label: None,
        layout: &copy_bind_group_layout,
        entries: &[
            wgpu::BindGroupEntry {
                binding: 0,
                resource: wgpu::BindingResource::TextureView(&img_view),
            },
            wgpu::BindGroupEntry {
                binding: 1,
                resource: wgpu::BindingResource::Sampler(&sampler),
            },
        ],
    });
    let start_time = std::time::Instant::now();

    event_loop.run(move |event, _, control_flow| {
        // TODO: this may be excessive polling. It really should be synchronized with
        // swapchain presentation, but that's currently underbaked in wgpu.
        *control_flow = ControlFlow::Poll;
        match event {
            Event::RedrawRequested(_) => {
                let frame = surface
                    .get_current_texture()
                    .expect("error getting texture from swap chain");

                let i_time: f32 = 0.5 + start_time.elapsed().as_micros() as f32 * 1e-6;
                let config_data = [size.width, size.height, i_time.to_bits()];
                let config_host = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
                    label: None,
                    contents: bytemuck::bytes_of(&config_data),
                    usage: BufferUsages::COPY_SRC,
                });
                let mut encoder = device.create_command_encoder(&Default::default());
                encoder.copy_buffer_to_buffer(&config_host, 0, &config_dev, 0, CONFIG_SIZE);
                {
                    let mut cpass = encoder.begin_compute_pass(&Default::default());
                    cpass.set_pipeline(&pipeline);
                    cpass.set_bind_group(0, &bind_group, &[]);
                    cpass.dispatch_workgroups(size.width / 16, size.height / 16, 1);
                }
                {
                    let view = frame
                        .texture
                        .create_view(&wgpu::TextureViewDescriptor::default());
                    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                        label: None,
                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                            view: &view,
                            resolve_target: None,
                            ops: wgpu::Operations {
                                load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
                                store: true,
                            },
                        })],
                        depth_stencil_attachment: None,
                    });
                    rpass.set_pipeline(&render_pipeline);
                    rpass.set_bind_group(0, &copy_bind_group, &[]);
                    rpass.draw(0..3, 0..2);
                }
                queue.submit(Some(encoder.finish()));
                frame.present();
            }
            Event::MainEventsCleared => {
                window.request_redraw();
            }
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                ..
            } => *control_flow = ControlFlow::Exit,
            _ => (),
        }
    });
}

fn main() {
    let event_loop = EventLoop::new();
    let window = Window::new(&event_loop).unwrap();
    pollster::block_on(run(event_loop, window));
}
<commit_msg>Make the window resizable<commit_after>// Copyright 2021 Google LLC
//
// 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
//
//     https://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.
//
// Also licensed under MIT license, at your choice.

//! A simple compute shader example that draws into a window, based on wgpu.

use wgpu::util::DeviceExt;
use wgpu::{BufferUsages, Extent3d, SamplerBindingType};

use winit::{
    event::{Event, WindowEvent},
    event_loop::{ControlFlow, EventLoop},
    window::Window,
};

async fn run(event_loop: EventLoop<()>, window: Window) {
    let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
    let surface = unsafe { instance.create_surface(&window) };
    let adapter = instance
        .request_adapter(&wgpu::RequestAdapterOptions {
            power_preference: Default::default(),
            force_fallback_adapter: false,
            compatible_surface: Some(&surface),
        })
        .await
        .expect("error finding adapter");

    let (device, queue) = adapter
        .request_device(&Default::default(), None)
        .await
        .expect("error creating device");
    let size = window.inner_size();
    let format = surface.get_supported_formats(&adapter)[0];
    let sc = wgpu::SurfaceConfiguration {
        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
        format: format,
        width: size.width,
        height: size.height,
        present_mode: wgpu::PresentMode::Fifo,
        alpha_mode: surface.get_supported_alpha_modes(&adapter)[0],
    };
    surface.configure(&device, &sc);

    // We use a render pipeline just to copy the output buffer of the compute shader to the
    // swapchain. It would be nice if we could skip this, but swapchains with storage usage
    // are not fully portable.
    let copy_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
        label: None,
        source: wgpu::ShaderSource::Wgsl(include_str!("copy.wgsl").into()),
    });
    let copy_bind_group_layout =
        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: None,
            entries: &[
                wgpu::BindGroupLayoutEntry {
                    binding: 0,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Texture {
                        multisampled: false,
                        // Should filterable be false if we want nearest-neighbor?
                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
                        view_dimension: wgpu::TextureViewDimension::D2,
                    },
                    count: None,
                },
                wgpu::BindGroupLayoutEntry {
                    binding: 1,
                    visibility: wgpu::ShaderStages::FRAGMENT,
                    ty: wgpu::BindingType::Sampler(SamplerBindingType::NonFiltering),
                    count: None,
                },
            ],
        });
    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
        label: None,
        bind_group_layouts: &[&copy_bind_group_layout],
        push_constant_ranges: &[],
    });
    let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
        label: None,
        layout: Some(&pipeline_layout),
        vertex: wgpu::VertexState {
            module: &copy_shader,
            entry_point: "vs_main",
            buffers: &[],
        },
        fragment: Some(wgpu::FragmentState {
            module: &copy_shader,
            entry_point: "fs_main",
            targets: &[Some(format.into())],
        }),
        primitive: wgpu::PrimitiveState::default(),
        depth_stencil: None,
        multisample: wgpu::MultisampleState::default(),
        multiview: None,
    });

    let img = device.create_texture(&wgpu::TextureDescriptor {
        label: None,
        size: Extent3d {
            width: size.width,
            height: size.height,
            depth_or_array_layers: 1,
        },
        mip_level_count: 1,
        sample_count: 1,
        dimension: wgpu::TextureDimension::D2,
        format: wgpu::TextureFormat::Rgba8Unorm,
        usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
    });
    let img_view = img.create_view(&Default::default());

    const CONFIG_SIZE: u64 = 12;

    let config_dev = device.create_buffer(&wgpu::BufferDescriptor {
        label: None,
        size: CONFIG_SIZE,
        usage: BufferUsages::COPY_DST | BufferUsages::STORAGE | BufferUsages::UNIFORM,
        mapped_at_creation: false,
    });
    let config_resource = config_dev.as_entire_binding();

    let cs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
        label: None,
        source: wgpu::ShaderSource::Wgsl(include_str!("paint.wgsl").into()),
    });
    let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
        label: None,
        entries: &[
            wgpu::BindGroupLayoutEntry {
                binding: 0,
                visibility: wgpu::ShaderStages::COMPUTE,
                ty: wgpu::BindingType::Buffer {
                    ty: wgpu::BufferBindingType::Uniform,
                    has_dynamic_offset: false,
                    min_binding_size: None,
                },
                count: None,
            },
            wgpu::BindGroupLayoutEntry {
                binding: 1,
                visibility: wgpu::ShaderStages::COMPUTE,
                ty: wgpu::BindingType::StorageTexture {
                    access: wgpu::StorageTextureAccess::WriteOnly,
                    format: wgpu::TextureFormat::Rgba8Unorm,
                    view_dimension: wgpu::TextureViewDimension::D2,
                },
                count: None,
            },
        ],
    });
    let compute_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
        label: None,
        bind_group_layouts: &[&bind_group_layout],
        push_constant_ranges: &[],
    });
    let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
        label: None,
        layout: Some(&compute_pipeline_layout),
        module: &cs_module,
        entry_point: "main",
    });
    let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
        label: None,
        layout: &bind_group_layout,
        entries: &[
            wgpu::BindGroupEntry {
                binding: 0,
                resource: config_resource,
            },
            wgpu::BindGroupEntry {
                binding: 1,
                resource: wgpu::BindingResource::TextureView(&img_view),
            },
        ],
    });
    let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
        address_mode_u: wgpu::AddressMode::ClampToEdge,
        address_mode_v: wgpu::AddressMode::ClampToEdge,
        address_mode_w: wgpu::AddressMode::ClampToEdge,
        mag_filter: wgpu::FilterMode::Nearest,
        min_filter: wgpu::FilterMode::Nearest,
        mipmap_filter: wgpu::FilterMode::Nearest,
        ..Default::default()
    });
    let copy_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
        label: None,
        layout: &copy_bind_group_layout,
        entries: &[
            wgpu::BindGroupEntry {
                binding: 0,
                resource: wgpu::BindingResource::TextureView(&img_view),
            },
            wgpu::BindGroupEntry {
                binding: 1,
                resource: wgpu::BindingResource::Sampler(&sampler),
            },
        ],
    });
    let start_time = std::time::Instant::now();

    event_loop.run(move |event, _, control_flow| {
        // TODO: this may be excessive polling. It really should be synchronized with
        // swapchain presentation, but that's currently underbaked in wgpu.
        *control_flow = ControlFlow::Poll;
        match event {
            Event::RedrawRequested(_) => {
                let frame = surface
                    .get_current_texture()
                    .expect("error getting texture from swap chain");

                let i_time: f32 = 0.5 + start_time.elapsed().as_micros() as f32 * 1e-6;
                let config_data = [size.width, size.height, i_time.to_bits()];
                let config_host = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
                    label: None,
                    contents: bytemuck::bytes_of(&config_data),
                    usage: BufferUsages::COPY_SRC,
                });
                let mut encoder = device.create_command_encoder(&Default::default());
                encoder.copy_buffer_to_buffer(&config_host, 0, &config_dev, 0, CONFIG_SIZE);
                {
                    let mut cpass = encoder.begin_compute_pass(&Default::default());
                    cpass.set_pipeline(&pipeline);
                    cpass.set_bind_group(0, &bind_group, &[]);
                    cpass.dispatch_workgroups(size.width / 16, size.height / 16, 1);
                }
                {
                    let view = frame
                        .texture
                        .create_view(&wgpu::TextureViewDescriptor::default());
                    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
                        label: None,
                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                            view: &view,
                            resolve_target: None,
                            ops: wgpu::Operations {
                                load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
                                store: true,
                            },
                        })],
                        depth_stencil_attachment: None,
                    });
                    rpass.set_pipeline(&render_pipeline);
                    rpass.set_bind_group(0, &copy_bind_group, &[]);
                    rpass.draw(0..3, 0..2);
                }
                queue.submit(Some(encoder.finish()));
                frame.present();
            }
            Event::MainEventsCleared => {
                window.request_redraw();
            }
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                ..
            } => *control_flow = ControlFlow::Exit,
            _ => (),
        }
    });
}

fn main() {
    let event_loop = EventLoop::new();
    let window = Window::new(&event_loop).unwrap();
    window.set_resizable(false);
    pollster::block_on(run(event_loop, window));
}
<|endoftext|>


<commit_before>/// An interpreter for the rust-installer [1] installation format.
///
/// https://github.com/rust-lang/rust-installer

pub use self::transaction::*;
pub use self::components::*;
pub use self::package::*;

// Transactional file system tools
mod transaction;
// The representation of a package, its components, and installation
//
// FIXME: Because of rust-lang/rust#18241 this must be pub to pub reexport
// the inner Package trait.
pub mod package;
// The representation of *installed* components, and uninstallation
mod components;

<commit_msg>Remove unnecessary pub mod rust-lang/rust#18241 is fixed now, so this mod doesn't need to be pub.<commit_after>/// An interpreter for the rust-installer [1] installation format.
///
/// https://github.com/rust-lang/rust-installer

pub use self::transaction::*;
pub use self::components::*;
pub use self::package::*;

// Transactional file system tools
mod transaction;
// The representation of a package, its components, and installation
mod package;
// The representation of *installed* components, and uninstallation
mod components;

<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate osciemu;
extern crate byteorder;

use std::vec::Vec;
use std::fs::File;
use std::io;
use osciemu::memory::SliceMemory;
use osciemu::emulator::Emulator;

use byteorder::{NetworkEndian, ReadBytesExt};

fn read_file_into_slice(filename: &str) -> Result<Box<[i32]>, io::Error> {
    let mut file = File::open(filename)?;
    let file_meta = file.metadata()?;
    let size = file_meta.len() as usize;
    let mut vec = Vec::<i32>::with_capacity(size);
    vec.resize(size / 4, 0);
    let mut slice: Box<[i32]> = vec.into_boxed_slice();
    file.read_i32_into::<NetworkEndian>(slice.as_mut())?;
    Ok(slice)
}

fn main() {
    let matches = clap_app!(myapp =>
            (version: "0.1.0")
            (author: "Surma <surma@surma.link>")
            (about: "Emulates an osci CPU")
            (@arg IMAGE: -i --image +required +takes_value "Image to load into memory")
            (@arg BIOS: -b --bios +required +takes_value "BIOS to load")
            (@arg STEP: --step "Walk through in stepping mode")
            (@arg MAX_STEP: --maxstep +takes_value "Maximum number of CPU cycles")
        )
        .get_matches();

    let max_steps = matches.value_of("MAX_STEP")
                            .unwrap_or("1000000");
    let max_steps = max_steps.parse::<usize>()
                            .expect("--max-step needs to be a number");

    let image_buf = read_file_into_slice(matches.value_of("IMAGE").unwrap())
                            .expect("Could not load image");
    let bios_buf = read_file_into_slice(matches.value_of("BIOS").unwrap())
                            .expect("Could not load bios");

    let step_mode = matches.is_present("STEP");

    let img_mem = Box::new(SliceMemory(image_buf));
    let bios_mem = Box::new(SliceMemory(bios_buf));
    let mut emulator = Emulator::new(img_mem, bios_mem);

    println!("FLAGS_START_ADDRESS = 0x{:08X}", osciemu::memory::address::FLAGS_START_ADDRESS);
    println!("IVT_START_ADDRESS = 0x{:08X}", osciemu::memory::address::IVT_START_ADDRESS);
    println!("REGISTERS_START_ADDRESS = 0x{:08X}", osciemu::memory::address::REGISTERS_START_ADDRESS);
    println!("STACK_POINTER_ADDRESS = 0x{:08X}", osciemu::memory::address::STACK_POINTER_ADDRESS);

    for count in 1..max_steps+1 {
        if emulator.is_halted() {
            break;
        }
        println!("count: {:4}, ip: 0x{:08X}, r0: 0x{:08X}, r1: 0x{:08X}, r2: 0x{:08X}, r3: \
                  0x{:08X}",
                 count,
                 emulator.ip,
                 emulator.get_register(0),
                 emulator.get_register(1),
                 emulator.get_register(2),
                 emulator.get_register(3));
        emulator.step();
        if step_mode {
            let mut buffer = String::new();
            let _ = io::stdin().read_line(&mut buffer);
        }
    }
    if !emulator.is_halted() {
        std::process::exit(1);
    }
}
<commit_msg>feat(osci-cli): Add allow for infinite execution<commit_after>#[macro_use]
extern crate clap;
extern crate osciemu;
extern crate byteorder;

use std::vec::Vec;
use std::fs::File;
use std::io;
use osciemu::memory::SliceMemory;
use osciemu::emulator::Emulator;

use byteorder::{NetworkEndian, ReadBytesExt};

fn read_file_into_slice(filename: &str) -> Result<Box<[i32]>, io::Error> {
    let mut file = File::open(filename)?;
    let file_meta = file.metadata()?;
    let size = file_meta.len() as usize;
    let mut vec = Vec::<i32>::with_capacity(size);
    vec.resize(size / 4, 0);
    let mut slice: Box<[i32]> = vec.into_boxed_slice();
    file.read_i32_into::<NetworkEndian>(slice.as_mut())?;
    Ok(slice)
}

fn main() {
    let matches = clap_app!(myapp =>
            (version: "0.1.0")
            (author: "Surma <surma@surma.link>")
            (about: "Emulates an osci CPU")
            (@arg IMAGE: -i --image +required +takes_value "Image to load into memory")
            (@arg BIOS: -b --bios +required +takes_value "BIOS to load")
            (@arg STEP: --step "Walk through in stepping mode")
            (@arg MAX_STEP: --maxstep +takes_value "Maximum number of CPU cycles (0 means infinite)")
        )
        .get_matches();

    let max_steps = matches.value_of("MAX_STEP")
                        .map(|s| {
                            s.parse::<usize>()
                                .expect("--max-step needs to be a number")
                        })
                        .unwrap_or(0);

    let image_buf = read_file_into_slice(matches.value_of("IMAGE").unwrap())
                            .expect("Could not load image");
    let bios_buf = read_file_into_slice(matches.value_of("BIOS").unwrap())
                            .expect("Could not load bios");

    let step_mode = matches.is_present("STEP");

    let img_mem = Box::new(SliceMemory(image_buf));
    let bios_mem = Box::new(SliceMemory(bios_buf));
    let mut emulator = Emulator::new(img_mem, bios_mem);

    println!("FLAGS_START_ADDRESS = 0x{:08X}", osciemu::memory::address::FLAGS_START_ADDRESS);
    println!("IVT_START_ADDRESS = 0x{:08X}", osciemu::memory::address::IVT_START_ADDRESS);
    println!("REGISTERS_START_ADDRESS = 0x{:08X}", osciemu::memory::address::REGISTERS_START_ADDRESS);
    println!("STACK_POINTER_ADDRESS = 0x{:08X}", osciemu::memory::address::STACK_POINTER_ADDRESS);

    for count in RangeIterator::new(0, max_steps) {
        if emulator.is_halted() {
            break;
        }
        println!("count: {:4}, ip: 0x{:08X}, r0: 0x{:08X}, r1: 0x{:08X}, r2: 0x{:08X}, r3: \
                  0x{:08X}",
                 count,
                 emulator.ip,
                 emulator.get_register(0),
                 emulator.get_register(1),
                 emulator.get_register(2),
                 emulator.get_register(3));
        emulator.step();
        if step_mode {
            let mut buffer = String::new();
            let _ = io::stdin().read_line(&mut buffer);
        }
    }
    if !emulator.is_halted() {
        std::process::exit(1);
    }
}

struct RangeIterator {
    count: usize,
    max: usize
}

impl RangeIterator {
    fn new(start: usize, max: usize) -> RangeIterator {
        RangeIterator {
            count: start,
            max,
        }
    }
}

impl Iterator for RangeIterator {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        if self.count == self.max && self.max != 0 {
            return None;
        }
        let result = Some(self.count);
        if self.count != std::usize::MAX {
            self.count = self.count + 1;
        }
        result
    }
}
<|endoftext|>