<commit_before>use std::sync::Arc;

use libc;
use std::{mem, ptr};
use std::ffi::CString;
use std::slice::from_raw_parts;

use WindowAttributes;

use events::Event;

use super::{events, ffi};
use super::XConnection;

#[derive(Debug)]
enum AxisType {
    HorizontalScroll,
    VerticalScroll
}

#[derive(Debug)]
struct Axis {
    id: i32,
    device_id: i32,
    axis_number: i32,
    axis_type: AxisType,
    scroll_increment: f64,
}

#[derive(Debug)]
struct AxisValue {
    device_id: i32,
    axis_number: i32,
    value: f64
}

struct InputState {
    /// Last-seen cursor position within a window in (x, y)
    /// coordinates
    cursor_pos: (f64, f64),
    /// Last-seen positions of axes, used to report delta
    /// movements when a new absolute axis value is received
    axis_values: Vec<AxisValue>
}

pub struct XInputEventHandler {
    display: Arc<XConnection>,
    window: ffi::Window,
    ic: ffi::XIC,
    axis_list: Vec<Axis>,
    current_state: InputState,
    multitouch: bool,
}

impl XInputEventHandler {
    pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC,
               window_attrs: &WindowAttributes) -> XInputEventHandler {
        // query XInput support
        let mut opcode: libc::c_int = 0;
        let mut event: libc::c_int = 0;
        let mut error: libc::c_int = 0;
        let xinput_str = CString::new("XInputExtension").unwrap();

        unsafe {
            if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
                panic!("XInput not available")
            }
        }

        let mut xinput_major_ver = ffi::XI_2_Major;
        let mut xinput_minor_ver = ffi::XI_2_Minor;

        unsafe {
            if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
                panic!("Unable to determine XInput version");
            }
        }

        // specify the XInput events we want to receive.
        // Button clicks and mouse events are handled via XInput
        // events. Key presses are still handled via plain core
        // X11 events.
        let mut mask: [libc::c_uchar; 3] = [0; 3];
        let mut input_event_mask = ffi::XIEventMask {
            deviceid: ffi::XIAllMasterDevices,
            mask_len: mask.len() as i32,
            mask: mask.as_mut_ptr()
        };
        let events = &[
            ffi::XI_ButtonPress,
            ffi::XI_ButtonRelease,
            ffi::XI_Motion,
            ffi::XI_Enter,
            ffi::XI_Leave,
            ffi::XI_FocusIn,
            ffi::XI_FocusOut,
            ffi::XI_TouchBegin,
            ffi::XI_TouchUpdate,
            ffi::XI_TouchEnd,
        ];
        for event in events {
            ffi::XISetMask(&mut mask, *event);
        }

        unsafe {
            match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
                status if status as u8 == ffi::Success => (),
                err => panic!("Failed to select events {:?}", err)
            }
        }

        XInputEventHandler {
            display: display.clone(),
            window: window,
            ic: ic,
            axis_list: read_input_axis_info(display),
            current_state: InputState {
                cursor_pos: (0.0, 0.0),
                axis_values: Vec::new()
            },
            multitouch: window_attrs.multitouch,
        }
    }

    pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
        use events::Event::{KeyboardInput, ReceivedCharacter};
        use events::ElementState::{Pressed, Released};

        let mut translated_events = Vec::new();

        let state;
        if event.type_ == ffi::KeyPress {
            let raw_ev: *mut ffi::XKeyEvent = event;
            unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
            state = Pressed;
        } else {
            state = Released;
        }

        let mut kp_keysym = 0;

        let written = unsafe {
            use std::str;

            let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
            let raw_ev: *mut ffi::XKeyEvent = event;
            let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
            mem::transmute(buffer.as_mut_ptr()),
            buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());

            str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
        };

        for chr in written.chars() {
            translated_events.push(ReceivedCharacter(chr));
        }

        let mut keysym = unsafe {
            (self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
        };

        if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
            keysym = kp_keysym
        };

        let vkey = events::keycode_to_element(keysym as libc::c_uint);

        translated_events.push(KeyboardInput(state, event.keycode as u8, vkey));
        translated_events
    }

    pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
        use events::Event::{Focused, MouseInput, MouseMoved, MouseWheel};
        use events::ElementState::{Pressed, Released};
        use events::MouseButton::{Left, Right, Middle};
        use events::MouseScrollDelta::LineDelta;
        use events::{Touch, TouchPhase};

        match cookie.evtype {
            ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
                    // Deliver multi-touch events instead of emulated mouse events.
                    return None
                }
                let state = if cookie.evtype == ffi::XI_ButtonPress {
                    Pressed
                } else {
                    Released
                };
                match event_data.detail as u32 {
                    ffi::Button1 => Some(MouseInput(state, Left)),
                    ffi::Button2 => Some(MouseInput(state, Middle)),
                    ffi::Button3 => Some(MouseInput(state, Right)),
                    ffi::Button4 | ffi::Button5 => {
                        if event_data.flags & ffi::XIPointerEmulated == 0 {
                            // scroll event from a traditional wheel with
                            // distinct 'clicks'
                            let delta = if event_data.detail as u32 == ffi::Button4 {
                                1.0
                            } else {
                                -1.0
                            };
                            Some(MouseWheel(LineDelta(0.0, delta)))
                        } else {
                            // emulated button event from a touch/smooth-scroll
                            // event. Ignore these events and handle scrolling
                            // via XI_Motion event handler instead
                            None
                        }
                    }
                    _ => None
                }
            },
            ffi::XI_Motion => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
                    // Deliver multi-touch events instead of emulated mouse events.
                    return None
                }
                let axis_state = event_data.valuators;
                let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
                let mut axis_count = 0;

                let mut scroll_delta = (0.0, 0.0);
                for axis_id in 0..axis_state.mask_len {
                    if ffi::XIMaskIsSet(&mask, axis_id) {
                        let axis_value = unsafe{*axis_state.values.offset(axis_count)};
                        let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
                                                       &mut self.current_state.axis_values);
                        scroll_delta.0 += delta.0;
                        scroll_delta.1 += delta.1;
                        axis_count += 1;
                    }
                }

                if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
                    Some(MouseWheel(LineDelta(scroll_delta.0 as f32, scroll_delta.1 as f32)))
                } else {
                    let new_cursor_pos = (event_data.event_x, event_data.event_y);
                    if new_cursor_pos != self.current_state.cursor_pos {
                        self.current_state.cursor_pos = new_cursor_pos;
                        Some(MouseMoved((new_cursor_pos.0 as i32, new_cursor_pos.1 as i32)))
                    } else {
                        None
                    }
                }
            },
            ffi::XI_Enter => {
                // axis movements whilst the cursor is outside the window
                // will alter the absolute value of the axes. We only want to
                // report changes in the axis value whilst the cursor is above
                // our window however, so clear the previous axis state whenever
                // the cursor re-enters the window
                self.current_state.axis_values.clear();
                None
            },
            ffi::XI_Leave => None,
            ffi::XI_FocusIn => Some(Focused(true)),
            ffi::XI_FocusOut => Some(Focused(false)),
            ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
                if !self.multitouch {
                    return None
                }
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let phase = match cookie.evtype {
                    ffi::XI_TouchBegin => TouchPhase::Started,
                    ffi::XI_TouchUpdate => TouchPhase::Moved,
                    ffi::XI_TouchEnd => TouchPhase::Ended,
                    _ => unreachable!()
                };
                Some(Event::Touch(Touch {
                    phase: phase,
                    location: (event_data.event_x, event_data.event_y),
                    id: event_data.detail as u64,
                }))
            }
            _ => None
        }
    }
}

fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
    let mut axis_list = Vec::new();
    let mut device_count = 0;

    // Check all input devices for scroll axes.
    let devices = unsafe{
        (display.xinput2.XIQueryDevice)(display.display, ffi::XIAllDevices, &mut device_count)
    };
    for i in 0..device_count {
        let device = unsafe { *(devices.offset(i as isize)) };
        for k in 0..device.num_classes {
            let class = unsafe { *(device.classes.offset(k as isize)) };
            match unsafe { (*class)._type } {
                // Note that scroll axis
                // are reported both as 'XIScrollClass' and 'XIValuatorClass'
                // axes. For the moment we only care about scrolling axes.
                ffi::XIScrollClass => {
                    let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
                    axis_list.push(Axis{
                        id: scroll_class.sourceid,
                        device_id: device.deviceid,
                        axis_number: scroll_class.number,
                        axis_type: match scroll_class.scroll_type {
                            ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
                            ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
                            _ => { unreachable!() }
                        },
                        scroll_increment: scroll_class.increment,
                    })
                },
                _ => {}
            }
        }
    }

    axis_list
}

/// Given an input motion event for an axis and the previous
/// state of the axes, return the horizontal/vertical
/// scroll deltas
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
                     axis_id: i32,
                     axis_value: f64,
                     axis_list: &[Axis],
                     prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
    let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
        prev_axis.device_id == event.sourceid &&
            prev_axis.axis_number == axis_id
    });
    let delta = match prev_value_pos {
        Some(idx) => prev_axis_values[idx].value - axis_value,
        None => 0.0
    };

    let new_axis_value = AxisValue{
        device_id: event.sourceid,
        axis_number: axis_id,
        value: axis_value
    };

    match prev_value_pos {
        Some(idx) => prev_axis_values[idx] = new_axis_value,
        None => prev_axis_values.push(new_axis_value)
    }

    let mut scroll_delta = (0.0, 0.0);

    for axis in axis_list.iter() {
        if axis.id == event.sourceid &&
            axis.axis_number == axis_id {
                match axis.axis_type {
                    AxisType::HorizontalScroll => scroll_delta.0 = delta / axis.scroll_increment,
                    AxisType::VerticalScroll => scroll_delta.1 = delta / axis.scroll_increment
                }
            }
    }

    scroll_delta
}

<commit_msg>x11: Prevent leak on XIQueryDevice<commit_after>use std::sync::Arc;

use libc;
use std::{mem, ptr};
use std::ffi::CString;
use std::slice::from_raw_parts;

use WindowAttributes;

use events::Event;

use super::{events, ffi};
use super::XConnection;

#[derive(Debug)]
enum AxisType {
    HorizontalScroll,
    VerticalScroll
}

#[derive(Debug)]
struct Axis {
    id: i32,
    device_id: i32,
    axis_number: i32,
    axis_type: AxisType,
    scroll_increment: f64,
}

#[derive(Debug)]
struct AxisValue {
    device_id: i32,
    axis_number: i32,
    value: f64
}

struct InputState {
    /// Last-seen cursor position within a window in (x, y)
    /// coordinates
    cursor_pos: (f64, f64),
    /// Last-seen positions of axes, used to report delta
    /// movements when a new absolute axis value is received
    axis_values: Vec<AxisValue>
}

pub struct XInputEventHandler {
    display: Arc<XConnection>,
    window: ffi::Window,
    ic: ffi::XIC,
    axis_list: Vec<Axis>,
    current_state: InputState,
    multitouch: bool,
}

impl XInputEventHandler {
    pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC,
               window_attrs: &WindowAttributes) -> XInputEventHandler {
        // query XInput support
        let mut opcode: libc::c_int = 0;
        let mut event: libc::c_int = 0;
        let mut error: libc::c_int = 0;
        let xinput_str = CString::new("XInputExtension").unwrap();

        unsafe {
            if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
                panic!("XInput not available")
            }
        }

        let mut xinput_major_ver = ffi::XI_2_Major;
        let mut xinput_minor_ver = ffi::XI_2_Minor;

        unsafe {
            if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
                panic!("Unable to determine XInput version");
            }
        }

        // specify the XInput events we want to receive.
        // Button clicks and mouse events are handled via XInput
        // events. Key presses are still handled via plain core
        // X11 events.
        let mut mask: [libc::c_uchar; 3] = [0; 3];
        let mut input_event_mask = ffi::XIEventMask {
            deviceid: ffi::XIAllMasterDevices,
            mask_len: mask.len() as i32,
            mask: mask.as_mut_ptr()
        };
        let events = &[
            ffi::XI_ButtonPress,
            ffi::XI_ButtonRelease,
            ffi::XI_Motion,
            ffi::XI_Enter,
            ffi::XI_Leave,
            ffi::XI_FocusIn,
            ffi::XI_FocusOut,
            ffi::XI_TouchBegin,
            ffi::XI_TouchUpdate,
            ffi::XI_TouchEnd,
        ];
        for event in events {
            ffi::XISetMask(&mut mask, *event);
        }

        unsafe {
            match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
                status if status as u8 == ffi::Success => (),
                err => panic!("Failed to select events {:?}", err)
            }
        }

        XInputEventHandler {
            display: display.clone(),
            window: window,
            ic: ic,
            axis_list: read_input_axis_info(display),
            current_state: InputState {
                cursor_pos: (0.0, 0.0),
                axis_values: Vec::new()
            },
            multitouch: window_attrs.multitouch,
        }
    }

    pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
        use events::Event::{KeyboardInput, ReceivedCharacter};
        use events::ElementState::{Pressed, Released};

        let mut translated_events = Vec::new();

        let state;
        if event.type_ == ffi::KeyPress {
            let raw_ev: *mut ffi::XKeyEvent = event;
            unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
            state = Pressed;
        } else {
            state = Released;
        }

        let mut kp_keysym = 0;

        let written = unsafe {
            use std::str;

            let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
            let raw_ev: *mut ffi::XKeyEvent = event;
            let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
            mem::transmute(buffer.as_mut_ptr()),
            buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());

            str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
        };

        for chr in written.chars() {
            translated_events.push(ReceivedCharacter(chr));
        }

        let mut keysym = unsafe {
            (self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
        };

        if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
            keysym = kp_keysym
        };

        let vkey = events::keycode_to_element(keysym as libc::c_uint);

        translated_events.push(KeyboardInput(state, event.keycode as u8, vkey));
        translated_events
    }

    pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
        use events::Event::{Focused, MouseInput, MouseMoved, MouseWheel};
        use events::ElementState::{Pressed, Released};
        use events::MouseButton::{Left, Right, Middle};
        use events::MouseScrollDelta::LineDelta;
        use events::{Touch, TouchPhase};

        match cookie.evtype {
            ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
                    // Deliver multi-touch events instead of emulated mouse events.
                    return None
                }
                let state = if cookie.evtype == ffi::XI_ButtonPress {
                    Pressed
                } else {
                    Released
                };
                match event_data.detail as u32 {
                    ffi::Button1 => Some(MouseInput(state, Left)),
                    ffi::Button2 => Some(MouseInput(state, Middle)),
                    ffi::Button3 => Some(MouseInput(state, Right)),
                    ffi::Button4 | ffi::Button5 => {
                        if event_data.flags & ffi::XIPointerEmulated == 0 {
                            // scroll event from a traditional wheel with
                            // distinct 'clicks'
                            let delta = if event_data.detail as u32 == ffi::Button4 {
                                1.0
                            } else {
                                -1.0
                            };
                            Some(MouseWheel(LineDelta(0.0, delta)))
                        } else {
                            // emulated button event from a touch/smooth-scroll
                            // event. Ignore these events and handle scrolling
                            // via XI_Motion event handler instead
                            None
                        }
                    }
                    _ => None
                }
            },
            ffi::XI_Motion => {
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
                    // Deliver multi-touch events instead of emulated mouse events.
                    return None
                }
                let axis_state = event_data.valuators;
                let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
                let mut axis_count = 0;

                let mut scroll_delta = (0.0, 0.0);
                for axis_id in 0..axis_state.mask_len {
                    if ffi::XIMaskIsSet(&mask, axis_id) {
                        let axis_value = unsafe{*axis_state.values.offset(axis_count)};
                        let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
                                                       &mut self.current_state.axis_values);
                        scroll_delta.0 += delta.0;
                        scroll_delta.1 += delta.1;
                        axis_count += 1;
                    }
                }

                if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
                    Some(MouseWheel(LineDelta(scroll_delta.0 as f32, scroll_delta.1 as f32)))
                } else {
                    let new_cursor_pos = (event_data.event_x, event_data.event_y);
                    if new_cursor_pos != self.current_state.cursor_pos {
                        self.current_state.cursor_pos = new_cursor_pos;
                        Some(MouseMoved((new_cursor_pos.0 as i32, new_cursor_pos.1 as i32)))
                    } else {
                        None
                    }
                }
            },
            ffi::XI_Enter => {
                // axis movements whilst the cursor is outside the window
                // will alter the absolute value of the axes. We only want to
                // report changes in the axis value whilst the cursor is above
                // our window however, so clear the previous axis state whenever
                // the cursor re-enters the window
                self.current_state.axis_values.clear();
                None
            },
            ffi::XI_Leave => None,
            ffi::XI_FocusIn => Some(Focused(true)),
            ffi::XI_FocusOut => Some(Focused(false)),
            ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
                if !self.multitouch {
                    return None
                }
                let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
                let phase = match cookie.evtype {
                    ffi::XI_TouchBegin => TouchPhase::Started,
                    ffi::XI_TouchUpdate => TouchPhase::Moved,
                    ffi::XI_TouchEnd => TouchPhase::Ended,
                    _ => unreachable!()
                };
                Some(Event::Touch(Touch {
                    phase: phase,
                    location: (event_data.event_x, event_data.event_y),
                    id: event_data.detail as u64,
                }))
            }
            _ => None
        }
    }
}

fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
    let mut axis_list = Vec::new();
    let mut device_count = 0;

    // Check all input devices for scroll axes.
    let devices = unsafe{
        (display.xinput2.XIQueryDevice)(display.display, ffi::XIAllDevices, &mut device_count)
    };
    for i in 0..device_count {
        let device = unsafe { *(devices.offset(i as isize)) };
        for k in 0..device.num_classes {
            let class = unsafe { *(device.classes.offset(k as isize)) };
            match unsafe { (*class)._type } {
                // Note that scroll axis
                // are reported both as 'XIScrollClass' and 'XIValuatorClass'
                // axes. For the moment we only care about scrolling axes.
                ffi::XIScrollClass => {
                    let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
                    axis_list.push(Axis{
                        id: scroll_class.sourceid,
                        device_id: device.deviceid,
                        axis_number: scroll_class.number,
                        axis_type: match scroll_class.scroll_type {
                            ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
                            ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
                            _ => { unreachable!() }
                        },
                        scroll_increment: scroll_class.increment,
                    })
                },
                _ => {}
            }
        }
    }

    unsafe {
        (display.xinput2.XIFreeDeviceInfo)(devices);
    }

    axis_list
}

/// Given an input motion event for an axis and the previous
/// state of the axes, return the horizontal/vertical
/// scroll deltas
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
                     axis_id: i32,
                     axis_value: f64,
                     axis_list: &[Axis],
                     prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
    let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
        prev_axis.device_id == event.sourceid &&
            prev_axis.axis_number == axis_id
    });
    let delta = match prev_value_pos {
        Some(idx) => prev_axis_values[idx].value - axis_value,
        None => 0.0
    };

    let new_axis_value = AxisValue{
        device_id: event.sourceid,
        axis_number: axis_id,
        value: axis_value
    };

    match prev_value_pos {
        Some(idx) => prev_axis_values[idx] = new_axis_value,
        None => prev_axis_values.push(new_axis_value)
    }

    let mut scroll_delta = (0.0, 0.0);

    for axis in axis_list.iter() {
        if axis.id == event.sourceid &&
            axis.axis_number == axis_id {
                match axis.axis_type {
                    AxisType::HorizontalScroll => scroll_delta.0 = delta / axis.scroll_increment,
                    AxisType::VerticalScroll => scroll_delta.1 = delta / axis.scroll_increment
                }
            }
    }

    scroll_delta
}

<|endoftext|>


<commit_before>mod backend;
mod models;
pub mod show_karma;

use std::collections::HashMap;

use regex::Regex;

use ::bot::{Bot, Node, RoomEvent};
use self::backend::Backend;

pub struct KarmaTracker {
    vote_db: Backend,
}

impl KarmaTracker {
    pub fn new() -> Self {
        Self {
            vote_db: Backend::new()
        }
    }
}

struct VoteCount {
    up: i32,
    down: i32,
}

impl<'a> Node<'a> for KarmaTracker {
    fn handle(&mut self, bot: &Bot, event: RoomEvent) {
        let event = event.raw_event;
        if event.type_ == "m.room.message" &&
            event.content["msgtype"] == "m.text" {
            let body = event.content["body"].as_str().unwrap();

            // Don't karma based off anything that is a command to the bot
            if body.starts_with("!") {
                return;
            }

            let mut votes: HashMap<String, VoteCount> = HashMap::new();

            let re = Regex::new(r"([^ ]+|\(.+?\))(\+\+|--)").unwrap();

            for cap in re.captures_iter(body) {
                let ent = cap[1].trim_matches(|c| c == '(' || c == ')').to_string();
                let e = votes.entry(ent).or_insert(VoteCount{up: 0, down: 0});

                if &cap[2] == "++" {
                    (*e).up += 1;
                }

                if &cap[2] == "--" {
                    (*e).down += 1;
                }
            }

            for (k, v) in votes.iter() {
                println!("{} up: {} down: {}", &k, v.up, v.down);
                self.vote_db.vote(&event.sender, k, v.up, v.down);
            }
        }
    }
}
<commit_msg>Slightly improve karma regex<commit_after>mod backend;
mod models;
pub mod show_karma;

use std::collections::HashMap;

use regex::Regex;

use ::bot::{Bot, Node, RoomEvent};
use self::backend::Backend;

pub struct KarmaTracker {
    vote_db: Backend,
}

impl KarmaTracker {
    pub fn new() -> Self {
        Self {
            vote_db: Backend::new()
        }
    }
}

struct VoteCount {
    up: i32,
    down: i32,
}

