<commit_before>//! Experiments with parallel processing
//!
//! The provided functions focus on the possibility of
//! returning results while the parser proceeds. Sequences are processesd in
//! batches (`RecordSet`) because sending across channels has a performance
//! impact. FASTA/FASTQ records can be accessed in both the 'worker' function and
//! (after processing) a function running in the main thread.
//!
//! # Search first occurrence of a sequence pattern
//!
//! ```no_run
//! use seq_io::fastq::{Reader,Record};
//! use seq_io::parallel::read_parallel;
//!
//! let reader = Reader::from_path("seqs.fastq").unwrap();
//!
//! read_parallel(reader, 4, 2, |record_set| {
//!     // this function does the heavy work
//!     for (i, record) in record_set.into_iter().enumerate() {
//!         // this is not very efficient code, just for demonstration
//!         if let Some(pos) = record.seq().windows(3).position(|s| s == b"AAA") {
//!             return Some((i, pos));
//!         }
//!     }
//!     None
//! }, |record_sets| {
//!     // This function runs in the main thread. It provides a streaming iterator over
//!     // record sets and the corresponding return values from the worker function
//!     // (not necessarily in the same order as in the file)
//!     while let Some(result) = record_sets.next() {
//!         let (record_set, found) = result.unwrap();
//!         if let Some((i, pos)) = found {
//!             let record = record_set.into_iter().nth(i).unwrap();
//!             println!("Found AAA in record {} at position {}", record.id().unwrap(), pos);
//!              // this will also stop the worker threads, although with some delay
//!             return;
//!         }
//!     }
//! });
//! ```
//!
//! # Per-record processsing
//! The `parallel_fasta` / `parallel_fastq` functions are designed to efficiently pass
//! results for **each record** to the main thread without having to care about record sets.
//! This example filters sequences by the occurrence of a pattern:
//!
//! ```no_run
//! use seq_io::fastq::{Reader,Record};
//! use seq_io::parallel::parallel_fastq;
//! use std::fs::File;
//! use std::io::BufWriter;
//!
//! let reader = Reader::from_path("seqs.fastq").unwrap();
//! let mut writer = BufWriter::new(File::create("filtered.fastq").unwrap());
//!
//! parallel_fastq(reader, 4, 2,
//!     |record, found| { // runs in worker
//!         *found = record.seq().windows(3).position(|s| s == b"AAA").is_some();
//!     },
//!     |record, found| { // runs in main thread
//!         if *found {
//!             record.write(&mut writer).unwrap();
//!         }
//!         // Some(value) will stop the reader, and the value will be returned.
//!         // In the case of never stopping, we need to give the compiler a hint about the
//!         // type parameter, thus the special 'turbofish' notation is needed,
//!         // hoping on progress here: https://github.com/rust-lang/rust/issues/27336
//!         None::<()>
//! }).unwrap();
//! ```

use std::io;

extern crate crossbeam;
extern crate scoped_threadpool;

use std::sync::mpsc;
use std::marker::PhantomData;

pub trait Reader: Send {
    type DataSet: Send;
    type Err: Send;
    fn fill_data(&mut self, record: &mut Self::DataSet) -> Option<Result<(), Self::Err>>;
}

pub fn read_parallel<R, O, W, F, Out>(reader: R, n_threads: u32, queue_len: usize, work: W, func: F) -> Out
    where R: Reader,
          R::DataSet: Default,
          O: Default + Send,
          W: Send + Sync,
          W: Fn(&mut R::DataSet) -> O,
          F: FnMut(&mut ParallelRecordsets<R::DataSet, R::Err, O>) -> Out,
{
    read_parallel_with(reader, n_threads, queue_len, || R::DataSet::default(), work, func)
}

pub fn read_parallel_with<R, O, D, W, F, Out>(
        mut reader: R, n_threads: u32, queue_len: usize,
        mut dataset_init: D, work: W, func: F
    ) -> Out
    where R: Reader,
          O: Send,
          D: Send + Sync + FnMut() -> R::DataSet,
          W: Send + Sync,
          W: Fn(&mut R::DataSet) -> O,
          F: FnOnce(&mut ParallelRecordsets<R::DataSet, R::Err, O>) -> Out,
{

    let (done_send, done_recv) = mpsc::sync_channel(queue_len);
    let (empty_send, empty_recv): (mpsc::SyncSender<R::DataSet>, _) = mpsc::sync_channel(queue_len);

    crossbeam::scope(|scope| {

        scope.spawn(move || {

            let mut pool = scoped_threadpool::Pool::new(n_threads);

            pool.scoped(|pool_scope| {

                let work = &work;

                loop {
                    // recycle an old DataSet sent back after use by the streaming iterator
                    let mut data =
                        if let Ok(r) = empty_recv.recv() {
                            r
                        } else {
                            // ParallelRecordsets dropped -> stop
                            return;
                        };

                    let done_send = done_send.clone();

                    if let Some(res) = reader.fill_data(&mut data) {
                        match res {
                            Ok(_) => {
                                // expensive work carried out by func()
                                pool_scope.execute(move || {

                                    let out = work(&mut data);

                                    done_send.send(Some(Ok((data, out)))).ok();
                                });
                            }
                            Err(e) => {
                                done_send.send(Some(Err(e))).ok();
                                break;
                            }
                        }
                    } else {
                        break;
                    }
                }

                // make sure that the 'done' signal is only sent after everything else is done
                pool_scope.join_all();

                done_send.send(None).ok();
            });
        });

        for _ in 0..queue_len  {
            empty_send.send(dataset_init()).unwrap();
        }

        let mut rsets = ParallelRecordsets {
            empty_send: empty_send,
            done_recv: done_recv,
            current_recordset: dataset_init(),
        };

        let out = func(&mut rsets);
        ::std::mem::drop(rsets);
        out
    })
}

pub struct ParallelRecordsets<R, E, O>
    where R: Send,
          E: Send,
          O: Send,
{
    empty_send: mpsc::SyncSender<R>,
    done_recv: mpsc::Receiver<Option<Result<(R, O), E>>>,
    current_recordset: R,
}

impl<R, E, O> ParallelRecordsets<R, E, O>
    where R: Send,
          E: Send,
          O: Send,
 {
    pub fn next<'b>(&'b mut self) -> Option<Result<(&'b mut R, O), E>> {
        self.done_recv.recv().unwrap().map(move |result| {
            match result {
                Ok((r, o)) => {
                     let prev_rset = ::std::mem::replace(&mut self.current_recordset, r);
                     self.empty_send.send(prev_rset).ok(); // error: channel closed is not a problem, happens after calling stop()
                     Ok((&mut self.current_recordset, o))
                }
                Err(e) => Err(e)
            }
        })
    }
}

#[macro_export]
macro_rules! parallel_record_impl {
    ($name:ident, $io_r:tt, $rdr:ty, $record:ty, $err:ty) => {

        pub fn $name<$io_r, O, W, F, Out>(parser: $rdr, n_threads: u32, queue_len: usize, work: W, mut func: F) -> Result<Option<Out>, $err>
            where $io_r: io::Read + Send,
                  O: Default + Send,
                  W: Send + Sync,
                  W: Fn($record, &mut O),
                  F: FnMut($record, &mut O) -> Option<Out>,
        {
            let reader = $crate::parallel::ReusableReader::new(parser);

            $crate::parallel::read_parallel(reader, n_threads, queue_len, |d| {
                let mut iter = d.0.into_iter();
                let out: &mut Vec<O> = &mut d.1;
                for mut x in out.iter_mut().zip(&mut iter) {
                    work(x.1, &mut x.0);
                }
                for i in iter {
                    out.push(O::default());
                    work(i, out.last_mut().unwrap())
                }

            }, |records| {
                while let Some(result) = records.next() {
                    let (r, _) = result?;
                    for x in r.0.into_iter().zip(&mut r.1) {
                        if let Some(out) = func(x.0, x.1) {
                            return Ok(Some(out));
                        }
                    }
                }
                Ok(None)
            })
        }
     };
}

parallel_record_impl!(parallel_fasta, R, fasta::Reader<R>, fasta::RefRecord, fasta::Error);

parallel_record_impl!(parallel_fastq, R, fastq::Reader<R>, fastq::RefRecord, fastq::Error);

/// Wrapper for `parallel::Reader` instances allowing
/// the output to be reused in order to save allocations.
/// Used by `parallel_fasta`/`parallel_fastq`
///
/// ```no_run
/// use seq_io::fastq::{Reader,Record,RecordSet};
/// use seq_io::parallel::{read_parallel,ReusableReader};
///
/// let inner = Reader::from_path("seqs.fastq").unwrap();
/// let reader = ReusableReader::new(inner);
///
/// read_parallel(reader, 4, 2, |&mut (ref record_set, ref mut out): &mut (RecordSet, Vec<bool>)| {
///     out.clear();
///     for record in record_set {
///         let found = record.seq().windows(3).position(|s| s == b"AAA").is_some();
///         out.push(found);
///     }
/// }, |record_sets| {
///     while let Some(result) = record_sets.next() {
///         let &(ref record_set, ref out) = &*result.unwrap().0;
///         for (record, found) in record_set.into_iter().zip(out) {
///             // ...
///         }
///     }
/// });
/// ```
pub struct ReusableReader<P, O>(P, PhantomData<O>);

impl<P, O> ReusableReader<P, O> {
    pub fn new(p: P) -> ReusableReader<P, O> {
        ReusableReader(p, PhantomData)
    }
}

impl<P, O> Reader for ReusableReader<P, O>
    where P: Reader,
          O: Send {

    type DataSet = (P::DataSet, O);
    type Err = P::Err;
    fn fill_data(&mut self, data: &mut Self::DataSet) -> Option<Result<(), P::Err>> {
        self.0.fill_data(&mut data.0)
    }
}

/// Using this function currently does not work due to a
/// [compiler bug](https://github.com/rust-lang/rust/issues/42950).
///
/// `parallel_fasta`/`parallel_fastq` provide the same functionality for now
/// (implemented using `parallel_record_impl` macro)
pub fn parallel_records<R, O, W, F, Out>(parser: R, n_threads: u32, queue_len: usize, work: W, mut func: F) -> Result<Option<Out>, R::Err>
    where R: Reader,
          for<'a> &'a R::DataSet: IntoIterator,
          R::DataSet: Default,
          O: Default + Send,
          W: Send + Sync,
          W: Fn(<&R::DataSet as IntoIterator>::Item, &mut O),
          F: FnMut(<&R::DataSet as IntoIterator>::Item, &O) -> Option<Out>,
{
    let reader = ReusableReader(parser, PhantomData);

    read_parallel(reader, n_threads, queue_len, |d| {
        let mut iter = d.0.into_iter();
        let out: &mut Vec<O> = &mut d.1;
        for mut x in out.iter_mut().zip(&mut iter) {
            work(x.1, &mut x.0);
        }
        for i in iter {
            out.push(O::default());
            work(i, out.last_mut().unwrap())
        }

    }, |records| {
        while let Some(result) = records.next() {
            let (r, _) = result?;
            for x in r.0.into_iter().zip(&r.1) {
                if let Some(out) = func(x.0, x.1) {
                    return Ok(Some(out));
                }
            }
        }
        Ok(None)
    })
}

// trait impls

use super::fasta;

