<commit_before>use std::cell::{Cell, RefCell};
use std::io::{self, ErrorKind};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::mpsc;

use mio;
use slab::Slab;
use futures::{Future, Tokens, Wake};

use slot::{self, Slot};

pub type Source = Arc<mio::Evented + Send + Sync>;

static NEXT_LOOP_ID: AtomicUsize = ATOMIC_USIZE_INIT;
scoped_thread_local!(static CURRENT_LOOP: Loop);

const SLAB_CAPACITY: usize = 1024 * 64;

pub struct Loop {
    id: usize,
    active: Cell<bool>,
    io: RefCell<mio::Poll>,
    tx: mio::channel::Sender<Message>,
    rx: mio::channel::Receiver<Message>,
    dispatch: RefCell<Slab<Scheduled, usize>>,
}

/// Handle to an event loop, used to construct I/O objects, send messages, and
/// otherwise interact indirectly with the event loop itself.
///
/// Handles can be cloned, and when cloned they will still refer to the
/// same underlying event loop.
#[derive(Clone)]
pub struct LoopHandle {
    id: usize,
    tx: mio::channel::Sender<Message>,
}

#[derive(Copy, Clone)]
pub enum Direction {
    Read,
    Write,
}

struct Scheduled {
    source: Source,
    reader: Option<Arc<Wake>>,
    writer: Option<Arc<Wake>>,
}

impl Scheduled {
    fn waiter_for(&mut self, dir: Direction) -> &mut Option<Arc<Wake>> {
        match dir {
            Direction::Read => &mut self.reader,
            Direction::Write => &mut self.writer,
        }
    }

    fn event_set(&self) -> mio::EventSet {
        let mut set = mio::EventSet::none();
        if self.reader.is_some() {
            set = set | mio::EventSet::readable()
        }
        if self.writer.is_some() {
            set = set | mio::EventSet::writable()
        }
        set
    }
}

enum Message {
    AddSource(Source, Arc<Slot<io::Result<usize>>>),
    DropSource(usize),
    Schedule(usize, Direction, Arc<Wake>),
    Deschedule(usize, Direction),
    Shutdown,
}

fn register(poll: &mut mio::Poll, token: usize, sched: &Scheduled) {
    // TODO: handle error
    poll.register(&*sched.source,
                  mio::Token(token),
                  mio::EventSet::none(),
                  mio::PollOpt::level())
        .unwrap();
}

fn reregister(poll: &mut mio::Poll, token: usize, sched: &Scheduled) {
    // TODO: handle error
    poll.reregister(&*sched.source,
                    mio::Token(token),
                    sched.event_set(),
                    mio::PollOpt::edge() | mio::PollOpt::oneshot())
        .unwrap();
}

fn deregister(poll: &mut mio::Poll, sched: &Scheduled) {
    // TODO: handle error
    poll.deregister(&*sched.source).unwrap();
}

impl Loop {
    pub fn new() -> io::Result<Loop> {
        let (tx, rx) = mio::channel::from_std_channel(mpsc::channel());
        let io = try!(mio::Poll::new());
        try!(io.register(&rx,
                         mio::Token(0),
                         mio::EventSet::readable(),
                         mio::PollOpt::edge()));
        Ok(Loop {
            id: NEXT_LOOP_ID.fetch_add(1, Ordering::Relaxed),
            active: Cell::new(true),
            io: RefCell::new(io),
            tx: tx,
            rx: rx,
            dispatch: RefCell::new(Slab::new_starting_at(1, SLAB_CAPACITY)),
        })
    }

    pub fn handle(&self) -> LoopHandle {
        LoopHandle {
            id: self.id,
            tx: self.tx.clone(),
        }
    }

    pub fn run<F: Future>(self, f: F) -> Result<F::Item, F::Error> {
        let (tx_res, rx_res) = mpsc::channel();
        let handle = self.handle();
        f.then(move |res| {
            handle.shutdown();
            tx_res.send(res)
        }).forget();

        while self.active.get() {
            let amt;
            // On Linux, Poll::poll is epoll_wait, which may return EINTR if a
            // ptracer attaches. This retry loop prevents crashing when
            // attaching strace, or similar.
            loop {
                match self.io.borrow_mut().poll(None) {
                    Ok(a) => {
                        amt = a;
                        break;
                    }
                    Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                    err @ Err(_) => {
                        err.unwrap();
                    }
                }
            }

            // TODO: coalesce token sets for a given Wake?
            for i in 0..amt {
                let event = self.io.borrow_mut().events().get(i).unwrap();
                let token = event.token().as_usize();
                if token == 0 {
                    self.consume_queue();
                } else {
                    let mut reader = None;
                    let mut writer = None;

                    if let Some(sched) = self.dispatch.borrow_mut().get_mut(token) {
                        if event.kind().is_readable() {
                            reader = sched.reader.take();
                        }

                        if event.kind().is_writable() {
                            writer = sched.writer.take();
                        }
                    }

                    CURRENT_LOOP.set(&self, || {
                        if let Some(reader_wake) = reader.take() {
                            reader_wake.wake(&Tokens::from_usize(token));
                        }
                        if let Some(writer_wake) = writer.take() {
                            writer_wake.wake(&Tokens::from_usize(token));
                        }
                    });

                    // For now, always reregister, to deal with the fact that
                    // combined oneshot + read|write requires rearming even if
                    // only one side fired.
                    //
                    // TODO: optimize this
                    if let Some(sched) = self.dispatch.borrow().get(token) {
                        reregister(&mut self.io.borrow_mut(), token, &sched);
                    }
                }
            }
        }

        rx_res.recv().unwrap()
    }

    fn add_source(&self, source: Source) -> io::Result<usize> {
        let sched = Scheduled {
            source: source,
            reader: None,
            writer: None,
        };
        let mut dispatch = self.dispatch.borrow_mut();
        // TODO: handle out of space
        let entry = dispatch.vacant_entry().unwrap();
        register(&mut self.io.borrow_mut(), entry.index(), &sched);
        Ok(entry.insert(sched).index())
    }

    fn drop_source(&self, token: usize) {
        let sched = self.dispatch.borrow_mut().remove(token).unwrap();
        deregister(&mut self.io.borrow_mut(), &sched);
    }

    fn schedule(&self, token: usize, dir: Direction, wake: Arc<Wake>) {
        let mut dispatch = self.dispatch.borrow_mut();
        let sched = dispatch.get_mut(token).unwrap();
        *sched.waiter_for(dir) = Some(wake);
        reregister(&mut self.io.borrow_mut(), token, sched);
    }

    fn deschedule(&self, token: usize, dir: Direction) {
        let mut dispatch = self.dispatch.borrow_mut();
        let sched = dispatch.get_mut(token).unwrap();
        *sched.waiter_for(dir) = None;
        reregister(&mut self.io.borrow_mut(), token, sched);
    }

    fn consume_queue(&self) {
        while let Ok(msg) = self.rx.try_recv() {
            self.notify(msg);
        }
    }

    fn notify(&self, msg: Message) {
        match msg {
            Message::AddSource(source, slot) => {
                // This unwrap() should always be ok as we're the only producer
                slot.try_produce(self.add_source(source))
                    .ok().expect("interference with try_produce");
            }
            Message::DropSource(tok) => self.drop_source(tok),
            Message::Schedule(tok, dir, wake) => self.schedule(tok, dir, wake),
            Message::Deschedule(tok, dir) => self.deschedule(tok, dir),
            Message::Shutdown => self.active.set(false),
        }
    }
}

impl LoopHandle {
    fn send(&self, msg: Message) {
        self.with_loop(|lp| {
            match lp {
                Some(lp) => {
                    // Need to execute all existing requests first, to ensure
                    // that our message is processed "in order"
                    lp.consume_queue();
                    lp.notify(msg);
                }
                None => {
                    // TODO: handle failure
                    self.tx
                        .send(msg)
                        .map_err(|_| ())
                        .expect("failed to send register message")
                }
            }
        })
    }

    fn with_loop<F, R>(&self, f: F) -> R
        where F: FnOnce(Option<&Loop>) -> R
    {
        if CURRENT_LOOP.is_set() {
            CURRENT_LOOP.with(|lp| {
                if lp.id == self.id {
                    f(Some(lp))
                } else {
                    f(None)
                }
            })
        } else {
            f(None)
        }
    }

    /// Add a new source to an event loop, returning a future which will resolve
    /// to the token that can be used to identify this source.
    ///
    /// When a new I/O object is created it needs to be communicated to the
    /// event loop to ensure that it's registered and ready to receive
    /// notifications. The event loop with then respond with a unique token that
    /// this handle can be identified with (the resolved value of the returned
    /// future).
    ///
    /// This token is then passed in turn to each of the methods below to
    /// interact with notifications on the I/O object itself.
    pub fn add_source(&self, source: Source) -> AddSource {
        AddSource {
            loop_handle: self.clone(),
            source: Some(source),
            result: None,
        }
    }

    fn add_source_(&self, source: Source, slot: Arc<Slot<io::Result<usize>>>) {
        self.send(Message::AddSource(source, slot));
    }

    /// Begin listening for events on an event loop.
    ///
    /// Once an I/O object has been registered with the event loop through the
    /// `add_source` method, this method can be used with the assigned token to
    /// begin awaiting notifications.
    ///
    /// The `dir` argument indicates how the I/O object is expected to be
    /// awaited on (either readable or writable) and the `wake` callback will be
    /// invoked. Note that one the `wake` callback is invoked once it will not
    /// be invoked again, it must be re-`schedule`d to continue receiving
    /// notifications.
    pub fn schedule(&self, tok: usize, dir: Direction, wake: Arc<Wake>) {
        self.send(Message::Schedule(tok, dir, wake));
    }