impl<'a> Node<'a> for KarmaTracker {
    fn handle(&mut self, bot: &Bot, event: RoomEvent) {
        let event = event.raw_event;
        if event.type_ == "m.room.message" &&
            event.content["msgtype"] == "m.text" {
            let body = event.content["body"].as_str().unwrap();

            // Don't karma based off anything that is a command to the bot
            if body.starts_with("!") {
                return;
            }

            let mut votes: HashMap<String, VoteCount> = HashMap::new();

            let re = Regex::new(r"([^\- ]+|\(.+?\))(\+\+|--)").unwrap();

            for cap in re.captures_iter(body) {
                let ent = cap[1].trim_matches(|c| c == '(' || c == ')').to_string();
                let e = votes.entry(ent).or_insert(VoteCount{up: 0, down: 0});

                if &cap[2] == "++" {
                    (*e).up += 1;
                }

                if &cap[2] == "--" {
                    (*e).down += 1;
                }
            }

            for (k, v) in votes.iter() {
                println!("{} up: {} down: {}", &k, v.up, v.down);
                self.vote_db.vote(&event.sender, k, v.up, v.down);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use super::common::{Document, I18NString, Translatable};
use super::translation::TranslationDocument;

pub type PageDocument = Document<Page>;
pub type Components = Vec<Component>;

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct Page {
    pub title: I18NString,
    pub components: Vec<Component>,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
#[serde(tag = "component")]
pub enum Component {
    Row(RowComponent),
    Header1(Header1Component),
    Header2(Header2Component),
    Header3(Header3Component),
    Column3(Column3Component),
    Column6(Column6Component),
    Column12(Column12Component),
    Datatable(DatatableComponent),
    Form(FormComponent),
    TextInput(TextInputComponent),
    Label(LabelComponent),
    Button(ButtonComponent),
    FormControlGroup(FormControlGroupComponent),
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct ContainerComponent<T> {
    pub config: T,
    pub components: Components,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct ConfigOnlyComponent<T> {
    pub config: T,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct FormControlGroupComponent {
    pub config: FormControlGroupConfig,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct LayoutComponent {
    pub components: Components,
}

pub type RowComponent = LayoutComponent;
pub type Header1Component = ConfigOnlyComponent<TextConfig>;
pub type Header2Component = ConfigOnlyComponent<TextConfig>;
pub type Header3Component = ConfigOnlyComponent<TextConfig>;
pub type Column3Component = LayoutComponent;
pub type Column6Component = LayoutComponent;
pub type Column12Component = LayoutComponent;
pub type DatatableComponent = ConfigOnlyComponent<DatatableConfig>;
pub type FormComponent = ContainerComponent<FormConfig>;
pub type TextInputComponent = ConfigOnlyComponent<TextInputConfig>;
pub type LabelComponent = ConfigOnlyComponent<LabelConfig>;
pub type ButtonComponent = ConfigOnlyComponent<ButtonConfig>;

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct TextConfig {
    pub text: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct DatatableConfig {
    pub entity: String,
    pub attributes: Vec<String>,
    pub eventbindings: HashMap<String, String>,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct FormConfig {
    pub entity: String,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct TextInputConfig {
    pub placeholder: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct LabelConfig {
    pub text: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct ButtonConfig {
    pub text: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub enum FormControlType {
    Text,
    Radio,
    Checkbox,
}

impl FormControlType {
    pub fn to_text(&self) -> String {
        match self {
            &FormControlType::Text => "text".to_owned(),
            &FormControlType::Radio => "radio".to_owned(),
            &FormControlType::Checkbox => "checkbox".to_owned(),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct FormControlGroupConfig {
    pub label: I18NString,
    pub placeholder: I18NString,
    pub form_control_type: FormControlType,
}

fn collect_i18nstring(c: &Component) -> Vec<&I18NString> {
    let mut res = Vec::<&I18NString>::new();

    match *c {
        Component::Header1(ref c) => res.push(&c.config.text),
        Component::Header2(ref c) => res.push(&c.config.text),
        Component::Header3(ref c) => res.push(&c.config.text),
        Component::Label(ref c) => res.push(&c.config.text),
        Component::Button(ref c) => res.push(&c.config.text),
        Component::TextInput(ref c) => res.push(&c.config.placeholder),
        Component::Column3(ref c) => res.append(&mut collect_i18nstrings(&c.components)),
        Component::Column6(ref c) => res.append(&mut collect_i18nstrings(&c.components)),
        Component::Column12(ref c) => res.append(&mut collect_i18nstrings(&c.components)),
        // XXX: Implement others
        _ => {}
    }
    res
}

fn collect_i18nstrings(components: &Components) -> Vec<&I18NString> {
    let mut res = Vec::<&I18NString>::new();
    for component in components {
        res.append(&mut collect_i18nstring(&component));
    }
    res
}

fn translate_component(c: &mut Component, t: &TranslationDocument) -> () {
    match *c {
        Component::Header1(ref mut c) => c.config.text.translate_self(t),
        Component::Header2(ref mut c) => c.config.text.translate_self(t),
        Component::Header3(ref mut c) => c.config.text.translate_self(t),
        Component::Label(ref mut c) => c.config.text.translate_self(t),
        Component::Button(ref mut c) => c.config.text.translate_self(t),
        Component::TextInput(ref mut c) => c.config.placeholder.translate_self(t),

        Component::Column3(ref mut c) => translate_components(&mut c.components, &t),
        Component::Column6(ref mut c) => translate_components(&mut c.components, &t),
        Component::Column12(ref mut c) => translate_components(&mut c.components, &t),
        Component::Row(ref mut c) => translate_components(&mut c.components, &t),

        Component::Datatable(ref mut c) => {}
        Component::Form(ref mut c) => {}
        Component::FormControlGroup(ref mut c) => {
            c.config.label.translate_self(t);
            c.config.placeholder.translate_self(t);
        }
    }
}

fn translate_components(components: &mut Components, t: &TranslationDocument) -> () {
    for ref mut component in components {
        translate_component(component, &t);
    }
}

impl Translatable for PageDocument {
    fn translate_in_place(&mut self, t: &TranslationDocument) -> () {
        self.doc.title.translate_self(&t);
        translate_components(&mut self.doc.components, &t);
    }

    fn translate(&self, t: &TranslationDocument) -> PageDocument {
        let mut doc = self.clone();
        doc.translate_in_place(&t);
        doc
    }

    fn all_i18n_strings(&self) -> Vec<&I18NString> {
        collect_i18nstrings(&self.doc.components)
    }
}
<commit_msg>chore: Make clippy happier<commit_after>use std::collections::HashMap;

use super::common::{Document, I18NString, Translatable};
use super::translation::TranslationDocument;

pub type PageDocument = Document<Page>;
pub type Components = Vec<Component>;

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct Page {
    pub title: I18NString,
    pub components: Vec<Component>,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
#[serde(tag = "component")]
pub enum Component {
    Row(RowComponent),
    Header1(Header1Component),
    Header2(Header2Component),
    Header3(Header3Component),
    Column3(Column3Component),
    Column6(Column6Component),
    Column12(Column12Component),
    Datatable(DatatableComponent),
    Form(FormComponent),
    TextInput(TextInputComponent),
    Label(LabelComponent),
    Button(ButtonComponent),
    FormControlGroup(FormControlGroupComponent),
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct ContainerComponent<T> {
    pub config: T,
    pub components: Components,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct ConfigOnlyComponent<T> {
    pub config: T,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct FormControlGroupComponent {
    pub config: FormControlGroupConfig,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct LayoutComponent {
    pub components: Components,
}

pub type RowComponent = LayoutComponent;
pub type Header1Component = ConfigOnlyComponent<TextConfig>;
pub type Header2Component = ConfigOnlyComponent<TextConfig>;
pub type Header3Component = ConfigOnlyComponent<TextConfig>;
pub type Column3Component = LayoutComponent;
pub type Column6Component = LayoutComponent;
pub type Column12Component = LayoutComponent;
pub type DatatableComponent = ConfigOnlyComponent<DatatableConfig>;
pub type FormComponent = ContainerComponent<FormConfig>;
pub type TextInputComponent = ConfigOnlyComponent<TextInputConfig>;
pub type LabelComponent = ConfigOnlyComponent<LabelConfig>;
pub type ButtonComponent = ConfigOnlyComponent<ButtonConfig>;

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct TextConfig {
    pub text: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct DatatableConfig {
    pub entity: String,
    pub attributes: Vec<String>,
    pub eventbindings: HashMap<String, String>,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct FormConfig {
    pub entity: String,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct TextInputConfig {
    pub placeholder: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct LabelConfig {
    pub text: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct ButtonConfig {
    pub text: I18NString,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub enum FormControlType {
    Text,
    Radio,
    Checkbox,
}

impl FormControlType {
    pub fn to_text(&self) -> String {
        match *self {
            FormControlType::Text => "text".to_owned(),
            FormControlType::Radio => "radio".to_owned(),
            FormControlType::Checkbox => "checkbox".to_owned(),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
pub struct FormControlGroupConfig {
    pub label: I18NString,
    pub placeholder: I18NString,
    pub form_control_type: FormControlType,
}

fn collect_i18nstring(c: &Component) -> Vec<&I18NString> {
    let mut res = Vec::<&I18NString>::new();

    match *c {
        Component::Header1(ref c) => res.push(&c.config.text),
        Component::Header2(ref c) => res.push(&c.config.text),
        Component::Header3(ref c) => res.push(&c.config.text),
        Component::Label(ref c) => res.push(&c.config.text),
        Component::Button(ref c) => res.push(&c.config.text),
        Component::TextInput(ref c) => res.push(&c.config.placeholder),
        Component::Column3(ref c) => res.append(&mut collect_i18nstrings(&c.components)),
        Component::Column6(ref c) => res.append(&mut collect_i18nstrings(&c.components)),
        Component::Column12(ref c) => res.append(&mut collect_i18nstrings(&c.components)),
        // XXX: Implement others
        _ => {}
    }
    res
}

fn collect_i18nstrings(components: &Components) -> Vec<&I18NString> {
    let mut res = Vec::<&I18NString>::new();
    for component in components {
        res.append(&mut collect_i18nstring(&component));
    }
    res
}

fn translate_component(c: &mut Component, t: &TranslationDocument) -> () {
    match *c {
        Component::Header1(ref mut c) => c.config.text.translate_self(t),
        Component::Header2(ref mut c) => c.config.text.translate_self(t),
        Component::Header3(ref mut c) => c.config.text.translate_self(t),
        Component::Label(ref mut c) => c.config.text.translate_self(t),
        Component::Button(ref mut c) => c.config.text.translate_self(t),
        Component::TextInput(ref mut c) => c.config.placeholder.translate_self(t),

        Component::Column3(ref mut c) => translate_components(&mut c.components, &t),
        Component::Column6(ref mut c) => translate_components(&mut c.components, &t),
        Component::Column12(ref mut c) => translate_components(&mut c.components, &t),
        Component::Row(ref mut c) => translate_components(&mut c.components, &t),

        Component::Datatable(ref mut c) => {}
        Component::Form(ref mut c) => {}
        Component::FormControlGroup(ref mut c) => {
            c.config.label.translate_self(t);
            c.config.placeholder.translate_self(t);
        }
    }
}

fn translate_components(components: &mut Components, t: &TranslationDocument) -> () {
    for ref mut component in components {
        translate_component(component, &t);
    }
}

impl Translatable for PageDocument {
    fn translate_in_place(&mut self, t: &TranslationDocument) -> () {
        self.doc.title.translate_self(&t);
        translate_components(&mut self.doc.components, &t);
    }

    fn translate(&self, t: &TranslationDocument) -> PageDocument {
        let mut doc = self.clone();
        doc.translate_in_place(&t);
        doc
    }

    fn all_i18n_strings(&self) -> Vec<&I18NString> {
        collect_i18nstrings(&self.doc.components)
    }
}
<|endoftext|>


<commit_before>extern crate hal;
extern crate docopt;
extern crate arrayfire as af;

use af::{Array, Dim4};
use docopt::Docopt;
use hal::Model;
use hal::Layer;
use hal::model::{Sequential};
use hal::layer::{Dense};

static USAGE: &'static str = "
Usage:
  hal <input_csv> <output_csv>
";

fn main() {
  // let args = Docopt::new(USAGE)
  //                    .and_then(|dopt| dopt.parse())
  //                    .unwrap_or_else(|e| e.exit());
  // println!("{:?}", args);

  let input_dims = 512;
  let output_dims = 256;

  let mut model = Box::new(Sequential::new("L2", "SGD"));
  model.add(Box::new(Dense::new(input_dims, output_dims)));
  model.info();

  let dims = Dim4::new(&[input_dims, 1, 1, 1]);
  let a = af::randu(dims, af::Aftype::F32).unwrap();
  let inference = model.forward(&a);
  af::print(&inference);
}
<commit_msg>cleanup<commit_after>extern crate hal;
extern crate docopt;
extern crate arrayfire as af;

use docopt::Docopt;
use af::{Array, Dim4};
use hal::{Model, Layer};
use hal::model::{Sequential};
use hal::layer::{Dense};

static USAGE: &'static str = "
Usage:
  hal <input_csv> <output_csv>
";

fn main() {
  // let args = Docopt::new(USAGE)
  //                    .and_then(|dopt| dopt.parse())
  //                    .unwrap_or_else(|e| e.exit());
  // println!("{:?}", args);

  let input_dims = 512;
  let hidden_dims = 256;
  let output_dims = 5;

  let mut model = Box::new(Sequential::new("L2", "SGD"));
  model.add(Box::new(Dense::new(input_dims, hidden_dims)));
  model.add(Box::new(Dense::new(hidden_dims, output_dims)));
  model.info();

  let dims = Dim4::new(&[input_dims, 1, 1, 1]);
  let a = af::randu(dims, af::Aftype::F32).unwrap();
  let inference = model.forward(&a);
  af::print(&inference);
}
<|endoftext|>


<commit_before>//! Unix peer credentials.

// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on
//       GitHub.
//
//       For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13
//       Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work.

use libc::{gid_t, uid_t};

/// Credentials for a UNIX process for credentials passing.
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UCred {
    pub uid: uid_t,
    pub gid: gid_t,
}

#[cfg(any(target_os = "android", target_os = "linux"))]
pub use self::impl_linux::peer_cred;

#[cfg(any(
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "ios",
    target_os = "macos",
    target_os = "openbsd"
))]
pub use self::impl_bsd::peer_cred;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod impl_linux {
    use super::UCred;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;
    use crate::{io, mem};

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        use libc::{c_void, ucred};

        let ucred_size = mem::size_of::<ucred>();

        // Trivial sanity checks.
        assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
        assert!(ucred_size <= u32::max_value() as usize);

        let mut ucred_size = ucred_size as u32;
        let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 };

        unsafe {
            let ret = libc::getsockopt(
                socket.as_raw_fd(),
                libc::SOL_SOCKET,
                libc::SO_PEERCRED,
                &mut ucred as *mut ucred as *mut c_void,
                &mut ucred_size,
            );

            if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
                Ok(UCred { uid: ucred.uid, gid: ucred.gid })
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

#[cfg(any(
    target_os = "dragonfly",
    target_os = "macos",
    target_os = "ios",
    target_os = "freebsd",
    target_os = "openbsd"
))]
pub mod impl_bsd {
    use super::UCred;
    use crate::io;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        let mut cred = UCred { uid: 1, gid: 1 };
        unsafe {
            let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);

            if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) }
        }
    }
}

#[cfg(test)]
mod test {
    use crate::os::unix::net::UnixStream;
    use libc::{getegid, geteuid};

    #[test]
    fn test_socket_pair() {
        // Create two connected sockets and get their peer credentials. They should be equal.
        let (sock_a, sock_b) = UnixStream::pair().unwrap();
        let (cred_a, cred_b) = (sock_a.peer_cred().unwrap(), sock_b.peer_cred().unwrap());
        assert_eq!(cred_a, cred_b);

        // Check that the UID and GIDs match up.
        let uid = unsafe { geteuid() };
        let gid = unsafe { getegid() };
        assert_eq!(cred_a.uid, uid);
        assert_eq!(cred_a.gid, gid);
    }
}
<commit_msg>Use `u32::MAX` instead of `u32::max_value`<commit_after>//! Unix peer credentials.

// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on
//       GitHub.
//
//       For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13
//       Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work.

use libc::{gid_t, uid_t};

/// Credentials for a UNIX process for credentials passing.
#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UCred {
    pub uid: uid_t,
    pub gid: gid_t,
}

#[cfg(any(target_os = "android", target_os = "linux"))]
pub use self::impl_linux::peer_cred;

#[cfg(any(
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "ios",
    target_os = "macos",
    target_os = "openbsd"
))]
pub use self::impl_bsd::peer_cred;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod impl_linux {
    use super::UCred;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;
    use crate::{io, mem};

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        use libc::{c_void, ucred};

        let ucred_size = mem::size_of::<ucred>();

        // Trivial sanity checks.
        assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
        assert!(ucred_size <= u32::MAX as usize);

        let mut ucred_size = ucred_size as u32;
        let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 };

        unsafe {
            let ret = libc::getsockopt(
                socket.as_raw_fd(),
                libc::SOL_SOCKET,
                libc::SO_PEERCRED,
                &mut ucred as *mut ucred as *mut c_void,
                &mut ucred_size,
            );

            if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
                Ok(UCred { uid: ucred.uid, gid: ucred.gid })
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }
}

#[cfg(any(
    target_os = "dragonfly",
    target_os = "macos",
    target_os = "ios",
    target_os = "freebsd",
    target_os = "openbsd"
))]
pub mod impl_bsd {
    use super::UCred;
    use crate::io;
    use crate::os::unix::io::AsRawFd;
    use crate::os::unix::net::UnixStream;

    pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
        let mut cred = UCred { uid: 1, gid: 1 };
        unsafe {
            let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);

            if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) }
        }
    }
}

#[cfg(test)]
mod test {
    use crate::os::unix::net::UnixStream;
    use libc::{getegid, geteuid};

    #[test]
    fn test_socket_pair() {
        // Create two connected sockets and get their peer credentials. They should be equal.
        let (sock_a, sock_b) = UnixStream::pair().unwrap();
        let (cred_a, cred_b) = (sock_a.peer_cred().unwrap(), sock_b.peer_cred().unwrap());
        assert_eq!(cred_a, cred_b);

        // Check that the UID and GIDs match up.
        let uid = unsafe { geteuid() };
        let gid = unsafe { getegid() };
        assert_eq!(cred_a.uid, uid);
        assert_eq!(cred_a.gid, gid);
    }
}
<|endoftext|>


<commit_before>use async_trait::async_trait;
use futures::stream::Stream;
use serde::de::DeserializeOwned;
use snafu::Snafu;
use std::pin::Pin;

use crate::domain::model::{configuration::root_doctype, query::Query as SearchQuery};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Document Retrieval Error: {}", source))]
    DocumentRetrievalError { source: Box<dyn std::error::Error> },
}

#[derive(Debug, Clone)]
pub struct ListParameters {
    pub index: String,
}

impl From<crate::domain::ports::list::ListParameters> for ListParameters {
    fn from(input: crate::domain::ports::list::ListParameters) -> Self {
        // We get a doc_type, and we need to translate that into the name of an index.
        ListParameters {
            index: root_doctype(&input.doc_type),
        }
    }
}

#[derive(Debug, Clone)]
pub struct SearchParameters {
    pub indices: Vec<String>, // if you want to target all indices, use vec![munin]
    pub query: SearchQuery,   // if you want to target all documents, use { match_all: {} }
}

impl From<crate::domain::ports::search::SearchParameters> for SearchParameters {
    fn from(input: crate::domain::ports::search::SearchParameters) -> Self {
        SearchParameters {
            indices: input
                .doc_types
                .iter()
                .map(|doc_type| root_doctype(&doc_type))
                .collect(),
            query: input.query,
        }
    }
}

#[derive(Debug, Clone)]
pub struct ExplainParameters {
    pub index: String,      // if you want to target all indices, use vec![munin]
    pub query: SearchQuery, // if you want to target all documents, use { match_all: {} }
    pub id: String,
}

impl From<crate::domain::ports::explain::ExplainParameters> for ExplainParameters {
    fn from(input: crate::domain::ports::explain::ExplainParameters) -> Self {
        ExplainParameters {
            index: root_doctype(&input.doc_type),
            query: input.query,
            id: input.id,
        }
    }
}

#[async_trait]
pub trait Query {
    type Doc: DeserializeOwned + Send + Sync + 'static;

    async fn search_documents(&self, parameters: SearchParameters)
        -> Result<Vec<Self::Doc>, Error>;

    async fn explain_document(&self, parameters: ExplainParameters) -> Result<Self::Doc, Error>;

    fn list_documents(
        &self,
        parameters: ListParameters,
    ) -> Result<Pin<Box<dyn Stream<Item = Self::Doc> + Send + 'static>>, Error>;
}
<commit_msg>Clippy Correction<commit_after>use async_trait::async_trait;
use futures::stream::Stream;
use serde::de::DeserializeOwned;
use snafu::Snafu;
use std::pin::Pin;

use crate::domain::model::{configuration::root_doctype, query::Query as SearchQuery};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("Document Retrieval Error: {}", source))]
    DocumentRetrievalError { source: Box<dyn std::error::Error> },
}

#[derive(Debug, Clone)]
pub struct ListParameters {
    pub index: String,
}

impl From<crate::domain::ports::list::ListParameters> for ListParameters {
    fn from(input: crate::domain::ports::list::ListParameters) -> Self {
        // We get a doc_type, and we need to translate that into the name of an index.
        ListParameters {
            index: root_doctype(&input.doc_type),
        }
    }
}

#[derive(Debug, Clone)]
pub struct SearchParameters {
    pub indices: Vec<String>, // if you want to target all indices, use vec![munin]
    pub query: SearchQuery,   // if you want to target all documents, use { match_all: {} }
}

impl From<crate::domain::ports::search::SearchParameters> for SearchParameters {
    fn from(input: crate::domain::ports::search::SearchParameters) -> Self {
        SearchParameters {
            indices: input
                .doc_types
                .iter()
                .map(|doc_type| root_doctype(doc_type))
                .collect(),
            query: input.query,
        }
    }
}

#[derive(Debug, Clone)]
pub struct ExplainParameters {
    pub index: String,      // if you want to target all indices, use vec![munin]
    pub query: SearchQuery, // if you want to target all documents, use { match_all: {} }
    pub id: String,
}

impl From<crate::domain::ports::explain::ExplainParameters> for ExplainParameters {
    fn from(input: crate::domain::ports::explain::ExplainParameters) -> Self {
        ExplainParameters {
            index: root_doctype(&input.doc_type),
            query: input.query,
            id: input.id,
        }
    }
}

#[async_trait]
pub trait Query {
    type Doc: DeserializeOwned + Send + Sync + 'static;

    async fn search_documents(&self, parameters: SearchParameters)
        -> Result<Vec<Self::Doc>, Error>;

    async fn explain_document(&self, parameters: ExplainParameters) -> Result<Self::Doc, Error>;

    fn list_documents(
        &self,
        parameters: ListParameters,
    ) -> Result<Pin<Box<dyn Stream<Item = Self::Doc> + Send + 'static>>, Error>;
}
<|endoftext|>


<commit_before>//! TODO Fill in

use rlua::{self, AnyUserData, Lua, MetaMethod, Table, UserData, UserDataMethods, Value};
use wlroots;

use std::default::Default;
use std::fmt::{self, Display, Formatter};

use compositor::Server;

const INDEX_MISS_FUNCTION: &'static str = "__index_miss_function";
const NEWINDEX_MISS_FUNCTION: &'static str = "__newindex_miss_function";

#[derive(Clone, Debug)]
pub struct MouseState {
    // TODO Fill in
    dummy: i32
}

impl Default for MouseState {
    fn default() -> Self {
        MouseState { dummy: 0 }
    }
}

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

impl UserData for MouseState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_meta_function(MetaMethod::Index, index);
    }
}

pub fn init(lua: &Lua) -> rlua::Result<()> {
    let mouse_table = lua.create_table()?;
    let meta_table = lua.create_table()?;
    let mouse = lua.create_userdata(MouseState::default())?;
    method_setup(lua, &mouse_table)?;
    let globals = lua.globals();
    mouse_table.set_metatable(Some(meta_table));
    mouse.set_user_value(mouse_table)?;
    globals.set("mouse", mouse)
}

fn method_setup(lua: &Lua, mouse_table: &Table) -> rlua::Result<()> {
    mouse_table.set("coords", lua.create_function(coords)?)?;
    mouse_table.set("set_index_miss_handler",
                     lua.create_function(set_index_miss)?)?;
    mouse_table.set("set_newindex_miss_handler",
                     lua.create_function(set_newindex_miss)?)?;
    Ok(())
}

fn coords<'lua>(lua: &'lua Lua,
                (coords, _ignore_enter): (rlua::Value<'lua>, rlua::Value<'lua>))
                -> rlua::Result<Table<'lua>> {
    dehandle!(
        @compositor = {wlroots::compositor_handle().unwrap()};
        let server: &mut Server = compositor.into();
        // TODO Update pointer as well?
        @cursor = {&server.cursor};
        match coords {
            rlua::Value::Table(coords) => {
                let (x, y): (i32, i32) = (coords.get("x")?, coords.get("y")?);
                // TODO The ignore_enter is supposed to not send a send event to
                // the client
                cursor.warp(None, x as _, y as _);
                Ok(coords)
            }
            _ => {
                // get the coords
                let (x, y) = cursor.coords();
                let coords = lua.create_table()?;
                coords.set("x", x as i32)?;
                coords.set("y", y as i32)?;
                // TODO It expects a table of what buttons were pressed.
                coords.set("buttons", lua.create_table()?)?;
                Ok(coords)
            }
        }
    )
}

fn set_index_miss(lua: &Lua, func: rlua::Function) -> rlua::Result<()> {
    let button = lua.globals().get::<_, AnyUserData>("button")?;
    let table = button.get_user_value::<Table>()?;
    table.set(INDEX_MISS_FUNCTION, func)
}

fn set_newindex_miss(lua: &Lua, func: rlua::Function) -> rlua::Result<()> {
    let button = lua.globals().get::<_, AnyUserData>("button")?;
    let table = button.get_user_value::<Table>()?;
    table.set(NEWINDEX_MISS_FUNCTION, func)
}

fn index<'lua>(_: &'lua Lua,
               (mouse, index): (AnyUserData<'lua>, Value<'lua>))
               -> rlua::Result<Value<'lua>> {
    let obj_table = mouse.get_user_value::<Table>()?;
    match index {
        Value::String(ref string) => {
            let string = string.to_str()?;
            if string != "screen" {
                return obj_table.get(string)
                // TODO call miss index handler if it exists
            }
            // TODO Might need a more robust way to get the current output...
            // E.g they look at where the cursor is, I don't think we need to do that.

            dehandle!(
                @compositor = {wlroots::compositor_handle().unwrap()};
                let server: &mut Server = compositor.into();
                // TODO FIXME Actually returned the "focused" output.
                // We need to define that in the compositor.
                server.outputs[0].clone()
                //use super::screen::SCREENS_HANDLE;
                //let index = outputs.iter()
                //    .position(|output| output.focused)
                //// NOTE Best to just lie because no one handles nil screens properly
                //    .unwrap_or(0);
                //let screens = lua.named_registry_value::<Vec<AnyUserData>>(SCREENS_HANDLE)?;
                //if index < screens.len() {
                //    return screens[index].clone().to_lua(lua)
                //}
                // TODO Return screen even in bad case,
                // see how awesome does it for maximal compatibility
                //panic!("Could not find a screen")
            );
        }
        _ => {}
    }
    return obj_table.get(index)
}
<commit_msg>implement mouse.screen<commit_after>//! TODO Fill in

use rlua::{self, AnyUserData, Lua, MetaMethod, Table, ToLua, UserData, UserDataMethods, Value};
use wlroots;

use std::default::Default;
use std::fmt::{self, Display, Formatter};

use awesome::screen::{Screen, SCREENS_HANDLE};
use awesome::object::Objectable;

use compositor::Server;

const INDEX_MISS_FUNCTION: &'static str = "__index_miss_function";
const NEWINDEX_MISS_FUNCTION: &'static str = "__newindex_miss_function";

#[derive(Clone, Debug)]
pub struct MouseState {
    // TODO Fill in
    dummy: i32
}

impl Default for MouseState {
    fn default() -> Self {
        MouseState { dummy: 0 }
    }
}

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

impl UserData for MouseState {
    fn add_methods(methods: &mut UserDataMethods<Self>) {
        methods.add_meta_function(MetaMethod::Index, index);
    }
}

pub fn init(lua: &Lua) -> rlua::Result<()> {
    let mouse_table = lua.create_table()?;
    let meta_table = lua.create_table()?;
    let mouse = lua.create_userdata(MouseState::default())?;
    method_setup(lua, &mouse_table)?;
    let globals = lua.globals();
    mouse_table.set_metatable(Some(meta_table));
    mouse.set_user_value(mouse_table)?;
    globals.set("mouse", mouse)
}

fn method_setup(lua: &Lua, mouse_table: &Table) -> rlua::Result<()> {
    mouse_table.set("coords", lua.create_function(coords)?)?;
    mouse_table.set("set_index_miss_handler",
                     lua.create_function(set_index_miss)?)?;
    mouse_table.set("set_newindex_miss_handler",
                     lua.create_function(set_newindex_miss)?)?;
    Ok(())
}

fn coords<'lua>(lua: &'lua Lua,
                (coords, _ignore_enter): (rlua::Value<'lua>, rlua::Value<'lua>))
                -> rlua::Result<Table<'lua>> {
    dehandle!(
        @compositor = {wlroots::compositor_handle().unwrap()};
        let server: &mut Server = compositor.into();
        // TODO Update pointer as well?
        @cursor = {&server.cursor};
        match coords {
            rlua::Value::Table(coords) => {
                let (x, y): (i32, i32) = (coords.get("x")?, coords.get("y")?);
                // TODO The ignore_enter is supposed to not send a send event to
                // the client
                cursor.warp(None, x as _, y as _);
                Ok(coords)
            }
            _ => {
                // get the coords
                let (x, y) = cursor.coords();
                let coords = lua.create_table()?;
                coords.set("x", x as i32)?;
                coords.set("y", y as i32)?;
                // TODO It expects a table of what buttons were pressed.
                coords.set("buttons", lua.create_table()?)?;
                Ok(coords)
            }
        }
    )
}