impl<R, S> Reader for fasta::Reader<R, S>
    where R: io::Read + Send,
          S: super::strategy::BufStrategy + Send
{
    type DataSet = fasta::RecordSet;
    type Err = fasta::Error;
    fn fill_data(&mut self, rset: &mut fasta::RecordSet) -> Option<Result<(), fasta::Error>> {
        self.read_record_set(rset)
    }
}

use super::fastq;

impl<R, S> Reader for fastq::Reader<R, S>
    where R: io::Read + Send,
          S: super::strategy::BufStrategy + Send
{
    type DataSet = fastq::RecordSet;
    type Err = fastq::Error;
    fn fill_data(&mut self, rset: &mut fastq::RecordSet) -> Option<Result<(), fastq::Error>> {
        self.read_record_set(rset)
    }
}
<commit_msg>Add more flexible methods for parallel processing.<commit_after>//! Experiments with parallel processing
//!
//! The provided functions focus on the possibility of
//! returning results while the parser proceeds. Sequences are processesd in
//! batches (`RecordSet`) because sending across channels has a performance
//! impact. FASTA/FASTQ records can be accessed in both the 'worker' function and
//! (after processing) a function running in the main thread.
//!
//! # Search first occurrence of a sequence pattern
//!
//! ```no_run
//! use seq_io::fastq::{Reader,Record};
//! use seq_io::parallel::read_parallel;
//!
//! let reader = Reader::from_path("seqs.fastq").unwrap();
//!
//! read_parallel(reader, 4, 2, |record_set| {
//!     // this function does the heavy work
//!     for (i, record) in record_set.into_iter().enumerate() {
//!         // this is not very efficient code, just for demonstration
//!         if let Some(pos) = record.seq().windows(3).position(|s| s == b"AAA") {
//!             return Some((i, pos));
//!         }
//!     }
//!     None
//! }, |record_sets| {
//!     // This function runs in the main thread. It provides a streaming iterator over
//!     // record sets and the corresponding return values from the worker function
//!     // (not necessarily in the same order as in the file)
//!     while let Some(result) = record_sets.next() {
//!         let (record_set, found) = result.unwrap();
//!         if let Some((i, pos)) = found {
//!             let record = record_set.into_iter().nth(i).unwrap();
//!             println!("Found AAA in record {} at position {}", record.id().unwrap(), pos);
//!              // this will also stop the worker threads, although with some delay
//!             return;
//!         }
//!     }
//! });
//! ```
//!
//! # Per-record processsing
//! The `parallel_fasta` / `parallel_fastq` functions are designed to efficiently pass
//! results for **each record** to the main thread without having to care about record sets.
//! This example filters sequences by the occurrence of a pattern:
//!
//! ```no_run
//! use seq_io::fastq::{Reader,Record};
//! use seq_io::parallel::parallel_fastq;
//! use std::fs::File;
//! use std::io::BufWriter;
//!
//! let reader = Reader::from_path("seqs.fastq").unwrap();
//! let mut writer = BufWriter::new(File::create("filtered.fastq").unwrap());
//!
//! parallel_fastq(reader, 4, 2,
//!     |record, found| { // runs in worker
//!         *found = record.seq().windows(3).position(|s| s == b"AAA").is_some();
//!     },
//!     |record, found| { // runs in main thread
//!         if *found {
//!             record.write(&mut writer).unwrap();
//!         }
//!         // Some(value) will stop the reader, and the value will be returned.
//!         // In the case of never stopping, we need to give the compiler a hint about the
//!         // type parameter, thus the special 'turbofish' notation is needed,
//!         // hoping on progress here: https://github.com/rust-lang/rust/issues/27336
//!         None::<()>
//! }).unwrap();
//! ```

use std::io;

extern crate crossbeam;
extern crate scoped_threadpool;

use std::sync::mpsc;
use std::marker::PhantomData;

pub trait Reader {
    type DataSet: Send;
    type Err: Send;
    fn fill_data(&mut self, record: &mut Self::DataSet) -> Option<Result<(), Self::Err>>;
}

pub fn read_parallel<R, O, W, F, Out>(reader: R, n_threads: u32, queue_len: usize, work: W, func: F) -> Out
    where R: Reader + Send,
          R::DataSet: Default,
          O: Default + Send,
          W: Send + Sync + Fn(&mut R::DataSet) -> O,
          F: FnMut(&mut ParallelRecordsets<R::DataSet, R::Err, O>) -> Out,
{
    read_parallel_init::<_, (), _, (), _, _, (), _, _, Out>(
        n_threads, queue_len,
        || Ok::<_, ()>(reader),
        || Ok::<_, ()>(R::DataSet::default()),
        work, func
    ).unwrap()
}

/// This function allows initiating the reader and datasets using a closure.
/// This is more flexible and allows readers not to be `Send`
pub fn read_parallel_init<R, E, Ri, Er, O, Di, Ed, W, F, Out>(
    n_threads: u32,
    queue_len: usize,
    reader_init: Ri,
    mut dataset_init: Di,
    work: W,
    func: F
) -> Result<Out, E>
where R: Reader,
      Ri: Send + FnOnce() -> Result<R, Er>,
      Er: Send,
      E: From<Er> + From<Ed>,
      O: Send,
      Di: Send + Sync + FnMut() -> Result<R::DataSet, Ed>,
      W: Send + Sync + Fn(&mut R::DataSet) -> O,
      F: FnOnce(&mut ParallelRecordsets<R::DataSet, R::Err, O>) -> Out,
{

    let (done_send, done_recv) = mpsc::sync_channel(queue_len);
    let (empty_send, empty_recv): (mpsc::SyncSender<R::DataSet>, _) = mpsc::sync_channel(queue_len);

    crossbeam::scope(|scope| {

        let handle = scope.spawn::<_, Result<(), Er>>(move || {

            let mut reader = reader_init()?;

            let mut pool = scoped_threadpool::Pool::new(n_threads);

            pool.scoped(|pool_scope| {

                let work = &work;

                loop {
                    // recycle an old DataSet sent back after use
                    let mut data =
                        if let Ok(r) = empty_recv.recv() {
                            r
                        } else {
                            // ParallelRecordsets dropped -> stop
                            return;
                        };

                    let done_send = done_send.clone();

                    if let Some(res) = reader.fill_data(&mut data) {
                        match res {
                            Ok(_) => {
                                // expensive work carried out by func()
                                pool_scope.execute(move || {

                                    let out = work(&mut data);

                                    done_send.send(Some(Ok((data, out)))).ok();
                                });
                            }
                            Err(e) => {
                                done_send.send(Some(Err(e))).ok();
                                break;
                            }
                        }
                    } else {
                        break;
                    }
                }

                pool_scope.join_all();

                done_send.send(None).ok();
            });
            Ok(())
        });

        for _ in 0..queue_len  {
            empty_send.send(dataset_init()?).unwrap();
        }

        let mut rsets = ParallelRecordsets {
            empty_send: empty_send,
            done_recv: done_recv,
            current_recordset: dataset_init()?,
        };

        let out = func(&mut rsets);
        ::std::mem::drop(rsets);

        handle.join()?;
        Ok(out)
    })
}

pub struct ParallelRecordsets<R, E, O>
    where R: Send,
          E: Send,
          O: Send,
{
    empty_send: mpsc::SyncSender<R>,
    done_recv: mpsc::Receiver<Option<Result<(R, O), E>>>,
    current_recordset: R,
}

impl<R, E, O> ParallelRecordsets<R, E, O>
    where R: Send,
          E: Send,
          O: Send,
 {
    pub fn next<'b>(&'b mut self) -> Option<Result<(&'b mut R, O), E>> {
        self.done_recv.recv().unwrap().map(move |result| {
            match result {
                Ok((r, o)) => {
                     let prev_rset = ::std::mem::replace(&mut self.current_recordset, r);
                     self.empty_send.send(prev_rset).ok(); // error: channel closed is not a problem, happens after calling stop()
                     Ok((&mut self.current_recordset, o))
                }
                Err(e) => Err(e)
            }
        })
    }
}

#[macro_export]
macro_rules! parallel_record_impl {
    ($name:ident, $name_init:ident, $io_r:tt, $rdr:ty, $dataset:ty, $record:ty, $err:ty) => {

        /// Function reading records in a different thread.
        /// processing them in another worker thread
        /// and finally returning the results to the main thread.
        ///
        /// The output is passed around between threads, allowing
        /// allocations to be 'recycled'. This also means, that the
        /// data must implement `Default`, and data handled to the 'work'
        /// function will receive 'old' data from earlier records which
        /// has to be overwritten.
        pub fn $name<$io_r, D, W, F, Out>(
            reader: $rdr,
            n_threads: u32,
            queue_len: usize,
            work: W,
            mut func: F
        ) -> Result<Option<Out>, $err>
            where $io_r: io::Read + Send,
                  D: Default + Send,
                  W: Send + Sync + Fn($record, &mut D),
                  F: FnMut($record, &mut D) -> Option<Out>,
        {
            $name_init(
                n_threads, queue_len,
                || Ok::<_, $err>(reader),
                || Ok::<_, $err>(D::default()),
                || Ok::<_, $err>(()),
                |record, record_out, _| work(record, record_out),
                |record, record_out, _| func(record, record_out),
            )
        }

        /// More customisable function doing per-record processing with
        /// closures for initialization and moer options.
        ///
        /// The reader is lazily initialized in a closure (`reader_init`) and therefore does not
        /// need to implement `Send`. There is also an initializer for the output data
        /// for each record, therefore the type is not required to implement.
        /// `Default` (`record_data_init`). Finally, each record set can have
        /// its own data (kind of thread local data, but actually passed around
        /// with the record set) (`rset_data_init`).
        pub fn $name_init<Ri, E, $io_r, Er, Di, D, Ed, Si, S, Es, W, F, Out>(
            n_threads: u32,
            queue_len: usize,
            reader_init: Ri,
            record_data_init: Di,
            rset_data_init: Si,
            work: W,
            mut func: F
        ) -> Result<Option<Out>, E>

            where $io_r: io::Read,
                  Ri: Send + FnOnce() -> Result<$rdr, Er>,
                  Er: Send, Ed: Send,
                  E: From<$err> + From<Er> + From<Ed> + From<Es>,
                  Di: Fn() -> Result<D, Ed> + Send + Sync,
                  D: Send,
                  Si: Fn() -> Result<S, Es> + Send + Sync,
                  S: Send,
                  W: Send + Sync + Fn($record, &mut D, &mut S),
                  F: FnMut($record, &mut D, &mut S) -> Option<Out>,
        {
            let res = $crate::parallel::read_parallel_init::<_, E, _, _, _, _, Es, _, _, _>(
                n_threads, queue_len,
                || reader_init().map($crate::parallel::ReusableReader::<$rdr, (Vec<D>, S)>::new),
                || rset_data_init().map(|d| (<$dataset>::default(), (vec![], d))),
                |&mut (ref mut recordset, (ref mut out, ref mut rset_data))| {
                    let mut record_iter = recordset.into_iter();
                    //let &mut (ref mut out, ref mut rset_data): &mut (Vec<D>, Option<S>) = &mut d.1;
                    for mut d in out.iter_mut().zip(&mut record_iter) {
                        work(d.1, &mut d.0, rset_data);
                    }
                    for record in record_iter {
                        out.push(record_data_init()?);
                        work(record, out.last_mut().unwrap(), rset_data);
                    }
                    Ok::<_, Ed>(())

                }, |records| {
                    while let Some(result) = records.next() {
                        let (r, res) = result?;
                        res?;
                        let &mut (ref records, (ref mut out, ref mut rset_data)) = r;
                        for x in records.into_iter().zip(out.iter_mut()) {
                            if let Some(out) = func(x.0, x.1, rset_data) {
                                return Ok(Some(out));
                            }
                        }
                    }
                    Ok(None)
                }
            )?;
            res
        }
     };

}