    /// Stop listening for events on an event loop.
    ///
    /// Once a callback has been scheduled with the `schedule` method, it can be
    /// unregistered from the event loop with this method. This method does not
    /// guarantee that the callback will not be invoked if it hasn't already,
    /// but a best effort will be made to ensure it is not called.
    pub fn deschedule(&self, tok: usize, dir: Direction) {
        self.send(Message::Deschedule(tok, dir));
    }

    /// Unregister all information associated with a token on an event loop,
    /// deallocating all internal resources assigned to the given token.
    ///
    /// This method should be called whenever a source of events is being
    /// destroyed. This will ensure that the event loop can reuse `tok` for
    /// another I/O object if necessary and also remove it from any poll
    /// notifications and callbacks.
    ///
    /// Note that wake callbacks may still be invoked after this method is
    /// called as it may take some time for the message to drop a source to
    /// reach the event loop. Despite this fact, this method will attempt to
    /// ensure that the callbacks are **not** invoked, so pending scheduled
    /// callbacks cannot be relied upon to get called.
    pub fn drop_source(&self, tok: usize) {
        self.send(Message::DropSource(tok));
    }

    pub fn shutdown(&self) {
        self.send(Message::Shutdown);
    }
}

const ADD_SOURCE_TOKEN: usize = 0;

/// A future which will resolve a unique `tok` token for an I/O object.
///
/// Created through the `LoopHandle::add_source` method, this future can also
/// resolve to an error if there's an issue communicating with the event loop.
pub struct AddSource {
    loop_handle: LoopHandle,
    source: Option<Source>,
    result: Option<(Arc<Slot<io::Result<usize>>>, slot::Token)>,
}

impl Future for AddSource {
    type Item = usize;
    type Error = io::Error;

    fn poll(&mut self, tokens: &Tokens) -> Option<Result<usize, io::Error>> {
        match self.result {
            Some((ref result, _)) => {
                if tokens.may_contain(&Tokens::from_usize(ADD_SOURCE_TOKEN)) {
                    result.try_consume().ok()
                } else {
                    None
                }
            }
            None => {
                let source = &mut self.source;
                self.loop_handle.with_loop(|lp| {
                    lp.map(|lp| {
                        lp.add_source(source.take().unwrap())
                    })
                })
            }
        }
    }

    fn schedule(&mut self, wake: Arc<Wake>) {
        if let Some((ref result, ref mut token)) = self.result {
            result.cancel(*token);
            *token = result.on_full(move |_| {
                wake.wake(&Tokens::from_usize(ADD_SOURCE_TOKEN));
            });
            return
        }

        let result = Arc::new(Slot::new(None));
        let token = result.on_full(move |_| {
            wake.wake(&Tokens::from_usize(ADD_SOURCE_TOKEN));
        });
        self.result = Some((result.clone(), token));
        self.loop_handle.add_source_(self.source.take().unwrap(), result);
    }
}
<commit_msg>Propagate errors in register()<commit_after>use std::cell::{Cell, RefCell};
use std::io::{self, ErrorKind};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::mpsc;

use mio;
use slab::Slab;
use futures::{Future, Tokens, Wake};

use slot::{self, Slot};

pub type Source = Arc<mio::Evented + Send + Sync>;

static NEXT_LOOP_ID: AtomicUsize = ATOMIC_USIZE_INIT;
scoped_thread_local!(static CURRENT_LOOP: Loop);

const SLAB_CAPACITY: usize = 1024 * 64;

pub struct Loop {
    id: usize,
    active: Cell<bool>,
    io: RefCell<mio::Poll>,
    tx: mio::channel::Sender<Message>,
    rx: mio::channel::Receiver<Message>,
    dispatch: RefCell<Slab<Scheduled, usize>>,
}

/// Handle to an event loop, used to construct I/O objects, send messages, and
/// otherwise interact indirectly with the event loop itself.
///
/// Handles can be cloned, and when cloned they will still refer to the
/// same underlying event loop.
#[derive(Clone)]
pub struct LoopHandle {
    id: usize,
    tx: mio::channel::Sender<Message>,
}

#[derive(Copy, Clone)]
pub enum Direction {
    Read,
    Write,
}

struct Scheduled {
    source: Source,
    reader: Option<Arc<Wake>>,
    writer: Option<Arc<Wake>>,
}

impl Scheduled {
    fn waiter_for(&mut self, dir: Direction) -> &mut Option<Arc<Wake>> {
        match dir {
            Direction::Read => &mut self.reader,
            Direction::Write => &mut self.writer,
        }
    }

    fn event_set(&self) -> mio::EventSet {
        let mut set = mio::EventSet::none();
        if self.reader.is_some() {
            set = set | mio::EventSet::readable()
        }
        if self.writer.is_some() {
            set = set | mio::EventSet::writable()
        }
        set
    }
}

enum Message {
    AddSource(Source, Arc<Slot<io::Result<usize>>>),
    DropSource(usize),
    Schedule(usize, Direction, Arc<Wake>),
    Deschedule(usize, Direction),
    Shutdown,
}

fn register(poll: &mut mio::Poll,
            token: usize,
            sched: &Scheduled) -> io::Result<()> {
    poll.register(&*sched.source,
                  mio::Token(token),
                  mio::EventSet::none(),
                  mio::PollOpt::level())
}

fn reregister(poll: &mut mio::Poll, token: usize, sched: &Scheduled) {
    // TODO: handle error
    poll.reregister(&*sched.source,
                    mio::Token(token),
                    sched.event_set(),
                    mio::PollOpt::edge() | mio::PollOpt::oneshot())
        .unwrap();
}

fn deregister(poll: &mut mio::Poll, sched: &Scheduled) {
    // TODO: handle error
    poll.deregister(&*sched.source).unwrap();
}

impl Loop {
    pub fn new() -> io::Result<Loop> {
        let (tx, rx) = mio::channel::from_std_channel(mpsc::channel());
        let io = try!(mio::Poll::new());
        try!(io.register(&rx,
                         mio::Token(0),
                         mio::EventSet::readable(),
                         mio::PollOpt::edge()));
        Ok(Loop {
            id: NEXT_LOOP_ID.fetch_add(1, Ordering::Relaxed),
            active: Cell::new(true),
            io: RefCell::new(io),
            tx: tx,
            rx: rx,
            dispatch: RefCell::new(Slab::new_starting_at(1, SLAB_CAPACITY)),
        })
    }

    pub fn handle(&self) -> LoopHandle {
        LoopHandle {
            id: self.id,
            tx: self.tx.clone(),
        }
    }

    pub fn run<F: Future>(self, f: F) -> Result<F::Item, F::Error> {
        let (tx_res, rx_res) = mpsc::channel();
        let handle = self.handle();
        f.then(move |res| {
            handle.shutdown();
            tx_res.send(res)
        }).forget();

        while self.active.get() {
            let amt;
            // On Linux, Poll::poll is epoll_wait, which may return EINTR if a
            // ptracer attaches. This retry loop prevents crashing when
            // attaching strace, or similar.
            loop {
                match self.io.borrow_mut().poll(None) {
                    Ok(a) => {
                        amt = a;
                        break;
                    }
                    Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                    err @ Err(_) => {
                        err.unwrap();
                    }
                }
            }

            // TODO: coalesce token sets for a given Wake?
            for i in 0..amt {
                let event = self.io.borrow_mut().events().get(i).unwrap();
                let token = event.token().as_usize();
                if token == 0 {
                    self.consume_queue();
                } else {
                    let mut reader = None;
                    let mut writer = None;

                    if let Some(sched) = self.dispatch.borrow_mut().get_mut(token) {
                        if event.kind().is_readable() {
                            reader = sched.reader.take();
                        }

                        if event.kind().is_writable() {
                            writer = sched.writer.take();
                        }
                    }

                    CURRENT_LOOP.set(&self, || {
                        if let Some(reader_wake) = reader.take() {
                            reader_wake.wake(&Tokens::from_usize(token));
                        }
                        if let Some(writer_wake) = writer.take() {
                            writer_wake.wake(&Tokens::from_usize(token));
                        }
                    });

                    // For now, always reregister, to deal with the fact that
                    // combined oneshot + read|write requires rearming even if
                    // only one side fired.
                    //
                    // TODO: optimize this
                    if let Some(sched) = self.dispatch.borrow().get(token) {
                        reregister(&mut self.io.borrow_mut(), token, &sched);
                    }
                }
            }
        }

        rx_res.recv().unwrap()
    }

    fn add_source(&self, source: Source) -> io::Result<usize> {
        let sched = Scheduled {
            source: source,
            reader: None,
            writer: None,
        };
        let mut dispatch = self.dispatch.borrow_mut();
        // TODO: handle out of space
        let entry = dispatch.vacant_entry().unwrap();
        try!(register(&mut self.io.borrow_mut(), entry.index(), &sched));
        Ok(entry.insert(sched).index())
    }

    fn drop_source(&self, token: usize) {
        let sched = self.dispatch.borrow_mut().remove(token).unwrap();
        deregister(&mut self.io.borrow_mut(), &sched);
    }

    fn schedule(&self, token: usize, dir: Direction, wake: Arc<Wake>) {
        let mut dispatch = self.dispatch.borrow_mut();
        let sched = dispatch.get_mut(token).unwrap();
        *sched.waiter_for(dir) = Some(wake);
        reregister(&mut self.io.borrow_mut(), token, sched);
    }

    fn deschedule(&self, token: usize, dir: Direction) {
        let mut dispatch = self.dispatch.borrow_mut();
        let sched = dispatch.get_mut(token).unwrap();
        *sched.waiter_for(dir) = None;
        reregister(&mut self.io.borrow_mut(), token, sched);
    }

