<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::io;
use std::collections::VecDeque;

use sys_util::{EventFd, Result};

use BusDevice;

const LOOP_SIZE: usize = 0x40;

const DATA: u8 = 0;
const IER: u8 = 1;
const IIR: u8 = 2;
const LCR: u8 = 3;
const MCR: u8 = 4;
const LSR: u8 = 5;
const MSR: u8 = 6;
const SCR: u8 = 7;
const DLAB_LOW: u8 = 0;
const DLAB_HIGH: u8 = 1;

const IER_RECV_BIT: u8 = 0x1;
const IER_THR_BIT: u8 = 0x2;
const IER_FIFO_BITS: u8 = 0x0f;

const IIR_FIFO_BITS: u8 = 0xc0;
const IIR_NONE_BIT: u8 = 0x1;
const IIR_THR_BIT: u8 = 0x2;
const IIR_RECV_BIT: u8 = 0x4;

const LSR_DATA_BIT: u8 = 0x1;
const LSR_EMPTY_BIT: u8 = 0x20;
const LSR_IDLE_BIT: u8 = 0x40;

const MCR_LOOP_BIT: u8 = 0x10;

const DEFAULT_INTERRUPT_IDENTIFICATION: u8 = IIR_NONE_BIT; // no pending interrupt
const DEFAULT_LINE_STATUS: u8 = LSR_EMPTY_BIT | LSR_IDLE_BIT; // THR empty and line is idle
const DEFAULT_LINE_CONTROL: u8 = 0x3; // 8-bits per character
const DEFAULT_MODEM_CONTROL: u8 = 0x8; // Auxiliary output 2
const DEFAULT_MODEM_STATUS: u8 = 0x20 | 0x10 | 0x80; // data ready, clear to send, carrier detect
const DEFAULT_BAUD_DIVISOR: u16 = 12; // 9600 bps

/// Emulates serial COM ports commonly seen on x86 I/O ports 0x3f8/0x2f8/0x3e8/0x2e8.
///
/// This can optionally write the guest's output to a Write trait object. To send input to the
/// guest, use `queue_input_bytes`.
pub struct Serial {
    interrupt_enable: u8,
    interrupt_identification: u8,
    interrupt_evt: EventFd,
    line_control: u8,
    line_status: u8,
    modem_control: u8,
    modem_status: u8,
    scratch: u8,
    baud_divisor: u16,
    in_buffer: VecDeque<u8>,
    out: Option<Box<io::Write + Send>>,
}

impl Serial {
    fn new(interrupt_evt: EventFd, out: Option<Box<io::Write + Send>>) -> Serial {
        Serial {
            interrupt_enable: 0,
            interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION,
            interrupt_evt: interrupt_evt,
            line_control: DEFAULT_LINE_CONTROL,
            line_status: DEFAULT_LINE_STATUS,
            modem_control: DEFAULT_MODEM_CONTROL,
            modem_status: DEFAULT_MODEM_STATUS,
            scratch: 0,
            baud_divisor: DEFAULT_BAUD_DIVISOR,
            in_buffer: VecDeque::new(),
            out: out,
        }
    }

    /// Constructs a Serial port ready for output.
    pub fn new_out(interrupt_evt: EventFd, out: Box<io::Write + Send>) -> Serial {
        Self::new(interrupt_evt, Some(out))
    }

    /// Constructs a Serial port with no connected output.
    pub fn new_sink(interrupt_evt: EventFd) -> Serial {
        Self::new(interrupt_evt, None)
    }

    /// Queues raw bytes for the guest to read and signals the interrupt if the line status would
    /// change.
    pub fn queue_input_bytes(&mut self, c: &[u8]) -> Result<()> {
        if !self.is_loop() {
            self.in_buffer.extend(c);
            self.recv_data()?;
        }
        Ok(())
    }

    fn is_dlab_set(&self) -> bool {
        (self.line_control & 0x80) != 0
    }

    fn is_recv_intr_enabled(&self) -> bool {
        (self.interrupt_enable & IER_RECV_BIT) != 0
    }

    fn is_thr_intr_enabled(&self) -> bool {
        (self.interrupt_enable & IER_THR_BIT) != 0
    }

    fn is_loop(&self) -> bool {
        (self.modem_control & MCR_LOOP_BIT) != 0
    }

    fn add_intr_bit(&mut self, bit: u8) {
        self.interrupt_identification &= !IIR_NONE_BIT;
        self.interrupt_identification |= bit;
    }

    fn del_intr_bit(&mut self, bit: u8) {
        self.interrupt_identification &= !bit;
        if self.interrupt_identification == 0x0 {
            self.interrupt_identification = IIR_NONE_BIT;
        }
    }

    fn thr_empty(&mut self) -> Result<()> {
        if self.is_thr_intr_enabled() {
            self.add_intr_bit(IIR_THR_BIT);
            self.trigger_interrupt()?
        }
        Ok(())
    }

    fn recv_data(&mut self) -> Result<()> {
        if self.is_recv_intr_enabled() {
            self.add_intr_bit(IIR_RECV_BIT);
            self.trigger_interrupt()?
        }
        self.line_status |= LSR_DATA_BIT;
        Ok(())
    }

    fn trigger_interrupt(&mut self) -> Result<()> {
        self.interrupt_evt.write(1)
    }

    fn iir_reset(&mut self) {
        self.interrupt_identification = DEFAULT_INTERRUPT_IDENTIFICATION;
    }

    fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
        match offset as u8 {
            DLAB_LOW if self.is_dlab_set() => {
                self.baud_divisor = (self.baud_divisor & 0xff00) | v as u16
            }
            DLAB_HIGH if self.is_dlab_set() => {
                self.baud_divisor = (self.baud_divisor & 0x00ff) | ((v as u16) << 8)
            }
            DATA => {
                if self.is_loop() {
                    if self.in_buffer.len() < LOOP_SIZE {
                        self.in_buffer.push_back(v);
                        self.recv_data()?;
                    }
                } else {
                    if let Some(out) = self.out.as_mut() {
                        out.write_all(&[v])?;
                        out.flush()?;
                    }
                    self.thr_empty()?;
                }
            }
            IER => self.interrupt_enable = v & IER_FIFO_BITS,
            LCR => self.line_control = v,
            MCR => self.modem_control = v,
            SCR => self.scratch = v,
            _ => {}
        }
        Ok(())
    }
}

impl BusDevice for Serial {
    fn write(&mut self, offset: u64, data: &[u8]) {
        if data.len() != 1 {
            return;
        }

        if let Err(e) = self.handle_write(offset as u8, data[0]) {
            error!("serial failed write: {:?}", e);
        }
    }

    fn read(&mut self, offset: u64, data: &mut [u8]) {
        if data.len() != 1 {
            return;
        }

        data[0] = match offset as u8 {
            DLAB_LOW if self.is_dlab_set() => self.baud_divisor as u8,
            DLAB_HIGH if self.is_dlab_set() => (self.baud_divisor >> 8) as u8,
            DATA => {
                self.del_intr_bit(IIR_RECV_BIT);
                if self.in_buffer.len() <= 1 {
                    self.line_status &= !LSR_DATA_BIT;
                }
                self.in_buffer.pop_front().unwrap_or_default()
            }
            IER => self.interrupt_enable,
            IIR => {
                let v = self.interrupt_identification | IIR_FIFO_BITS;
                self.iir_reset();
                v
            }
            LCR => self.line_control,
            MCR => self.modem_control,
            LSR => self.line_status,
            MSR => self.modem_status,
            SCR => self.scratch,
            _ => 0,
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::{Arc, Mutex};
    use std::io;

    #[derive(Clone)]
    struct SharedBuffer {
        buf: Arc<Mutex<Vec<u8>>>,
    }

    impl SharedBuffer {
        fn new() -> SharedBuffer {
            SharedBuffer {
                buf: Arc::new(Mutex::new(Vec::new())),
            }
        }
    }

    impl io::Write for SharedBuffer {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.buf.lock().unwrap().write(buf)
        }
        fn flush(&mut self) -> io::Result<()> {
            self.buf.lock().unwrap().flush()
        }
    }

    #[test]
    fn serial_output() {
        let intr_evt = EventFd::new().unwrap();
        let serial_out = SharedBuffer::new();

        let mut serial = Serial::new_out(intr_evt, Box::new(serial_out.clone()));

        serial.write(DATA as u64, &['a' as u8]);
        serial.write(DATA as u64, &['b' as u8]);
        serial.write(DATA as u64, &['c' as u8]);
        assert_eq!(
            serial_out.buf.lock().unwrap().as_slice(),
            &['a' as u8, 'b' as u8, 'c' as u8]
        );
    }

    #[test]
    fn serial_input() {
        let intr_evt = EventFd::new().unwrap();
        let serial_out = SharedBuffer::new();

        let mut serial =
            Serial::new_out(intr_evt.try_clone().unwrap(), Box::new(serial_out.clone()));

        serial.write(IER as u64, &[IER_RECV_BIT]);
        serial
            .queue_input_bytes(&['a' as u8, 'b' as u8, 'c' as u8])
            .unwrap();

        assert_eq!(intr_evt.read(), Ok(1));
        let mut data = [0u8; 1];
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'a' as u8);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'b' as u8);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'c' as u8);
    }
}
<commit_msg>devices: serial: add thr, dlab, modem and scratch unit tests<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::io;
use std::collections::VecDeque;

use sys_util::{EventFd, Result};

use BusDevice;

const LOOP_SIZE: usize = 0x40;

const DATA: u8 = 0;
const IER: u8 = 1;
const IIR: u8 = 2;
const LCR: u8 = 3;
const MCR: u8 = 4;
const LSR: u8 = 5;
const MSR: u8 = 6;
const SCR: u8 = 7;
const DLAB_LOW: u8 = 0;
const DLAB_HIGH: u8 = 1;