parallel_record_impl!(parallel_fasta, parallel_fasta_init, R, fasta::Reader<R>, fasta::RecordSet, fasta::RefRecord, fasta::Error);

parallel_record_impl!(parallel_fastq, parallel_fastq_init, R, fastq::Reader<R>, fastq::RecordSet, fastq::RefRecord, fastq::Error);

/// Wrapper for `parallel::Reader` instances allowing
/// the output to be reused in order to save allocations.
/// Used by `parallel_fasta`/`parallel_fastq`
///
/// ```no_run
/// use seq_io::fastq::{Reader,Record,RecordSet};
/// use seq_io::parallel::{read_parallel,ReusableReader};
///
/// let inner = Reader::from_path("seqs.fastq").unwrap();
/// let reader = ReusableReader::new(inner);
///
/// read_parallel(reader, 4, 2, |&mut (ref record_set, ref mut out): &mut (RecordSet, Vec<bool>)| {
///     out.clear();
///     for record in record_set {
///         let found = record.seq().windows(3).position(|s| s == b"AAA").is_some();
///         out.push(found);
///     }
/// }, |record_sets| {
///     while let Some(result) = record_sets.next() {
///         let &(ref record_set, ref out) = &*result.unwrap().0;
///         for (record, found) in record_set.into_iter().zip(out) {
///             // ...
///         }
///     }
/// });
/// ```
pub struct ReusableReader<P, O>(P, PhantomData<O>);

impl<P, O> ReusableReader<P, O> {
    pub fn new(p: P) -> ReusableReader<P, O> {
        ReusableReader(p, PhantomData)
    }
}

impl<P, O> Reader for ReusableReader<P, O>
    where P: Reader,
          O: Send {

    type DataSet = (P::DataSet, O);
    type Err = P::Err;
    fn fill_data(&mut self, data: &mut Self::DataSet) -> Option<Result<(), P::Err>> {
        self.0.fill_data(&mut data.0)
    }
}

/// Using this function currently does not work due to a
/// [compiler bug](https://github.com/rust-lang/rust/issues/42950).
///
/// `parallel_fasta`/`parallel_fastq` provide the same functionality for now
/// (implemented using `parallel_record_impl` macro)
pub fn parallel_records<R, O, W, F, Out>(parser: R, n_threads: u32, queue_len: usize, work: W, mut func: F) -> Result<Option<Out>, R::Err>
    where R: Reader + Send,
          for<'a> &'a R::DataSet: IntoIterator,
          R::DataSet: Default,
          O: Default + Send,
          W: Send + Sync,
          W: Fn(<&R::DataSet as IntoIterator>::Item, &mut O),
          F: FnMut(<&R::DataSet as IntoIterator>::Item, &O) -> Option<Out>,
{
    let reader = ReusableReader(parser, PhantomData);

    read_parallel(reader, n_threads, queue_len, |d| {
        let mut iter = d.0.into_iter();
        let out: &mut Vec<O> = &mut d.1;
        for mut x in out.iter_mut().zip(&mut iter) {
            work(x.1, &mut x.0);
        }
        for i in iter {
            out.push(O::default());
            work(i, out.last_mut().unwrap())
        }

    }, |records| {
        while let Some(result) = records.next() {
            let (r, _) = result?;
            for x in r.0.into_iter().zip(&r.1) {
                if let Some(out) = func(x.0, x.1) {
                    return Ok(Some(out));
                }
            }
        }
        Ok(None)
    })
}

// trait impls

use super::fasta;

impl<R, S> Reader for fasta::Reader<R, S>
    where R: io::Read,
          S: super::strategy::BufStrategy + Send
{
    type DataSet = fasta::RecordSet;
    type Err = fasta::Error;
    fn fill_data(&mut self, rset: &mut fasta::RecordSet) -> Option<Result<(), fasta::Error>> {
        self.read_record_set(rset)
    }
}

use super::fastq;

impl<R, S> Reader for fastq::Reader<R, S>
    where R: io::Read,
          S: super::strategy::BufStrategy + Send
{
    type DataSet = fastq::RecordSet;
    type Err = fastq::Error;
    fn fill_data(&mut self, rset: &mut fastq::RecordSet) -> Option<Result<(), fastq::Error>> {
        self.read_record_set(rset)
    }
}
<|endoftext|>


<commit_before>//! Define methods for triangular matrices

use ndarray::*;
use num_traits::Zero;

use super::layout::*;
use super::error::*;
use super::impl2::*;

pub use super::impl2::Diag;

/// solve a triangular system with upper triangular matrix
pub trait SolveTriangular<Rhs> {
    type Output;
    fn solve_triangular(&self, UPLO, Diag, Rhs) -> Result<Self::Output>;
}

impl<A, Si, So, D> SolveTriangular<ArrayBase<So, D>> for ArrayBase<Si, Ix2>
    where A: LapackScalar,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A>,
          D: Dimension,
          ArrayBase<So, D>: AllocatedArrayMut<Elem = A>
{
    type Output = ArrayBase<So, D>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, mut b: ArrayBase<So, D>) -> Result<Self::Output> {
        self.solve_triangular(uplo, diag, &mut b)?;
        Ok(b)
    }
}

impl<'a, A, Si, So, D> SolveTriangular<&'a mut ArrayBase<So, D>> for ArrayBase<Si, Ix2>
    where A: LapackScalar,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A>,
          D: Dimension,
          ArrayBase<So, D>: AllocatedArrayMut<Elem = A>
{
    type Output = &'a mut ArrayBase<So, D>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, mut b: &'a mut ArrayBase<So, D>) -> Result<Self::Output> {
        let la = self.layout()?;
        let lb = b.layout()?;
        let a_ = self.as_allocated()?;
        A::solve_triangular(la, lb, uplo, diag, a_, b.as_allocated_mut()?)?;
        Ok(b)
    }
}

impl<'a, A, Si, So, D> SolveTriangular<&'a ArrayBase<So, D>> for ArrayBase<Si, Ix2>
    where A: LapackScalar + Copy,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A> + DataOwned,
          D: Dimension,
          ArrayBase<So, D>: AllocatedArrayMut<Elem = A>
{
    type Output = ArrayBase<So, D>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, b: &'a ArrayBase<So, D>) -> Result<Self::Output> {
        let b = replicate(b);
        self.solve_triangular(uplo, diag, b)
    }
}

pub trait IntoTriangular<T> {
    fn into_triangular(self, UPLO) -> T;
}

impl<'a, A, S> IntoTriangular<&'a mut ArrayBase<S, Ix2>> for &'a mut ArrayBase<S, Ix2>
    where A: Zero,
          S: DataMut<Elem = A>
{
    fn into_triangular(self, uplo: UPLO) -> &'a mut ArrayBase<S, Ix2> {
        match uplo {
            UPLO::Upper => {
                for ((i, j), val) in self.indexed_iter_mut() {
                    if i > j {
                        *val = A::zero();
                    }
                }
            }
            UPLO::Lower => {
                for ((i, j), val) in self.indexed_iter_mut() {
                    if i < j {
                        *val = A::zero();
                    }
                }
            }
        }
        self
    }
}

impl<A, S> IntoTriangular<ArrayBase<S, Ix2>> for ArrayBase<S, Ix2>
    where A: Zero,
          S: DataMut<Elem = A>
{
    fn into_triangular(mut self, uplo: UPLO) -> ArrayBase<S, Ix2> {
        (&mut self).into_triangular(uplo);
        self
    }
}

pub fn drop_upper<A: Zero, S>(a: ArrayBase<S, Ix2>) -> ArrayBase<S, Ix2>
    where S: DataMut<Elem = A>
{
    a.into_triangular(UPLO::Lower)
}

pub fn drop_lower<A: Zero, S>(a: ArrayBase<S, Ix2>) -> ArrayBase<S, Ix2>
    where S: DataMut<Elem = A>
{
    a.into_triangular(UPLO::Upper)
}
<commit_msg>Revise impl SolveTriangular<commit_after>//! Define methods for triangular matrices

use ndarray::*;
use num_traits::Zero;

use super::layout::*;
use super::error::*;
use super::impl2::*;

pub use super::impl2::Diag;

/// solve a triangular system with upper triangular matrix
pub trait SolveTriangular<Rhs> {
    type Output;
    fn solve_triangular(&self, UPLO, Diag, Rhs) -> Result<Self::Output>;
}

impl<A, Si, So> SolveTriangular<ArrayBase<So, Ix2>> for ArrayBase<Si, Ix2>
    where A: LapackScalar + Copy,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A> + DataOwned
{
    type Output = ArrayBase<So, Ix2>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, mut b: ArrayBase<So, Ix2>) -> Result<Self::Output> {
        self.solve_triangular(uplo, diag, &mut b)?;
        Ok(b)
    }
}

impl<'a, A, Si, So> SolveTriangular<&'a mut ArrayBase<So, Ix2>> for ArrayBase<Si, Ix2>
    where A: LapackScalar + Copy,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A> + DataOwned
{
    type Output = &'a mut ArrayBase<So, Ix2>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, mut b: &'a mut ArrayBase<So, Ix2>) -> Result<Self::Output> {
        let la = self.layout()?;
        let a_ = self.as_allocated()?;
        let lb = b.layout()?;
        if !la.same_order(&lb) {
            data_transpose(b)?;
        }
        let lb = b.layout()?;
        A::solve_triangular(la, lb, uplo, diag, a_, b.as_allocated_mut()?)?;
        Ok(b)
    }
}

impl<'a, A, Si, So> SolveTriangular<&'a ArrayBase<So, Ix2>> for ArrayBase<Si, Ix2>
    where A: LapackScalar + Copy,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A> + DataOwned
{
    type Output = ArrayBase<So, Ix2>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, b: &'a ArrayBase<So, Ix2>) -> Result<Self::Output> {
        let b = replicate(b);
        self.solve_triangular(uplo, diag, b)
    }
}

impl<A, Si, So> SolveTriangular<ArrayBase<So, Ix1>> for ArrayBase<Si, Ix2>
    where A: LapackScalar + Copy,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A> + DataOwned
{
    type Output = ArrayBase<So, Ix1>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, b: ArrayBase<So, Ix1>) -> Result<Self::Output> {
        let b = into_col_vec(b);
        let b = self.solve_triangular(uplo, diag, b)?;
        Ok(into_vec(b))
    }
}

impl<'a, A, Si, So> SolveTriangular<&'a ArrayBase<So, Ix1>> for ArrayBase<Si, Ix2>
    where A: LapackScalar + Copy,
          Si: Data<Elem = A>,
          So: DataMut<Elem = A> + DataOwned
{
    type Output = ArrayBase<So, Ix1>;

    fn solve_triangular(&self, uplo: UPLO, diag: Diag, b: &'a ArrayBase<So, Ix1>) -> Result<Self::Output> {
        let b = replicate(b);
        self.solve_triangular(uplo, diag, b)
    }
}