    fn consume_queue(&self) {
        while let Ok(msg) = self.rx.try_recv() {
            self.notify(msg);
        }
    }

    fn notify(&self, msg: Message) {
        match msg {
            Message::AddSource(source, slot) => {
                // This unwrap() should always be ok as we're the only producer
                slot.try_produce(self.add_source(source))
                    .ok().expect("interference with try_produce");
            }
            Message::DropSource(tok) => self.drop_source(tok),
            Message::Schedule(tok, dir, wake) => self.schedule(tok, dir, wake),
            Message::Deschedule(tok, dir) => self.deschedule(tok, dir),
            Message::Shutdown => self.active.set(false),
        }
    }
}

impl LoopHandle {
    fn send(&self, msg: Message) {
        self.with_loop(|lp| {
            match lp {
                Some(lp) => {
                    // Need to execute all existing requests first, to ensure
                    // that our message is processed "in order"
                    lp.consume_queue();
                    lp.notify(msg);
                }
                None => {
                    // TODO: handle failure
                    self.tx
                        .send(msg)
                        .map_err(|_| ())
                        .expect("failed to send register message")
                }
            }
        })
    }

    fn with_loop<F, R>(&self, f: F) -> R
        where F: FnOnce(Option<&Loop>) -> R
    {
        if CURRENT_LOOP.is_set() {
            CURRENT_LOOP.with(|lp| {
                if lp.id == self.id {
                    f(Some(lp))
                } else {
                    f(None)
                }
            })
        } else {
            f(None)
        }
    }

    /// Add a new source to an event loop, returning a future which will resolve
    /// to the token that can be used to identify this source.
    ///
    /// When a new I/O object is created it needs to be communicated to the
    /// event loop to ensure that it's registered and ready to receive
    /// notifications. The event loop with then respond with a unique token that
    /// this handle can be identified with (the resolved value of the returned
    /// future).
    ///
    /// This token is then passed in turn to each of the methods below to
    /// interact with notifications on the I/O object itself.
    pub fn add_source(&self, source: Source) -> AddSource {
        AddSource {
            loop_handle: self.clone(),
            source: Some(source),
            result: None,
        }
    }

    fn add_source_(&self, source: Source, slot: Arc<Slot<io::Result<usize>>>) {
        self.send(Message::AddSource(source, slot));
    }

    /// Begin listening for events on an event loop.
    ///
    /// Once an I/O object has been registered with the event loop through the
    /// `add_source` method, this method can be used with the assigned token to
    /// begin awaiting notifications.
    ///
    /// The `dir` argument indicates how the I/O object is expected to be
    /// awaited on (either readable or writable) and the `wake` callback will be
    /// invoked. Note that one the `wake` callback is invoked once it will not
    /// be invoked again, it must be re-`schedule`d to continue receiving
    /// notifications.
    pub fn schedule(&self, tok: usize, dir: Direction, wake: Arc<Wake>) {
        self.send(Message::Schedule(tok, dir, wake));
    }

    /// Stop listening for events on an event loop.
    ///
    /// Once a callback has been scheduled with the `schedule` method, it can be
    /// unregistered from the event loop with this method. This method does not
    /// guarantee that the callback will not be invoked if it hasn't already,
    /// but a best effort will be made to ensure it is not called.
    pub fn deschedule(&self, tok: usize, dir: Direction) {
        self.send(Message::Deschedule(tok, dir));
    }

    /// Unregister all information associated with a token on an event loop,
    /// deallocating all internal resources assigned to the given token.
    ///
    /// This method should be called whenever a source of events is being
    /// destroyed. This will ensure that the event loop can reuse `tok` for
    /// another I/O object if necessary and also remove it from any poll
    /// notifications and callbacks.
    ///
    /// Note that wake callbacks may still be invoked after this method is
    /// called as it may take some time for the message to drop a source to
    /// reach the event loop. Despite this fact, this method will attempt to
    /// ensure that the callbacks are **not** invoked, so pending scheduled
    /// callbacks cannot be relied upon to get called.
    pub fn drop_source(&self, tok: usize) {
        self.send(Message::DropSource(tok));
    }

    pub fn shutdown(&self) {
        self.send(Message::Shutdown);
    }
}

const ADD_SOURCE_TOKEN: usize = 0;

/// A future which will resolve a unique `tok` token for an I/O object.
///
/// Created through the `LoopHandle::add_source` method, this future can also
/// resolve to an error if there's an issue communicating with the event loop.
pub struct AddSource {
    loop_handle: LoopHandle,
    source: Option<Source>,
    result: Option<(Arc<Slot<io::Result<usize>>>, slot::Token)>,
}

impl Future for AddSource {
    type Item = usize;
    type Error = io::Error;

    fn poll(&mut self, tokens: &Tokens) -> Option<Result<usize, io::Error>> {
        match self.result {
            Some((ref result, _)) => {
                if tokens.may_contain(&Tokens::from_usize(ADD_SOURCE_TOKEN)) {
                    result.try_consume().ok()
                } else {
                    None
                }
            }
            None => {
                let source = &mut self.source;
                self.loop_handle.with_loop(|lp| {
                    lp.map(|lp| {
                        lp.add_source(source.take().unwrap())
                    })
                })
            }
        }
    }

    fn schedule(&mut self, wake: Arc<Wake>) {
        if let Some((ref result, ref mut token)) = self.result {
            result.cancel(*token);
            *token = result.on_full(move |_| {
                wake.wake(&Tokens::from_usize(ADD_SOURCE_TOKEN));
            });
            return
        }

        let result = Arc::new(Slot::new(None));
        let token = result.on_full(move |_| {
            wake.wake(&Tokens::from_usize(ADD_SOURCE_TOKEN));
        });
        self.result = Some((result.clone(), token));
        self.loop_handle.add_source_(self.source.take().unwrap(), result);
    }
}
<|endoftext|>


<commit_before>//! The tags.

#![allow(non_upper_case_globals)]

use node::Attributes;
use parser::{Error, Reader, Result};

/// A tag.
#[derive(Clone)]
pub struct Tag<'l>(pub &'l str, pub Type, pub Attributes);

/// A [type][1] of a tag.
///
/// [1]: http://www.w3.org/TR/REC-xml/#sec-starttags
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Type {
    /// A start tag.
    Start,
    /// An end tag.
    End,
    /// An empty tag.
    Empty,
}

struct Parser<'l> {
    reader: Reader<'l>,
}

impl<'l> Tag<'l> {
    /// Parse a tag.
    #[inline]
    pub fn parse(content: &'l str) -> Result<Tag<'l>> {
        Parser::new(content).process()
    }
}

macro_rules! raise(
    ($parser:expr, $($argument:tt)*) => (
        return Err(Error::new($parser.reader.position(), format!($($argument)*)));
    );
);

impl<'l> Parser<'l> {
    #[inline]
    fn new(content: &'l str) -> Self {
        Parser {
            reader: Reader::new(content),
        }
    }

    fn process(&mut self) -> Result<Tag<'l>> {
        if self.reader.consume_char('/') {
            self.read_end_tag()
        } else {
            self.read_start_or_empty_tag()
        }
    }

    fn read_attribute(&mut self) -> Result<Option<(String, String)>> {
        let attribute = self.reader
            .capture(|reader| reader.consume_attribute())
            .and_then(|attribute| Some(String::from(attribute)));
        match attribute {
            Some(attribute) => {
                let k = (&attribute).find('=').unwrap();
                let name = (&attribute[0..k]).trim_right();
                let value = (&attribute[(k + 1)..]).trim_left();
                let value = &value[1..(value.len() - 1)];
                Ok(Some((String::from(name), String::from(value))))
            }
            _ => Ok(None),
        }
    }

    fn read_attributes(&mut self) -> Result<Attributes> {
        let mut attributes = Attributes::new();
        loop {
            self.reader.consume_whitespace();
            match try!(self.read_attribute()) {
                Some((name, value)) => {
                    attributes.insert(name, value.into());
                }
                _ => break,
            }
        }
        Ok(attributes)
    }

    fn read_end_tag(&mut self) -> Result<Tag<'l>> {
        let name = try!(self.read_name());
        self.reader.consume_whitespace();
        if !self.reader.is_done() {
            raise!(self, "found an end tag with excessive data");
        }
        Ok(Tag(name, Type::End, Attributes::default()))
    }

    fn read_name(&mut self) -> Result<&'l str> {
        let name = self.reader.capture(|reader| reader.consume_name());
        match name {
            Some(name) => Ok(name),
            _ => raise!(self, "expected a name"),
        }
    }

    fn read_start_or_empty_tag(&mut self) -> Result<Tag<'l>> {
        let name = try!(self.read_name());
        let attributes = try!(self.read_attributes());
        self.reader.consume_whitespace();
        let tail = self.reader.capture(|reader| reader.consume_all());
        match tail {
            Some("/") => Ok(Tag(name, Type::Empty, attributes)),
            Some(_) => raise!(self, "found an unexpected ending of a tag"),
            _ => Ok(Tag(name, Type::Start, attributes)),
        }
    }
}

macro_rules! implement {
    ($($const_name:ident: $tag_name:expr,)*) => ($(
        #[doc = $tag_name]
        pub const $const_name: &'static str = $tag_name;
    )*);
}