const IER_RECV_BIT: u8 = 0x1;
const IER_THR_BIT: u8 = 0x2;
const IER_FIFO_BITS: u8 = 0x0f;

const IIR_FIFO_BITS: u8 = 0xc0;
const IIR_NONE_BIT: u8 = 0x1;
const IIR_THR_BIT: u8 = 0x2;
const IIR_RECV_BIT: u8 = 0x4;

const LCR_DLAB_BIT: u8 = 0x80;

const LSR_DATA_BIT: u8 = 0x1;
const LSR_EMPTY_BIT: u8 = 0x20;
const LSR_IDLE_BIT: u8 = 0x40;

const MCR_LOOP_BIT: u8 = 0x10;

const DEFAULT_INTERRUPT_IDENTIFICATION: u8 = IIR_NONE_BIT; // no pending interrupt
const DEFAULT_LINE_STATUS: u8 = LSR_EMPTY_BIT | LSR_IDLE_BIT; // THR empty and line is idle
const DEFAULT_LINE_CONTROL: u8 = 0x3; // 8-bits per character
const DEFAULT_MODEM_CONTROL: u8 = 0x8; // Auxiliary output 2
const DEFAULT_MODEM_STATUS: u8 = 0x20 | 0x10 | 0x80; // data ready, clear to send, carrier detect
const DEFAULT_BAUD_DIVISOR: u16 = 12; // 9600 bps

/// Emulates serial COM ports commonly seen on x86 I/O ports 0x3f8/0x2f8/0x3e8/0x2e8.
///
/// This can optionally write the guest's output to a Write trait object. To send input to the
/// guest, use `queue_input_bytes`.
pub struct Serial {
    interrupt_enable: u8,
    interrupt_identification: u8,
    interrupt_evt: EventFd,
    line_control: u8,
    line_status: u8,
    modem_control: u8,
    modem_status: u8,
    scratch: u8,
    baud_divisor: u16,
    in_buffer: VecDeque<u8>,
    out: Option<Box<io::Write + Send>>,
}

impl Serial {
    fn new(interrupt_evt: EventFd, out: Option<Box<io::Write + Send>>) -> Serial {
        Serial {
            interrupt_enable: 0,
            interrupt_identification: DEFAULT_INTERRUPT_IDENTIFICATION,
            interrupt_evt: interrupt_evt,
            line_control: DEFAULT_LINE_CONTROL,
            line_status: DEFAULT_LINE_STATUS,
            modem_control: DEFAULT_MODEM_CONTROL,
            modem_status: DEFAULT_MODEM_STATUS,
            scratch: 0,
            baud_divisor: DEFAULT_BAUD_DIVISOR,
            in_buffer: VecDeque::new(),
            out: out,
        }
    }

    /// Constructs a Serial port ready for output.
    pub fn new_out(interrupt_evt: EventFd, out: Box<io::Write + Send>) -> Serial {
        Self::new(interrupt_evt, Some(out))
    }

    /// Constructs a Serial port with no connected output.
    pub fn new_sink(interrupt_evt: EventFd) -> Serial {
        Self::new(interrupt_evt, None)
    }

    /// Queues raw bytes for the guest to read and signals the interrupt if the line status would
    /// change.
    pub fn queue_input_bytes(&mut self, c: &[u8]) -> Result<()> {
        if !self.is_loop() {
            self.in_buffer.extend(c);
            self.recv_data()?;
        }
        Ok(())
    }

    fn is_dlab_set(&self) -> bool {
        (self.line_control & LCR_DLAB_BIT) != 0
    }

    fn is_recv_intr_enabled(&self) -> bool {
        (self.interrupt_enable & IER_RECV_BIT) != 0
    }

    fn is_thr_intr_enabled(&self) -> bool {
        (self.interrupt_enable & IER_THR_BIT) != 0
    }

    fn is_loop(&self) -> bool {
        (self.modem_control & MCR_LOOP_BIT) != 0
    }

    fn add_intr_bit(&mut self, bit: u8) {
        self.interrupt_identification &= !IIR_NONE_BIT;
        self.interrupt_identification |= bit;
    }

    fn del_intr_bit(&mut self, bit: u8) {
        self.interrupt_identification &= !bit;
        if self.interrupt_identification == 0x0 {
            self.interrupt_identification = IIR_NONE_BIT;
        }
    }

    fn thr_empty(&mut self) -> Result<()> {
        if self.is_thr_intr_enabled() {
            self.add_intr_bit(IIR_THR_BIT);
            self.trigger_interrupt()?
        }
        Ok(())
    }

    fn recv_data(&mut self) -> Result<()> {
        if self.is_recv_intr_enabled() {
            self.add_intr_bit(IIR_RECV_BIT);
            self.trigger_interrupt()?
        }
        self.line_status |= LSR_DATA_BIT;
        Ok(())
    }

    fn trigger_interrupt(&mut self) -> Result<()> {
        self.interrupt_evt.write(1)
    }

    fn iir_reset(&mut self) {
        self.interrupt_identification = DEFAULT_INTERRUPT_IDENTIFICATION;
    }

    fn handle_write(&mut self, offset: u8, v: u8) -> Result<()> {
        match offset as u8 {
            DLAB_LOW if self.is_dlab_set() => {
                self.baud_divisor = (self.baud_divisor & 0xff00) | v as u16
            }
            DLAB_HIGH if self.is_dlab_set() => {
                self.baud_divisor = (self.baud_divisor & 0x00ff) | ((v as u16) << 8)
            }
            DATA => {
                if self.is_loop() {
                    if self.in_buffer.len() < LOOP_SIZE {
                        self.in_buffer.push_back(v);
                        self.recv_data()?;
                    }
                } else {
                    if let Some(out) = self.out.as_mut() {
                        out.write_all(&[v])?;
                        out.flush()?;
                    }
                    self.thr_empty()?;
                }
            }
            IER => self.interrupt_enable = v & IER_FIFO_BITS,
            LCR => self.line_control = v,
            MCR => self.modem_control = v,
            SCR => self.scratch = v,
            _ => {}
        }
        Ok(())
    }
}

impl BusDevice for Serial {
    fn write(&mut self, offset: u64, data: &[u8]) {
        if data.len() != 1 {
            return;
        }

        if let Err(e) = self.handle_write(offset as u8, data[0]) {
            error!("serial failed write: {:?}", e);
        }
    }

    fn read(&mut self, offset: u64, data: &mut [u8]) {
        if data.len() != 1 {
            return;
        }

        data[0] = match offset as u8 {
            DLAB_LOW if self.is_dlab_set() => self.baud_divisor as u8,
            DLAB_HIGH if self.is_dlab_set() => (self.baud_divisor >> 8) as u8,
            DATA => {
                self.del_intr_bit(IIR_RECV_BIT);
                if self.in_buffer.len() <= 1 {
                    self.line_status &= !LSR_DATA_BIT;
                }
                self.in_buffer.pop_front().unwrap_or_default()
            }
            IER => self.interrupt_enable,
            IIR => {
                let v = self.interrupt_identification | IIR_FIFO_BITS;
                self.iir_reset();
                v
            }
            LCR => self.line_control,
            MCR => self.modem_control,
            LSR => self.line_status,
            MSR => self.modem_status,
            SCR => self.scratch,
            _ => 0,
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::{Arc, Mutex};
    use std::io;

    #[derive(Clone)]
    struct SharedBuffer {
        buf: Arc<Mutex<Vec<u8>>>,
    }

    impl SharedBuffer {
        fn new() -> SharedBuffer {
            SharedBuffer {
                buf: Arc::new(Mutex::new(Vec::new())),
            }
        }
    }

    impl io::Write for SharedBuffer {
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
            self.buf.lock().unwrap().write(buf)
        }
        fn flush(&mut self) -> io::Result<()> {
            self.buf.lock().unwrap().flush()
        }
    }

    #[test]
    fn serial_output() {
        let intr_evt = EventFd::new().unwrap();
        let serial_out = SharedBuffer::new();

        let mut serial = Serial::new_out(intr_evt, Box::new(serial_out.clone()));

        serial.write(DATA as u64, &['x' as u8, 'y' as u8]);
        serial.write(DATA as u64, &['a' as u8]);
        serial.write(DATA as u64, &['b' as u8]);
        serial.write(DATA as u64, &['c' as u8]);
        assert_eq!(
            serial_out.buf.lock().unwrap().as_slice(),
            &['a' as u8, 'b' as u8, 'c' as u8]
        );
    }

    #[test]
    fn serial_input() {
        let intr_evt = EventFd::new().unwrap();
        let serial_out = SharedBuffer::new();

        let mut serial =
            Serial::new_out(intr_evt.try_clone().unwrap(), Box::new(serial_out.clone()));

        // write 1 to the interrupt event fd, so that read doesn't block in case the event fd
        // counter doesn't change (for 0 it blocks)
        assert!(intr_evt.write(1).is_ok());
        serial.write(IER as u64, &[IER_RECV_BIT]);
        serial
            .queue_input_bytes(&['a' as u8, 'b' as u8, 'c' as u8])
            .unwrap();

        assert_eq!(intr_evt.read(), Ok(2));

        // check if reading in a 2-length array doesn't have side effects
        let mut data = [0u8, 0u8];
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data, [0u8, 0u8]);

        let mut data = [0u8];
        serial.read(LSR as u64, &mut data[..]);
        assert_ne!(data[0] & LSR_DATA_BIT, 0);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'a' as u8);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'b' as u8);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'c' as u8);

        // check if reading from the largest u8 offset returns 0
        serial.read(0xff, &mut data[..]);
        assert_eq!(data[0], 0);
    }

    #[test]
    fn serial_thr() {
        let intr_evt = EventFd::new().unwrap();
        let mut serial = Serial::new_sink(intr_evt.try_clone().unwrap());

        // write 1 to the interrupt event fd, so that read doesn't block in case the event fd
        // counter doesn't change (for 0 it blocks)
        assert!(intr_evt.write(1).is_ok());
        serial.write(IER as u64, &[IER_THR_BIT]);
        serial.write(DATA as u64, &['a' as u8]);

        assert_eq!(intr_evt.read(), Ok(2));
        let mut data = [0u8];
        serial.read(IER as u64, &mut data[..]);
        assert_eq!(data[0] & IER_FIFO_BITS, IER_THR_BIT);
        serial.read(IIR as u64, &mut data[..]);
        assert_ne!(data[0] & IIR_THR_BIT, 0);
    }

    #[test]
    fn serial_dlab() {
        let mut serial = Serial::new_sink(EventFd::new().unwrap());

        serial.write(LCR as u64, &[LCR_DLAB_BIT as u8]);
        serial.write(DLAB_LOW as u64, &[0x12 as u8]);
        serial.write(DLAB_HIGH as u64, &[0x34 as u8]);

        let mut data = [0u8];
        serial.read(LCR as u64, &mut data[..]);
        assert_eq!(data[0], LCR_DLAB_BIT as u8);
        serial.read(DLAB_LOW as u64, &mut data[..]);
        assert_eq!(data[0], 0x12);
        serial.read(DLAB_HIGH as u64, &mut data[..]);
        assert_eq!(data[0], 0x34);
    }

    #[test]
    fn serial_modem() {
        let mut serial = Serial::new_sink(EventFd::new().unwrap());

        serial.write(MCR as u64, &[MCR_LOOP_BIT as u8]);
        serial.write(DATA as u64, &['a' as u8]);
        serial.write(DATA as u64, &['b' as u8]);
        serial.write(DATA as u64, &['c' as u8]);

        let mut data = [0u8];
        serial.read(MSR as u64, &mut data[..]);
        assert_eq!(data[0], DEFAULT_MODEM_STATUS as u8);
        serial.read(MCR as u64, &mut data[..]);
        assert_eq!(data[0], MCR_LOOP_BIT as u8);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'a' as u8);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'b' as u8);
        serial.read(DATA as u64, &mut data[..]);
        assert_eq!(data[0], 'c' as u8);
    }

    #[test]
    fn serial_scratch() {
        let mut serial = Serial::new_sink(EventFd::new().unwrap());

        serial.write(SCR as u64, &[0x12 as u8]);

        let mut data = [0u8];
        serial.read(SCR as u64, &mut data[..]);
        assert_eq!(data[0], 0x12 as u8);
    }
}
<|endoftext|>