pub trait IntoTriangular<T> {
    fn into_triangular(self, UPLO) -> T;
}

impl<'a, A, S> IntoTriangular<&'a mut ArrayBase<S, Ix2>> for &'a mut ArrayBase<S, Ix2>
    where A: Zero,
          S: DataMut<Elem = A>
{
    fn into_triangular(self, uplo: UPLO) -> &'a mut ArrayBase<S, Ix2> {
        match uplo {
            UPLO::Upper => {
                for ((i, j), val) in self.indexed_iter_mut() {
                    if i > j {
                        *val = A::zero();
                    }
                }
            }
            UPLO::Lower => {
                for ((i, j), val) in self.indexed_iter_mut() {
                    if i < j {
                        *val = A::zero();
                    }
                }
            }
        }
        self
    }
}

impl<A, S> IntoTriangular<ArrayBase<S, Ix2>> for ArrayBase<S, Ix2>
    where A: Zero,
          S: DataMut<Elem = A>
{
    fn into_triangular(mut self, uplo: UPLO) -> ArrayBase<S, Ix2> {
        (&mut self).into_triangular(uplo);
        self
    }
}

pub fn drop_upper<A: Zero, S>(a: ArrayBase<S, Ix2>) -> ArrayBase<S, Ix2>
    where S: DataMut<Elem = A>
{
    a.into_triangular(UPLO::Lower)
}

pub fn drop_lower<A: Zero, S>(a: ArrayBase<S, Ix2>) -> ArrayBase<S, Ix2>
    where S: DataMut<Elem = A>
{
    a.into_triangular(UPLO::Upper)
}
<|endoftext|>