implement! {
    Animate: "animate",
    AnimateColor: "animateColor",
    AnimateMotion: "animateMotion",
    AnimateTransform: "animateTransform",
    Circle: "circle",
    ClipPath: "clipPath",
    Definitions: "defs",
    Description: "desc",
    Ellipse: "ellipse",
    Filter: "filter",
    Group: "g",
    Image: "image",
    Line: "line",
    LinearGradient: "linearGradient",
    Link: "a",
    Marker: "marker",
    Mask: "mask",
    MotionPath: "mpath",
    Path: "path",
    Pattern: "pattern",
    Polygon: "polygon",
    Polyline: "polyline",
    RadialGradient: "radialGradient",
    Rectangle: "rect",
    Script: "script",
    Stop: "stop",
    Style: "style",
    SVG: "svg",
    Symbol: "symbol",
    Text: "text",
    TextPath: "textPath",
    Title: "title",
    Use: "use",
}

#[cfg(test)]
mod tests {
    use super::{Parser, Tag, Type};

    #[test]
    fn parser_process() {
        macro_rules! test(
            ($content:expr, $kind:ident) => ({
                let mut parser = Parser::new($content);
                match parser.process().unwrap() {
                    Tag("foo", Type::$kind, _) => {}
                    _ => unreachable!(),
                }
            });
        );

        test!("foo", Start);
        test!("foo ", Start);
        test!("/foo", End);
        test!("/foo ", End);
        test!("foo/", Empty);
        test!("foo /", Empty);
    }

    #[test]
    fn parser_read_attribute() {
        macro_rules! test(
            ($content:expr, $name:expr, $value:expr) => ({
                let mut parser = Parser::new($content);
                let (name, value) = parser.read_attribute().unwrap().unwrap();
                assert_eq!(&*name, $name);
                assert_eq!(&*value, $value);
            });
        );

        test!("foo=''", "foo", "");
        test!("foo='bar'", "foo", "bar");
        test!("foo =\"bar\"", "foo", "bar");
        test!("foo= \"bar\"", "foo", "bar");
        test!("foo\t=\n'bar'  ", "foo", "bar");
    }
}
<commit_msg>Add Debug to Tag<commit_after>//! The tags.

#![allow(non_upper_case_globals)]

use node::Attributes;
use parser::{Error, Reader, Result};

/// A tag.
#[derive(Clone, Debug)]
pub struct Tag<'l>(pub &'l str, pub Type, pub Attributes);

/// A [type][1] of a tag.
///
/// [1]: http://www.w3.org/TR/REC-xml/#sec-starttags
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Type {
    /// A start tag.
    Start,
    /// An end tag.
    End,
    /// An empty tag.
    Empty,
}

struct Parser<'l> {
    reader: Reader<'l>,
}

impl<'l> Tag<'l> {
    /// Parse a tag.
    #[inline]
    pub fn parse(content: &'l str) -> Result<Tag<'l>> {
        Parser::new(content).process()
    }
}

macro_rules! raise(
    ($parser:expr, $($argument:tt)*) => (
        return Err(Error::new($parser.reader.position(), format!($($argument)*)));
    );
);

impl<'l> Parser<'l> {
    #[inline]
    fn new(content: &'l str) -> Self {
        Parser {
            reader: Reader::new(content),
        }
    }

    fn process(&mut self) -> Result<Tag<'l>> {
        if self.reader.consume_char('/') {
            self.read_end_tag()
        } else {
            self.read_start_or_empty_tag()
        }
    }

    fn read_attribute(&mut self) -> Result<Option<(String, String)>> {
        let attribute = self.reader
            .capture(|reader| reader.consume_attribute())
            .and_then(|attribute| Some(String::from(attribute)));
        match attribute {
            Some(attribute) => {
                let k = (&attribute).find('=').unwrap();
                let name = (&attribute[0..k]).trim_right();
                let value = (&attribute[(k + 1)..]).trim_left();
                let value = &value[1..(value.len() - 1)];
                Ok(Some((String::from(name), String::from(value))))
            }
            _ => Ok(None),
        }
    }

    fn read_attributes(&mut self) -> Result<Attributes> {
        let mut attributes = Attributes::new();
        loop {
            self.reader.consume_whitespace();
            match try!(self.read_attribute()) {
                Some((name, value)) => {
                    attributes.insert(name, value.into());
                }
                _ => break,
            }
        }
        Ok(attributes)
    }

    fn read_end_tag(&mut self) -> Result<Tag<'l>> {
        let name = try!(self.read_name());
        self.reader.consume_whitespace();
        if !self.reader.is_done() {
            raise!(self, "found an end tag with excessive data");
        }
        Ok(Tag(name, Type::End, Attributes::default()))
    }

    fn read_name(&mut self) -> Result<&'l str> {
        let name = self.reader.capture(|reader| reader.consume_name());
        match name {
            Some(name) => Ok(name),
            _ => raise!(self, "expected a name"),
        }
    }

    fn read_start_or_empty_tag(&mut self) -> Result<Tag<'l>> {
        let name = try!(self.read_name());
        let attributes = try!(self.read_attributes());
        self.reader.consume_whitespace();
        let tail = self.reader.capture(|reader| reader.consume_all());
        match tail {
            Some("/") => Ok(Tag(name, Type::Empty, attributes)),
            Some(_) => raise!(self, "found an unexpected ending of a tag"),
            _ => Ok(Tag(name, Type::Start, attributes)),
        }
    }
}

macro_rules! implement {
    ($($const_name:ident: $tag_name:expr,)*) => ($(
        #[doc = $tag_name]
        pub const $const_name: &'static str = $tag_name;
    )*);
}

implement! {
    Animate: "animate",
    AnimateColor: "animateColor",
    AnimateMotion: "animateMotion",
    AnimateTransform: "animateTransform",
    Circle: "circle",
    ClipPath: "clipPath",
    Definitions: "defs",
    Description: "desc",
    Ellipse: "ellipse",
    Filter: "filter",
    Group: "g",
    Image: "image",
    Line: "line",
    LinearGradient: "linearGradient",
    Link: "a",
    Marker: "marker",
    Mask: "mask",
    MotionPath: "mpath",
    Path: "path",
    Pattern: "pattern",
    Polygon: "polygon",
    Polyline: "polyline",
    RadialGradient: "radialGradient",
    Rectangle: "rect",
    Script: "script",
    Stop: "stop",
    Style: "style",
    SVG: "svg",
    Symbol: "symbol",
    Text: "text",
    TextPath: "textPath",
    Title: "title",
    Use: "use",
}

#[cfg(test)]
mod tests {
    use super::{Parser, Tag, Type};

    #[test]
    fn parser_process() {
        macro_rules! test(
            ($content:expr, $kind:ident) => ({
                let mut parser = Parser::new($content);
                match parser.process().unwrap() {
                    Tag("foo", Type::$kind, _) => {}
                    _ => unreachable!(),
                }
            });
        );

        test!("foo", Start);
        test!("foo ", Start);
        test!("/foo", End);
        test!("/foo ", End);
        test!("foo/", Empty);
        test!("foo /", Empty);
    }

    #[test]
    fn parser_read_attribute() {
        macro_rules! test(
            ($content:expr, $name:expr, $value:expr) => ({
                let mut parser = Parser::new($content);
                let (name, value) = parser.read_attribute().unwrap().unwrap();
                assert_eq!(&*name, $name);
                assert_eq!(&*value, $value);
            });
        );

        test!("foo=''", "foo", "");
        test!("foo='bar'", "foo", "bar");
        test!("foo =\"bar\"", "foo", "bar");
        test!("foo= \"bar\"", "foo", "bar");
        test!("foo\t=\n'bar'  ", "foo", "bar");
    }
}
<|endoftext|>


<commit_before>use player;
use player::Player;
use arena::Arena;
use super::enums;
use super::moves::Technique;
use super::resolve;
use super::pokemon_token::PokemonToken;
use super::movedex::Movedex;
//use super::moves;
use self::rand::{Rng, thread_rng};
use graphic;
use player::Next;

extern crate rand;