fn set_index_miss(lua: &Lua, func: rlua::Function) -> rlua::Result<()> {
    let button = lua.globals().get::<_, AnyUserData>("button")?;
    let table = button.get_user_value::<Table>()?;
    table.set(INDEX_MISS_FUNCTION, func)
}

fn set_newindex_miss(lua: &Lua, func: rlua::Function) -> rlua::Result<()> {
    let button = lua.globals().get::<_, AnyUserData>("button")?;
    let table = button.get_user_value::<Table>()?;
    table.set(NEWINDEX_MISS_FUNCTION, func)
}

fn index<'lua>(lua: &'lua Lua,
               (mouse, index): (AnyUserData<'lua>, Value<'lua>))
               -> rlua::Result<Value<'lua>> {
    let obj_table = mouse.get_user_value::<Table>()?;
    match index {
        Value::String(ref string) => {
            let string = string.to_str()?;
            if string != "screen" {
                return obj_table.get(string)
            }

            let output = dehandle!(
                @compositor = {wlroots::compositor_handle().unwrap()};
                let server: &mut Server = compositor.into();
                let Server { ref mut cursor,
                             ref mut layout,
                             .. } = *server;

                @cursor = {cursor};
                @layout = {layout};
                let (lx, ly) = cursor.coords();
                layout.output_at(lx, ly)
            );

            let mut screens: Vec<Screen> = lua.named_registry_value::<Vec<AnyUserData>>(SCREENS_HANDLE)?
                .into_iter()
                .map(|obj| Screen::cast(obj.into()).unwrap())
                .collect();

            if let Some(output) = output {
                for screen in &screens {
                    let state = screen.state()?;
                    if state.outputs.contains(&output) {
                        return screen.clone().to_lua(lua);
                    }
                }
            }
            if screens.len() > 0 {
                return screens[0].clone().to_lua(lua)
            }

            return Ok(Value::Nil)
        }
        _ => {}
    }
    return obj_table.get(index)
}
<|endoftext|>