<commit_before>/// Used to run some code when a value goes out of scope.
/// This is sometimes called a 'destructor'.
///
/// When a value goes out of scope, it will have its `drop` method called if
/// its type implements `Drop`. Then, any fields the value contains will also
/// be dropped recursively.
///
/// Because of this recursive dropping, you do not need to implement this trait
/// unless your type needs its own destructor logic.
///
/// Refer to [the chapter on `Drop` in *The Rust Programming Language*][book]
/// for some more elaboration.
///
/// [book]: ../../book/ch15-03-drop.html
///
/// # Examples
///
/// ## Implementing `Drop`
///
/// The `drop` method is called when `_x` goes out of scope, and therefore
/// `main` prints `Dropping!`.
///
/// ```
/// struct HasDrop;
///
/// impl Drop for HasDrop {
///     fn drop(&mut self) {
///         println!("Dropping!");
///     }
/// }
///
/// fn main() {
///     let _x = HasDrop;
/// }
/// ```
///
/// ## Dropping is done recursively
///
/// When `outer` goes out of scope, the `drop` method will be called first for
/// `Outer`, then for `Inner`. Therefore, `main` prints `Dropping Outer!` and
/// then `Dropping Inner!`.
///
/// ```
/// struct Inner;
/// struct Outer(Inner);
///
/// impl Drop for Inner {
///     fn drop(&mut self) {
///         println!("Dropping Inner!");
///     }
/// }
///
/// impl Drop for Outer {
///     fn drop(&mut self) {
///         println!("Dropping Outer!");
///     }
/// }
///
/// fn main() {
///     let _x = Outer(Inner);
/// }
/// ```
///
/// ## Variables are dropped in reverse order of declaration
///
/// `_first` is declared first and `_second` is declared second, so `main` will
/// print `Declared second!` and then `Declared first!`.
///
/// ```
/// struct PrintOnDrop(&'static str);
///
/// impl Drop for PrintOnDrop {
///     fn drop(&mut self) {
///         println!("{}", self.0);
///     }
/// }
///
/// fn main() {
///     let _first = PrintOnDrop("Declared first!");
///     let _second = PrintOnDrop("Declared second!");
/// }
/// ```
#[lang = "drop"]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Drop {
    /// Executes the destructor for this type.
    ///
    /// This method is called implicitly when the value goes out of scope,
    /// and cannot be called explicitly (this is compiler error [E0040]).
    /// However, the [`std::mem::drop`] function in the prelude can be
    /// used to call the argument's `Drop` implementation.
    ///
    /// When this method has been called, `self` has not yet been deallocated.
    /// That only happens after the method is over.
    /// If this wasn't the case, `self` would be a dangling reference.
    ///
    /// # Panics
    ///
    /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`]
    /// in a `drop` implementation will likely abort.
    ///
    /// Note that even if this panics, the value is considered to be dropped;
    /// you must not cause `drop` to be called again. This is normally automatically
    /// handled by the compiler, but when using unsafe code, can sometimes occur
    /// unintentionally, particularly when using [`std::ptr::drop_in_place`].
    ///
    /// [E0040]: ../../error-index.html#E0040
    /// [`panic!`]: ../macro.panic.html
    /// [`std::mem::drop`]: ../../std/mem/fn.drop.html
    /// [`std::ptr::drop_in_place`]: ../../std/ptr/fn.drop_in_place.html
    #[stable(feature = "rust1", since = "1.0.0")]
    fn drop(&mut self);
}
<commit_msg>Rollup merge of #71598 - lcnr:drop-docs, r=RalfJung,Mark-Simulacrum<commit_after>/// Custom code within the destructor.
///
/// When a value is no longer needed, Rust will run a "destructor" on that value.
/// The most common way that a value is no longer needed is when it goes out of
/// scope. Destructors may still run in other circumstances, but we're going to
/// focus on scope for the examples here. To learn about some of those other cases,
/// please see [the reference] section on destructors.
///
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// This destructor consists of two components:
/// - A call to `Drop::drop` for that value, if this special `Drop` trait is implemented for its type.
/// - The automatically generated "drop glue" which recursively calls the destructors
///     of the all fields of this value.
///
/// As Rust automatically calls the destructors of all contained fields,
/// you don't have to implement `Drop` in most cases. But there are some cases where
/// it is useful, for example for types which directly manage a resource.
/// That resource may be memory, it may be a file descriptor, it may be a network socket.
/// Once a value of that type is no longer going to be used, it should "clean up" its
/// resource by freeing the memory or closing the file or socket. This is
/// the job of a destructor, and therefore the job of `Drop::drop`.
///
/// ## Examples
///
/// To see destructors in action, let's take a look at the following program:
///
/// ```rust
/// struct HasDrop;
///
/// impl Drop for HasDrop {
///     fn drop(&mut self) {
///         println!("Dropping HasDrop!");
///     }
/// }
///
/// struct HasTwoDrops {
///     one: HasDrop,
///     two: HasDrop,
/// }
///
/// impl Drop for HasTwoDrops {
///     fn drop(&mut self) {
///         println!("Dropping HasTwoDrops!");
///     }
/// }
///
/// fn main() {
///     let _x = HasTwoDrops { one: HasDrop, two: HasDrop };
///     println!("Running!");
/// }
/// ```
///
/// Rust will first call `Drop::drop` for `_x` and then for both `_x.one` and `_x.two`,
/// meaning that running this will print
///
/// ```text
/// Running!
/// Dropping HasTwoDrops!
/// Dropping HasDrop!
/// Dropping HasDrop!
/// ```
///
/// Even if we remove the implementation of `Drop` for `HasTwoDrop`, the destructors of its fields are still called.
/// This would result in
///
/// ```test
/// Running!
/// Dropping HasDrop!
/// Dropping HasDrop!
/// ```
///
/// ## You cannot call `Drop::drop` yourself
///
/// Because `Drop::drop` is used to clean up a value, it may be dangerous to use this value after
/// the method has been called. As `Drop::drop` does not take ownership of its input,
/// Rust prevents misuse by not allowing you to call `Drop::drop` directly.
///
/// In other words, if you tried to explicitly call `Drop::drop` in the above example, you'd get a compiler error.
///
/// If you'd like explicitly call the destructor of a value, [`std::mem::drop`] can be used instead.
///
/// [`std::mem::drop`]: ../../std/mem/fn.drop.html
///
/// ## Drop order
///
/// Which of our two `HasDrop` drops first, though? For structs, it's the same
/// order that they're declared: first `one`, then `two`. If you'd like to try
/// this yourself, you can modify `HasDrop` above to contain some data, like an
/// integer, and then use it in the `println!` inside of `Drop`. This behavior is
/// guaranteed by the language.
///
/// Unlike for structs, local variables are dropped in reverse order:
///
/// ```rust
/// struct Foo;
///
/// impl Drop for Foo {
///     fn drop(&mut self) {
///         println!("Dropping Foo!")
///     }
/// }
///
/// struct Bar;
///
/// impl Drop for Bar {
///     fn drop(&mut self) {
///         println!("Dropping Bar!")
///     }
/// }
///
/// fn main() {
///     let _foo = Foo;
///     let _bar = Bar;
/// }
/// ```
///
/// This will print
///
/// ```text
/// Dropping Bar!
/// Dropping Foo!
/// ```
///
/// Please see [the reference] for the full rules.
///
/// [the reference]: https://doc.rust-lang.org/reference/destructors.html
///
/// ## `Copy` and `Drop` are exclusive
///
/// You cannot implement both [`Copy`] and `Drop` on the same type. Types that
/// are `Copy` get implicitly duplicated by the compiler, making it very
/// hard to predict when, and how often destructors will be executed. As such,
/// these types cannot have destructors.
///
/// [`Copy`]: ../../std/marker/trait.Copy.html
#[lang = "drop"]
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Drop {
    /// Executes the destructor for this type.
    ///
    /// This method is called implicitly when the value goes out of scope,
    /// and cannot be called explicitly (this is compiler error [E0040]).
    /// However, the [`std::mem::drop`] function in the prelude can be
    /// used to call the argument's `Drop` implementation.
    ///
    /// When this method has been called, `self` has not yet been deallocated.
    /// That only happens after the method is over.
    /// If this wasn't the case, `self` would be a dangling reference.
    ///
    /// # Panics
    ///
    /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`]
    /// in a `drop` implementation will likely abort.
    ///
    /// Note that even if this panics, the value is considered to be dropped;
    /// you must not cause `drop` to be called again. This is normally automatically
    /// handled by the compiler, but when using unsafe code, can sometimes occur
    /// unintentionally, particularly when using [`std::ptr::drop_in_place`].
    ///
    /// [E0040]: ../../error-index.html#E0040
    /// [`panic!`]: ../macro.panic.html
    /// [`std::mem::drop`]: ../../std/mem/fn.drop.html
    /// [`std::ptr::drop_in_place`]: ../../std/ptr/fn.drop_in_place.html
    #[stable(feature = "rust1", since = "1.0.0")]
    fn drop(&mut self);
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{
    action_plugins::{
        check_ha, check_stonith, lctl,
        ntp::action_configure,
        ostpool, package_installed,
        stratagem::{action_purge, action_warning, server},
    },
    systemd,
};
use iml_util::action_plugins;
use iml_wire_types::ActionName;
use tracing::info;

/// The registry of available actions to the `AgentDaemon`.
/// Add new Actions to the fn body as they are created.
pub fn create_registry() -> action_plugins::Actions {
    let map = action_plugins::Actions::new()
        .add_plugin("start_unit", systemd::start_unit)
        .add_plugin("stop_unit", systemd::stop_unit)
        .add_plugin("enable_unit", systemd::enable_unit)
        .add_plugin("disable_unit", systemd::disable_unit)
        .add_plugin("restart_unit", systemd::restart_unit)
        .add_plugin("get_unit_run_state", systemd::get_run_state)
        .add_plugin("package_installed", package_installed::package_installed)
        .add_plugin("start_scan_stratagem", server::trigger_scan)
        .add_plugin("stream_fidlists_stratagem", server::stream_fidlists)
        .add_plugin("action_warning_stratagem", action_warning::read_mailbox)
        .add_plugin("action_purge_stratagem", action_purge::read_mailbox)
        .add_plugin("action_check_ha", check_ha::check_ha)
        .add_plugin("action_check_stonith", check_stonith::check_stonith)
        .add_plugin("lctl", lctl::lctl)
        .add_plugin("ostpool_create", ostpool::action_pool_create)
        .add_plugin("ostpool_wait", ostpool::action_pool_wait)
        .add_plugin("ostpool_destroy", ostpool::action_pool_destroy)
        .add_plugin("ostpool_add", ostpool::action_pool_add)
        .add_plugin("ostpool_remove", ostpool::action_pool_remove)
        .add_plugin(
            "configure_ntp",
            action_configure::update_and_write_new_config,
        );

    info!("Loaded the following ActionPlugins:");

    for ActionName(key) in map.keys() {
        info!("{}", key)
    }

    map
}
<commit_msg>agent: Add get_kernel action to action_plugins (#1406)<commit_after>// Copyright (c) 2019 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{
    action_plugins::{
        check_ha, check_kernel, check_stonith, lctl,
        ntp::action_configure,
        ostpool, package_installed,
        stratagem::{action_purge, action_warning, server},
    },
    systemd,
};
use iml_util::action_plugins;
use iml_wire_types::ActionName;
use tracing::info;

/// The registry of available actions to the `AgentDaemon`.
/// Add new Actions to the fn body as they are created.
pub fn create_registry() -> action_plugins::Actions {
    let map = action_plugins::Actions::new()
        .add_plugin("start_unit", systemd::start_unit)
        .add_plugin("stop_unit", systemd::stop_unit)
        .add_plugin("enable_unit", systemd::enable_unit)
        .add_plugin("disable_unit", systemd::disable_unit)
        .add_plugin("restart_unit", systemd::restart_unit)
        .add_plugin("get_unit_run_state", systemd::get_run_state)
        .add_plugin("package_installed", package_installed::package_installed)
        .add_plugin("start_scan_stratagem", server::trigger_scan)
        .add_plugin("stream_fidlists_stratagem", server::stream_fidlists)
        .add_plugin("action_warning_stratagem", action_warning::read_mailbox)
        .add_plugin("action_purge_stratagem", action_purge::read_mailbox)
        .add_plugin("action_check_ha", check_ha::check_ha)
        .add_plugin("action_check_stonith", check_stonith::check_stonith)
        .add_plugin("get_kernel", check_kernel::get_kernel)
        .add_plugin("lctl", lctl::lctl)
        .add_plugin("ostpool_create", ostpool::action_pool_create)
        .add_plugin("ostpool_wait", ostpool::action_pool_wait)
        .add_plugin("ostpool_destroy", ostpool::action_pool_destroy)
        .add_plugin("ostpool_add", ostpool::action_pool_add)
        .add_plugin("ostpool_remove", ostpool::action_pool_remove)
        .add_plugin(
            "configure_ntp",
            action_configure::update_and_write_new_config,
        );

    info!("Loaded the following ActionPlugins:");

    for ActionName(key) in map.keys() {
        info!("{}", key)
    }

    map
}
<|endoftext|>


<commit_before>use crate::attr::HasAttrs;
use crate::feature_gate::{
    feature_err,
    EXPLAIN_STMT_ATTR_SYNTAX,
    Features,
    get_features,
    GateIssue,
};
use crate::attr;
use crate::ast;
use crate::edition::Edition;
use crate::mut_visit::*;
use crate::parse::{token, ParseSess};
use crate::ptr::P;
use crate::symbol::sym;
use crate::util::map_in_place::MapInPlace;

use errors::Applicability;
use smallvec::SmallVec;

/// A folder that strips out items that do not belong in the current configuration.
pub struct StripUnconfigured<'a> {
    pub sess: &'a ParseSess,
    pub features: Option<&'a Features>,
}

// `cfg_attr`-process the crate's attributes and compute the crate's features.
pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition,
                allow_features: &Option<Vec<String>>) -> (ast::Crate, Features) {
    let features;
    {
        let mut strip_unconfigured = StripUnconfigured {
            sess,
            features: None,
        };

        let unconfigured_attrs = krate.attrs.clone();
        let err_count = sess.span_diagnostic.err_count();
        if let Some(attrs) = strip_unconfigured.configure(krate.attrs) {
            krate.attrs = attrs;
        } else { // the entire crate is unconfigured
            krate.attrs = Vec::new();
            krate.module.items = Vec::new();
            return (krate, Features::new());
        }

        features = get_features(&sess.span_diagnostic, &krate.attrs, edition, allow_features);

        // Avoid reconfiguring malformed `cfg_attr`s
        if err_count == sess.span_diagnostic.err_count() {
            strip_unconfigured.features = Some(&features);
            strip_unconfigured.configure(unconfigured_attrs);
        }
    }

    (krate, features)
}

macro_rules! configure {
    ($this:ident, $node:ident) => {
        match $this.configure($node) {
            Some(node) => node,
            None => return Default::default(),
        }
    }
}

impl<'a> StripUnconfigured<'a> {
    pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
        self.process_cfg_attrs(&mut node);
        if self.in_cfg(node.attrs()) { Some(node) } else { None }
    }

    /// Parse and expand all `cfg_attr` attributes into a list of attributes
    /// that are within each `cfg_attr` that has a true configuration predicate.
    ///
    /// Gives compiler warnigns if any `cfg_attr` does not contain any
    /// attributes and is in the original source code. Gives compiler errors if
    /// the syntax of any `cfg_attr` is incorrect.
    pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: &mut T) {
        node.visit_attrs(|attrs| {
            attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
        });
    }

    /// Parse and expand a single `cfg_attr` attribute into a list of attributes
    /// when the configuration predicate is true, or otherwise expand into an
    /// empty list of attributes.
    ///
    /// Gives a compiler warning when the `cfg_attr` contains no attributes and
    /// is in the original source file. Gives a compiler error if the syntax of
    /// the attribute is incorrect.
    fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
        if !attr.check_name(sym::cfg_attr) {
            return vec![attr];
        }
        if attr.tokens.len() == 0 {
            self.sess.span_diagnostic
                .struct_span_err(
                    attr.span,
                    "malformed `cfg_attr` attribute input",
                ).span_suggestion(
                    attr.span,
                    "missing condition and attribute",
                    "#[cfg_attr(condition, attribute, other_attribute, ...)]".to_owned(),
                    Applicability::HasPlaceholders,
                ).note("for more information, visit \
                       <https://doc.rust-lang.org/reference/conditional-compilation.html\
                       #the-cfg_attr-attribute>")
                .emit();
            return Vec::new();
        }

        let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
            parser.expect(&token::OpenDelim(token::Paren))?;

            let cfg_predicate = parser.parse_meta_item()?;
            parser.expect(&token::Comma)?;

            // Presumably, the majority of the time there will only be one attr.
            let mut expanded_attrs = Vec::with_capacity(1);

            while !parser.check(&token::CloseDelim(token::Paren)) {
                let lo = parser.token.span.lo();
                let (path, tokens) = parser.parse_meta_item_unrestricted()?;
                expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo)));
                parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
            }

            parser.expect(&token::CloseDelim(token::Paren))?;
            Ok((cfg_predicate, expanded_attrs))
        }) {
            Ok(result) => result,
            Err(mut e) => {
                e.emit();
                return Vec::new();
            }
        };

        // Check feature gate and lint on zero attributes in source. Even if the feature is gated,
        // we still compute as if it wasn't, since the emitted error will stop compilation further
        // along the compilation.
        if expanded_attrs.len() == 0 {
            // FIXME: Emit unused attribute lint here.
        }

        if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
            // We call `process_cfg_attr` recursively in case there's a
            // `cfg_attr` inside of another `cfg_attr`. E.g.
            //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
            expanded_attrs.into_iter()
            .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute {
                id: attr::mk_attr_id(),
                style: attr.style,
                path,
                tokens,
                is_sugared_doc: false,
                span,
            }))
            .collect()
        } else {
            Vec::new()
        }
    }

    /// Determines if a node with the given attributes should be included in this configuration.
    pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
        attrs.iter().all(|attr| {
            if !is_cfg(attr) {
                return true;
            }

            let error = |span, msg, suggestion: &str| {
                let mut err = self.sess.span_diagnostic.struct_span_err(span, msg);
                if !suggestion.is_empty() {
                    err.span_suggestion(
                        span,
                        "expected syntax is",
                        suggestion.into(),
                        Applicability::MaybeIncorrect,
                    );
                }
                err.emit();
                true
            };

            let meta_item = match attr.parse_meta(self.sess) {
                Ok(meta_item) => meta_item,
                Err(mut err) => { err.emit(); return true; }
            };
            let nested_meta_items = if let Some(nested_meta_items) = meta_item.meta_item_list() {
                nested_meta_items
            } else {
                return error(meta_item.span, "`cfg` is not followed by parentheses",
                                             "cfg(/* predicate */)");
            };

            if nested_meta_items.is_empty() {
                return error(meta_item.span, "`cfg` predicate is not specified", "");
            } else if nested_meta_items.len() > 1 {
                return error(nested_meta_items.last().unwrap().span(),
                             "multiple `cfg` predicates are specified", "");
            }

            match nested_meta_items[0].meta_item() {
                Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
                None => error(nested_meta_items[0].span(),
                              "`cfg` predicate key cannot be a literal", ""),
            }
        })
    }

    /// Visit attributes on expression and statements (but not attributes on items in blocks).
    fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
        // flag the offending attributes
        for attr in attrs.iter() {
            self.maybe_emit_expr_attr_err(attr);
        }
    }

    /// If attributes are not allowed on expressions, emit an error for `attr`
    pub fn maybe_emit_expr_attr_err(&self, attr: &ast::Attribute) {
        if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
            let mut err = feature_err(self.sess,
                                      sym::stmt_expr_attributes,
                                      attr.span,
                                      GateIssue::Language,
                                      EXPLAIN_STMT_ATTR_SYNTAX);

            if attr.is_sugared_doc {
                err.help("`///` is for documentation comments. For a plain comment, use `//`.");
            }

            err.emit();
        }
    }

    pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
        let ast::ForeignMod { abi: _, items } = foreign_mod;
        items.flat_map_in_place(|item| self.configure(item));
    }

    pub fn configure_generic_params(&mut self, params: &mut Vec<ast::GenericParam>) {
        params.flat_map_in_place(|param| self.configure(param));
    }

    fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
        match vdata {
            ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) =>
                fields.flat_map_in_place(|field| self.configure(field)),
            ast::VariantData::Unit(_) => {}
        }
    }

    pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) {
        match item {
            ast::ItemKind::Struct(def, _generics) |
            ast::ItemKind::Union(def, _generics) => self.configure_variant_data(def),
            ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => {
                variants.flat_map_in_place(|variant| self.configure(variant));
                for variant in variants {
                    self.configure_variant_data(&mut variant.node.data);
                }
            }
            _ => {}
        }
    }

    pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) {
        match expr_kind {
            ast::ExprKind::Match(_m, arms) => {
                arms.flat_map_in_place(|arm| self.configure(arm));
            }
            ast::ExprKind::Struct(_path, fields, _base) => {
                fields.flat_map_in_place(|field| self.configure(field));
            }
            _ => {}
        }
    }

    pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) {
        self.visit_expr_attrs(expr.attrs());

        // If an expr is valid to cfg away it will have been removed by the
        // outer stmt or expression folder before descending in here.
        // Anything else is always required, and thus has to error out
        // in case of a cfg attr.
        //
        // N.B., this is intentionally not part of the visit_expr() function
        //     in order for filter_map_expr() to be able to avoid this check
        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
            let msg = "removing an expression is not supported in this position";
            self.sess.span_diagnostic.span_err(attr.span, msg);
        }

        self.process_cfg_attrs(expr)
    }

    pub fn configure_pat(&mut self, pat: &mut P<ast::Pat>) {
        if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.node {
            fields.flat_map_in_place(|field| self.configure(field));
        }
    }

    pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) {
        fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg));
    }
}