pub fn unique(attack: &Technique,
              name: &str,
              mut user: PokemonToken,
              mut target: PokemonToken,
              attacker: &mut Player,
              defender: &mut Player,
              arena: &mut Arena,
              flag: enums::Player,
              mut window: &mut graphic::gui::App) {
    let movedex = Movedex::new();
    let mut rng = thread_rng();

    // target.get_moves(dex.move_by_id());
    // match dex.get_entries() {
    match name {
        "disable" => {
            println!("disable");
        }
        "teleport" => {
            resolve::switch_pokemon(attacker);
        }
        "mimic" => {
            if defender.get_last_move().is_none() {
                window.set_battle_text("failed attack...");
            } else {
                let attack = movedex.move_by_id(defender.get_last_move().unwrap().get_id())
                    .unwrap();
                attack.resolve(arena, flag, window);
            }
        }
        //"focus-energy" => {}
        "metronome" => {
            let random = rng.gen_range(1, 617);
            let attack = movedex.move_by_id(random).unwrap();
            attack.resolve(arena, flag, window);
        }
        "mirror-move" => {
            if defender.get_last_move().is_none() {
                window.set_battle_text("failed attack...");
            } else {
                let attack = movedex.move_by_id(defender.get_last_move().unwrap().get_id())
                    .unwrap();
                attack.resolve(arena, flag, window);
            }
        }
        "nature-power" => {
            match arena.get_current_effect().0 {
                enums::Types::Normal => {
                    //"tri-attack"
                    let attack = movedex.move_by_id(161).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Flying => {
                    //"air-slash" {
                    let attack = movedex.move_by_id(403).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Ground => {
                    //"earth-power" {
                    let attack = movedex.move_by_id(414).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Rock => {
                    //"power_gem" {
                    let attack = movedex.move_by_id(408).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Fire => {
                    //"lava-plume" {
                    let attack = movedex.move_by_id(436).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Water => {
                    //"hydro-pump" {
                    let attack = movedex.move_by_id(56).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Grass => {
                    //"energy-ball" {
                    let attack = movedex.move_by_id(412).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Electric => {
                    //"thunderbolt" {
                    let attack = movedex.move_by_id(85).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Ice => {
                    //"ice-beam" {
                    let attack = movedex.move_by_id(58).unwrap();
                    attack.resolve(arena, flag, window);
                }
                _ => {}
            }
        }
        // "transform" => {
        // },
        "splash" => {
            //ob flag für gravity gesetz ist
            if arena.get_current_effect().0 == enums::Types::Flying {
                window.set_battle_text("The attack can not be used because" + enums::Types::Flying +
                                     "is activated.");
            } else {
                window.set_battle_text("Nothing happens...");
            }
        }
        // "rest" => {
        //     for entry in Movedex::new().get_entries() {
        //         if entry.get_name() == "rest" {
        //             ////Fehler muss noch behofen werden
        //             //let mut user_clone = moves::get_user(flag, arena).clone();
        //             // resolve::ailment("rest",
        //             //                  entry.get_type(),
        //             //                  entry.get_ailment(),
        //             //                  100,
        //             //                  user,
        //             //                  &mut target,
        //             //                  defender,
        //             //                  window);
        //         }
        //     }

        // }
        "conversion" => {
            user.set_type(0, attack.get_type());
        }
        "spite" => {
            if defender.get_last_move().is_none() {
                window.set_battle_text("failed attack...");
            } else {
                defender.get_last_move().unwrap().get_power_points();
                target.decrement_ap();
            }
        }
        "sleep-talk" => {
            if user.is_asleep() {
                let mut id_vec = Vec::new();
                if attacker.get_attack(&player::AttackSlot::One).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::One).get_id());
                }
                if attacker.get_attack(&player::AttackSlot::Two).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::Two).get_id());
                }
                if attacker.get_attack(&player::AttackSlot::Three).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::Three).get_id());
                }
                if attacker.get_attack(&player::AttackSlot::Four).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::Four).get_id());
                }
                let random = rng.gen_range(0, id_vec.len());
                let attack = movedex.move_by_id(id_vec[random]).unwrap();
                attack.resolve(arena, flag, window);
            }
        }
        "celebrate" => {
            window.set_battle_text(user.get_name().to_string() +
                                   " sappears and then rises out of a birthday present that \
                                    falls into the picture from above.");
        }
        "powder" => {
            if target.get_types().0 == enums::Types::Grass ||
               target.get_types().1 == enums::Types::Grass {
                window.set_battle_text(name + " has no effect on  Pokemon type plants");
            } else {
                if defender.get_next_move().is_some() {
                    match defender.get_next_move().unwrap() {
                        Next::Move(x) => {
                            if x.get_type() == enums::Types::Fire {
                                let mut a_stats = target.get_base();
                                a_stats.set_stats(enums::Stats::Hp,
                                                  target.get_base().get_stat(&enums::Stats::Hp) -
                                                  (25 / 100 *
                                                   target.get_base().get_stat(&enums::Stats::Hp)));
                            }
                        }
                        _ => {}
                    };
                } else {
                    window.set_battle_text("failed move...");
                }
            }
        }
        "reflect-type" => {
            user.set_type(0, target.get_types().0);
            user.set_type(1, target.get_types().1);
        }
        "soak" => {
            target.set_type(0, enums::Types::Water);
            target.set_type(1, enums::Types::Water);
        }
        // "conversion-2" => {
        // },
        // "substitute" => {
        // },
        // "sketch" => {
        // },
        // "spider-web" => {
        // },
        // "mind-reader" => {
        // },
        // "curse" => {
        // },
        // "protect" => {
        // },
        // "belly-drum" => {
        // },
        // "destiny-bond" => {
        // },
        // "detect" => {
        // },
        // "lock-on" => {
        // },
        // "endure" => {
        // },
        // "mean-look" => {
        // },
        // "heal-bell" => {
        // },
        // "pain-split" => {
        // },
        // "baton-pass" => {
        // },
        // "encore" => {
        // },
        // "psych-up" => {
        // },
        // "future-sight" => {
        // },
        // "stockpile" => {
        // },
        // "memento" => {
        // },
        // "follow-me" => {
        // },
        // "taunt" => {
        // },
        // "helping-hand" => {
        // },
        // "trick" => {
        // },
        // "role-play" => {
        // },
        // "wish" => {
        // },
        // "assist" => {
        // },
        // "magic-coat" => {
        // },
        // "recycle" => {
        // },
        // "skill-swap" => {
        // },
        // "imprison" => {
        // },
        // "refresh" => {
        // },
        // "grudge" => {
        // },
        // "snatch" => {
        // },
        // "camouflage" => {
        // },
        // "aromatherapy" => {
        // },
        // "block" => {
        // },
        // "doom-desire" => {
        // },
        // "healing-wish" => {
        // },
        // "acupressure" => {
        // },
        // "psycho-shift" => {
        // },
        // "power-trick" => {
        // },
        // "gastro-acid" => {
        // },
        // "copycat" => {
        // },
        // "power-swap" => {
        // },
        // "guard-swap" => {
        // },
        // "worry-seed" => {
        // },
        // "heart-swap" => {
        // },
        // "aqua-ring" => {
        // },
        // "magnet-rise" => {
        // },
        // "switcheroo" => {
        // },
        // "defog" => {
        // },
        // "lunar-dance" => {
        // },
        // "guard-split" => {
        // },
        // "power-split" => {
        // },
        // "rage-powder" => {
        // },
        // "simple-beam" => {
        // },
        // "entrainment" => {
        // },
        // "after-you" => {
        // },
        // "ally-switch" => {
        // },
        // "shell-smash" => {
        // },
        // "quash" => {
        // },
        // "bestow" => {
        // },
        // "trick-or-treat" => {
        // },
        // "forests-curse" => {
        // },
        // "topsy-turvy" => {
        // },
        // "flower-shield" => {
        // },
        // "electrify" => {
        // },
        // "kings-shield" => {
        // },
        // "spiky-shield" => {
        // },
        // "happy-hour" => {
        // },
        // "hold-hands" => {
        // },
        _ => {}
    };
}
<commit_msg>some fix<commit_after>use player;
use player::Player;
use arena::Arena;
use super::enums;
use super::moves::Technique;
use super::resolve;
use super::pokemon_token::PokemonToken;
use super::movedex::Movedex;
//use super::moves;
use self::rand::{Rng, thread_rng};
use graphic;
use player::Next;

extern crate rand;