<commit_before>/// Brainfuck language interpreter
/// As specified here: http://www.muppetlabs.com/~breadbox/bf/
#[macro_use]
extern crate clap;

use std::process;
use std::path::{Path};
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::collections::VecDeque;
use std::thread;
use std::time::Duration;

use clap::{Arg, App};

macro_rules! exit_with_error(
    ($($arg:tt)*) => { {
        writeln!(&mut ::std::io::stderr(), $($arg)*)
            .expect("Failed while printing to stderr");
        process::exit(1);
    } }
);

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn main() {
    let args = App::new(crate_name!())
        .version(crate_version!())
        .version_short("v")
        .author(crate_authors!())
        .about(crate_description!())
        .arg(Arg::with_name("input-file")
            .help("The brainfuck file to process. Should contain brainfuck instructions")
            .value_name("file")
            .takes_value(true)
            .required(true)
        )
        .arg(Arg::with_name("debug-enabled")
            .short("D")
            .long("debug")
            .help("Enables debug mode which outputs debugging information to stderr")
        )
        .arg(Arg::with_name("delay")
            .long("delay")
            .takes_value(true)
            .help("Delays execution of each instruction by this amount in ms")
        )
        .get_matches();

    let source_path = Path::new(args.value_of("input-file").unwrap());
    if !source_path.exists() || !source_path.is_file() {
        exit_with_error!("Not a valid file: '{}'", source_path.display());
    }

    let debug_mode = args.is_present("debug-enabled");

    let delay: u64 = if let Some(delay_str) = args.value_of("delay") {
        delay_str.parse().unwrap_or_else(|e: std::num::ParseIntError| exit_with_error!("Invalid delay: {}", e))
    } else {
        0
    };

    let f = File::open(source_path).unwrap_or_else(|e| {
        exit_with_error!("Could not open source file: {}", e);
    });

    let program = f.bytes().map(
        |c| c.expect("Fatal: Could not read char") as char
    ).collect::<Vec<char>>();

    interpret(program, debug_mode, delay);
}