impl<'a> MutVisitor for StripUnconfigured<'a> {
    fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
        self.configure_foreign_mod(foreign_mod);
        noop_visit_foreign_mod(foreign_mod, self);
    }

    fn visit_item_kind(&mut self, item: &mut ast::ItemKind) {
        self.configure_item_kind(item);
        noop_visit_item_kind(item, self);
    }

    fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
        self.configure_expr(expr);
        self.configure_expr_kind(&mut expr.node);
        noop_visit_expr(expr, self);
    }

    fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
        let mut expr = configure!(self, expr);
        self.configure_expr_kind(&mut expr.node);
        noop_visit_expr(&mut expr, self);
        Some(expr)
    }

    fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
        noop_flat_map_stmt(configure!(self, stmt), self)
    }

    fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        noop_flat_map_item(configure!(self, item), self)
    }

    fn flat_map_impl_item(&mut self, item: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]> {
        noop_flat_map_impl_item(configure!(self, item), self)
    }

    fn flat_map_trait_item(&mut self, item: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> {
        noop_flat_map_trait_item(configure!(self, item), self)
    }

    fn visit_mac(&mut self, _mac: &mut ast::Mac) {
        // Don't configure interpolated AST (cf. issue #34171).
        // Interpolated AST will get configured once the surrounding tokens are parsed.
    }

    fn visit_pat(&mut self, pat: &mut P<ast::Pat>) {
        self.configure_pat(pat);
        noop_visit_pat(pat, self)
    }

    fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
        self.configure_fn_decl(&mut fn_decl);
        noop_visit_fn_decl(fn_decl, self);
    }
}

fn is_cfg(attr: &ast::Attribute) -> bool {
    attr.check_name(sym::cfg)
}
<commit_msg>Lint on 'cfg_attr(,).'<commit_after>use crate::attr::HasAttrs;
use crate::feature_gate::{
    feature_err,
    EXPLAIN_STMT_ATTR_SYNTAX,
    Features,
    get_features,
    GateIssue,
};
use crate::attr;
use crate::ast;
use crate::edition::Edition;
use crate::mut_visit::*;
use crate::parse::{token, ParseSess};
use crate::ptr::P;
use crate::symbol::sym;
use crate::util::map_in_place::MapInPlace;

use errors::Applicability;
use smallvec::SmallVec;

/// A folder that strips out items that do not belong in the current configuration.
pub struct StripUnconfigured<'a> {
    pub sess: &'a ParseSess,
    pub features: Option<&'a Features>,
}

// `cfg_attr`-process the crate's attributes and compute the crate's features.
pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition,
                allow_features: &Option<Vec<String>>) -> (ast::Crate, Features) {
    let features;
    {
        let mut strip_unconfigured = StripUnconfigured {
            sess,
            features: None,
        };

        let unconfigured_attrs = krate.attrs.clone();
        let err_count = sess.span_diagnostic.err_count();
        if let Some(attrs) = strip_unconfigured.configure(krate.attrs) {
            krate.attrs = attrs;
        } else { // the entire crate is unconfigured
            krate.attrs = Vec::new();
            krate.module.items = Vec::new();
            return (krate, Features::new());
        }

        features = get_features(&sess.span_diagnostic, &krate.attrs, edition, allow_features);

        // Avoid reconfiguring malformed `cfg_attr`s
        if err_count == sess.span_diagnostic.err_count() {
            strip_unconfigured.features = Some(&features);
            strip_unconfigured.configure(unconfigured_attrs);
        }
    }

    (krate, features)
}

macro_rules! configure {
    ($this:ident, $node:ident) => {
        match $this.configure($node) {
            Some(node) => node,
            None => return Default::default(),
        }
    }
}

impl<'a> StripUnconfigured<'a> {
    pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
        self.process_cfg_attrs(&mut node);
        if self.in_cfg(node.attrs()) { Some(node) } else { None }
    }

    /// Parse and expand all `cfg_attr` attributes into a list of attributes
    /// that are within each `cfg_attr` that has a true configuration predicate.
    ///
    /// Gives compiler warnigns if any `cfg_attr` does not contain any
    /// attributes and is in the original source code. Gives compiler errors if
    /// the syntax of any `cfg_attr` is incorrect.
    pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: &mut T) {
        node.visit_attrs(|attrs| {
            attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
        });
    }

    /// Parse and expand a single `cfg_attr` attribute into a list of attributes
    /// when the configuration predicate is true, or otherwise expand into an
    /// empty list of attributes.
    ///
    /// Gives a compiler warning when the `cfg_attr` contains no attributes and
    /// is in the original source file. Gives a compiler error if the syntax of
    /// the attribute is incorrect.
    fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Vec<ast::Attribute> {
        if attr.path != sym::cfg_attr {
            return vec![attr];
        }
        if attr.tokens.is_empty() {
            self.sess.span_diagnostic
                .struct_span_err(
                    attr.span,
                    "malformed `cfg_attr` attribute input",
                ).span_suggestion(
                    attr.span,
                    "missing condition and attribute",
                    "#[cfg_attr(condition, attribute, other_attribute, ...)]".to_owned(),
                    Applicability::HasPlaceholders,
                ).note("for more information, visit \
                       <https://doc.rust-lang.org/reference/conditional-compilation.html\
                       #the-cfg_attr-attribute>")
                .emit();
            return vec![];
        }

        let (cfg_predicate, expanded_attrs) = match attr.parse(self.sess, |parser| {
            parser.expect(&token::OpenDelim(token::Paren))?;

            let cfg_predicate = parser.parse_meta_item()?;
            parser.expect(&token::Comma)?;

            // Presumably, the majority of the time there will only be one attr.
            let mut expanded_attrs = Vec::with_capacity(1);

            while !parser.check(&token::CloseDelim(token::Paren)) {
                let lo = parser.token.span.lo();
                let (path, tokens) = parser.parse_meta_item_unrestricted()?;
                expanded_attrs.push((path, tokens, parser.prev_span.with_lo(lo)));
                parser.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Paren)])?;
            }

            parser.expect(&token::CloseDelim(token::Paren))?;
            Ok((cfg_predicate, expanded_attrs))
        }) {
            Ok(result) => result,
            Err(mut e) => {
                e.emit();
                return vec![];
            }
        };

        // Lint on zero attributes in source.
        if expanded_attrs.is_empty() {
            return vec![attr];
        }

        // At this point we know the attribute is considered used.
        attr::mark_used(&attr);

        if attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
            // We call `process_cfg_attr` recursively in case there's a
            // `cfg_attr` inside of another `cfg_attr`. E.g.
            //  `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
            expanded_attrs.into_iter()
            .flat_map(|(path, tokens, span)| self.process_cfg_attr(ast::Attribute {
                id: attr::mk_attr_id(),
                style: attr.style,
                path,
                tokens,
                is_sugared_doc: false,
                span,
            }))
            .collect()
        } else {
            vec![]
        }
    }

    /// Determines if a node with the given attributes should be included in this configuration.
    pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
        attrs.iter().all(|attr| {
            if !is_cfg(attr) {
                return true;
            }

            let error = |span, msg, suggestion: &str| {
                let mut err = self.sess.span_diagnostic.struct_span_err(span, msg);
                if !suggestion.is_empty() {
                    err.span_suggestion(
                        span,
                        "expected syntax is",
                        suggestion.into(),
                        Applicability::MaybeIncorrect,
                    );
                }
                err.emit();
                true
            };

            let meta_item = match attr.parse_meta(self.sess) {
                Ok(meta_item) => meta_item,
                Err(mut err) => { err.emit(); return true; }
            };
            let nested_meta_items = if let Some(nested_meta_items) = meta_item.meta_item_list() {
                nested_meta_items
            } else {
                return error(meta_item.span, "`cfg` is not followed by parentheses",
                                             "cfg(/* predicate */)");
            };

            if nested_meta_items.is_empty() {
                return error(meta_item.span, "`cfg` predicate is not specified", "");
            } else if nested_meta_items.len() > 1 {
                return error(nested_meta_items.last().unwrap().span(),
                             "multiple `cfg` predicates are specified", "");
            }

            match nested_meta_items[0].meta_item() {
                Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
                None => error(nested_meta_items[0].span(),
                              "`cfg` predicate key cannot be a literal", ""),
            }
        })
    }

    /// Visit attributes on expression and statements (but not attributes on items in blocks).
    fn visit_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
        // flag the offending attributes
        for attr in attrs.iter() {
            self.maybe_emit_expr_attr_err(attr);
        }
    }

    /// If attributes are not allowed on expressions, emit an error for `attr`
    pub fn maybe_emit_expr_attr_err(&self, attr: &ast::Attribute) {
        if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
            let mut err = feature_err(self.sess,
                                      sym::stmt_expr_attributes,
                                      attr.span,
                                      GateIssue::Language,
                                      EXPLAIN_STMT_ATTR_SYNTAX);

            if attr.is_sugared_doc {
                err.help("`///` is for documentation comments. For a plain comment, use `//`.");
            }

            err.emit();
        }
    }

    pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
        let ast::ForeignMod { abi: _, items } = foreign_mod;
        items.flat_map_in_place(|item| self.configure(item));
    }

    pub fn configure_generic_params(&mut self, params: &mut Vec<ast::GenericParam>) {
        params.flat_map_in_place(|param| self.configure(param));
    }

    fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
        match vdata {
            ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) =>
                fields.flat_map_in_place(|field| self.configure(field)),
            ast::VariantData::Unit(_) => {}
        }
    }

    pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) {
        match item {
            ast::ItemKind::Struct(def, _generics) |
            ast::ItemKind::Union(def, _generics) => self.configure_variant_data(def),
            ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => {
                variants.flat_map_in_place(|variant| self.configure(variant));
                for variant in variants {
                    self.configure_variant_data(&mut variant.node.data);
                }
            }
            _ => {}
        }
    }

    pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) {
        match expr_kind {
            ast::ExprKind::Match(_m, arms) => {
                arms.flat_map_in_place(|arm| self.configure(arm));
            }
            ast::ExprKind::Struct(_path, fields, _base) => {
                fields.flat_map_in_place(|field| self.configure(field));
            }
            _ => {}
        }
    }

    pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) {
        self.visit_expr_attrs(expr.attrs());

        // If an expr is valid to cfg away it will have been removed by the
        // outer stmt or expression folder before descending in here.
        // Anything else is always required, and thus has to error out
        // in case of a cfg attr.
        //
        // N.B., this is intentionally not part of the visit_expr() function
        //     in order for filter_map_expr() to be able to avoid this check
        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
            let msg = "removing an expression is not supported in this position";
            self.sess.span_diagnostic.span_err(attr.span, msg);
        }

        self.process_cfg_attrs(expr)
    }

    pub fn configure_pat(&mut self, pat: &mut P<ast::Pat>) {
        if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.node {
            fields.flat_map_in_place(|field| self.configure(field));
        }
    }

    pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) {
        fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg));
    }
}