<commit_before>#![feature(conservative_impl_trait, fnbox)]

extern crate crossbeam;
extern crate futures;

use std::cell::UnsafeCell;
use std::sync::Arc;
use std::boxed::FnBox;
use std::sync::atomic::{self, AtomicBool};

use crossbeam::sync::{AtomicOption, SegQueue};
use futures::Future;
use futures::task::{self, Task};

const ORDERING: atomic::Ordering = atomic::Ordering::Relaxed;

pub struct Worker<T, R, E> {
    ret: Arc<AtomicOption<Result<R, E>>>,
    lutex: Arc<LutexInternal<T>>,
}

impl<T, R, E> Future for Worker<T, R, E> {
    type Item = R;
    type Error = E;

    fn poll(&mut self) -> futures::Poll<R, E> {
        if self.lutex.progress() {
            match self.ret.take(ORDERING) {
                Some(Ok(x)) => Ok(futures::Async::Ready(x)),
                Some(Err(x)) => Err(x),
                None => Ok(futures::Async::NotReady),
            }
        } else {
            Ok(futures::Async::NotReady)
        }
    }
}

struct LutexInternal<T> {
    data: UnsafeCell<T>,
    locked: AtomicBool,
    queue: SegQueue<Box<FnBox(*mut T)>>,
    parked: SegQueue<Task>,
}

impl<T> LutexInternal<T> {
    fn new(data: T) -> LutexInternal<T> {
        LutexInternal {
            data: UnsafeCell::new(data),
            locked: AtomicBool::new(false),
            queue: SegQueue::new(),
            parked: SegQueue::new(),
        }
    }

    fn progress(&self) -> bool {
        if !self.locked.swap(true, ORDERING) {
            let f = self.queue.try_pop().unwrap();
            f(self.data.get());
            self.locked.store(false, ORDERING);
            self.parked.try_pop().map(|task| task.unpark());

            true
        } else {
            self.parked.push(task::park());
            false
        }
    }
}

pub struct Lutex<T> {
    inner: Arc<LutexInternal<T>>,
}

impl<T> Lutex<T> {
    pub fn new(data: T) -> Lutex<T> {
        Lutex {
            inner: Arc::new(LutexInternal::new(data)),
        }
    }

    pub fn with<F: 'static>(&self, f: F) -> impl Future<Item = (), Error = ()>
        where F: FnOnce(&mut T) {
        self.try_with::<(), (), _>(|x| {
            f(x);
            Ok(())
        })
    }