pub fn unique(attack: &Technique,
              name: &str,
              mut user: PokemonToken,
              mut target: PokemonToken,
              attacker: &mut Player,
              defender: &mut Player,
              arena: &mut Arena,
              flag: enums::Player,
              mut window: &mut graphic::gui::App) {
    let movedex = Movedex::new();
    let mut rng = thread_rng();

    // target.get_moves(dex.move_by_id());
    // match dex.get_entries() {
    match name {
        "disable" => {
            println!("disable");
        }
        "teleport" => {
            resolve::switch_pokemon(attacker);
        }
        "mimic" => {
            if defender.get_last_move().is_none() {
                window.set_battle_text("failed attack...".to_string());
            } else {
                let attack = movedex.move_by_id(defender.get_last_move().unwrap().get_id())
                    .unwrap();
                attack.resolve(arena, flag, window);
            }
        }
        //"focus-energy" => {}
        "metronome" => {
            let random = rng.gen_range(1, 617);
            let attack = movedex.move_by_id(random).unwrap();
            attack.resolve(arena, flag, window);
        }
        "mirror-move" => {
            if defender.get_last_move().is_none() {
                window.set_battle_text("failed attack...".to_string());
            } else {
                let attack = movedex.move_by_id(defender.get_last_move().unwrap().get_id())
                    .unwrap();
                attack.resolve(arena, flag, window);
            }
        }
        "nature-power" => {
            match arena.get_current_effect().0 {
                enums::Types::Normal => {
                    //"tri-attack"
                    let attack = movedex.move_by_id(161).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Flying => {
                    //"air-slash" {
                    let attack = movedex.move_by_id(403).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Ground => {
                    //"earth-power" {
                    let attack = movedex.move_by_id(414).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Rock => {
                    //"power_gem" {
                    let attack = movedex.move_by_id(408).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Fire => {
                    //"lava-plume" {
                    let attack = movedex.move_by_id(436).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Water => {
                    //"hydro-pump" {
                    let attack = movedex.move_by_id(56).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Grass => {
                    //"energy-ball" {
                    let attack = movedex.move_by_id(412).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Electric => {
                    //"thunderbolt" {
                    let attack = movedex.move_by_id(85).unwrap();
                    attack.resolve(arena, flag, window);
                }
                enums::Types::Ice => {
                    //"ice-beam" {
                    let attack = movedex.move_by_id(58).unwrap();
                    attack.resolve(arena, flag, window);
                }
                _ => {}
            }
        }
        // "transform" => {
        // },
        "splash" => {
            //ob flag für gravity gesetz ist
            if arena.get_current_effect().0 == enums::Types::Flying {
                window.set_battle_text("The attack can not be used because Flyibg is activated."
                    .to_string());
            } else {
                println!("Nothing happens...");
            }
        }
        "rest" => {
            window.set_battle_text(target.get_name() + " was not affected by " + name);
            for entry in Movedex::new().get_entries() {
                if entry.get_name() == "rest" {
                    ////Fehler muss noch behofen werden
                    //let mut user_clone = moves::get_user(flag, arena).clone();
                    // resolve::ailment("rest",
                    //                  entry.get_type(),
                    //                  entry.get_ailment(),
                    //                  100,
                    //                  user,
                    //                  &mut target,
                    //                  defender,
                    //                  window);
                }
            }

        }
        "conversion" => {
            user.set_type(0, attack.get_type());
        }
        "spite" => {
            if defender.get_last_move().is_none() {
                window.set_battle_text("failed attack...".to_string());
            } else {
                defender.get_last_move().unwrap().get_power_points();
                target.decrement_ap();
            }
        }
        "sleep-talk" => {
            if user.is_asleep() {
                let mut id_vec = Vec::new();
                if attacker.get_attack(&player::AttackSlot::One).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::One).get_id());
                }
                if attacker.get_attack(&player::AttackSlot::Two).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::Two).get_id());
                }
                if attacker.get_attack(&player::AttackSlot::Three).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::Three).get_id());
                }
                if attacker.get_attack(&player::AttackSlot::Four).get_name() != "sleep-talk" {
                    id_vec.push(attacker.get_attack(&player::AttackSlot::Four).get_id());
                }
                let random = rng.gen_range(0, id_vec.len());
                let attack = movedex.move_by_id(id_vec[random]).unwrap();
                attack.resolve(arena, flag, window);
            }
        }
        "celebrate" => {
            window.set_battle_text(user.get_name().to_string() +
                                   " sappears and then rises out of a birthday present that \
                                    falls into the picture from above.");
        }
        "powder" => {
            if target.get_types().0 == enums::Types::Grass ||
               target.get_types().1 == enums::Types::Grass {
                window.set_battle_text(name.to_string() + " has no effect on  Pokemon type plants");
            } else {
                if defender.get_next_move().is_some() {
                    match defender.get_next_move().unwrap() {
                        Next::Move(x) => {
                            if x.get_type() == enums::Types::Fire {
                                let mut a_stats = target.get_base();
                                a_stats.set_stats(enums::Stats::Hp,
                                                  target.get_base().get_stat(&enums::Stats::Hp) -
                                                  (25 / 100 *
                                                   target.get_base().get_stat(&enums::Stats::Hp)));
                            }
                        }
                        _ => {}
                    };
                } else {
                    window.set_battle_text("failed attack...".to_string());
                }
            }
        }
        "reflect-type" => {
            user.set_type(0, target.get_types().0);
            user.set_type(1, target.get_types().1);
        }
        "soak" => {
            target.set_type(0, enums::Types::Water);
            target.set_type(1, enums::Types::Water);
        }
        // "conversion-2" => {
        // },
        // "substitute" => {
        // },
        // "sketch" => {
        // },
        // "spider-web" => {
        // },
        // "mind-reader" => {
        // },
        // "curse" => {
        // },
        // "protect" => {
        // },
        // "belly-drum" => {
        // },
        // "destiny-bond" => {
        // },
        // "detect" => {
        // },
        // "lock-on" => {
        // },
        // "endure" => {
        // },
        // "mean-look" => {
        // },
        // "heal-bell" => {
        // },
        // "pain-split" => {
        // },
        // "baton-pass" => {
        // },
        // "encore" => {
        // },
        // "psych-up" => {
        // },
        // "future-sight" => {
        // },
        // "stockpile" => {
        // },
        // "memento" => {
        // },
        // "follow-me" => {
        // },
        // "taunt" => {
        // },
        // "helping-hand" => {
        // },
        // "trick" => {
        // },
        // "role-play" => {
        // },
        // "wish" => {
        // },
        // "assist" => {
        // },
        // "magic-coat" => {
        // },
        // "recycle" => {
        // },
        // "skill-swap" => {
        // },
        // "imprison" => {
        // },
        // "refresh" => {
        // },
        // "grudge" => {
        // },
        // "snatch" => {
        // },
        // "camouflage" => {
        // },
        // "aromatherapy" => {
        // },
        // "block" => {
        // },
        // "doom-desire" => {
        // },
        // "healing-wish" => {
        // },
        // "acupressure" => {
        // },
        // "psycho-shift" => {
        // },
        // "power-trick" => {
        // },
        // "gastro-acid" => {
        // },
        // "copycat" => {
        // },
        // "power-swap" => {
        // },
        // "guard-swap" => {
        // },
        // "worry-seed" => {
        // },
        // "heart-swap" => {
        // },
        // "aqua-ring" => {
        // },
        // "magnet-rise" => {
        // },
        // "switcheroo" => {
        // },
        // "defog" => {
        // },
        // "lunar-dance" => {
        // },
        // "guard-split" => {
        // },
        // "power-split" => {
        // },
        // "rage-powder" => {
        // },
        // "simple-beam" => {
        // },
        // "entrainment" => {
        // },
        // "after-you" => {
        // },
        // "ally-switch" => {
        // },
        // "shell-smash" => {
        // },
        // "quash" => {
        // },
        // "bestow" => {
        // },
        // "trick-or-treat" => {
        // },
        // "forests-curse" => {
        // },
        // "topsy-turvy" => {
        // },
        // "flower-shield" => {
        // },
        // "electrify" => {
        // },
        // "kings-shield" => {
        // },
        // "spiky-shield" => {
        // },
        // "happy-hour" => {
        // },
        // "hold-hands" => {
        // },
        _ => {}
    };
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use rustc::front::map as ast_map;
use rustc::lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
use rustc::middle::ty;
use rustc_front::hir;
use rustc_front::intravisit as visit;
use syntax::attr::AttrMetaMethods;
use syntax::{ast, codemap};
use utils::{match_def_path, in_derive_expn};

declare_lint!(UNROOTED_MUST_ROOT, Deny,
              "Warn and report usage of unrooted jsmanaged objects");

/// Lint for ensuring safe usage of unrooted pointers
///
/// This lint (disable with `-A unrooted-must-root`/`#[allow(unrooted_must_root)]`) ensures that `#[must_root]`
/// values are used correctly.
///
/// "Incorrect" usage includes:
///
///  - Not being used in a struct/enum field which is not `#[must_root]` itself
///  - Not being used as an argument to a function (Except onces named `new` and `new_inherited`)
///  - Not being bound locally in a `let` statement, assignment, `for` loop, or `match` statement.
///
/// This helps catch most situations where pointers like `JS<T>` are used in a way that they can be invalidated by a
/// GC pass.
///
/// Structs which have their own mechanism of rooting their unrooted contents (e.g. `ScriptTask`)
/// can be marked as `#[allow(unrooted_must_root)]`. Smart pointers which root their interior type
/// can be marked as `#[allow_unrooted_interior]`
pub struct UnrootedPass;

impl UnrootedPass {
    pub fn new() -> UnrootedPass {
        UnrootedPass
    }
}

/// Checks if a type is unrooted or contains any owned unrooted types
fn is_unrooted_ty(cx: &LateContext, ty: &ty::TyS, in_new_function: bool) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::TyStruct(did, _) |
            ty::TyEnum(did, _) => {
                if cx.tcx.has_attr(did.did, "must_root") {
                    ret = true;
                    false
                } else if cx.tcx.has_attr(did.did, "allow_unrooted_interior") {
                    false
                } else if match_def_path(cx, did.did, &["core", "cell", "Ref"])
                        || match_def_path(cx, did.did, &["core", "cell", "RefMut"])
                        || match_def_path(cx, did.did, &["core", "slice", "Iter"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "OccupiedEntry"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "VacantEntry"]) {
                    // Structures which are semantically similar to an &ptr.
                    false
                } else {
                    true
                }
            },
            ty::TyBox(..) if in_new_function => false, // box in new() is okay
            ty::TyRef(..) => false, // don't recurse down &ptrs
            ty::TyRawPtr(..) => false, // don't recurse down *ptrs
            _ => true
        }
    });
    ret
}

impl LintPass for UnrootedPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNROOTED_MUST_ROOT)
    }
}

impl LateLintPass for UnrootedPass {
    /// All structs containing #[must_root] types must be #[must_root] themselves
    fn check_struct_def(&mut self,
                        cx: &LateContext,
                        def: &hir::VariantData,
                        _n: ast::Name,
                        _gen: &hir::Generics,
                        id: ast::NodeId) {
        let item = match cx.tcx.map.get(id) {
            ast_map::Node::NodeItem(item) => item,
            _ => cx.tcx.map.expect_item(cx.tcx.map.get_parent(id)),
        };
        if item.attrs.iter().all(|a| !a.check_name("must_root")) {
            for ref field in def.fields() {
                if is_unrooted_ty(cx, cx.tcx.node_id_to_type(field.node.id), false) {
                    cx.span_lint(UNROOTED_MUST_ROOT, field.span,
                                 "Type must be rooted, use #[must_root] on the struct definition to propagate")
                }
            }
        }
    }

    /// All enums containing #[must_root] types must be #[must_root] themselves
    fn check_variant(&mut self, cx: &LateContext, var: &hir::Variant, _gen: &hir::Generics) {
        let ref map = cx.tcx.map;
        if map.expect_item(map.get_parent(var.node.data.id())).attrs.iter().all(|a| !a.check_name("must_root")) {
            match var.node.data {
                hir::VariantData::Tuple(ref vec, _) => {
                    for ty in vec {
                        cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.node.id).map(|t| {
                            if is_unrooted_ty(cx, t, false) {
                                cx.span_lint(UNROOTED_MUST_ROOT, ty.node.ty.span,
                                             "Type must be rooted, use #[must_root] on \
                                              the enum definition to propagate")
                            }
                        });
                    }
                }
                _ => () // Struct variants already caught by check_struct_def
            }
        }
    }
    /// Function arguments that are #[must_root] types are not allowed
    fn check_fn(&mut self, cx: &LateContext, kind: visit::FnKind, decl: &hir::FnDecl,
                block: &hir::Block, span: codemap::Span, _id: ast::NodeId) {
        let in_new_function = match kind {
            visit::FnKind::ItemFn(n, _, _, _, _, _) |
            visit::FnKind::Method(n, _, _) => {
                n.as_str() == "new" || n.as_str().starts_with("new_")
            }
            visit::FnKind::Closure => return,
        };

        for arg in &decl.inputs {
            cx.tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id).map(|t| {
                if is_unrooted_ty(cx, t, false) {
                    if in_derive_expn(cx, span) {
                        return;
                    }
                    cx.span_lint(UNROOTED_MUST_ROOT, arg.ty.span, "Type must be rooted")
                }
            });
        }

        if !in_new_function {
            if let hir::Return(ref ty) = decl.output {
                cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.id).map(|t| {
                    if is_unrooted_ty(cx, t, false) {
                        if in_derive_expn(cx, span) {
                            return;
                        }
                        cx.span_lint(UNROOTED_MUST_ROOT, ty.span, "Type must be rooted")
                    }
                });
            }
        }

        let mut visitor = FnDefVisitor {
            cx: cx,
            in_new_function: in_new_function,
        };
        visit::walk_block(&mut visitor, block);
    }
}