fn interpret(program: Vec<char>, debug: bool, delay: u64) {
    let mut buffer: VecDeque<u8> = VecDeque::new();
    // Make sure there is at least one cell to begin with
    buffer.push_back(0u8);

    // p is the position "pointer" in the buffer
    let mut p: usize = 0;
    // i is the instruction index in the program
    let mut i: usize = 0;

    loop {
        if i >= program.len() {
            break;
        }
        let c = program[i];
        i += 1;

        match c {
            '>' => {
                p += 1;
                if p >= buffer.len() {
                    buffer.push_back(0u8);
                }
            },
            '<' => {
                if p == 0 {
                    buffer.push_front(0u8);
                }
                else {
                    p -= 1;
                }
            },
            '+' => buffer[p] = buffer[p].wrapping_add(1),
            '-' => buffer[p] = buffer[p].wrapping_sub(1),
            '.' => print!("{}", buffer[p] as char),
            ',' => {
                let chr = io::stdin().bytes().next();
                if chr.is_none() {
                    buffer[p] = 0;
                }
                else {
                    buffer[p] = chr.unwrap().expect("Could not read input");
                }
            },
            '[' => {
                if buffer[p] == 0 {
                    i = find_matching(&program, i - 1) + 1;
                }
            },
            ']' => {
                if buffer[p] != 0 {
                    i = find_matching(&program, i - 1) + 1;
                }
            },
            _ => continue,
        }

        if debug {
            println_stderr!("{{\"lastInstructionIndex\": {}, \"lastInstruction\": \"{}\", \"currentPointer\": {}, \"memory\": \"{}\"}}", i-1, c, p,
                buffer.iter().fold(String::new(), |acc, v| format!("{} {}", acc, v)));
        }

        thread::sleep(Duration::from_millis(delay));
    }
}