    pub fn try_with<R: 'static, E: 'static, F: 'static>(&self, f: F) -> impl Future<Item = R, Error = E>
        where F: FnOnce(&mut T) -> Result<R, E> {
        let ret = Arc::new(AtomicOption::new());

        let ret2 = ret.clone();
        self.inner.queue.push(Box::new(move |x: *mut T| {
            ret2.swap(f(unsafe { &mut *x }), ORDERING);
        }));

        Worker {
            ret: ret,
            lutex: self.inner.clone(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn single_thread_queue() {
        let lutex = Lutex::new(200);

        lutex.with(|x| *x += 2).wait().unwrap();
        lutex.with(|x| assert_eq!(*x, 202)).wait().unwrap();
        lutex.with(|x| *x = 100).wait().unwrap();
        lutex.with(|x| *x += 2).wait().unwrap();
        lutex.with(|x| *x += 2).wait().unwrap();
        lutex.with(|x| assert_eq!(*x, 104)).wait().unwrap();
    }
}
<commit_msg>Add multithreaded tests for `Lutex`.<commit_after>#![feature(conservative_impl_trait, fnbox)]

extern crate crossbeam;
extern crate futures;

use std::cell::UnsafeCell;
use std::sync::Arc;
use std::boxed::FnBox;
use std::sync::atomic::{self, AtomicBool};

use crossbeam::sync::{AtomicOption, SegQueue};
use futures::Future;
use futures::task::{self, Task};

const ORDERING: atomic::Ordering = atomic::Ordering::Relaxed;

/// A `Lutex` worker future.
///
/// This future does on job queued to the lutex every poll. When the job of the data, it
/// represents, is done, the return value of the inner data's job is returned.
pub struct Worker<T, R, E> {
    ret: Arc<AtomicOption<Result<R, E>>>,
    lutex: Arc<LutexInternal<T>>,
}

impl<T, R, E> Future for Worker<T, R, E> {
    type Item = R;
    type Error = E;

    fn poll(&mut self) -> futures::Poll<R, E> {
        self.lutex.progress();

        match self.ret.take(ORDERING) {
            Some(Ok(x)) => Ok(futures::Async::Ready(x)),
            Some(Err(x)) => Err(x),
            None => {
                self.lutex.park();
                Ok(futures::Async::NotReady)
            },
        }
    }
}

struct LutexInternal<T> {
    data: UnsafeCell<T>,
    locked: AtomicBool,
    queue: SegQueue<Box<FnBox(*mut T)>>,
    parked: SegQueue<Task>,
}

impl<T> LutexInternal<T> {
    fn new(data: T) -> LutexInternal<T> {
        LutexInternal {
            data: UnsafeCell::new(data),
            locked: AtomicBool::new(false),
            queue: SegQueue::new(),
            parked: SegQueue::new(),
        }
    }

    fn progress(&self) {
        if !self.locked.swap(true, ORDERING) {
            let f = self.queue.try_pop().unwrap();
            f(self.data.get());
            self.locked.store(false, ORDERING);
            self.parked.try_pop().map(|task| task.unpark());
        }
    }

    fn park(&self) {
        self.parked.push(task::park());
    }
}

unsafe impl<T> Sync for LutexInternal<T> {}
unsafe impl<T> Send for LutexInternal<T> {}

pub struct Lutex<T> {
    inner: Arc<LutexInternal<T>>,
}

impl<T> Lutex<T> {
    /// Create a new `Lutex<T>` with some initial data.
    pub fn new(data: T) -> Lutex<T> {
        Lutex {
            inner: Arc::new(LutexInternal::new(data)),
        }
    }

    pub fn with<R: 'static, F: 'static + Send>(&self, f: F) -> Worker<T, R, ()>
        where F: FnOnce(&mut T) -> R {
        self.try_with::<R, (), _>(|x| {
            Ok(f(x))
        })
    }

    pub fn try_with<R: 'static, E: 'static, F: 'static + Send>(&self, f: F) -> Worker<T, R, E>
        where F: FnOnce(&mut T) -> Result<R, E> {
        let ret = Arc::new(AtomicOption::new());

        let ret2 = ret.clone();
        self.inner.queue.push(Box::new(move |x: *mut T| {
            ret2.swap(f(unsafe { &mut *x }), ORDERING);
        }));

        Worker {
            ret: ret,
            lutex: self.inner.clone(),
        }
    }
}

impl<T> Clone for Lutex<T> {
    fn clone(&self) -> Lutex<T> {
        Lutex {
            inner: self.inner.clone(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;

    #[test]
    fn single_thread_queue() {
        let lutex = Lutex::new(200);

        lutex.with(|x| *x += 2).wait().unwrap();
        lutex.with(|x| assert_eq!(*x, 202)).wait().unwrap();
        lutex.with(|x| *x = 100).wait().unwrap();
        lutex.with(|x| *x += 2).wait().unwrap();
        lutex.with(|x| *x += 2).wait().unwrap();
        lutex.with(|x| assert_eq!(*x, 104)).wait().unwrap();
    }

    #[test]
    fn many_threads() {
        let lutex = Lutex::new(0);
        let mut v = Vec::new();
        for _ in 0..1000 {
            let lutex = lutex.clone();
            v.push(thread::spawn(move || lutex.with(|x| *x += 1)));
        }

        v.push(thread::spawn(move || lutex.with(|x| assert_eq!(*x, 100))));

        for i in v {
            i.join().unwrap();
        }
    }

    #[test]
    fn mutually_exclusive() {
        let lutex = Lutex::new(false);
        let mut v = Vec::new();
        for _ in 0..1000 {
            let lutex = lutex.clone();
            v.push(thread::spawn(move || lutex.with(|x| {
                assert!(!*x);
                *x = true;
                *x = false;
            })));
        }

        for i in v {
            i.join().unwrap();
        }
    }
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ast::*;

pub fn list(rule: Rule) -> Rule {
    match rule {
        Rule { name, ty, expr } => Rule {
            name,
            ty,
            expr: expr.map_bottom_up(|expr| {
                // TODO: Use box syntax when it gets stabilized.
                match expr {
                    Expr::Seq(l, r) => match *l {
                        Expr::Rep(l) => match *l {
                            Expr::Seq(l1, l2) => match *r {
                                // Converts `(rule ~ rest)* ~ rule?` to `rule ~ (rest ~ rule)* ~ rest?`
                                Expr::Opt(r) if l1 == r => {
                                    Expr::Seq(
                                        l1,
                                        Box::new(Expr::Seq(
                                            Box::new(Expr::Rep(Box::new(Expr::Seq(
                                                l2.clone(),
                                                r
                                            )))),
                                            Box::new(Expr::Opt(l2))
                                        ))
                                    )
                                },
                                // Converts `(rule ~ rest)* ~ rule` to `rule ~ (rest ~ rule)*`
                                r if *l1 == r => {
                                    Expr::Seq(
                                        l1,
                                        Box::new(Expr::Rep(Box::new(Expr::Seq(
                                            l2,
                                            Box::new(r)
                                        ))))
                                    )
                                }
                                r => {
                                    Expr::Seq(
                                        Box::new(Expr::Rep(Box::new(Expr::Seq(l1, l2)))),
                                        Box::new(r)
                                    )
                                }
                            }
                            expr => Expr::Seq(Box::new(Expr::Rep(Box::new(expr))), r)
                        }
                        expr => Expr::Seq(Box::new(expr), r)
                    }
                    expr => expr
                }
            })
        }
    }
}<commit_msg>Add newline in lister.rs<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use ast::*;

pub fn list(rule: Rule) -> Rule {
    match rule {
        Rule { name, ty, expr } => Rule {
            name,
            ty,
            expr: expr.map_bottom_up(|expr| {
                // TODO: Use box syntax when it gets stabilized.
                match expr {
                    Expr::Seq(l, r) => match *l {
                        Expr::Rep(l) => match *l {
                            Expr::Seq(l1, l2) => match *r {
                                // Converts `(rule ~ rest)* ~ rule?` to `rule ~ (rest ~ rule)* ~ rest?`
                                Expr::Opt(r) if l1 == r => {
                                    Expr::Seq(
                                        l1,
                                        Box::new(Expr::Seq(
                                            Box::new(Expr::Rep(Box::new(Expr::Seq(
                                                l2.clone(),
                                                r
                                            )))),
                                            Box::new(Expr::Opt(l2))
                                        ))
                                    )
                                },
                                // Converts `(rule ~ rest)* ~ rule` to `rule ~ (rest ~ rule)*`
                                r if *l1 == r => {
                                    Expr::Seq(
                                        l1,
                                        Box::new(Expr::Rep(Box::new(Expr::Seq(
                                            l2,
                                            Box::new(r)
                                        ))))
                                    )
                                }
                                r => {
                                    Expr::Seq(
                                        Box::new(Expr::Rep(Box::new(Expr::Seq(l1, l2)))),
                                        Box::new(r)
                                    )
                                }
                            }
                            expr => Expr::Seq(Box::new(Expr::Rep(Box::new(expr))), r)
                        }
                        expr => Expr::Seq(Box::new(expr), r)
                    }
                    expr => expr
                }
            })
        }
    }
}
<|endoftext|>


<commit_before>// Lumol, an extensible molecular simulation engine
// Copyright (C) Lumol's contributors — BSD license
extern crate env_logger;
extern crate test;
extern crate walkdir;

extern crate lumol_core;
extern crate lumol_input;

use std::{env, fs, io};
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use walkdir::WalkDir;

use test::{DynTestFn, DynTestName, TestDesc, TestDescAndFn};
use test::ShouldPanic::No;

use lumol_core::sys::System;
use lumol_input::{Error, Input, InteractionsInput};

fn main() {
    env_logger::init().expect("could not set logger");
    let _cleanup = TestsCleanup;

    let mut args: Vec<_> = env::args()
                              .filter(|arg| !arg.contains("test-threads"))
                              .collect();

    let mut opts = match test::parse_opts(&args).expect("no options") {
        Ok(opts) => opts,
        Err(msg) => panic!("{:?}", msg),
    };
    opts.verbose = true;

    let tests = all_tests();
    let result = test::run_tests_console(&opts, tests);
    match result {
        Ok(true) => {}
        Ok(false) => std::process::exit(-1),
        Err(err) => panic!("io error when running tests: {:?}", err),
    }
}

fn all_tests() -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();

    tests.extend(
        generate_tests("simulation/good", |path| {
            Box::new(move || {
                let input = Input::new(path.clone()).unwrap();
                input.read().unwrap();
            })
        }).expect("Could not generate the tests"),
    );

    tests.extend(
        generate_tests("simulation/bad", |path| {
            Box::new(move || {
                let message = get_error_message(&path);
                let result = Input::new(path.clone()).and_then(|input| input.read());

                match result {
                    Err(Error::Config(reason)) => assert_eq!(reason, message),
                    _ => panic!("This test should fail with a Config error"),
                }
            })
        }).expect("Could not generate the tests"),
    );

    tests.extend(
        generate_tests("interactions/good", |path| {
            Box::new(move || {
                let mut system = System::new();
                let input = InteractionsInput::new(path.clone()).unwrap();
                input.read(&mut system).unwrap();
            })
        }).expect("Could not generate the tests"),
    );

    tests.extend(
        generate_tests("interactions/bad", |path| {
            Box::new(move || {
                let message = get_error_message(&path);

                let mut system = System::new();
                let result =
                    InteractionsInput::new(path.clone()).and_then(|input| input.read(&mut system));

                match result {
                    Err(Error::Config(reason)) => assert_eq!(reason, message),
                    _ => panic!("This test should fail with a Config error"),
                }
            })
        }).expect("Could not generate the tests"),
    );

    return tests;
}

/// Generate the tests by calling `callback` for every TOML files at the given
/// `root`.
fn generate_tests<F>(root: &str, callback: F) -> Result<Vec<TestDescAndFn>, io::Error>
where
    F: Fn(PathBuf) -> Box<FnMut() + Send>,
{
    let mut tests = Vec::new();

    let dir = PathBuf::new().join(env!("CARGO_MANIFEST_DIR")).join("tests").join(root);
    for entry in WalkDir::new(dir) {
        let entry = try!(entry);
        let file_type = entry.file_type();
        if file_type.is_file() {
            if let Some(extension) = entry.path().extension() {
                if extension == "toml" {
                    let path = entry.path();
                    let name = String::from(root) + "/";
                    let name = name
                        + path.file_name().expect("Missing file name")
                              .to_str()
                              .expect("File name is invalid UTF-8");

                    let test = TestDescAndFn {
                        desc: TestDesc {
                            name: DynTestName(name),
                            ignore: false,
                            should_panic: No,
                        },
                        testfn: DynTestFn(callback(path.to_path_buf())),
                    };

                    tests.push(test);
                }
            }
        }
    }

    Ok(tests)
}

/// Cleanup temporary files after the tests
struct TestsCleanup;
impl Drop for TestsCleanup {
    fn drop(&mut self) {
        const REMOVE: &[&str] = &[
            "energy.dat",
            "filename.xyz",
            "cell.dat",
            "properties.dat",
            "file.log",
            "custom.dat",
            "stress.dat",
            "forces.xyz",
        ];

        for file in REMOVE {
            if let Err(err) = fs::remove_file(file) {
                match err.kind() {
                    io::ErrorKind::NotFound => {}
                    _ => panic!("io error in cleanup code: {}", err),
                }
            }
        }
    }
}

fn get_error_message(path: &Path) -> String {
    let mut buffer = String::new();
    File::open(path).and_then(|mut file| file.read_to_string(&mut buffer))
                    .expect("Could not read the input file");

    for line in buffer.lines() {
        let line = line.trim();
        if line.starts_with("#^ ") {
            return String::from(&line[3..]);
        }
    }

    panic!(
        "No error message found in {}. Please add one with the '#^ <message>' syntax.",
        path.display()
    );
}
<commit_msg>Fix warning<commit_after>// Lumol, an extensible molecular simulation engine
// Copyright (C) Lumol's contributors — BSD license
extern crate env_logger;
extern crate test;
extern crate walkdir;

extern crate lumol_core;
extern crate lumol_input;

use std::{env, fs, io};
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use walkdir::WalkDir;

use test::{DynTestFn, DynTestName, TestDesc, TestDescAndFn};
use test::ShouldPanic::No;

use lumol_core::sys::System;
use lumol_input::{Error, Input, InteractionsInput};

fn main() {
    env_logger::init().expect("could not set logger");
    let _cleanup = TestsCleanup;

    let args: Vec<_> = env::args()
                           .filter(|arg| !arg.contains("test-threads"))
                           .collect();

    let mut opts = match test::parse_opts(&args).expect("no options") {
        Ok(opts) => opts,
        Err(msg) => panic!("{:?}", msg),
    };
    opts.verbose = true;

    let tests = all_tests();
    let result = test::run_tests_console(&opts, tests);
    match result {
        Ok(true) => {}
        Ok(false) => std::process::exit(-1),
        Err(err) => panic!("io error when running tests: {:?}", err),
    }
}

fn all_tests() -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();

    tests.extend(
        generate_tests("simulation/good", |path| {
            Box::new(move || {
                let input = Input::new(path.clone()).unwrap();
                input.read().unwrap();
            })
        }).expect("Could not generate the tests"),
    );

    tests.extend(
        generate_tests("simulation/bad", |path| {
            Box::new(move || {
                let message = get_error_message(&path);
                let result = Input::new(path.clone()).and_then(|input| input.read());

                match result {
                    Err(Error::Config(reason)) => assert_eq!(reason, message),
                    _ => panic!("This test should fail with a Config error"),
                }
            })
        }).expect("Could not generate the tests"),
    );

    tests.extend(
        generate_tests("interactions/good", |path| {
            Box::new(move || {
                let mut system = System::new();
                let input = InteractionsInput::new(path.clone()).unwrap();
                input.read(&mut system).unwrap();
            })
        }).expect("Could not generate the tests"),
    );

    tests.extend(
        generate_tests("interactions/bad", |path| {
            Box::new(move || {
                let message = get_error_message(&path);

                let mut system = System::new();
                let result =
                    InteractionsInput::new(path.clone()).and_then(|input| input.read(&mut system));

                match result {
                    Err(Error::Config(reason)) => assert_eq!(reason, message),
                    _ => panic!("This test should fail with a Config error"),
                }
            })
        }).expect("Could not generate the tests"),
    );

    return tests;
}

/// Generate the tests by calling `callback` for every TOML files at the given
/// `root`.
fn generate_tests<F>(root: &str, callback: F) -> Result<Vec<TestDescAndFn>, io::Error>
where
    F: Fn(PathBuf) -> Box<FnMut() + Send>,
{
    let mut tests = Vec::new();

    let dir = PathBuf::new().join(env!("CARGO_MANIFEST_DIR")).join("tests").join(root);
    for entry in WalkDir::new(dir) {
        let entry = try!(entry);
        let file_type = entry.file_type();
        if file_type.is_file() {
            if let Some(extension) = entry.path().extension() {
                if extension == "toml" {
                    let path = entry.path();
                    let name = String::from(root) + "/";
                    let name = name
                        + path.file_name().expect("Missing file name")
                              .to_str()
                              .expect("File name is invalid UTF-8");

                    let test = TestDescAndFn {
                        desc: TestDesc {
                            name: DynTestName(name),
                            ignore: false,
                            should_panic: No,
                        },
                        testfn: DynTestFn(callback(path.to_path_buf())),
                    };

                    tests.push(test);
                }
            }
        }
    }

    Ok(tests)
}

/// Cleanup temporary files after the tests
struct TestsCleanup;
impl Drop for TestsCleanup {
    fn drop(&mut self) {
        const REMOVE: &[&str] = &[
            "energy.dat",
            "filename.xyz",
            "cell.dat",
            "properties.dat",
            "file.log",
            "custom.dat",
            "stress.dat",
            "forces.xyz",
        ];

        for file in REMOVE {
            if let Err(err) = fs::remove_file(file) {
                match err.kind() {
                    io::ErrorKind::NotFound => {}
                    _ => panic!("io error in cleanup code: {}", err),
                }
            }
        }
    }
}

fn get_error_message(path: &Path) -> String {
    let mut buffer = String::new();
    File::open(path).and_then(|mut file| file.read_to_string(&mut buffer))
                    .expect("Could not read the input file");

    for line in buffer.lines() {
        let line = line.trim();
        if line.starts_with("#^ ") {
            return String::from(&line[3..]);
        }
    }

    panic!(
        "No error message found in {}. Please add one with the '#^ <message>' syntax.",
        path.display()
    );
}
<|endoftext|>


<commit_before>use keyboard::Key;
use keymap::{KeyMap, KeyMapState, CommandInfo};
use command::{BuilderEvent, BuilderArgs };
use textobject::{ Offset, Kind, Anchor };
use buffer::Mark;
use overlay::OverlayType;
use modes::ModeType;

use super::Mode;

/// `NormalMode` mimics Vi's Normal mode.
pub struct NormalMode {
    keymap: KeyMap,
    number: Option<i32>,
    reading_number: bool,
}

impl NormalMode {

    /// Create a new instance of `NormalMode`
    pub fn new() -> NormalMode {
        NormalMode {
            keymap: NormalMode::key_defaults(),
            number: None,
            reading_number: false,
        }
    }

    /// Creates a `KeyMap` with default `NormalMode` key bindings
    fn key_defaults() -> KeyMap {
        let mut keymap = KeyMap::new();
        // movement
        // { keys: 'h', command: 'buffer::move_cursor', args: { direction: backward, kind: char, number: 1 } }
        keymap.bind_key(
            Key::Char('h'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Char)
                                             .with_offset(Offset::Backward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('j'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::Same))
                                             .with_offset(Offset::Forward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('k'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::Same))
                                             .with_offset(Offset::Backward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('l'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Char)
                                             .with_offset(Offset::Forward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('w'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Word(Anchor::Start))
                                             .with_offset(Offset::Forward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('b'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Word(Anchor::Start))
                                             .with_offset(Offset::Backward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('$'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::End))
                                             .with_offset(Offset::Forward(0, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('0'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::End))
                                             .with_offset(Offset::Backward(0, Mark::Cursor(0))))
            }
        );

        // actions
        keymap.bind_key(
            Key::Char('u'),
            CommandInfo {
                command_name: String::from("editor::undo"),
                args: None,
            }
        );
        keymap.bind_key(
            Key::Ctrl('r'),
            CommandInfo {
                command_name: String::from("editor::redo"),
                args: None,
            }
        );

        keymap.bind_key(
            Key::Char('i'),
            CommandInfo {
                command_name: String::from("editor::set_mode"),
                args: Some(BuilderArgs::new().with_mode(ModeType::Insert)),
            }
        );
        keymap.bind_key(
            Key::Char(':'),
            CommandInfo {
                command_name: String::from("editor::set_overlay"),
                args: Some(BuilderArgs::new().with_overlay(OverlayType::CommandPrompt)),
            }
        );

        keymap
    }

}

impl Mode for NormalMode {
    fn handle_key_event(&mut self, key: Key) -> BuilderEvent {
        if let Key::Char(c) = key {
            // '0' might be bound (start of line), and cannot be the start of a number sequence
            if c.is_digit(10) && (self.reading_number || c != '0') {
                let n = c.to_digit(10).unwrap() as i32;
                self.reading_number = true;
                if let Some(current) = self.number {
                    self.number = Some((current*10) + n);
                } else {
                    self.number = Some(n);
                }
                return BuilderEvent::Incomplete;
            } else if self.reading_number {
                self.reading_number = false;
            }
        }
        match self.keymap.check_key(key) {
            KeyMapState::Match(mut c) => {
                if let Some(num) = self.number {
                    if let Some(args) = c.args {
                        c.args = Some(args.with_number(num));
                    }
                }
                self.number = None;
                BuilderEvent::Complete(c)
            }
            _ => {
                BuilderEvent::Incomplete
            }
        }
    }
}

impl Default for NormalMode {
    fn default() -> Self {
        Self::new()
    }
}
<commit_msg>Fixes '0' keybinding in vi mode (#145)<commit_after>use keyboard::Key;
use keymap::{KeyMap, KeyMapState, CommandInfo};
use command::{BuilderEvent, BuilderArgs };
use textobject::{ Offset, Kind, Anchor };
use buffer::Mark;
use overlay::OverlayType;
use modes::ModeType;

use super::Mode;

/// `NormalMode` mimics Vi's Normal mode.
pub struct NormalMode {
    keymap: KeyMap,
    number: Option<i32>,
    reading_number: bool,
}

impl NormalMode {

    /// Create a new instance of `NormalMode`
    pub fn new() -> NormalMode {
        NormalMode {
            keymap: NormalMode::key_defaults(),
            number: None,
            reading_number: false,
        }
    }

    /// Creates a `KeyMap` with default `NormalMode` key bindings
    fn key_defaults() -> KeyMap {
        let mut keymap = KeyMap::new();
        // movement
        // { keys: 'h', command: 'buffer::move_cursor', args: { direction: backward, kind: char, number: 1 } }
        keymap.bind_key(
            Key::Char('h'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Char)
                                             .with_offset(Offset::Backward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('j'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::Same))
                                             .with_offset(Offset::Forward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('k'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::Same))
                                             .with_offset(Offset::Backward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('l'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Char)
                                             .with_offset(Offset::Forward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('w'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Word(Anchor::Start))
                                             .with_offset(Offset::Forward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('b'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Word(Anchor::Start))
                                             .with_offset(Offset::Backward(1, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('$'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::End))
                                             .with_offset(Offset::Forward(0, Mark::Cursor(0))))
            }
        );
        keymap.bind_key(
            Key::Char('0'),
            CommandInfo {
                command_name: String::from("buffer::move_cursor"),
                args: Some(BuilderArgs::new().with_kind(Kind::Line(Anchor::Start))
                                             .with_offset(Offset::Backward(0, Mark::Cursor(0))))
            }
        );

        // actions
        keymap.bind_key(
            Key::Char('u'),
            CommandInfo {
                command_name: String::from("editor::undo"),
                args: None,
            }
        );
        keymap.bind_key(
            Key::Ctrl('r'),
            CommandInfo {
                command_name: String::from("editor::redo"),
                args: None,
            }
        );

        keymap.bind_key(
            Key::Char('i'),
            CommandInfo {
                command_name: String::from("editor::set_mode"),
                args: Some(BuilderArgs::new().with_mode(ModeType::Insert)),
            }
        );
        keymap.bind_key(
            Key::Char(':'),
            CommandInfo {
                command_name: String::from("editor::set_overlay"),
                args: Some(BuilderArgs::new().with_overlay(OverlayType::CommandPrompt)),
            }
        );

        keymap
    }

}

impl Mode for NormalMode {
    fn handle_key_event(&mut self, key: Key) -> BuilderEvent {
        if let Key::Char(c) = key {
            // '0' might be bound (start of line), and cannot be the start of a number sequence
            if c.is_digit(10) && (self.reading_number || c != '0') {
                let n = c.to_digit(10).unwrap() as i32;
                self.reading_number = true;
                if let Some(current) = self.number {
                    self.number = Some((current*10) + n);
                } else {
                    self.number = Some(n);
                }
                return BuilderEvent::Incomplete;
            } else if self.reading_number {
                self.reading_number = false;
            }
        }
        match self.keymap.check_key(key) {
            KeyMapState::Match(mut c) => {
                if let Some(num) = self.number {
                    if let Some(args) = c.args {
                        c.args = Some(args.with_number(num));
                    }
                }
                self.number = None;
                BuilderEvent::Complete(c)
            }
            _ => {
                BuilderEvent::Incomplete
            }
        }
    }
}

impl Default for NormalMode {
    fn default() -> Self {
        Self::new()
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{AtomicUsize, Ordering};

use gc::bump::BumpAllocator;
use gc::{align_space, arena};
use gc::{Address, Region};
use os::{self, ProtType};

const SEMI_RATIO: usize = 3;

pub struct YoungGen {
    // bounds of eden & semi-spaces
    total: Region,

    // eden space
    eden: Eden,

    // from/to-space
    semi: SemiSpace,
}

impl YoungGen {
    pub fn new(total: Region, committed_size: usize, protect: bool) -> YoungGen {
        let half_size = total.size() / 2;

        let eden_total = total.start.region_start(half_size);
        let semi_total = Region::new(eden_total.end, total.end);

        let semi_committed = align_space(committed_size / SEMI_RATIO);
        let eden_committed = committed_size - semi_committed;

        let young = YoungGen {
            total: total,
            eden: Eden::new(eden_total, eden_committed),
            semi: SemiSpace::new(semi_total, semi_committed, protect),
        };

        young.commit();

        young
    }

    fn commit(&self) {
        self.eden.commit();
        self.semi.commit();
    }

    pub fn eden_active(&self) -> Region {
        self.eden.active()
    }

    pub fn eden_committed(&self) -> Region {
        self.eden.committed()
    }

    pub fn from_active(&self) -> Region {
        self.semi.from_active()
    }

    pub fn from_committed(&self) -> Region {
        self.semi.from_committed()
    }

    pub fn from_total(&self) -> Region {
        self.semi.from_total()
    }

    pub fn to_committed(&self) -> Region {
        self.semi.to_committed()
    }

    pub fn total(&self) -> Region {
        self.total.clone()
    }

    pub fn contains(&self, addr: Address) -> bool {
        self.total.contains(addr)
    }

    pub fn valid_top(&self, addr: Address) -> bool {
        self.total.valid_top(addr)
    }

    pub fn clear(&self) {
        self.eden.reset_top();
        self.semi.clear_from();
    }

    pub fn active_size(&self) -> usize {
        self.eden.active().size() + self.semi.from_block().active().size()
    }

    pub fn unprotect_to(&self) {
        self.semi.unprotect_to();
    }

    pub fn protect_to(&self) {
        self.semi.protect_to();
    }

    pub fn clear_eden(&self) {
        self.eden.reset_top();
    }

    pub fn swap_semi(&self, top: Address) {
        self.semi.swap(top);
    }

    pub fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.total.contains(addr));

        if addr < self.semi.total.start {
            return false;
        }

        self.semi.should_be_promoted(addr)
    }

    pub fn bump_alloc(&self, size: usize) -> Address {
        self.eden.bump_alloc(size)
    }
}

struct Eden {
    total: Region,
    block: Block,
    alloc: BumpAllocator,
}

impl Eden {
    fn new(total: Region, committed_size: usize) -> Eden {
        let limit = total.start.offset(committed_size);

        Eden {
            total: total.clone(),
            block: Block::new(total.clone(), committed_size),
            alloc: BumpAllocator::new(total.start, limit),
        }
    }

    fn commit(&self) {
        self.block.commit();
    }

    fn total(&self) -> Region {
        self.block.total()
    }

    fn committed(&self) -> Region {
        self.block.committed()
    }

    fn active(&self) -> Region {
        self.block.active()
    }

    fn reset_top(&self) {
        self.block.reset_top();
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.block.bump_alloc(size)
    }
}

struct SemiSpace {
    total: Region,

    first: Block,
    second: Block,

    // decides whether first or second is the from-space (value 1=first or 2=second)
    from_index: AtomicUsize,

    protect: bool,
    alloc: BumpAllocator,
    age_marker: AtomicUsize,
}

impl SemiSpace {
    fn new(total: Region, committed_size: usize, protect: bool) -> SemiSpace {
        let committed_semi_size = committed_size / 2;

        let semi_size = total.size() / 2;
        assert!(committed_semi_size <= semi_size);

        let first = total.start.region_start(semi_size);
        let second = Region::new(first.end, total.end);
        assert!(first.size() == second.size());

        let limit = total.start.offset(committed_semi_size);

        SemiSpace {
            total: total.clone(),

            first: Block::new(first.clone(), committed_semi_size),
            second: Block::new(second, committed_semi_size),

            from_index: AtomicUsize::new(1),
            protect: protect,
            alloc: BumpAllocator::new(total.start, limit),
            age_marker: AtomicUsize::new(first.start.to_usize()),
        }
    }

    fn commit(&self) {
        self.from_block().commit();
    }

    fn from_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.first
        } else {
            assert!(from_index == 2);
            &self.second
        }
    }

    fn to_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.second
        } else {
            assert!(from_index == 2);
            &self.first
        }
    }

    fn from_total(&self) -> Region {
        self.from_block().total()
    }

    fn from_committed(&self) -> Region {
        self.from_block().committed()
    }

    fn from_active(&self) -> Region {
        self.from_block().active()
    }

    fn to_total(&self) -> Region {
        self.to_block().total()
    }

    fn to_committed(&self) -> Region {
        self.to_block().committed()
    }

    fn to_active(&self) -> Region {
        self.to_block().active()
    }

    // Make to-space writable.
    fn unprotect_to(&self) {
        // make memory writable again, so that we
        // can copy objects to the to-space.
        // Since this has some overhead, do it only in debug builds.

        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();

            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::Writable,
            );
        }
    }

    // Make to-space inaccessible.
    fn protect_to(&self) {
        // Make from-space unaccessible both from read/write.
        // Since this has some overhead, do it only in debug builds.
        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();
            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::None,
            );
        }
    }

    // Free all objects in from-semi-space.
    fn clear_from(&self) {
        let from_space = self.from_block();

        self.age_marker
            .store(from_space.start.to_usize(), Ordering::Relaxed);
        self.from_block().reset_top();
    }

    // Switch from- & to-semi-space.
    fn swap(&self, top: Address) {
        // current from-space is now empty
        self.from_block().reset_top();

        // swap from_index
        self.swap_from_index();

        // new from-space is already used up to `top`.
        self.from_block().set_top(top);
        self.age_marker.store(top.to_usize(), Ordering::Relaxed);
    }

    fn swap_from_index(&self) {
        let from_index = self.from_index.load(Ordering::Relaxed);

        let updated_from_index = if from_index == 1 {
            2
        } else {
            assert!(from_index == 2);
            1
        };

        self.from_index.store(updated_from_index, Ordering::Relaxed);
    }

    fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.from_active().contains(addr));
        return addr.to_usize() < self.age_marker.load(Ordering::Relaxed);
    }
}