struct FnDefVisitor<'a, 'b: 'a, 'tcx: 'a+'b> {
    cx: &'a LateContext<'b, 'tcx>,
    in_new_function: bool,
}

impl<'a, 'b: 'a, 'tcx: 'a+'b> visit::Visitor<'a> for FnDefVisitor<'a, 'b, 'tcx> {
    fn visit_expr(&mut self, expr: &'a hir::Expr) {
        let cx = self.cx;

        fn require_rooted(cx: &LateContext, in_new_function: bool, subexpr: &hir::Expr) {
            let ty = cx.tcx.expr_ty(&*subexpr);
            if is_unrooted_ty(cx, ty, in_new_function) {
                cx.span_lint(UNROOTED_MUST_ROOT,
                             subexpr.span,
                             &format!("Expression of type {:?} must be rooted", ty))
            }
        }

        match expr.node {
            /// Trait casts from #[must_root] types are not allowed
            hir::ExprCast(ref subexpr, _) => require_rooted(cx, self.in_new_function, &*subexpr),
            // This catches assignments... the main point of this would be to catch mutable
            // references to `JS<T>`.
            // FIXME: Enable this? Triggers on certain kinds of uses of DOMRefCell.
            // hir::ExprAssign(_, ref rhs) => require_rooted(cx, self.in_new_function, &*rhs),
            // This catches calls; basically, this enforces the constraint that only constructors
            // can call other constructors.
            // FIXME: Enable this? Currently triggers with constructs involving DOMRefCell, and
            // constructs like Vec<JS<T>> and RootedVec<JS<T>>.
            // hir::ExprCall(..) if !self.in_new_function => {
            //     require_rooted(cx, self.in_new_function, expr);
            // }
            _ => {
                // TODO(pcwalton): Check generics with a whitelist of allowed generics.
            }
        }

        visit::walk_expr(self, expr);
    }

    fn visit_pat(&mut self, pat: &'a hir::Pat) {
        let cx = self.cx;

        if let hir::PatIdent(hir::BindByValue(_), _, _) = pat.node {
            let ty = cx.tcx.pat_ty(pat);
            if is_unrooted_ty(cx, ty, self.in_new_function) {
                cx.span_lint(UNROOTED_MUST_ROOT,
                            pat.span,
                            &format!("Expression of type {:?} must be rooted", ty))
            }
        }

        visit::walk_pat(self, pat);
    }

    fn visit_fn(&mut self, kind: visit::FnKind<'a>, decl: &'a hir::FnDecl,
                block: &'a hir::Block, span: codemap::Span, _id: ast::NodeId) {
        if kind == visit::FnKind::Closure {
            visit::walk_fn(self, kind, decl, block, span);
        }
    }

    fn visit_foreign_item(&mut self, _: &'a hir::ForeignItem) {}
    fn visit_ty(&mut self, _: &'a hir::Ty) { }
}
<commit_msg>Fix false positive in unrooted_must_root lint.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use rustc::front::map as ast_map;
use rustc::lint::{LateContext, LintPass, LintArray, LateLintPass, LintContext};
use rustc::middle::pat_util::pat_is_binding;
use rustc::middle::ty;
use rustc_front::hir;
use rustc_front::intravisit as visit;
use syntax::attr::AttrMetaMethods;
use syntax::{ast, codemap};
use utils::{match_def_path, in_derive_expn};

declare_lint!(UNROOTED_MUST_ROOT, Deny,
              "Warn and report usage of unrooted jsmanaged objects");

/// Lint for ensuring safe usage of unrooted pointers
///
/// This lint (disable with `-A unrooted-must-root`/`#[allow(unrooted_must_root)]`) ensures that `#[must_root]`
/// values are used correctly.
///
/// "Incorrect" usage includes:
///
///  - Not being used in a struct/enum field which is not `#[must_root]` itself
///  - Not being used as an argument to a function (Except onces named `new` and `new_inherited`)
///  - Not being bound locally in a `let` statement, assignment, `for` loop, or `match` statement.
///
/// This helps catch most situations where pointers like `JS<T>` are used in a way that they can be invalidated by a
/// GC pass.
///
/// Structs which have their own mechanism of rooting their unrooted contents (e.g. `ScriptTask`)
/// can be marked as `#[allow(unrooted_must_root)]`. Smart pointers which root their interior type
/// can be marked as `#[allow_unrooted_interior]`
pub struct UnrootedPass;

impl UnrootedPass {
    pub fn new() -> UnrootedPass {
        UnrootedPass
    }
}

/// Checks if a type is unrooted or contains any owned unrooted types
fn is_unrooted_ty(cx: &LateContext, ty: &ty::TyS, in_new_function: bool) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::TyStruct(did, _) |
            ty::TyEnum(did, _) => {
                if cx.tcx.has_attr(did.did, "must_root") {
                    ret = true;
                    false
                } else if cx.tcx.has_attr(did.did, "allow_unrooted_interior") {
                    false
                } else if match_def_path(cx, did.did, &["core", "cell", "Ref"])
                        || match_def_path(cx, did.did, &["core", "cell", "RefMut"])
                        || match_def_path(cx, did.did, &["core", "slice", "Iter"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "OccupiedEntry"])
                        || match_def_path(cx, did.did, &["std", "collections", "hash", "map", "VacantEntry"]) {
                    // Structures which are semantically similar to an &ptr.
                    false
                } else {
                    true
                }
            },
            ty::TyBox(..) if in_new_function => false, // box in new() is okay
            ty::TyRef(..) => false, // don't recurse down &ptrs
            ty::TyRawPtr(..) => false, // don't recurse down *ptrs
            _ => true
        }
    });
    ret
}

impl LintPass for UnrootedPass {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNROOTED_MUST_ROOT)
    }
}

impl LateLintPass for UnrootedPass {
    /// All structs containing #[must_root] types must be #[must_root] themselves
    fn check_struct_def(&mut self,
                        cx: &LateContext,
                        def: &hir::VariantData,
                        _n: ast::Name,
                        _gen: &hir::Generics,
                        id: ast::NodeId) {
        let item = match cx.tcx.map.get(id) {
            ast_map::Node::NodeItem(item) => item,
            _ => cx.tcx.map.expect_item(cx.tcx.map.get_parent(id)),
        };
        if item.attrs.iter().all(|a| !a.check_name("must_root")) {
            for ref field in def.fields() {
                if is_unrooted_ty(cx, cx.tcx.node_id_to_type(field.node.id), false) {
                    cx.span_lint(UNROOTED_MUST_ROOT, field.span,
                                 "Type must be rooted, use #[must_root] on the struct definition to propagate")
                }
            }
        }
    }

    /// All enums containing #[must_root] types must be #[must_root] themselves
    fn check_variant(&mut self, cx: &LateContext, var: &hir::Variant, _gen: &hir::Generics) {
        let ref map = cx.tcx.map;
        if map.expect_item(map.get_parent(var.node.data.id())).attrs.iter().all(|a| !a.check_name("must_root")) {
            match var.node.data {
                hir::VariantData::Tuple(ref vec, _) => {
                    for ty in vec {
                        cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.node.id).map(|t| {
                            if is_unrooted_ty(cx, t, false) {
                                cx.span_lint(UNROOTED_MUST_ROOT, ty.node.ty.span,
                                             "Type must be rooted, use #[must_root] on \
                                              the enum definition to propagate")
                            }
                        });
                    }
                }
                _ => () // Struct variants already caught by check_struct_def
            }
        }
    }
    /// Function arguments that are #[must_root] types are not allowed
    fn check_fn(&mut self, cx: &LateContext, kind: visit::FnKind, decl: &hir::FnDecl,
                block: &hir::Block, span: codemap::Span, _id: ast::NodeId) {
        let in_new_function = match kind {
            visit::FnKind::ItemFn(n, _, _, _, _, _) |
            visit::FnKind::Method(n, _, _) => {
                n.as_str() == "new" || n.as_str().starts_with("new_")
            }
            visit::FnKind::Closure => return,
        };

        for arg in &decl.inputs {
            cx.tcx.ast_ty_to_ty_cache.borrow().get(&arg.ty.id).map(|t| {
                if is_unrooted_ty(cx, t, false) {
                    if in_derive_expn(cx, span) {
                        return;
                    }
                    cx.span_lint(UNROOTED_MUST_ROOT, arg.ty.span, "Type must be rooted")
                }
            });
        }

        if !in_new_function {
            if let hir::Return(ref ty) = decl.output {
                cx.tcx.ast_ty_to_ty_cache.borrow().get(&ty.id).map(|t| {
                    if is_unrooted_ty(cx, t, false) {
                        if in_derive_expn(cx, span) {
                            return;
                        }
                        cx.span_lint(UNROOTED_MUST_ROOT, ty.span, "Type must be rooted")
                    }
                });
            }
        }

        let mut visitor = FnDefVisitor {
            cx: cx,
            in_new_function: in_new_function,
        };
        visit::walk_block(&mut visitor, block);
    }
}