/// Finds the matching '[' or ']' for the given position within the program
/// panics if a match is not found
fn find_matching(program: &Vec<char>, start: usize) -> usize {
    let direction: isize = match program[start] {
        '[' => 1,
        ']' => -1,
        _ => unreachable!(),
    };

    let mut count = direction;
    let mut current = start;
    loop {
        if (direction < 0 && current == 0) || (direction > 0 && current >= program.len() - 1) {
            panic!("Could not find matching parenthesis for instruction {}", start);
        }
        current = (current as isize + direction) as usize;
        let c = program[current];

        count = match c {
            '[' => count + 1,
            ']' => count - 1,
            _ => count,
        };

        if count == 0 {
            break;
        }
    }

    current
}
<commit_msg>Implementing fancy matching jumps caching<commit_after>/// Brainfuck language interpreter
/// As specified here: http://www.muppetlabs.com/~breadbox/bf/
#[macro_use]
extern crate clap;

use std::process;
use std::path::{Path};
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::collections::{VecDeque, HashMap};
use std::thread;
use std::time::Duration;

use clap::{Arg, App};

macro_rules! exit_with_error(
    ($($arg:tt)*) => { {
        writeln!(&mut ::std::io::stderr(), $($arg)*)
            .expect("Failed while printing to stderr");
        process::exit(1);
    } }
);

macro_rules! println_stderr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

fn main() {
    let args = App::new(crate_name!())
        .version(crate_version!())
        .version_short("v")
        .author(crate_authors!())
        .about(crate_description!())
        .arg(Arg::with_name("input-file")
            .help("The brainfuck file to process. Should contain brainfuck instructions")
            .value_name("file")
            .takes_value(true)
            .required(true)
        )
        .arg(Arg::with_name("debug-enabled")
            .short("D")
            .long("debug")
            .help("Enables debug mode which outputs debugging information to stderr")
        )
        .arg(Arg::with_name("delay")
            .long("delay")
            .takes_value(true)
            .help("Delays execution of each instruction by this amount in ms")
        )
        .get_matches();

    let source_path = Path::new(args.value_of("input-file").unwrap());
    if !source_path.exists() || !source_path.is_file() {
        exit_with_error!("Not a valid file: '{}'", source_path.display());
    }

    let debug_mode = args.is_present("debug-enabled");

    let delay: u64 = if let Some(delay_str) = args.value_of("delay") {
        delay_str.parse().unwrap_or_else(|e: std::num::ParseIntError| exit_with_error!("Invalid delay: {}", e))
    } else {
        0
    };

    let f = File::open(source_path).unwrap_or_else(|e| {
        exit_with_error!("Could not open source file: {}", e);
    });

    let program = f.bytes().map(
        |c| c.expect("Fatal: Could not read char") as char
    ).collect::<Vec<char>>();

    interpret(program, debug_mode, delay);
}

fn interpret(program: Vec<char>, debug: bool, delay: u64) {
    let mut jumps: HashMap<usize, usize> = HashMap::new();
    let mut buffer: VecDeque<u8> = VecDeque::new();
    // Make sure there is at least one cell to begin with
    buffer.push_back(0u8);

    // p is the position "pointer" in the buffer
    let mut p: usize = 0;
    // i is the instruction index in the program
    let mut i: usize = 0;

    loop {
        if i >= program.len() {
            break;
        }
        let c = program[i];
        i += 1;

        match c {
            '>' => {
                p += 1;
                if p >= buffer.len() {
                    buffer.push_back(0u8);
                }
            },
            '<' => {
                if p == 0 {
                    buffer.push_front(0u8);
                }
                else {
                    p -= 1;
                }
            },
            '+' => buffer[p] = buffer[p].wrapping_add(1),
            '-' => buffer[p] = buffer[p].wrapping_sub(1),
            '.' => print!("{}", buffer[p] as char),
            ',' => {
                let chr = io::stdin().bytes().next();
                if chr.is_none() {
                    buffer[p] = 0;
                }
                else {
                    buffer[p] = chr.unwrap().expect("Could not read input");
                }
            },
            '[' => {
                if buffer[p] == 0 {
                    i = find_matching(&program, &mut jumps, i - 1) + 1;
                }
            },
            ']' => {
                if buffer[p] != 0 {
                    i = find_matching(&program, &mut jumps, i - 1) + 1;
                }
            },
            _ => continue,
        }

        if debug {
            println_stderr!("{{\"lastInstructionIndex\": {}, \"lastInstruction\": \"{}\", \"currentPointer\": {}, \"memory\": \"{}\"}}", i-1, c, p,
                buffer.iter().fold(String::new(), |acc, v| format!("{} {}", acc, v)));
        }

        thread::sleep(Duration::from_millis(delay));
    }
}

/// Finds the matching '[' or ']' for the given position within the program
/// panics if a match is not found
fn find_matching(program: &Vec<char>, jumps: &mut HashMap<usize, usize>, start: usize) -> usize {
    if let Some(matching) = jumps.get(&start) {
        return *matching;
    }

    let search_forward: bool = match program[start] {
        '[' => true,
        ']' => false,
        _ => unreachable!(),
    };

    // Here we are trying to make this search faster by caching the jumps we see along the way
    let mut encountered_jumps: VecDeque<usize> = VecDeque::new();
    encountered_jumps.push_back(start);

    let mut current = start;
    loop {
        if (!search_forward && current == 0) || (search_forward && current >= program.len() - 1) {
            panic!("Could not find matching parenthesis for instruction {}", start);
        }
        current = if search_forward { current + 1 } else { current - 1 };

        let c = program[current];
        match c {
            '[' => {
                if search_forward {
                    encountered_jumps.push_back(current);
                }
                else {
                    let backward = encountered_jumps.pop_back().expect("Mismatched [ instruction");
                    jumps.insert(current, backward);
                    jumps.insert(backward, current);
                }
            },
            ']' => {
                if search_forward {
                    let forward = encountered_jumps.pop_back().expect("Mismatched ] instruction");
                    jumps.insert(forward, current);
                    jumps.insert(current, forward);
                }
                else {
                    encountered_jumps.push_back(current);
                }
            },
            _ => (),
        };

        if encountered_jumps.len() == 0 {
            break;
        }
    }

    current
}
<|endoftext|>