struct Block {
    start: Address,
    end: Address,
    committed: AtomicUsize,
    alloc: BumpAllocator,
}

impl Block {
    fn new(region: Region, committed_size: usize) -> Block {
        assert!(committed_size <= region.size());
        let committed = region.start.offset(committed_size);

        Block {
            start: region.start,
            end: region.end,
            committed: AtomicUsize::new(committed.to_usize()),
            alloc: BumpAllocator::new(region.start, committed),
        }
    }

    fn commit(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        let size = committed - self.start.to_usize();

        if size > 0 {
            arena::commit(self.start, size, false);
        }
    }

    fn total(&self) -> Region {
        Region::new(self.start, self.end)
    }

    fn committed(&self) -> Region {
        let committed = self.committed.load(Ordering::Relaxed);
        Region::new(self.start, committed.into())
    }

    fn active(&self) -> Region {
        Region::new(self.start, self.alloc.top())
    }

    fn reset_top(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        self.alloc.reset(self.start, committed.into());
    }

    fn set_top(&self, addr: Address) {
        let committed = self.committed.load(Ordering::Relaxed);
        assert!(self.start <= addr && addr <= committed.into());
        self.alloc.reset(addr, committed.into())
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.alloc.bump_alloc(size)
    }
}
<commit_msg>swiper: allocate in from-space after eden<commit_after>use std::sync::atomic::{AtomicUsize, Ordering};