impl<'a> MutVisitor for StripUnconfigured<'a> {
    fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
        self.configure_foreign_mod(foreign_mod);
        noop_visit_foreign_mod(foreign_mod, self);
    }

    fn visit_item_kind(&mut self, item: &mut ast::ItemKind) {
        self.configure_item_kind(item);
        noop_visit_item_kind(item, self);
    }

    fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
        self.configure_expr(expr);
        self.configure_expr_kind(&mut expr.node);
        noop_visit_expr(expr, self);
    }

    fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
        let mut expr = configure!(self, expr);
        self.configure_expr_kind(&mut expr.node);
        noop_visit_expr(&mut expr, self);
        Some(expr)
    }

    fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
        noop_flat_map_stmt(configure!(self, stmt), self)
    }

    fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
        noop_flat_map_item(configure!(self, item), self)
    }

    fn flat_map_impl_item(&mut self, item: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]> {
        noop_flat_map_impl_item(configure!(self, item), self)
    }

    fn flat_map_trait_item(&mut self, item: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> {
        noop_flat_map_trait_item(configure!(self, item), self)
    }

    fn visit_mac(&mut self, _mac: &mut ast::Mac) {
        // Don't configure interpolated AST (cf. issue #34171).
        // Interpolated AST will get configured once the surrounding tokens are parsed.
    }

    fn visit_pat(&mut self, pat: &mut P<ast::Pat>) {
        self.configure_pat(pat);
        noop_visit_pat(pat, self)
    }

    fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
        self.configure_fn_decl(&mut fn_decl);
        noop_visit_fn_decl(fn_decl, self);
    }
}

fn is_cfg(attr: &ast::Attribute) -> bool {
    attr.check_name(sym::cfg)
}
<|endoftext|>


<commit_before>//! Wrapper for the `wlr_box` type.
//! Note that we renamed it to `Area` to avoid conflicts with Rust's Box.

use std::ops::{Deref, DerefMut};

use libc::{c_double, c_int};