struct FnDefVisitor<'a, 'b: 'a, 'tcx: 'a+'b> {
    cx: &'a LateContext<'b, 'tcx>,
    in_new_function: bool,
}

impl<'a, 'b: 'a, 'tcx: 'a+'b> visit::Visitor<'a> for FnDefVisitor<'a, 'b, 'tcx> {
    fn visit_expr(&mut self, expr: &'a hir::Expr) {
        let cx = self.cx;

        fn require_rooted(cx: &LateContext, in_new_function: bool, subexpr: &hir::Expr) {
            let ty = cx.tcx.expr_ty(&*subexpr);
            if is_unrooted_ty(cx, ty, in_new_function) {
                cx.span_lint(UNROOTED_MUST_ROOT,
                             subexpr.span,
                             &format!("Expression of type {:?} must be rooted", ty))
            }
        }

        match expr.node {
            /// Trait casts from #[must_root] types are not allowed
            hir::ExprCast(ref subexpr, _) => require_rooted(cx, self.in_new_function, &*subexpr),
            // This catches assignments... the main point of this would be to catch mutable
            // references to `JS<T>`.
            // FIXME: Enable this? Triggers on certain kinds of uses of DOMRefCell.
            // hir::ExprAssign(_, ref rhs) => require_rooted(cx, self.in_new_function, &*rhs),
            // This catches calls; basically, this enforces the constraint that only constructors
            // can call other constructors.
            // FIXME: Enable this? Currently triggers with constructs involving DOMRefCell, and
            // constructs like Vec<JS<T>> and RootedVec<JS<T>>.
            // hir::ExprCall(..) if !self.in_new_function => {
            //     require_rooted(cx, self.in_new_function, expr);
            // }
            _ => {
                // TODO(pcwalton): Check generics with a whitelist of allowed generics.
            }
        }

        visit::walk_expr(self, expr);
    }

    fn visit_pat(&mut self, pat: &'a hir::Pat) {
        let cx = self.cx;

        if let hir::PatIdent(hir::BindByValue(_), _, _) = pat.node {
            if pat_is_binding(&cx.tcx.def_map.borrow(), pat) {
                let ty = cx.tcx.pat_ty(pat);
                if is_unrooted_ty(cx, ty, self.in_new_function) {
                    cx.span_lint(UNROOTED_MUST_ROOT,
                                pat.span,
                                &format!("Expression of type {:?} must be rooted", ty))
                }
            }
        }

        visit::walk_pat(self, pat);
    }

    fn visit_fn(&mut self, kind: visit::FnKind<'a>, decl: &'a hir::FnDecl,
                block: &'a hir::Block, span: codemap::Span, _id: ast::NodeId) {
        if kind == visit::FnKind::Closure {
            visit::walk_fn(self, kind, decl, block, span);
        }
    }

    fn visit_foreign_item(&mut self, _: &'a hir::ForeignItem) {}
    fn visit_ty(&mut self, _: &'a hir::Ty) { }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use consensus_types::{block::block_test_utils, block::Block};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use libra_types::crypto_proxies::ValidatorSigner;
use rand::Rng;
use safety_rules::{
    test_utils, {InMemoryStorage, SafetyRules, TSafetyRules},
};
use std::sync::Arc;

/// Execute an in order series of blocks (0 <- 1 <- 2 <- 3 and commit 0 and continue to rotate
/// left, appending new blocks on the right, committing the left most block
fn lsr(n: u64) {
    let validator_signer = ValidatorSigner::from_int(0);
    let mut safety_rules = SafetyRules::new(
        InMemoryStorage::default_storage(),
        Arc::new(validator_signer.clone()),
    );

    let mut rng = rand::thread_rng();
    let data: Vec<u8> = (0..2048).map(|_| rng.gen::<u8>()).collect();

    let genesis_block = Block::<Vec<u8>>::make_genesis_block();
    let genesis_qc = block_test_utils::certificate_for_genesis();
    let mut round = genesis_block.round();

    round += 1;
    let mut b0 = test_utils::make_proposal_with_qc(round, genesis_qc.clone(), &validator_signer);
    safety_rules.update(b0.block().quorum_cert()).unwrap();
    safety_rules.construct_and_sign_vote(&b0).unwrap();

    round += 1;
    let mut b1 =
        test_utils::make_proposal_with_parent(data.clone(), round, &b0, None, &validator_signer);
    safety_rules.update(b1.block().quorum_cert()).unwrap();
    safety_rules.construct_and_sign_vote(&b1).unwrap();

    round += 1;
    let mut b2 =
        test_utils::make_proposal_with_parent(data.clone(), round, &b1, None, &validator_signer);
    safety_rules.update(b2.block().quorum_cert()).unwrap();
    safety_rules.construct_and_sign_vote(&b2).unwrap();

    for _i in 0..n {
        round += 1;
        let b3 = test_utils::make_proposal_with_parent(
            data.clone(),
            round,
            &b2,
            Some(&b0),
            &validator_signer,
        );

        safety_rules.update(b3.block().quorum_cert()).unwrap();
        safety_rules.construct_and_sign_vote(&b3).unwrap();

        b0 = b1;
        b1 = b2;
        b2 = b3;
    }
}

pub fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("lsr 10", |b| b.iter(|| lsr(black_box(10))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
<commit_msg>[consensus] better safety rules benchmark<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use consensus_types::{block::block_test_utils, block::Block};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use libra_types::crypto_proxies::ValidatorSigner;
use rand::Rng;
use safety_rules::{
    test_utils, {InMemoryStorage, OnDiskStorage, SafetyRulesManager, TSafetyRules},
};
use tempfile::NamedTempFile;

/// Execute an in order series of blocks (0 <- 1 <- 2 <- 3 and commit 0 and continue to rotate
/// left, appending new blocks on the right, committing the left most block
fn lsr(mut safety_rules: Box<dyn TSafetyRules<Vec<u8>>>, signer: ValidatorSigner, n: u64) {
    let mut rng = rand::thread_rng();
    let data: Vec<u8> = (0..2048).map(|_| rng.gen::<u8>()).collect();

    let genesis_block = Block::<Vec<u8>>::make_genesis_block();
    let genesis_qc = block_test_utils::certificate_for_genesis();
    let mut round = genesis_block.round();

    round += 1;
    let mut b0 = test_utils::make_proposal_with_qc(round, genesis_qc.clone(), &signer);
    safety_rules.update(b0.block().quorum_cert()).unwrap();
    safety_rules.construct_and_sign_vote(&b0).unwrap();

    round += 1;
    let mut b1 = test_utils::make_proposal_with_parent(data.clone(), round, &b0, None, &signer);
    safety_rules.update(b1.block().quorum_cert()).unwrap();
    safety_rules.construct_and_sign_vote(&b1).unwrap();

    round += 1;
    let mut b2 = test_utils::make_proposal_with_parent(data.clone(), round, &b1, None, &signer);
    safety_rules.update(b2.block().quorum_cert()).unwrap();
    safety_rules.construct_and_sign_vote(&b2).unwrap();

    for _i in 0..n {
        round += 1;
        let b3 =
            test_utils::make_proposal_with_parent(data.clone(), round, &b2, Some(&b0), &signer);

        safety_rules.update(b3.block().quorum_cert()).unwrap();
        safety_rules.construct_and_sign_vote(&b3).unwrap();

        b0 = b1;
        b1 = b2;
        b2 = b3;
    }
}

fn in_memory(n: u64) {
    let signer = ValidatorSigner::from_int(0);
    let storage = Box::new(InMemoryStorage::default());
    let safety_rules_manager = SafetyRulesManager::new_local(storage, signer.clone());
    lsr(safety_rules_manager.client(), signer, n);
}

fn on_disk(n: u64) {
    let signer = ValidatorSigner::from_int(0);
    let file_path = NamedTempFile::new().unwrap().into_temp_path().to_path_buf();
    let storage = OnDiskStorage::default_storage(file_path.clone()).unwrap();
    let safety_rules_manager = SafetyRulesManager::new_local(storage, signer.clone());
    lsr(safety_rules_manager.client(), signer, n);
}

fn serializer(n: u64) {
    let signer = ValidatorSigner::from_int(0);
    let file_path = NamedTempFile::new().unwrap().into_temp_path().to_path_buf();
    let storage = OnDiskStorage::default_storage(file_path.clone()).unwrap();
    let safety_rules_manager = SafetyRulesManager::new_local(storage, signer.clone());
    lsr(safety_rules_manager.client(), signer, n);
}

fn thread(n: u64) {
    let signer = ValidatorSigner::from_int(0);
    let file_path = NamedTempFile::new().unwrap().into_temp_path().to_path_buf();
    let storage = OnDiskStorage::default_storage(file_path.clone()).unwrap();
    let safety_rules_manager = SafetyRulesManager::new_local(storage, signer.clone());
    lsr(safety_rules_manager.client(), signer, n);
}

pub fn benchmark(c: &mut Criterion) {
    let count = 100;
    let mut group = c.benchmark_group("SafetyRules");
    group.bench_function("InMemory", |b| b.iter(|| in_memory(black_box(count))));
    group.bench_function("OnDisk", |b| b.iter(|| on_disk(black_box(count))));
    group.bench_function("Serializer", |b| b.iter(|| serializer(black_box(count))));
    group.bench_function("Thread", |b| b.iter(|| thread(black_box(count))));
}

criterion_group!(benches, benchmark);
criterion_main!(benches);
<|endoftext|>