use gc::bump::BumpAllocator;
use gc::{align_space, arena};
use gc::{Address, Region};
use os::{self, ProtType};

const SEMI_RATIO: usize = 3;

pub struct YoungGen {
    // bounds of eden & semi-spaces
    total: Region,

    // eden space
    eden: Eden,

    // from/to-space
    semi: SemiSpace,
}

impl YoungGen {
    pub fn new(total: Region, committed_size: usize, protect: bool) -> YoungGen {
        let half_size = total.size() / 2;

        let eden_total = total.start.region_start(half_size);
        let semi_total = Region::new(eden_total.end, total.end);

        let semi_committed = align_space(committed_size / SEMI_RATIO);
        let eden_committed = committed_size - semi_committed;

        let young = YoungGen {
            total: total,
            eden: Eden::new(eden_total, eden_committed),
            semi: SemiSpace::new(semi_total, semi_committed, protect),
        };

        young.commit();

        young
    }

    fn commit(&self) {
        self.eden.commit();
        self.semi.commit();
    }

    pub fn eden_active(&self) -> Region {
        self.eden.active()
    }

    pub fn eden_committed(&self) -> Region {
        self.eden.committed()
    }

    pub fn from_active(&self) -> Region {
        self.semi.from_active()
    }

    pub fn from_committed(&self) -> Region {
        self.semi.from_committed()
    }