use wlroots_sys::{wl_output_transform, wlr_box, wlr_box_closest_point, wlr_box_contains_point,
                  wlr_box_empty, wlr_box_intersection, wlr_box_transform};

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
/// Result of applying an intersection of two `Area`s.
pub enum IntersectionResult {
    /// This area is the intersection between the two points.
    Intersection(Area),
    /// There was not an intersection
    NoIntersection
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Origin {
    pub x: c_int,
    pub y: c_int
}

impl Origin {
    pub fn new(x: c_int, y: c_int) -> Self {
        Origin { x, y }
    }
}

impl Default for Origin {
    fn default() -> Self {
        Origin { x: 0, y: 0 }
    }
}

impl Into<Area> for Origin {
    fn into(self) -> Area {
        Area::new(self, Size::default())
    }
}

impl Into<Origin> for Area {
    fn into(self) -> Origin {
        Origin { x: self.0.x,
                 y: self.0.y }
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Size {
    pub width: c_int,
    pub height: c_int
}

impl Size {
    pub fn new(width: c_int, height: c_int) -> Self {
        Size { width, height }
    }
}

impl Default for Size {
    fn default() -> Self {
        Size { width: 0,
               height: 0 }
    }
}

impl Into<Area> for Size {
    fn into(self) -> Area {
        Area::new(Origin::default(), self)
    }
}

impl Into<Size> for Area {
    fn into(self) -> Size {
        Size { width: self.0.width,
               height: self.0.height }
    }
}

#[derive(Debug, Clone, Copy)]
/// Generic geometry-like struct. Container an origin (x, y) point and bounds
/// (width, height).
pub struct Area(pub wlr_box);

impl Default for Area {
    fn default() -> Area {
        Area(wlr_box { x: 0,
                       y: 0,
                       width: 0,
                       height: 0 })
    }
}

impl Area {
    pub fn new(origin: Origin, size: Size) -> Self {
        Area(wlr_box { x: origin.x,
                       y: origin.y,
                       width: size.width,
                       height: size.height })
    }

    /// Makes a new `Area` with width and height set to the values in the given
    /// `Size`.
    pub fn with_size(self, size: Size) -> Self {
        Area(wlr_box { width: size.width,
                       height: size.height,
                       ..self.0 })
    }

    /// Makes a new `Area` with x and y set to the value in the given `Origin`.
    pub fn with_origin(self, origin: Origin) -> Self {
        Area(wlr_box { x: origin.x,
                       y: origin.y,
                       ..self.0 })
    }

    /// Finds the closest point within the box to the given point.
    /// If the (x, y) point lies outside of the box, then it finds the closest
    /// corner and returns that.
    ///
    /// Returned value is in form of (x, y).
    pub fn closest_point(&mut self, x: c_double, y: c_double) -> (c_double, c_double) {
        unsafe {
            let (mut dest_x, mut dest_y) = (0.0, 0.0);
            wlr_box_closest_point(&mut self.0, x, y, &mut dest_x, &mut dest_y);
            (dest_x, dest_y)
        }
    }

    /// Gets the intersection of the two areas.
    pub fn intersection(&self, other_box: &Area) -> IntersectionResult {
        unsafe {
            let mut res = Area::default();
            let is_empty = wlr_box_intersection(&self.0, &other_box.0, &mut res.0);
            if is_empty {
                IntersectionResult::NoIntersection
            } else {
                IntersectionResult::Intersection(res)
            }
        }
    }

    /// Determines if the box contains the given point.
    pub fn contains_point(&mut self, x: c_double, y: c_double) -> bool {
        unsafe { wlr_box_contains_point(&mut self.0, x, y) }
    }

    /// Determines if the box is empty (e.g if the bounds give it an area of 0).
    pub fn is_empty(&mut self) -> bool {
        unsafe { wlr_box_empty(&mut self.0) }
    }

    /// Transforms the box coordinates and bounds according to the
    /// output transformation.
    ///
    /// e.g: If it's `WL_OUTPUT_TRANSFORM_90` then it will flip the Area 90° clockwise.
    pub fn transform(&mut self,
                     transform: wl_output_transform,
                     width: c_int,
                     height: c_int)
                     -> Area {
        unsafe {
            let mut res = Area::default();
            wlr_box_transform(&mut self.0, transform, width, height, &mut res.0);
            res
        }
    }
}

impl Deref for Area {
    type Target = wlr_box;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Area {
    fn deref_mut(&mut self) -> &mut wlr_box {
        &mut self.0
    }
}

impl PartialEq for Area {
    fn eq(&self, other: &Area) -> bool {
        self.x == other.x && self.y == other.y && self.height == other.height
        && self.width == other.width
    }
}

impl Eq for Area {}
<commit_msg>Added rotated_bounds, fixed sig for other Area functions<commit_after>//! Wrapper for the `wlr_box` type.
//! Note that we renamed it to `Area` to avoid conflicts with Rust's Box.

use std::ops::{Deref, DerefMut};

use libc::{c_double, c_float, c_int};

use wlroots_sys::{wl_output_transform, wlr_box, wlr_box_closest_point, wlr_box_contains_point,
                  wlr_box_empty, wlr_box_intersection, wlr_box_rotated_bounds, wlr_box_transform};

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
/// Result of applying an intersection of two `Area`s.
pub enum IntersectionResult {
    /// This area is the intersection between the two points.
    Intersection(Area),
    /// There was not an intersection
    NoIntersection
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Origin {
    pub x: c_int,
    pub y: c_int
}

impl Origin {
    pub fn new(x: c_int, y: c_int) -> Self {
        Origin { x, y }
    }
}

impl Default for Origin {
    fn default() -> Self {
        Origin { x: 0, y: 0 }
    }
}

impl Into<Area> for Origin {
    fn into(self) -> Area {
        Area::new(self, Size::default())
    }
}

impl Into<Origin> for Area {
    fn into(self) -> Origin {
        Origin { x: self.0.x,
                 y: self.0.y }
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct Size {
    pub width: c_int,
    pub height: c_int
}

impl Size {
    pub fn new(width: c_int, height: c_int) -> Self {
        Size { width, height }
    }
}

impl Default for Size {
    fn default() -> Self {
        Size { width: 0,
               height: 0 }
    }
}

impl Into<Area> for Size {
    fn into(self) -> Area {
        Area::new(Origin::default(), self)
    }
}

impl Into<Size> for Area {
    fn into(self) -> Size {
        Size { width: self.0.width,
               height: self.0.height }
    }
}

#[derive(Debug, Clone, Copy)]
/// Generic geometry-like struct. Container an origin (x, y) point and bounds
/// (width, height).
pub struct Area(pub wlr_box);

impl Default for Area {
    fn default() -> Area {
        Area(wlr_box { x: 0,
                       y: 0,
                       width: 0,
                       height: 0 })
    }
}

impl Area {
    pub fn new(origin: Origin, size: Size) -> Self {
        Area(wlr_box { x: origin.x,
                       y: origin.y,
                       width: size.width,
                       height: size.height })
    }

    /// Makes a new `Area` with width and height set to the values in the given
    /// `Size`.
    pub fn with_size(self, size: Size) -> Self {
        Area(wlr_box { width: size.width,
                       height: size.height,
                       ..self.0 })
    }

    /// Makes a new `Area` with x and y set to the value in the given `Origin`.
    pub fn with_origin(self, origin: Origin) -> Self {
        Area(wlr_box { x: origin.x,
                       y: origin.y,
                       ..self.0 })
    }

    /// Finds the closest point within the box to the given point.
    /// If the (x, y) point lies outside of the box, then it finds the closest
    /// corner and returns that.
    ///
    /// Returned value is in form of (x, y).
    pub fn closest_point(mut self, x: c_double, y: c_double) -> (c_double, c_double) {
        unsafe {
            let (mut dest_x, mut dest_y) = (0.0, 0.0);
            wlr_box_closest_point(&mut self.0, x, y, &mut dest_x, &mut dest_y);
            (dest_x, dest_y)
        }
    }

    /// Gets the intersection of the two areas.
    pub fn intersection(self, other_box: &Area) -> IntersectionResult {
        unsafe {
            let mut res = Area::default();
            let is_empty = wlr_box_intersection(&self.0, &other_box.0, &mut res.0);
            if is_empty {
                IntersectionResult::NoIntersection
            } else {
                IntersectionResult::Intersection(res)
            }
        }
    }

    /// Determines if the box contains the given point.
    pub fn contains_point(mut self, x: c_double, y: c_double) -> bool {
        unsafe { wlr_box_contains_point(&mut self.0, x, y) }
    }

    /// Determines if the box is empty (e.g if the bounds give it an area of 0).
    pub fn is_empty(mut self) -> bool {
        unsafe { wlr_box_empty(&mut self.0) }
    }

    /// Transforms the box coordinates and bounds according to the
    /// output transformation.
    ///
    /// e.g: If it's `WL_OUTPUT_TRANSFORM_90` then it will flip the Area 90° clockwise.
    pub fn transform(mut self,
                     transform: wl_output_transform,
                     width: c_int,
                     height: c_int)
                     -> Area {
        unsafe {
            let mut res = Area::default();
            wlr_box_transform(&mut self.0, transform, width, height, &mut res.0);
            res
        }
    }

    /// Creates the smallest box that contains the box rotated about its center.
    pub fn rotated_bounds(self, rotation: c_float) -> Area {
        unsafe {
            let mut dest = Area::default();
            wlr_box_rotated_bounds(&self.0, rotation, &mut dest.0);
            dest
        }
    }
}

impl Deref for Area {
    type Target = wlr_box;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Area {
    fn deref_mut(&mut self) -> &mut wlr_box {
        &mut self.0
    }
}

impl PartialEq for Area {
    fn eq(&self, other: &Area) -> bool {
        self.x == other.x && self.y == other.y && self.height == other.height
        && self.width == other.width
    }
}

impl Eq for Area {}
<|endoftext|>


<commit_before>/*
 * 8259A PIC interface
 *
 * TODO: some code here is common with PIC implementation in xvm
 *       make some common definition crate if possible
 */

use arch;
use pio::{inb, outb};

const PIC_MASTER_CMD: u16 = 0x20;
const PIC_MASTER_DATA: u16 = 0x21;
const PIC_SLAVE_CMD: u16 = 0xA0;
const PIC_SLAVE_DATA: u16 = 0xA1;

const ICW1_INIT: u8 = 0x10;
const ICW1_ICW4: u8 = 0x01;
const ICW4_8086: u8 = 0x01;

const PIC_READ_IRR: u8 = 0x0A;
const PIC_READ_ISR: u8 = 0x0B;
const PIC_EOI: u8 = 0x20;

static mut OFFSETS: u16 = 0xFFFF;

pub fn slave_arg(val: u16) -> u8 {
    (val >> 8) as u8
}

pub fn master_arg(val: u16) -> u8 {
    val as u8
}

pub fn make_arg(master: u8, slave: u8) -> u16 {
    master as u16 | ((slave as u16) << 8)
}

/**
 * Reset PIC to new vector bases and interrupts masks
 *
 * \offset  Vector offsets for master (LSB) and slave (MSB)
 * \mask    Interrupt masks for master (LSB) and slave (MSB)
 */
pub fn reset(offset: u16, mask: u16)
{
    let iscope = arch::InterruptGuard::default();

    unsafe {
        // ICW1
        outb(PIC_MASTER_CMD, ICW1_INIT | ICW1_ICW4);
        outb(PIC_SLAVE_CMD, ICW1_INIT | ICW1_ICW4);

        // ICW2 (vector offsets)
        outb(PIC_MASTER_DATA, master_arg(offset));
        outb(PIC_SLAVE_DATA, slave_arg(offset));

        // ICW3 (cascade)
        outb(PIC_MASTER_DATA, 4);
        outb(PIC_SLAVE_DATA, 2);

        // ICW4
        outb(PIC_MASTER_DATA, ICW4_8086);
        outb(PIC_SLAVE_DATA, ICW4_8086);

        // Masks
        set_mask(mask);
        OFFSETS = offset;
    }
}

pub fn mask() -> u16
{
    unsafe {
        make_arg(inb(PIC_MASTER_DATA), inb(PIC_SLAVE_DATA))
    }
}

pub fn set_mask(mask: u16)
{
    unsafe {
        outb(PIC_MASTER_DATA, master_arg(mask));
        outb(PIC_SLAVE_DATA, slave_arg(mask));
    }
}

pub fn set_mask2(master: u8, slave: u8)
{
    set_mask(make_arg(master, slave));
}

pub fn offset() -> u16
{
    unsafe {
        OFFSETS
    }
}

pub fn master_offset() -> u8
{
    master_arg(offset())
}

pub fn slave_offset() -> u8
{
    slave_arg(offset())
}

// TODO: move to common utils code
fn set_bit8(val: u8, bit: u8) -> u8 {
    assert!(bit < 8);
    val | (1_u8 << bit)
}

// TODO: move to common utils code
fn clear_bit8(val: u8, bit: u8) -> u8 {
    assert!(bit < 8);
    val & !(1_u8 << bit)
}

/**
 * For a given IRQ number returns interrupt vector for current PIC configuration
 */
pub fn get_interrupt_vector(irq: u8) -> u8
{
    if irq < 8 {
        master_offset() + irq
    } else if irq < 16 {
        slave_offset() + irq
    } else {
        panic!()
    }

}

/**
 * Mask or unmask and IRQ given its interrupt vector
 */
pub fn mask_vector(vec: u8, is_masked: bool)
{
    let mask = mask();
    let mut master = master_arg(mask);
    let mut slave = slave_arg(mask);

    if vec >= master_offset() && vec < (master_offset() + 8) {
        if is_masked {
            master = set_bit8(master, vec - master_offset());
        } else {
            master = clear_bit8(master, vec - master_offset());
        }
    } else if vec >= slave_offset() && vec < (slave_offset() + 8) {
        if is_masked {
            slave = set_bit8(slave, vec - master_offset());
        } else {
            slave = clear_bit8(slave, vec - master_offset());
        }
    }

    set_mask2(master, slave);
}
<commit_msg>test: PIC EOI, ISR and IRR implementation<commit_after>/*
 * 8259A PIC interface
 *
 * TODO: some code here is common with PIC implementation in xvm
 *       make some common definition crate if possible
 */

use arch;
use pio::{inb, outb};

const PIC_MASTER_CMD: u16 = 0x20;
const PIC_MASTER_DATA: u16 = 0x21;
const PIC_SLAVE_CMD: u16 = 0xA0;
const PIC_SLAVE_DATA: u16 = 0xA1;

const ICW1_INIT: u8 = 0x10;
const ICW1_ICW4: u8 = 0x01;
const ICW4_8086: u8 = 0x01;

const PIC_READ_IRR: u8 = 0x0A;
const PIC_READ_ISR: u8 = 0x0B;
const PIC_EOI: u8 = 0x20;

static mut OFFSETS: u16 = 0xFFFF;

pub fn slave_arg(val: u16) -> u8 {
    (val >> 8) as u8
}

pub fn master_arg(val: u16) -> u8 {
    val as u8
}

pub fn make_arg(master: u8, slave: u8) -> u16 {
    master as u16 | ((slave as u16) << 8)
}

/**
 * Reset PIC to new vector bases and interrupts masks
 *
 * \offset  Vector offsets for master (LSB) and slave (MSB)
 * \mask    Interrupt masks for master (LSB) and slave (MSB)
 */
pub fn reset(offset: u16, mask: u16)
{
    let iscope = arch::InterruptGuard::default();

    unsafe {
        // ICW1
        outb(PIC_MASTER_CMD, ICW1_INIT | ICW1_ICW4);
        outb(PIC_SLAVE_CMD, ICW1_INIT | ICW1_ICW4);

        // ICW2 (vector offsets)
        outb(PIC_MASTER_DATA, master_arg(offset));
        outb(PIC_SLAVE_DATA, slave_arg(offset));

        // ICW3 (cascade)
        outb(PIC_MASTER_DATA, 4);
        outb(PIC_SLAVE_DATA, 2);

        // ICW4
        outb(PIC_MASTER_DATA, ICW4_8086);
        outb(PIC_SLAVE_DATA, ICW4_8086);

        // Masks
        set_mask(mask);
        OFFSETS = offset;
    }
}

pub fn mask() -> u16
{
    unsafe {
        make_arg(inb(PIC_MASTER_DATA), inb(PIC_SLAVE_DATA))
    }
}

pub fn set_mask(mask: u16)
{
    unsafe {
        outb(PIC_MASTER_DATA, master_arg(mask));
        outb(PIC_SLAVE_DATA, slave_arg(mask));
    }
}

pub fn set_mask2(master: u8, slave: u8)
{
    set_mask(make_arg(master, slave));
}

pub fn offset() -> u16
{
    unsafe {
        OFFSETS
    }
}

pub fn master_offset() -> u8
{
    master_arg(offset())
}

pub fn slave_offset() -> u8
{
    slave_arg(offset())
}

// TODO: move to common utils code
fn set_bit8(val: u8, bit: u8) -> u8 {
    assert!(bit < 8);
    val | (1_u8 << bit)
}

// TODO: move to common utils code
fn clear_bit8(val: u8, bit: u8) -> u8 {
    assert!(bit < 8);
    val & !(1_u8 << bit)
}

/**
 * For a given IRQ number returns interrupt vector for current PIC configuration
 */
pub fn get_interrupt_vector(irq: u8) -> u8
{
    if irq < 8 {
        master_offset() + irq
    } else if irq < 16 {
        slave_offset() + irq
    } else {
        panic!()
    }

}

/**
 * Mask or unmask and IRQ given its interrupt vector
 */
pub fn mask_vector(vec: u8, is_masked: bool)
{
    let mask = mask();
    let mut master = master_arg(mask);
    let mut slave = slave_arg(mask);

    if vec >= master_offset() && vec < (master_offset() + 8) {
        if is_masked {
            master = set_bit8(master, vec - master_offset());
        } else {
            master = clear_bit8(master, vec - master_offset());
        }
    } else if vec >= slave_offset() && vec < (slave_offset() + 8) {
        if is_masked {
            slave = set_bit8(slave, vec - master_offset());
        } else {
            slave = clear_bit8(slave, vec - master_offset());
        }
    }

    set_mask2(master, slave);
}

/**
 * Send end-of-interrupt for IRQ
 */
pub fn EOI(irq: u8)
{
    unsafe {
        if irq >= 8 {
            outb(PIC_SLAVE_CMD, PIC_EOI);
        }
        outb(PIC_MASTER_CMD, PIC_EOI);
    }
}

/**
 * Read ISR registers
 */
pub fn ISR() -> u16
{
    unsafe {
        outb(PIC_MASTER_CMD, PIC_READ_ISR);
        outb(PIC_SLAVE_CMD, PIC_READ_ISR);
        make_arg(inb(PIC_MASTER_CMD), inb(PIC_SLAVE_CMD))
    }
}

/**
 * Read IRR registers
 */
pub fn IRR() -> u16
{
    unsafe {
        outb(PIC_MASTER_CMD, PIC_READ_IRR);
        outb(PIC_SLAVE_CMD, PIC_READ_IRR);
        make_arg(inb(PIC_MASTER_CMD), inb(PIC_SLAVE_CMD))
    }
}
<|endoftext|>