    pub fn from_total(&self) -> Region {
        self.semi.from_total()
    }

    pub fn to_committed(&self) -> Region {
        self.semi.to_committed()
    }

    pub fn total(&self) -> Region {
        self.total.clone()
    }

    pub fn contains(&self, addr: Address) -> bool {
        self.total.contains(addr)
    }

    pub fn valid_top(&self, addr: Address) -> bool {
        self.total.valid_top(addr)
    }

    pub fn clear(&self) {
        self.eden.reset_top();
        self.semi.clear_from();
    }

    pub fn active_size(&self) -> usize {
        self.eden.active().size() + self.semi.from_block().active().size()
    }

    pub fn unprotect_to(&self) {
        self.semi.unprotect_to();
    }

    pub fn protect_to(&self) {
        self.semi.protect_to();
    }

    pub fn clear_eden(&self) {
        self.eden.reset_top();
    }

    pub fn swap_semi(&self, top: Address) {
        self.semi.swap(top);
    }

    pub fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.total.contains(addr));

        if addr < self.semi.total.start {
            return false;
        }

        self.semi.should_be_promoted(addr)
    }

    pub fn bump_alloc(&self, size: usize) -> Address {
        let eden_ptr = self.eden.bump_alloc(size);

        if eden_ptr.is_non_null() {
            return eden_ptr;
        }

        return self.semi.bump_alloc(size);
    }
}

struct Eden {
    total: Region,
    block: Block,
    alloc: BumpAllocator,
}

impl Eden {
    fn new(total: Region, committed_size: usize) -> Eden {
        let limit = total.start.offset(committed_size);

        Eden {
            total: total.clone(),
            block: Block::new(total.clone(), committed_size),
            alloc: BumpAllocator::new(total.start, limit),
        }
    }

    fn commit(&self) {
        self.block.commit();
    }

    fn total(&self) -> Region {
        self.block.total()
    }

    fn committed(&self) -> Region {
        self.block.committed()
    }

    fn active(&self) -> Region {
        self.block.active()
    }

    fn reset_top(&self) {
        self.block.reset_top();
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.block.bump_alloc(size)
    }
}

struct SemiSpace {
    total: Region,

    first: Block,
    second: Block,

    // decides whether first or second is the from-space (value 1=first or 2=second)
    from_index: AtomicUsize,

    protect: bool,
    alloc: BumpAllocator,
    age_marker: AtomicUsize,
}

impl SemiSpace {
    fn new(total: Region, committed_size: usize, protect: bool) -> SemiSpace {
        let committed_semi_size = committed_size / 2;

        let semi_size = total.size() / 2;
        assert!(committed_semi_size <= semi_size);

        let first = total.start.region_start(semi_size);
        let second = Region::new(first.end, total.end);
        assert!(first.size() == second.size());

        let limit = total.start.offset(committed_semi_size);

        SemiSpace {
            total: total.clone(),

            first: Block::new(first.clone(), committed_semi_size),
            second: Block::new(second, committed_semi_size),

            from_index: AtomicUsize::new(1),
            protect: protect,
            alloc: BumpAllocator::new(total.start, limit),
            age_marker: AtomicUsize::new(first.start.to_usize()),
        }
    }

    fn commit(&self) {
        self.from_block().commit();
    }

    fn from_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.first
        } else {
            assert!(from_index == 2);
            &self.second
        }
    }

    fn to_block(&self) -> &Block {
        let from_index = self.from_index.load(Ordering::Relaxed);
        if from_index == 1 {
            &self.second
        } else {
            assert!(from_index == 2);
            &self.first
        }
    }

    fn from_total(&self) -> Region {
        self.from_block().total()
    }

    fn from_committed(&self) -> Region {
        self.from_block().committed()
    }

    fn from_active(&self) -> Region {
        self.from_block().active()
    }

    fn to_total(&self) -> Region {
        self.to_block().total()
    }

    fn to_committed(&self) -> Region {
        self.to_block().committed()
    }

    fn to_active(&self) -> Region {
        self.to_block().active()
    }

    // Make to-space writable.
    fn unprotect_to(&self) {
        // make memory writable again, so that we
        // can copy objects to the to-space.
        // Since this has some overhead, do it only in debug builds.

        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();

            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::Writable,
            );
        }
    }

    // Make to-space inaccessible.
    fn protect_to(&self) {
        // Make from-space unaccessible both from read/write.
        // Since this has some overhead, do it only in debug builds.
        if cfg!(debug_assertions) || self.protect {
            let to_space = self.to_committed();
            os::mprotect(
                to_space.start.to_ptr::<u8>(),
                to_space.size(),
                ProtType::None,
            );
        }
    }

    // Free all objects in from-semi-space.
    fn clear_from(&self) {
        let from_space = self.from_block();

        self.age_marker
            .store(from_space.start.to_usize(), Ordering::Relaxed);
        self.from_block().reset_top();
    }

    // Switch from- & to-semi-space.
    fn swap(&self, top: Address) {
        // current from-space is now empty
        self.from_block().reset_top();

        // swap from_index
        self.swap_from_index();

        // new from-space is already used up to `top`.
        self.from_block().set_top(top);
        self.age_marker.store(top.to_usize(), Ordering::Relaxed);
    }

    fn swap_from_index(&self) {
        let from_index = self.from_index.load(Ordering::Relaxed);

        let updated_from_index = if from_index == 1 {
            2
        } else {
            assert!(from_index == 2);
            1
        };

        self.from_index.store(updated_from_index, Ordering::Relaxed);
    }

    fn should_be_promoted(&self, addr: Address) -> bool {
        debug_assert!(self.from_active().contains(addr));
        return addr.to_usize() < self.age_marker.load(Ordering::Relaxed);
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.from_block().bump_alloc(size)
    }
}

struct Block {
    start: Address,
    end: Address,
    committed: AtomicUsize,
    alloc: BumpAllocator,
}

impl Block {
    fn new(region: Region, committed_size: usize) -> Block {
        assert!(committed_size <= region.size());
        let committed = region.start.offset(committed_size);

        Block {
            start: region.start,
            end: region.end,
            committed: AtomicUsize::new(committed.to_usize()),
            alloc: BumpAllocator::new(region.start, committed),
        }
    }

    fn commit(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        let size = committed - self.start.to_usize();

        if size > 0 {
            arena::commit(self.start, size, false);
        }
    }

    fn total(&self) -> Region {
        Region::new(self.start, self.end)
    }

    fn committed(&self) -> Region {
        let committed = self.committed.load(Ordering::Relaxed);
        Region::new(self.start, committed.into())
    }

    fn active(&self) -> Region {
        Region::new(self.start, self.alloc.top())
    }

    fn reset_top(&self) {
        let committed = self.committed.load(Ordering::Relaxed);
        self.alloc.reset(self.start, committed.into());
    }

    fn set_top(&self, addr: Address) {
        let committed = self.committed.load(Ordering::Relaxed);
        assert!(self.start <= addr && addr <= committed.into());
        self.alloc.reset(addr, committed.into())
    }

    fn bump_alloc(&self, size: usize) -> Address {
        self.alloc.bump_alloc(size)
    }
}
<|endoftext|>


<commit_before>/*
gl/src/gl_wrapper/texture.rs, 2017-07-13

Copyright (c) 2017 Juuso Tuononen

This file is licensed under

Apache License, Version 2.0

or

MIT License
*/

use super::gl_raw;
use self::gl_raw::types::*;

use std::os::raw::c_void;

pub struct TextureRGBA {
    id: GLuint,
}

impl TextureRGBA {
    pub fn new(width: u32, height: u32, data: Vec<u8>) -> TextureRGBA {
        if width*height != data.len() as u32 {
            panic!("image width and height does not match with data length");
        }

        let mut id: GLuint = 0;

        unsafe {
            gl_raw::GenTextures(1, &mut id);
        }

        let mut texture = TextureRGBA {id};
        texture.bind();

        unsafe {
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_WRAP_S, gl_raw::REPEAT as GLint);
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_WRAP_T, gl_raw::REPEAT as GLint);
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_MIN_FILTER, gl_raw::NEAREST as GLint);
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_MAG_FILTER, gl_raw::NEAREST as GLint);

            gl_raw::TexImage2D(gl_raw::TEXTURE_2D, 0, gl_raw::RGBA as GLint, width as GLsizei, height as GLsizei, 0, gl_raw::RGBA, gl_raw::UNSIGNED_BYTE, data.as_ptr() as *const c_void);
            gl_raw::GenerateMipmap(gl_raw::TEXTURE_2D);
        }

        texture
    }

    pub fn bind(&mut self) {
        unsafe {
            gl_raw::BindTexture(gl_raw::TEXTURE_2D, self.id);
        }
    }
}

impl Drop for TextureRGBA {
    fn drop(&mut self) {
        unsafe {
            gl_raw::DeleteTextures(1, &self.id);
        }
    }
}<commit_msg>fixed texture width and height check in function TextureRGBA::new<commit_after>/*
gl/src/gl_wrapper/texture.rs, 2017-07-14

Copyright (c) 2017 Juuso Tuononen

This file is licensed under

Apache License, Version 2.0

or

MIT License
*/

use super::gl_raw;
use self::gl_raw::types::*;

use std::os::raw::c_void;

pub struct TextureRGBA {
    id: GLuint,
}

impl TextureRGBA {
    pub fn new(width: u32, height: u32, data: Vec<u8>) -> TextureRGBA {
        if width*height*4 != data.len() as u32 {
            panic!("image width and height does not match with data length");
        }

        let mut id: GLuint = 0;

        unsafe {
            gl_raw::GenTextures(1, &mut id);
        }

        let mut texture = TextureRGBA {id};
        texture.bind();

        unsafe {
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_WRAP_S, gl_raw::REPEAT as GLint);
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_WRAP_T, gl_raw::REPEAT as GLint);
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_MIN_FILTER, gl_raw::NEAREST as GLint);
            gl_raw::TexParameteri(gl_raw::TEXTURE_2D, gl_raw::TEXTURE_MAG_FILTER, gl_raw::NEAREST as GLint);

            gl_raw::TexImage2D(gl_raw::TEXTURE_2D, 0, gl_raw::RGBA as GLint, width as GLsizei, height as GLsizei, 0, gl_raw::RGBA, gl_raw::UNSIGNED_BYTE, data.as_ptr() as *const c_void);
            gl_raw::GenerateMipmap(gl_raw::TEXTURE_2D);
        }

        texture
    }

    pub fn bind(&mut self) {
        unsafe {
            gl_raw::BindTexture(gl_raw::TEXTURE_2D, self.id);
        }
    }
}

impl Drop for TextureRGBA {
    fn drop(&mut self) {
        unsafe {
            gl_raw::DeleteTextures(1, &self.id);
        }
    }
}<|endoftext|>


<commit_before>use crate::ast::constructors::*;
use crate::ast::ExprKind::*;
use crate::ast::*;

use crate::optimizer::transforms::vectorizer::ShouldPredicate;

#[cfg(test)]
use crate::tests::*;

pub fn short_circuit_booleans(expr: &mut Expr) {
    // For If statements annotated as predicated, do not apply the transform on the condition,
    // since it removes the predication opportunity.
    let should_predicate = expr.should_predicate();
    let applied = match expr.kind {
        If {
            ref mut on_true,
            ref mut on_false,
            ..
        } if should_predicate => {
            short_circuit_booleans(on_true);
            short_circuit_booleans(on_false);
            true
        }
        _ => false,
    };

    if applied {
        return;
    }

    let new = match expr.kind {
        BinOp {
            ref kind,
            ref left,
            ref right,
        } if *kind == BinOpKind::LogicalAnd => Some(
            if_expr(
                left.as_ref().clone(),
                right.as_ref().clone(),
                literal_expr(LiteralKind::BoolLiteral(false)).unwrap(),
            )
            .unwrap(),
        ),
        BinOp {
            ref kind,
            ref left,
            ref right,
        } if *kind == BinOpKind::LogicalOr => Some(
            if_expr(
                left.as_ref().clone(),
                literal_expr(LiteralKind::BoolLiteral(true)).unwrap(),
                right.as_ref().clone(),
            )
            .unwrap(),
        ),
        _ => None,
    };

    if let Some(new) = new {
        *expr = new;
    }

    for child in expr.children_mut() {
        short_circuit_booleans(child);
    }
}

/// Parse and perform type inference on an expression.
#[cfg(test)]
fn typed_expression(code: &str) -> Expr {
    let mut e = parse_expr(code).unwrap();
    assert!(e.infer_types().is_ok());
    e
}

#[test]
fn simple_and() {
    let mut e = typed_expression("|x:i32| (x > 5 && x < 10)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if (x > 5, x < 10, false)");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn simple_or() {
    let mut e = typed_expression("|x:i32| (x > 5 || x < 10)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if (x > 5, true, x < 10)");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn compound_and() {
    let mut e = typed_expression("|x:i32| x > 5 && x < 10 && x == 7");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if ( if (x > 5, x < 10, false),  x == 7, false)");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn compound_or() {
    let mut e = typed_expression("|x:i32| (x > 5 || x < 10 || x == 7)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if ( if (x > 5, true, x < 10), true, x == 7 )");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn complex_and_or() {
    let mut e = typed_expression("|x:i32| (x > 5 || x < 10) && (x == 7 || x == 2)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression(
        "|x:i32|
                    if(
                        if (x > 5, true, x < 10),
                        if (x == 7, true, x == 2),
                        false
                    )",
    );
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn complex_and_or_2() {
    let mut e = typed_expression("|x:i32| (x > 5 && x < 10) || (x == 15)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression(
        "|x:i32|
                    if(
                        if (x > 5, x < 10, false),
                        true,
                        (x == 15)
                    )",
    );
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn predicated_if() {
    let mut e =
        typed_expression("|x:i32| @(predicate:true) if (x > 5 && x < 10, x > 3 || x < 4, false)");
    short_circuit_booleans(&mut e);

    // Since the If is predicated, the condition should not be short-circuited.
    let expect = &typed_expression(
        "|x:i32|
                    @(predicate:true)
                    if (
                        x > 5 && x < 10,
                        if (x > 3, true, x < 4),
                        false
                    )",
    );
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}
<commit_msg>Remove clones from short circuit booleans pass (#467)<commit_after>use crate::ast::ExprKind::*;
use crate::ast::*;

use crate::optimizer::transforms::vectorizer::ShouldPredicate;

#[cfg(test)]
use crate::tests::*;

pub fn short_circuit_booleans(expr: &mut Expr) {
    // For If statements annotated as predicated, do not apply the transform on the condition,
    // since it removes the predication opportunity.
    let should_predicate = expr.should_predicate();
    let applied = match expr.kind {
        If {
            ref mut on_true,
            ref mut on_false,
            ..
        } if should_predicate => {
            short_circuit_booleans(on_true);
            short_circuit_booleans(on_false);
            true
        }
        _ => false,
    };

    if applied {
        return;
    }

    let replaced = match expr.kind {
        BinOp {
            ref mut kind,
            ref mut left,
            ref mut right,
        } if *kind == BinOpKind::LogicalAnd => Some(
            Expr::new_if(
                *left.take(),
                *right.take(),
                Expr::new_literal(LiteralKind::BoolLiteral(false)).unwrap(),
            )
            .unwrap(),
        ),
        BinOp {
            ref mut kind,
            ref mut left,
            ref mut right,
        } if *kind == BinOpKind::LogicalOr => Some(
            Expr::new_if(
                *left.take(),
                Expr::new_literal(LiteralKind::BoolLiteral(true)).unwrap(),
                *right.take(),
            )
            .unwrap(),
        ),
        _ => None,
    };

    if let Some(replaced) = replaced {
        *expr = replaced;
    }

    for child in expr.children_mut() {
        short_circuit_booleans(child);
    }
}

/// Parse and perform type inference on an expression.
#[cfg(test)]
fn typed_expression(code: &str) -> Expr {
    let mut e = parse_expr(code).unwrap();
    assert!(e.infer_types().is_ok());
    e
}

#[test]
fn simple_and() {
    let mut e = typed_expression("|x:i32| (x > 5 && x < 10)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if (x > 5, x < 10, false)");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn simple_or() {
    let mut e = typed_expression("|x:i32| (x > 5 || x < 10)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if (x > 5, true, x < 10)");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn compound_and() {
    let mut e = typed_expression("|x:i32| x > 5 && x < 10 && x == 7");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if ( if (x > 5, x < 10, false),  x == 7, false)");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn compound_or() {
    let mut e = typed_expression("|x:i32| (x > 5 || x < 10 || x == 7)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression("|x:i32| if ( if (x > 5, true, x < 10), true, x == 7 )");
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn complex_and_or() {
    let mut e = typed_expression("|x:i32| (x > 5 || x < 10) && (x == 7 || x == 2)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression(
        "|x:i32|
                    if(
                        if (x > 5, true, x < 10),
                        if (x == 7, true, x == 2),
                        false
                    )",
    );
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn complex_and_or_2() {
    let mut e = typed_expression("|x:i32| (x > 5 && x < 10) || (x == 15)");
    short_circuit_booleans(&mut e);
    let expect = &typed_expression(
        "|x:i32|
                    if(
                        if (x > 5, x < 10, false),
                        true,
                        (x == 15)
                    )",
    );
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}

#[test]
fn predicated_if() {
    let mut e =
        typed_expression("|x:i32| @(predicate:true) if (x > 5 && x < 10, x > 3 || x < 4, false)");
    short_circuit_booleans(&mut e);

    // Since the If is predicated, the condition should not be short-circuited.
    let expect = &typed_expression(
        "|x:i32|
                    @(predicate:true)
                    if (
                        x > 5 && x < 10,
                        if (x > 3, true, x < 4),
                        false
                    )",
    );
    assert!(e.compare_ignoring_symbols(expect).unwrap());
}
<|endoftext|>