<commit_before>use env::{Environment, Point};
use loans_in_scope::{Loan, LoansInScope};
use nll_repr::repr;
use std::error::Error;
use std::fmt;

pub fn borrow_check(env: &Environment, loans_in_scope: &LoansInScope) -> Result<(), Box<Error>> {
    let mut result: Result<(), Box<Error>> = Ok(());
    loans_in_scope.walk(env, |point, opt_action, loans| {
        let borrowck = BorrowCheck { env, point, loans };
        if let Some(action) = opt_action {
            if let Err(e) = borrowck.check_action(action) {
                if !action.should_have_error {
                    result = Err(e);
                }
            } else if action.should_have_error {
                result = Err(Box::new(BorrowError::no_error(point)));
            }
        }
    });

    result
}

struct BorrowCheck<'cx> {
    env: &'cx Environment<'cx>,
    point: Point,
    loans: &'cx [&'cx Loan<'cx>],
}

enum Depth {
    Shallow,
    Deep,
}

enum Mode {
    Read,
    Write,
}

impl<'cx> BorrowCheck<'cx> {
    fn check_action(&self, action: &repr::Action) -> Result<(), Box<Error>> {
        log!("check_action({:?}) at {:?}", action, self.point);
        match action.kind {
            repr::ActionKind::Init(ref a, ref bs) => {
                self.check_instantaneous_write(a)?;
                for b in bs {
                    self.check_read(b)?;
                }
            }
            repr::ActionKind::Assign(ref a, ref b) => {
                self.check_instantaneous_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Shared, ref b) => {
                self.check_instantaneous_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Mut, ref b) => {
                self.check_instantaneous_write(a)?;
                self.check_mut_borrow(b)?;
            }
            repr::ActionKind::Constraint(_) => {}
            repr::ActionKind::Use(ref p) => {
                self.check_read(p)?;
            }
            repr::ActionKind::Drop(ref p) => {
                self.check_move(p)?;
            }
            repr::ActionKind::StorageDead(p) => {
                self.check_storage_dead(p)?;
            }
            repr::ActionKind::SkolemizedEnd(_) |
            repr::ActionKind::Noop => {}
        }

        Ok(())
    }

    /// `use(x)` may access `x` and (by going through the produced
    /// value) anything reachable from `x`.
    fn check_read(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        self.check_borrows(Depth::Deep, Mode::Read, path)
    }

    /// `x = ...` overwrites `x` (without reading it) and prevents any
    /// further reads from that path.
    fn check_instantaneous_write(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        self.check_borrows(Depth::Shallow, Mode::Write, path)
    }

    /// `&mut x` may mutate `x`, but it can also *read* from `x`, and
    /// mutate things reachable from `x`.
    fn check_mut_borrow(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        self.check_borrows(Depth::Deep, Mode::Write, path)
    }

    fn check_borrows(&self,
                     depth: Depth,
                     access_mode: Mode,
                     path: &repr::Path)
                     -> Result<(), Box<Error>> {
        let loans: Vec<_> = match depth {
            Depth::Shallow => self.find_loans_that_freeze(path).collect(),
            Depth::Deep => self.find_loans_that_intersect(path).collect(),
        };

        for loan in loans {
            match access_mode {
                Mode::Read => match loan.kind {
                    repr::BorrowKind::Shared => { /* Ok */ }
                    repr::BorrowKind::Mut => {
                        return Err(Box::new(BorrowError::for_read(
                            self.point,
                            path,
                            &loan.path,
                            loan.point,
                        )));
                    }
                },

                Mode::Write => {
                    return Err(Box::new(BorrowError::for_write(
                        self.point,
                        path,
                        &loan.path,
                        loan.point,
                    )));
                },
            }
        }

        Ok(())
    }

    /// Cannot move from a path `p` if:
    /// - `p` is borrowed;
    /// - some subpath `p.foo` is borrowed;
    /// - some prefix of `p` is borrowed.
    ///
    /// Note that this is stricter than both *writes* and
    /// *storage-dead*. In particular, you **can** write to a variable
    /// `x` that contains an `&mut` value when `*x` is borrowed, but
    /// you **cannot** move `x`. This is because moving it would make
    /// the `&mut` available in the new location, but writing (and
    /// storage-dead) both kill it forever.
    fn check_move(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_move of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        for loan in self.find_loans_that_intersect(path) {
            return Err(Box::new(BorrowError::for_move(
                self.point,
                path,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// Cannot free a local variable `var` if:
    /// - data interior to `var` is borrowed.
    ///
    /// In particular, having something like `*var` borrowed is ok.
    fn check_storage_dead(&self, var: repr::Variable) -> Result<(), Box<Error>> {
        log!(
            "check_storage_dead of {:?} at {:?} with loans={:#?}",
            var,
            self.point,
            self.loans
        );
        for loan in self.find_loans_that_freeze(&repr::Path::Var(var)) {
            return Err(Box::new(BorrowError::for_storage_dead(
                self.point,
                var,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// A loan L *intersects* a path P if either:
    ///
    /// - the loan is for the path P; or,
    /// - the path P can be extended to reach the data in the loan; or,
    /// - the loan path can be extended to reach the data in P.
    ///
    /// So, for example, is the path P is `a.b.c`, then:
    ///
    /// - a loan of `a.b.c` intersects P;
    /// - a loan of `a.b.c.d` intersects P, because (e.g.) after reading P
    ///   you have also read `a.b.c.d`;
    /// - a loan of `a.b` intersects P, because you can use the
    ///   reference to access the data at P.
    fn find_loans_that_intersect<'a>(
        &'a self,
        path: &'a repr::Path,
    ) -> impl Iterator<Item = &'a Loan> + 'a {
        let path_prefixes = path.prefixes();
        self.loans.iter().cloned().filter(move |loan| {
            // accessing `a.b.c` intersects a loan of `a.b.c` or `a.b`...
            path_prefixes.contains(&loan.path) ||

            // ...as well as a loan of `a.b.c.d`
                self.env.supporting_prefixes(&loan.path).contains(&path)
        })
    }

    /// Helper for `check_write` and `check_storage_dead`: finds if
    /// there is a loan that "freezes" the given path -- that is, a
    /// loan that would make modifying the `path` (or freeing it)
    /// illegal. This is slightly more permissive than the rules
    /// around move and reads, precisely because overwriting or
    /// freeing `path` makes the previous value unavailable from that
    /// point on.
    fn find_loans_that_freeze<'a>(
        &'a self,
        path: &repr::Path)
        -> impl Iterator<Item = &'a Loan> + 'a
    {
        let path: repr::Path = path.clone();
        self.loans.iter().cloned().filter(move |loan| {
            let prefixes = path.prefixes();

            // If you have borrowed `a.b`, this prevents writes to `a`
            // or `a.b`:
            let frozen_paths = self.frozen_by_borrow_of(&loan.path);
            frozen_paths.contains(&&path) ||

                // If you have borrowed `a.b`, this prevents writes to
                // `a.b.c`:
                prefixes.contains(&loan.path)
        })
    }

    /// If `path` is mutably borrowed, returns a vector of paths which -- if
    /// moved or if the storage went away -- would invalidate this
    /// reference.
    fn frozen_by_borrow_of<'a>(&self, mut path: &'a repr::Path) -> Vec<&'a repr::Path> {
        let mut result = vec![];
        loop {
            result.push(path);
            match *path {
                repr::Path::Var(_) => return result,
                repr::Path::Extension(ref base_path, field_name) => {
                    match *self.env.path_ty(base_path) {
                        // If you borrowed `*r`, writing to `r` does
                        // not actually affect the memory at `*r`, so
                        // we can stop iterating backwards now.
                        repr::Ty::Ref(_, _, _) => {
                            assert_eq!(field_name, repr::FieldName::star());
                            return result;
                        }

                        // If you have borrowed `a.b`, then writing to
                        // `a` would overwrite `a.b`, which is
                        // disallowed.
                        repr::Ty::Struct(..) => {
                            path = base_path;
                        }

                        repr::Ty::Unit => panic!("unit has no fields"),
                        repr::Ty::Bound(..) => panic!("unexpected bound type"),
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct BorrowError {
    description: String,
}

impl BorrowError {
    fn no_error(point: Point) -> Self {
        BorrowError {
            description: format!("point {:?} had no error, but should have", point),
        }
    }

    fn for_move(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot move {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_read(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot read {:?} because {:?} is mutably borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_write(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot write {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_storage_dead(
        point: Point,
        var: repr::Variable,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot kill storage for {:?} \
                 because {:?} is borrowed (at point `{:?}`)",
                point,
                var,
                loan_path,
                loan_point
            ),
        }
    }
}

impl Error for BorrowError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for BorrowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description)
    }
}
<commit_msg>rename to `check_shallow_write`<commit_after>use env::{Environment, Point};
use loans_in_scope::{Loan, LoansInScope};
use nll_repr::repr;
use std::error::Error;
use std::fmt;

pub fn borrow_check(env: &Environment, loans_in_scope: &LoansInScope) -> Result<(), Box<Error>> {
    let mut result: Result<(), Box<Error>> = Ok(());
    loans_in_scope.walk(env, |point, opt_action, loans| {
        let borrowck = BorrowCheck { env, point, loans };
        if let Some(action) = opt_action {
            if let Err(e) = borrowck.check_action(action) {
                if !action.should_have_error {
                    result = Err(e);
                }
            } else if action.should_have_error {
                result = Err(Box::new(BorrowError::no_error(point)));
            }
        }
    });

    result
}

struct BorrowCheck<'cx> {
    env: &'cx Environment<'cx>,
    point: Point,
    loans: &'cx [&'cx Loan<'cx>],
}

enum Depth {
    Shallow,
    Deep,
}

enum Mode {
    Read,
    Write,
}

impl<'cx> BorrowCheck<'cx> {
    fn check_action(&self, action: &repr::Action) -> Result<(), Box<Error>> {
        log!("check_action({:?}) at {:?}", action, self.point);
        match action.kind {
            repr::ActionKind::Init(ref a, ref bs) => {
                self.check_shallow_write(a)?;
                for b in bs {
                    self.check_read(b)?;
                }
            }
            repr::ActionKind::Assign(ref a, ref b) => {
                self.check_shallow_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Shared, ref b) => {
                self.check_shallow_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Mut, ref b) => {
                self.check_shallow_write(a)?;
                self.check_mut_borrow(b)?;
            }
            repr::ActionKind::Constraint(_) => {}
            repr::ActionKind::Use(ref p) => {
                self.check_read(p)?;
            }
            repr::ActionKind::Drop(ref p) => {
                self.check_move(p)?;
            }
            repr::ActionKind::StorageDead(p) => {
                self.check_storage_dead(p)?;
            }
            repr::ActionKind::SkolemizedEnd(_) |
            repr::ActionKind::Noop => {}
        }

        Ok(())
    }

    /// `use(x)` may access `x` and (by going through the produced
    /// value) anything reachable from `x`.
    fn check_read(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        self.check_borrows(Depth::Deep, Mode::Read, path)
    }

    /// `x = ...` overwrites `x` (without reading it) and prevents any
    /// further reads from that path.
    fn check_shallow_write(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        self.check_borrows(Depth::Shallow, Mode::Write, path)
    }

    /// `&mut x` may mutate `x`, but it can also *read* from `x`, and
    /// mutate things reachable from `x`.
    fn check_mut_borrow(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        self.check_borrows(Depth::Deep, Mode::Write, path)
    }

    fn check_borrows(&self,
                     depth: Depth,
                     access_mode: Mode,
                     path: &repr::Path)
                     -> Result<(), Box<Error>> {
        let loans: Vec<_> = match depth {
            Depth::Shallow => self.find_loans_that_freeze(path).collect(),
            Depth::Deep => self.find_loans_that_intersect(path).collect(),
        };

        for loan in loans {
            match access_mode {
                Mode::Read => match loan.kind {
                    repr::BorrowKind::Shared => { /* Ok */ }
                    repr::BorrowKind::Mut => {
                        return Err(Box::new(BorrowError::for_read(
                            self.point,
                            path,
                            &loan.path,
                            loan.point,
                        )));
                    }
                },

                Mode::Write => {
                    return Err(Box::new(BorrowError::for_write(
                        self.point,
                        path,
                        &loan.path,
                        loan.point,
                    )));
                },
            }
        }

        Ok(())
    }

    /// Cannot move from a path `p` if:
    /// - `p` is borrowed;
    /// - some subpath `p.foo` is borrowed;
    /// - some prefix of `p` is borrowed.
    ///
    /// Note that this is stricter than both *writes* and
    /// *storage-dead*. In particular, you **can** write to a variable
    /// `x` that contains an `&mut` value when `*x` is borrowed, but
    /// you **cannot** move `x`. This is because moving it would make
    /// the `&mut` available in the new location, but writing (and
    /// storage-dead) both kill it forever.
    fn check_move(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_move of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        for loan in self.find_loans_that_intersect(path) {
            return Err(Box::new(BorrowError::for_move(
                self.point,
                path,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// Cannot free a local variable `var` if:
    /// - data interior to `var` is borrowed.
    ///
    /// In particular, having something like `*var` borrowed is ok.
    fn check_storage_dead(&self, var: repr::Variable) -> Result<(), Box<Error>> {
        log!(
            "check_storage_dead of {:?} at {:?} with loans={:#?}",
            var,
            self.point,
            self.loans
        );
        for loan in self.find_loans_that_freeze(&repr::Path::Var(var)) {
            return Err(Box::new(BorrowError::for_storage_dead(
                self.point,
                var,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// A loan L *intersects* a path P if either:
    ///
    /// - the loan is for the path P; or,
    /// - the path P can be extended to reach the data in the loan; or,
    /// - the loan path can be extended to reach the data in P.
    ///
    /// So, for example, is the path P is `a.b.c`, then:
    ///
    /// - a loan of `a.b.c` intersects P;
    /// - a loan of `a.b.c.d` intersects P, because (e.g.) after reading P
    ///   you have also read `a.b.c.d`;
    /// - a loan of `a.b` intersects P, because you can use the
    ///   reference to access the data at P.
    fn find_loans_that_intersect<'a>(
        &'a self,
        path: &'a repr::Path,
    ) -> impl Iterator<Item = &'a Loan> + 'a {
        let path_prefixes = path.prefixes();
        self.loans.iter().cloned().filter(move |loan| {
            // accessing `a.b.c` intersects a loan of `a.b.c` or `a.b`...
            path_prefixes.contains(&loan.path) ||

            // ...as well as a loan of `a.b.c.d`
                self.env.supporting_prefixes(&loan.path).contains(&path)
        })
    }

    /// Helper for `check_write` and `check_storage_dead`: finds if
    /// there is a loan that "freezes" the given path -- that is, a
    /// loan that would make modifying the `path` (or freeing it)
    /// illegal. This is slightly more permissive than the rules
    /// around move and reads, precisely because overwriting or
    /// freeing `path` makes the previous value unavailable from that
    /// point on.
    fn find_loans_that_freeze<'a>(
        &'a self,
        path: &repr::Path)
        -> impl Iterator<Item = &'a Loan> + 'a
    {
        let path: repr::Path = path.clone();
        self.loans.iter().cloned().filter(move |loan| {
            let prefixes = path.prefixes();

            // If you have borrowed `a.b`, this prevents writes to `a`
            // or `a.b`:
            let frozen_paths = self.frozen_by_borrow_of(&loan.path);
            frozen_paths.contains(&&path) ||

                // If you have borrowed `a.b`, this prevents writes to
                // `a.b.c`:
                prefixes.contains(&loan.path)
        })
    }

    /// If `path` is mutably borrowed, returns a vector of paths which -- if
    /// moved or if the storage went away -- would invalidate this
    /// reference.
    fn frozen_by_borrow_of<'a>(&self, mut path: &'a repr::Path) -> Vec<&'a repr::Path> {
        let mut result = vec![];
        loop {
            result.push(path);
            match *path {
                repr::Path::Var(_) => return result,
                repr::Path::Extension(ref base_path, field_name) => {
                    match *self.env.path_ty(base_path) {
                        // If you borrowed `*r`, writing to `r` does
                        // not actually affect the memory at `*r`, so
                        // we can stop iterating backwards now.
                        repr::Ty::Ref(_, _, _) => {
                            assert_eq!(field_name, repr::FieldName::star());
                            return result;
                        }

                        // If you have borrowed `a.b`, then writing to
                        // `a` would overwrite `a.b`, which is
                        // disallowed.
                        repr::Ty::Struct(..) => {
                            path = base_path;
                        }

                        repr::Ty::Unit => panic!("unit has no fields"),
                        repr::Ty::Bound(..) => panic!("unexpected bound type"),
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct BorrowError {
    description: String,
}

impl BorrowError {
    fn no_error(point: Point) -> Self {
        BorrowError {
            description: format!("point {:?} had no error, but should have", point),
        }
    }

    fn for_move(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot move {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_read(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot read {:?} because {:?} is mutably borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_write(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot write {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_storage_dead(
        point: Point,
        var: repr::Variable,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot kill storage for {:?} \
                 because {:?} is borrowed (at point `{:?}`)",
                point,
                var,
                loan_path,
                loan_point
            ),
        }
    }
}

impl Error for BorrowError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for BorrowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description)
    }
}
<|endoftext|>


<commit_before>use std::fmt;

pub enum PebsType {
    Regular,
    PebsOrRegular,
    PebsOnly,
}

impl fmt::Debug for PebsType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match *self {
            PebsType::Regular => "Regular",
            PebsType::PebsOrRegular => "PebsOrRegular",
            PebsType::PebsOnly => "PebsOnly",
        };
        write!(f, "PebsType::{}", name)
    }
}

pub enum Tuple {
    One(u8),
    Two(u8, u8),
}

impl fmt::Debug for Tuple {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Tuple::One(a) => write!(f, "Tuple::One({})", a),
            Tuple::Two(a, b) => write!(f, "Tuple::Two({}, {})", a, b),
        }
    }
}

pub enum MSRIndex {
    None,
    One(u64),
    Two(u64, u64),
}

impl fmt::Debug for MSRIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MSRIndex::None => write!(f, "MSRIndex::None"),
            MSRIndex::One(a) => write!(f, "MSRIndex::One({})", a),
            MSRIndex::Two(a, b) => write!(f, "MSRIndex::Two({}, {})", a, b),
        }
    }
}

#[derive(Clone, Copy)]
pub enum Counter {
    /// Bit-mask containing the fixed counters
    /// usable with the corresponding performance event.
    Fixed(u8),

    /// Bit-mask containing the programmable counters
    /// usable with the corresponding performance event.
    Programmable(u8),
}

impl fmt::Debug for Counter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Counter::Fixed(a) => write!(f, "Counter::Fixed({})", a),
            Counter::Programmable(a) => write!(f, "Counter::Programmable({})", a),
        }
    }
}

#[derive(Debug)]
pub struct EventDescription {
    /// This field maps to the Event Select field in the IA32_PERFEVTSELx[7:0]MSRs.
    ///
    /// The set of values for this field is defined architecturally.
    /// Each value corresponds to an event logic unit and should be used with a unit
    /// mask value to obtain an architectural performance event.
    pub event_code: Tuple,

    /// This field maps to the Unit Mask filed in the IA32_PERFEVTSELx[15:8] MSRs.
    ///
    /// It further qualifies the event logic unit selected in the event select
    /// field to detect a specific micro-architectural condition.
    pub umask: Tuple,

    /// It is a string of characters to identify the programming of an event.
    pub event_name: &'static str,

    /// This field contains a description of what is being counted by a particular event.
    pub brief_description: &'static str,

    /// In some cases, this field will contain a more detailed description of what is counted by an event.
    pub public_description: Option<&'static str>,

    /// This field lists the fixed (PERF_FIXED_CTRX) or programmable (IA32_PMCX)
    /// counters that can be used to count the event.
    pub counter: Counter,

    /// This field lists the counters where this event can be sampled
    /// when Intel® Hyper-Threading Technology (Intel® HT Technology) is
    /// disabled.
    ///
    /// When Intel® HT Technology is disabled, some processor cores gain access to
    /// the programmable counters of the second thread, making a total of eight
    /// programmable counters available. The additional counters will be
    /// numbered 4,5,6,7. Fixed counter behavior remains unaffected.
    pub counter_ht_off: Option<Counter>,

    /// This field is only relevant to PEBS events.
    ///
    /// It lists the counters where the event can be sampled when it is programmed as a PEBS event.
    pub pebs_counters: Option<Counter>,

    /// Sample After Value (SAV) is the value that can be preloaded
    /// into the counter registers to set the point at which they will overflow.
    ///
    /// To make the counter overflow after N occurrences of the event,
    /// it should be loaded with (0xFF..FF – N) or –(N-1). On overflow a
    /// hardware interrupt is generated through the Local APIC and additional
    /// architectural state can be collected in the interrupt handler.
    /// This is useful in event-based sampling. This field gives a recommended
    /// default overflow value, which may be adjusted based on workload or tool preference.
    pub sample_after_value: u64,

    /// Additional MSRs may be required for programming certain events.
    /// This field gives the address of such MSRS.
    pub msr_index: MSRIndex,

    /// When an MSRIndex is used (indicated by the MSRIndex column), this field will
    /// contain the value that needs to be loaded into the
    /// register whose address is given in MSRIndex column.
    ///
    /// For example, in the case of the load latency events, MSRValue defines the
    /// latency threshold value to write into the MSR defined in MSRIndex (0x3F6).
    pub msr_value: u64,

    /// This field is set for an event which can only be sampled or counted by itself,
    /// meaning that when this event is being collected,
    /// the remaining programmable counters are not available to count any other events.
    pub taken_alone: bool,

    /// This field maps to the Counter Mask (CMASK) field in IA32_PERFEVTSELx[31:24] MSR.
    pub counter_mask: u8,

    /// This field corresponds to the Invert Counter Mask (INV) field in IA32_PERFEVTSELx[23] MSR.
    pub invert: bool,

    /// This field corresponds to the Any Thread (ANY) bit of IA32_PERFEVTSELx[21] MSR.
    pub any_thread: bool,

    /// This field corresponds to the Edge Detect (E) bit of IA32_PERFEVTSELx[18] MSR.
    pub edge_detect: bool,

    /// A '0' in this field means that the event cannot be programmed as a PEBS event.
    /// A '1' in this field means that the event is a  precise event and can be programmed
    /// in one of two ways – as a regular event or as a PEBS event.
    /// And a '2' in this field means that the event can only be programmed as a PEBS event.
    pub pebs: PebsType,

    /// A '1' in this field means the event uses the Precise Store feature and Bit 3 and
    /// bit 63 in IA32_PEBS_ENABLE MSR must be set to enable IA32_PMC3 as a PEBS counter
    /// and enable the precise store facility respectively.
    ///
    /// Processors based on SandyBridge and IvyBridge micro-architecture offer a
    /// precise store capability that provides a means to profile store memory
    /// references in the system.
    pub precise_store: bool,

    /// Applies to processors that support both precise and non-precise events in Processor Event Based Sampling, such as Goldmont.
    ///   0: The event cannot be programmed to collect a PEBS record.
    ///   1: The event may be programmed to collect a PEBS record, but caution is advised.
    /// For instance, PEBS collection of this event may consume limited PEBS resources whereas
    /// interrupt-based sampling may be sufficient for the usage model.
    ///   2: The event may programmed to collect a PEBS record, and due to the nature of the event,
    ///      PEBS collection may be preferred.  For instance, PEBS collection of Goldmont's
    ///      HW_INTERUPTS.RECIEVED event is recommended because the hardware interrupt being
    ///      counted may lead to the masking of interrupts which would interfere with
    ///      interrupt-based sampling.
    pub collect_pebs_record: Option<u64>,

    /// A '1' in this field means that when the event is configured as a PEBS event,
    /// the Data Linear Address facility is supported.
    ///
    /// The Data Linear Address facility is a new feature added to Haswell as a
    /// replacement or extension of the precise store facility in SNB.
    pub data_la: bool,

    /// A '1' in this field means that when the event is configured as a PEBS event,
    /// the DCU hit field of the PEBS record is set to 1 when the store hits in the
    /// L1 cache and 0 when it misses.
    pub l1_hit_indication: bool,

    /// This field lists the known bugs that apply to the events.
    ///
    /// For the latest, up to date errata refer to the following links:
    /// /
    /// * Haswell:
    ///   http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/4th-gen-core-family-mobile-specification-update.pdf
    ///
    /// * IvyBridge:
    ///   https://www-ssl.intel.com/content/dam/www/public/us/en/documents/specification-updates/3rd-gen-core-desktop-specification-update.pdf
    ///
    /// * SandyBridge:
    ///   https://www-ssl.intel.com/content/dam/www/public/us/en/documents/specification-updates/2nd-gen-core-family-mobile-specification-update.pdf
    pub errata: Option<&'static str>,

    /// There is only 1 file for core and offcore events in this format.
    /// This field is set to 1 for offcore events and 0 for core events.
    pub offcore: bool,

    pub unit: Option<&'static str>,

    pub filter: Option<&'static str>,

    pub extsel: bool,
}

impl EventDescription {
    #[allow(dead_code)]
    fn new(event_code: Tuple,
           umask: Tuple,
           event_name: &'static str,
           brief_description: &'static str,
           public_description: Option<&'static str>,
           counter: Counter,
           counter_ht_off: Option<Counter>,
           pebs_counters: Option<Counter>,
           sample_after_value: u64,
           msr_index: MSRIndex,
           msr_value: u64,
           taken_alone: bool,
           counter_mask: u8,
           invert: bool,
           any_thread: bool,
           edge_detect: bool,
           pebs: PebsType,
           precise_store: bool,
           collect_pebs_record: Option<u64>,
           data_la: bool,
           l1_hit_indication: bool,
           errata: Option<&'static str>,
           offcore: bool,
           unit: Option<&'static str>,
           filter: Option<&'static str>,
           extsel: bool)
           -> EventDescription {

        EventDescription {
            event_code: event_code,
            umask: umask,
            event_name: event_name,
            brief_description: brief_description,
            public_description: public_description,
            counter: counter,
            counter_ht_off: counter_ht_off,
            pebs_counters: pebs_counters,
            sample_after_value: sample_after_value,
            msr_index: msr_index,
            msr_value: msr_value,
            taken_alone: taken_alone,
            counter_mask: counter_mask,
            invert: invert,
            any_thread: any_thread,
            edge_detect: edge_detect,
            pebs: pebs,
            precise_store: precise_store,
            collect_pebs_record: collect_pebs_record,
            data_la: data_la,
            l1_hit_indication: l1_hit_indication,
            errata: errata,
            offcore: offcore,
            unit: unit,
            filter: filter,
            extsel: extsel,
        }
    }
}
<commit_msg>Implement equality and Debug for types.<commit_after>use std::fmt;

#[derive(Eq, PartialEq)]
pub enum PebsType {
    Regular,
    PebsOrRegular,
    PebsOnly,
}

impl fmt::Debug for PebsType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name = match *self {
            PebsType::Regular => "Regular",
            PebsType::PebsOrRegular => "PebsOrRegular",
            PebsType::PebsOnly => "PebsOnly",
        };
        write!(f, "PebsType::{}", name)
    }
}

#[derive(Eq, PartialEq)]
pub enum Tuple {
    One(u8),
    Two(u8, u8),
}

impl fmt::Debug for Tuple {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Tuple::One(a) => write!(f, "Tuple::One({})", a),
            Tuple::Two(a, b) => write!(f, "Tuple::Two({}, {})", a, b),
        }
    }
}

#[derive(Eq, PartialEq)]
pub enum MSRIndex {
    None,
    One(u64),
    Two(u64, u64),
}

impl fmt::Debug for MSRIndex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MSRIndex::None => write!(f, "MSRIndex::None"),
            MSRIndex::One(a) => write!(f, "MSRIndex::One({})", a),
            MSRIndex::Two(a, b) => write!(f, "MSRIndex::Two({}, {})", a, b),
        }
    }
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Counter {
    /// Bit-mask containing the fixed counters
    /// usable with the corresponding performance event.
    Fixed(u8),

    /// Bit-mask containing the programmable counters
    /// usable with the corresponding performance event.
    Programmable(u8),
}

impl fmt::Debug for Counter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Counter::Fixed(a) => write!(f, "Counter::Fixed({})", a),
            Counter::Programmable(a) => write!(f, "Counter::Programmable({})", a),
        }
    }
}

#[derive(Debug)]
pub struct EventDescription {
    /// This field maps to the Event Select field in the IA32_PERFEVTSELx[7:0]MSRs.
    ///
    /// The set of values for this field is defined architecturally.
    /// Each value corresponds to an event logic unit and should be used with a unit
    /// mask value to obtain an architectural performance event.
    pub event_code: Tuple,

    /// This field maps to the Unit Mask filed in the IA32_PERFEVTSELx[15:8] MSRs.
    ///
    /// It further qualifies the event logic unit selected in the event select
    /// field to detect a specific micro-architectural condition.
    pub umask: Tuple,

    /// It is a string of characters to identify the programming of an event.
    pub event_name: &'static str,

    /// This field contains a description of what is being counted by a particular event.
    pub brief_description: &'static str,

    /// In some cases, this field will contain a more detailed description of what is counted by an event.
    pub public_description: Option<&'static str>,

    /// This field lists the fixed (PERF_FIXED_CTRX) or programmable (IA32_PMCX)
    /// counters that can be used to count the event.
    pub counter: Counter,

    /// This field lists the counters where this event can be sampled
    /// when Intel® Hyper-Threading Technology (Intel® HT Technology) is
    /// disabled.
    ///
    /// When Intel® HT Technology is disabled, some processor cores gain access to
    /// the programmable counters of the second thread, making a total of eight
    /// programmable counters available. The additional counters will be
    /// numbered 4,5,6,7. Fixed counter behavior remains unaffected.
    pub counter_ht_off: Option<Counter>,

    /// This field is only relevant to PEBS events.
    ///
    /// It lists the counters where the event can be sampled when it is programmed as a PEBS event.
    pub pebs_counters: Option<Counter>,

    /// Sample After Value (SAV) is the value that can be preloaded
    /// into the counter registers to set the point at which they will overflow.
    ///
    /// To make the counter overflow after N occurrences of the event,
    /// it should be loaded with (0xFF..FF – N) or –(N-1). On overflow a
    /// hardware interrupt is generated through the Local APIC and additional
    /// architectural state can be collected in the interrupt handler.
    /// This is useful in event-based sampling. This field gives a recommended
    /// default overflow value, which may be adjusted based on workload or tool preference.
    pub sample_after_value: u64,

    /// Additional MSRs may be required for programming certain events.
    /// This field gives the address of such MSRS.
    pub msr_index: MSRIndex,

    /// When an MSRIndex is used (indicated by the MSRIndex column), this field will
    /// contain the value that needs to be loaded into the
    /// register whose address is given in MSRIndex column.
    ///
    /// For example, in the case of the load latency events, MSRValue defines the
    /// latency threshold value to write into the MSR defined in MSRIndex (0x3F6).
    pub msr_value: u64,

    /// This field is set for an event which can only be sampled or counted by itself,
    /// meaning that when this event is being collected,
    /// the remaining programmable counters are not available to count any other events.
    pub taken_alone: bool,

    /// This field maps to the Counter Mask (CMASK) field in IA32_PERFEVTSELx[31:24] MSR.
    pub counter_mask: u8,

    /// This field corresponds to the Invert Counter Mask (INV) field in IA32_PERFEVTSELx[23] MSR.
    pub invert: bool,

    /// This field corresponds to the Any Thread (ANY) bit of IA32_PERFEVTSELx[21] MSR.
    pub any_thread: bool,

    /// This field corresponds to the Edge Detect (E) bit of IA32_PERFEVTSELx[18] MSR.
    pub edge_detect: bool,

    /// A '0' in this field means that the event cannot be programmed as a PEBS event.
    /// A '1' in this field means that the event is a  precise event and can be programmed
    /// in one of two ways – as a regular event or as a PEBS event.
    /// And a '2' in this field means that the event can only be programmed as a PEBS event.
    pub pebs: PebsType,

    /// A '1' in this field means the event uses the Precise Store feature and Bit 3 and
    /// bit 63 in IA32_PEBS_ENABLE MSR must be set to enable IA32_PMC3 as a PEBS counter
    /// and enable the precise store facility respectively.
    ///
    /// Processors based on SandyBridge and IvyBridge micro-architecture offer a
    /// precise store capability that provides a means to profile store memory
    /// references in the system.
    pub precise_store: bool,

    /// Applies to processors that support both precise and non-precise events in Processor Event Based Sampling, such as Goldmont.
    ///   0: The event cannot be programmed to collect a PEBS record.
    ///   1: The event may be programmed to collect a PEBS record, but caution is advised.
    /// For instance, PEBS collection of this event may consume limited PEBS resources whereas
    /// interrupt-based sampling may be sufficient for the usage model.
    ///   2: The event may programmed to collect a PEBS record, and due to the nature of the event,
    ///      PEBS collection may be preferred.  For instance, PEBS collection of Goldmont's
    ///      HW_INTERUPTS.RECIEVED event is recommended because the hardware interrupt being
    ///      counted may lead to the masking of interrupts which would interfere with
    ///      interrupt-based sampling.
    pub collect_pebs_record: Option<u64>,

    /// A '1' in this field means that when the event is configured as a PEBS event,
    /// the Data Linear Address facility is supported.
    ///
    /// The Data Linear Address facility is a new feature added to Haswell as a
    /// replacement or extension of the precise store facility in SNB.
    pub data_la: bool,

    /// A '1' in this field means that when the event is configured as a PEBS event,
    /// the DCU hit field of the PEBS record is set to 1 when the store hits in the
    /// L1 cache and 0 when it misses.
    pub l1_hit_indication: bool,

    /// This field lists the known bugs that apply to the events.
    ///
    /// For the latest, up to date errata refer to the following links:
    /// /
    /// * Haswell:
    ///   http://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/4th-gen-core-family-mobile-specification-update.pdf
    ///
    /// * IvyBridge:
    ///   https://www-ssl.intel.com/content/dam/www/public/us/en/documents/specification-updates/3rd-gen-core-desktop-specification-update.pdf
    ///
    /// * SandyBridge:
    ///   https://www-ssl.intel.com/content/dam/www/public/us/en/documents/specification-updates/2nd-gen-core-family-mobile-specification-update.pdf
    pub errata: Option<&'static str>,

    /// There is only 1 file for core and offcore events in this format.
    /// This field is set to 1 for offcore events and 0 for core events.
    pub offcore: bool,

    pub unit: Option<&'static str>,

    pub filter: Option<&'static str>,

    pub extsel: bool,
}

impl EventDescription {
    #[allow(dead_code)]
    fn new(event_code: Tuple,
           umask: Tuple,
           event_name: &'static str,
           brief_description: &'static str,
           public_description: Option<&'static str>,
           counter: Counter,
           counter_ht_off: Option<Counter>,
           pebs_counters: Option<Counter>,
           sample_after_value: u64,
           msr_index: MSRIndex,
           msr_value: u64,
           taken_alone: bool,
           counter_mask: u8,
           invert: bool,
           any_thread: bool,
           edge_detect: bool,
           pebs: PebsType,
           precise_store: bool,
           collect_pebs_record: Option<u64>,
           data_la: bool,
           l1_hit_indication: bool,
           errata: Option<&'static str>,
           offcore: bool,
           unit: Option<&'static str>,
           filter: Option<&'static str>,
           extsel: bool)
           -> EventDescription {

        EventDescription {
            event_code: event_code,
            umask: umask,
            event_name: event_name,
            brief_description: brief_description,
            public_description: public_description,
            counter: counter,
            counter_ht_off: counter_ht_off,
            pebs_counters: pebs_counters,
            sample_after_value: sample_after_value,
            msr_index: msr_index,
            msr_value: msr_value,
            taken_alone: taken_alone,
            counter_mask: counter_mask,
            invert: invert,
            any_thread: any_thread,
            edge_detect: edge_detect,
            pebs: pebs,
            precise_store: precise_store,
            collect_pebs_record: collect_pebs_record,
            data_la: data_la,
            l1_hit_indication: l1_hit_indication,
            errata: errata,
            offcore: offcore,
            unit: unit,
            filter: filter,
            extsel: extsel,
        }
    }
}

impl fmt::Display for EventDescription {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.event_name)
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;

use rustc_serialize::json::{self, Json, ToJson};

use message::OutgoingMessage;

/// Data for a Event::Message
#[allow(dead_code)]
pub struct MessageData {
    text: String,
    channel: String,
    user: String,
    ts: String,
    team: String
}

macro_rules! str_accessor {
    ($s:ident) => {
        pub fn $s(&self) -> &str {
            self.$s.as_ref()
        }
    }
}

#[allow(dead_code)]
impl MessageData {
    str_accessor!(text);
    str_accessor!(channel);
    str_accessor!(user);
    str_accessor!(ts);
    str_accessor!(team);
}

/// Incoming slack messages on the websocket api
pub enum Event {
    /// A message was sent to a channel
    Message(Msg),

    /// Some other type of message arrived.
    /// [The list](https://api.slack.com/rtm) is quite extensive, and only the
    /// messages the adapter is concerned with are enumerated here.
    Other(json::Object)
}

/// Event::Message sub types. Message events are the only event capable of having a sub type.
pub enum Msg {
    /// A regular text message from a user
    Plain(MessageData),

    /// Subtype not explicitly handled. For a complete enumeration, check
    /// https://api.slack.com/events/message
    Other(json::Object)
}

#[derive(Debug)]
pub enum EventDecodingError {
    InvalidJson(json::BuilderError),
    MissingField(String),
    WrongType(String, String),
}

impl Error for EventDecodingError {
    fn description(&self) -> &str {
        match *self {
            EventDecodingError::InvalidJson(ref err) => err.description(),
            EventDecodingError::MissingField(_) => "JSON property missing",
            EventDecodingError::WrongType(_, _) => "JSON property had wrong type"
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            EventDecodingError::InvalidJson(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for EventDecodingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EventDecodingError::InvalidJson(ref err) => write!(f, "InvalidJson({})", err),
            EventDecodingError::MissingField(ref field) => write!(f, "MissingField({})", field),
            EventDecodingError::WrongType(ref field, ref t) => {
                write!(f, "WrongType({}, {})", field, t)
            }
        }
    }
}

impl From<json::BuilderError> for EventDecodingError {
    fn from(err: json::BuilderError) -> EventDecodingError {
        EventDecodingError::InvalidJson(err)
    }
}

/// Extract a &str ($key) from a json::Object ($obj)
/// Returns an Err(EventDecodingError) when it fails
macro_rules! get_json_string {
    ($obj:ident, $key:expr) => {
        {
            let json_str: &Json = match $obj.get($key) {
                Some(json_str) => json_str,
                None => return Err(EventDecodingError::MissingField($key.to_owned()))
            };

            match json_str.as_string() {
                Some(slice) => slice.to_owned(),
                None => {
                    return Err(EventDecodingError::WrongType($key.to_owned(), "string".to_owned()))
               }
            }
        }
    }
}

pub fn decode_msg_json(obj: json::Object) -> Result<Event, EventDecodingError> {
    // Messages with a `subtype` are not plain text messages.. Not interested in them for now.
    if obj.contains_key("subtype") {
        return Ok(Event::Message(Msg::Other(obj)))
    }

    // It's a plain message.
    Ok(Event::Message(Msg::Plain(MessageData {
        text: get_json_string!(obj, "text"),
        channel: get_json_string!(obj, "channel"),
        user: get_json_string!(obj, "user"),
        ts: get_json_string!(obj, "ts"),
        team: get_json_string!(obj, "team"),
    })))
}

pub fn decode_json(raw: &str) -> Result<Event, EventDecodingError> {
    let json = try!(Json::from_str(raw));
    let obj = match json {
        Json::Object(obj) => obj,
        _ => return Err(EventDecodingError::WrongType("root".to_owned(), "object".to_owned()))
    };

    // If the message does not have a `type`, it is a confirmation message
    if !obj.contains_key("type") {
        return Ok(Event::Other(obj))
    }

    let msg = match obj.get("type").expect("obj has key type but failed to get").as_string() {
        Some(s) => s.to_owned(),
        None => return Err(EventDecodingError::WrongType("type".to_owned(), "string".to_owned()))
    };

    match msg.as_ref() {
        // If it's a message type, try and return an Event::Message
        "message" => decode_msg_json(obj),
        // Some other type we don't explicitly handle
        _ => Ok(Event::Other(obj)),
    }
}

/// Convert a JSON string to a Event
///
/// This methods provides additional error handling around json::decode for certain errors
/// that cannot be handled in the Decodable implementation. Specifically, MissingFieldError
/// where the field is "type" are actually valid messages despite missing the "type" field.
pub fn string_to_slack_msg(raw: &str) -> Result<Event, EventDecodingError> {
    // Some messages arriving from the slack client don't have a type. So far I've only
    // witnessed confirmation messages arriving in this fashion. Since they go through the same
    // pipeline as content messages, the decoder should be able to handle them.
    decode_json(raw)
}

#[derive(Debug)]
pub struct OutgoingEvent {
    id: i64,
    channel: String,
    msg_type: String,
    text: String
}

impl OutgoingEvent {
    pub fn new(id: i64, m: OutgoingMessage) -> OutgoingEvent {
        OutgoingEvent {
            id: id,
            channel: m.get_incoming().channel().expect("missing channel").to_owned(),
            msg_type: "message".to_owned(),
            text: m.as_ref().to_owned() // TODO move instead of copy
        }
    }
}

impl ToJson for OutgoingEvent {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("id".to_string(), self.id.to_json());
        d.insert("channel".to_string(), self.channel.to_json());
        d.insert("type".to_string(), self.msg_type.to_json());
        d.insert("text".to_string(), self.text.to_json());
        Json::Object(d)
    }
}

#[cfg(test)]
mod tests {
    use adapter::slack::message::Event;
    use adapter::slack::message::Msg;
    use adapter::slack::message::string_to_slack_msg;

    #[test]
    fn decode_message() {
        let raw = "{\"type\":\"message\", \
                    \"channel\":\"D04UYUAMW\", \
                    \"user\":\"U02ALMR84\", \
                    \"text\":\"ping\", \
                    \"ts\":\"1432563914.000007\", \
                    \"team\":\"T02ALMR82\"}";

        let slack_msg = string_to_slack_msg(raw).unwrap();

        match slack_msg {
            Event::Message(Msg::Plain(data)) => {
                assert_eq!(data.text(), "ping");
                assert_eq!(data.channel(), "D04UYUAMW");
                assert_eq!(data.user(), "U02ALMR84");
                assert_eq!(data.ts(), "1432563914.000007");
                assert_eq!(data.team(), "T02ALMR82");
            },
            _ => panic!("Expected Event::Message")
        }
    }
    #[test]
    fn decode_confirmation_message() {
        let raw = r#"{"ok":true,"reply_to":0,"ts":"1432566639.000014","text":"pong"}"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Other(_) => (),
            _ => panic!("expected Event::Other")
        }
    }

    #[test]
    fn decode_unexpected_type() {
        let raw = r#"{"type":"not_a_slack_msg_type"}"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Other(_) => (),
            _ => panic!("expected Event::Other")
        }
    }

    #[test]
    fn decode_message_changed() {
        let raw = r#"{
            "type":"message",
            "message":{
                "type":"message",
                "user":"U02ALMR84",
                "text":"arst",
                "edited":{
                    "user":"U02ALMR84",
                    "ts":"1432695814.000000"
                },
                "ts":"1432695812.000056"
            },
            "subtype":"message_changed",
            "hidden":true,
            "channel":"D04UYUAMW",
            "event_ts":"1432695814.616510",
            "ts":"1432695814.000057"
        }"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Message(Msg::Other(_)) => return,
            _ => panic!("expected Msg::Other")
        }
    }

    #[test]
    fn decode_me_message() {
        let raw = r#"{
            "text":"is a potato",
            "type":"message",
            "subtype":"me_message",
            "user":"U02ALMR84",
            "channel":"D04UYUAMW",
            "ts":"1432695826.000060"
        }"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Message(Msg::Other(_)) => return,
            _ => panic!("expected Msg::Other")
        }
    }

    #[test]
    fn decode_message_deleted() {
        let raw = r#"{
            "type":"message",
            "deleted_ts":"1432695826.000060",
            "subtype":"message_deleted",
            "hidden":true,
            "channel":"D04UYUAMW",
            "event_ts":"1432695848.617155",
            "ts":"1432695848.000061"
        }"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Message(Msg::Other(_)) => return,
            _ => panic!("expected Msg::Other")
        }
    }

}
<commit_msg>Update doc comments in slack/message.rs<commit_after>use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;

use rustc_serialize::json::{self, Json, ToJson};

use message::OutgoingMessage;

/// Data for an Event::Message(Msg::Plain)
#[allow(dead_code)]
pub struct MessageData {
    text: String,
    channel: String,
    user: String,
    ts: String,
    team: String
}

macro_rules! str_accessor {
    ($s:ident) => {
        pub fn $s(&self) -> &str {
            self.$s.as_ref()
        }
    }
}

#[allow(dead_code)]
impl MessageData {
    /// Get the message text
    str_accessor!(text);
    /// Channel where the message was sent
    str_accessor!(channel);
    /// User who sent the message
    str_accessor!(user);
    /// When the message was sent
    str_accessor!(ts);
    /// Team owning the message
    str_accessor!(team);
}

/// Incoming slack messages on the websocket api
pub enum Event {
    /// A message was sent to a channel
    Message(Msg),

    /// Some other type of message arrived.
    /// [The list](https://api.slack.com/rtm) is quite extensive, and only the
    /// messages the adapter is concerned with are enumerated here.
    Other(json::Object)
}

/// Event::Message sub types. Message events are the only event capable of having a sub type.
pub enum Msg {
    /// A regular text message from a user
    Plain(MessageData),

    /// Subtype not explicitly handled. For a complete enumeration, check
    /// https://api.slack.com/events/message
    Other(json::Object)
}

/// Errors that can occur when parsing a slack JSON message
#[derive(Debug)]
pub enum EventDecodingError {
    InvalidJson(json::BuilderError),
    MissingField(String),
    WrongType(String, String),
}

impl Error for EventDecodingError {
    fn description(&self) -> &str {
        match *self {
            EventDecodingError::InvalidJson(ref err) => err.description(),
            EventDecodingError::MissingField(_) => "JSON property missing",
            EventDecodingError::WrongType(_, _) => "JSON property had wrong type"
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            EventDecodingError::InvalidJson(ref err) => Some(err),
            _ => None
        }
    }
}

impl fmt::Display for EventDecodingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            EventDecodingError::InvalidJson(ref err) => write!(f, "InvalidJson({})", err),
            EventDecodingError::MissingField(ref field) => write!(f, "MissingField({})", field),
            EventDecodingError::WrongType(ref field, ref t) => {
                write!(f, "WrongType({}, {})", field, t)
            }
        }
    }
}

impl From<json::BuilderError> for EventDecodingError {
    fn from(err: json::BuilderError) -> EventDecodingError {
        EventDecodingError::InvalidJson(err)
    }
}

/// Extract a &str ($key) from a json::Object ($obj)
/// Returns an Err(EventDecodingError) when it fails
macro_rules! get_json_string {
    ($obj:ident, $key:expr) => {
        {
            let json_str: &Json = match $obj.get($key) {
                Some(json_str) => json_str,
                None => return Err(EventDecodingError::MissingField($key.to_owned()))
            };

            match json_str.as_string() {
                Some(slice) => slice.to_owned(),
                None => {
                    return Err(EventDecodingError::WrongType($key.to_owned(), "string".to_owned()))
               }
            }
        }
    }
}

/// Decode the Event::Message data
fn decode_msg_json(obj: json::Object) -> Result<Event, EventDecodingError> {
    // Messages with a `subtype` are not plain text messages.. Not interested in them for now.
    if obj.contains_key("subtype") {
        return Ok(Event::Message(Msg::Other(obj)))
    }

    // It's a plain message.
    Ok(Event::Message(Msg::Plain(MessageData {
        text: get_json_string!(obj, "text"),
        channel: get_json_string!(obj, "channel"),
        user: get_json_string!(obj, "user"),
        ts: get_json_string!(obj, "ts"),
        team: get_json_string!(obj, "team"),
    })))
}

/// Convert a JSON string to a Event
pub fn string_to_slack_msg(raw: &str) -> Result<Event, EventDecodingError> {
    // The message should be a string representation of a JSON object
    let json = try!(Json::from_str(raw));
    let obj = match json {
        Json::Object(obj) => obj,
        _ => return Err(EventDecodingError::WrongType("root".to_owned(), "object".to_owned()))
    };

    // If the message does not have a `type`, it is a confirmation message
    if !obj.contains_key("type") {
        return Ok(Event::Other(obj))
    }

    // Get the message type. It should be a string.
    let msg = match obj.get("type").expect("obj has key type but failed to get").as_string() {
        Some(s) => s.to_owned(),
        None => return Err(EventDecodingError::WrongType("type".to_owned(), "string".to_owned()))
    };

    // Handle different message types
    match msg.as_ref() {
        // If it's a message type, try and return an Event::Message
        "message" => decode_msg_json(obj),
        // Some other type we don't explicitly handle
        _ => Ok(Event::Other(obj)),
    }
}

/// An outgoing slack event.
///
/// The OutgoingEvent type only supports type = "message" at this time. The `type` field is recorded
/// in the struct as `msg_type` since rust has a conflicting `type` keyword.
#[derive(Debug)]
pub struct OutgoingEvent {
    id: i64,
    channel: String,
    msg_type: String,
    text: String
}

impl OutgoingEvent {
    pub fn new(id: i64, m: OutgoingMessage) -> OutgoingEvent {
        OutgoingEvent {
            id: id,
            channel: m.get_incoming().channel().expect("missing channel").to_owned(),
            msg_type: "message".to_owned(),
            text: m.as_ref().to_owned() // TODO move instead of copy
        }
    }
}

impl ToJson for OutgoingEvent {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("id".to_string(), self.id.to_json());
        d.insert("channel".to_string(), self.channel.to_json());
        d.insert("type".to_string(), self.msg_type.to_json());
        d.insert("text".to_string(), self.text.to_json());
        Json::Object(d)
    }
}

#[cfg(test)]
mod tests {
    use adapter::slack::message::Event;
    use adapter::slack::message::Msg;
    use adapter::slack::message::string_to_slack_msg;

    #[test]
    fn decode_message() {
        let raw = "{\"type\":\"message\", \
                    \"channel\":\"D04UYUAMW\", \
                    \"user\":\"U02ALMR84\", \
                    \"text\":\"ping\", \
                    \"ts\":\"1432563914.000007\", \
                    \"team\":\"T02ALMR82\"}";

        let slack_msg = string_to_slack_msg(raw).unwrap();

        match slack_msg {
            Event::Message(Msg::Plain(data)) => {
                assert_eq!(data.text(), "ping");
                assert_eq!(data.channel(), "D04UYUAMW");
                assert_eq!(data.user(), "U02ALMR84");
                assert_eq!(data.ts(), "1432563914.000007");
                assert_eq!(data.team(), "T02ALMR82");
            },
            _ => panic!("Expected Event::Message")
        }
    }
    #[test]
    fn decode_confirmation_message() {
        let raw = r#"{"ok":true,"reply_to":0,"ts":"1432566639.000014","text":"pong"}"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Other(_) => (),
            _ => panic!("expected Event::Other")
        }
    }

    #[test]
    fn decode_unexpected_type() {
        let raw = r#"{"type":"not_a_slack_msg_type"}"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Other(_) => (),
            _ => panic!("expected Event::Other")
        }
    }

    #[test]
    fn decode_message_changed() {
        let raw = r#"{
            "type":"message",
            "message":{
                "type":"message",
                "user":"U02ALMR84",
                "text":"arst",
                "edited":{
                    "user":"U02ALMR84",
                    "ts":"1432695814.000000"
                },
                "ts":"1432695812.000056"
            },
            "subtype":"message_changed",
            "hidden":true,
            "channel":"D04UYUAMW",
            "event_ts":"1432695814.616510",
            "ts":"1432695814.000057"
        }"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Message(Msg::Other(_)) => return,
            _ => panic!("expected Msg::Other")
        }
    }

    #[test]
    fn decode_me_message() {
        let raw = r#"{
            "text":"is a potato",
            "type":"message",
            "subtype":"me_message",
            "user":"U02ALMR84",
            "channel":"D04UYUAMW",
            "ts":"1432695826.000060"
        }"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Message(Msg::Other(_)) => return,
            _ => panic!("expected Msg::Other")
        }
    }

    #[test]
    fn decode_message_deleted() {
        let raw = r#"{
            "type":"message",
            "deleted_ts":"1432695826.000060",
            "subtype":"message_deleted",
            "hidden":true,
            "channel":"D04UYUAMW",
            "event_ts":"1432695848.617155",
            "ts":"1432695848.000061"
        }"#;

        match string_to_slack_msg(raw).unwrap() {
            Event::Message(Msg::Other(_)) => return,
            _ => panic!("expected Msg::Other")
        }
    }

}
<|endoftext|>


<commit_before>#![allow(deprecated)]
use utils::data as D;
use std::ops::Index;

type Sample = (Vec<u8>, u32);

pub struct MNIST {
    pub root: String,
    pub train: bool,
    pub transform: Option<fn(Vec<u8>)>,
}

#[builder(pattern="owned")]
#[derive(Clone, Builder)]
pub struct MNISTArgs {
    root: String,
    #[builder(default="true")]
    train: bool,
    #[builder(default="false")]
    download: bool,
    #[builder(default="None")]
    transform: Option<fn(Vec<u8>)>,
}

impl MNISTArgsBuilder {
    pub fn done(self) -> MNIST {
        let args = self.build().unwrap();
        MNIST::new(&args)
    }
}

impl MNIST {
    pub fn build(root: &str) -> MNISTArgsBuilder {
        MNISTArgsBuilder::default().root(root.into())
    }
    pub fn new(args: &MNISTArgs) -> Self {
        MNIST {
            root: args.root.clone(),
            train: args.train,
            transform: args.transform,
        }
    }
}

impl Index<usize> for MNIST {
    type Output = Sample;
    fn index(&self, idx: usize) -> &Self::Output {
        unimplemented!()
    }
}

impl D::DatasetIntf<Sample> for MNIST {
    fn len(&self) -> usize {
        if self.train { 60000 } else { 10000 }
    }
    fn iter(&self) -> Box<Iterator<Item = Sample>> {
        unimplemented!();
    }
}
<commit_msg>implement download for MNIST<commit_after>#![allow(deprecated)]
use utils::data as D;
use std::ops::Index;
use std::path::PathBuf;
use std::{io, fs};
use curl::easy::Easy;
use flate2::{Flush, Decompress};
use std::io::Write;

type Sample = (Vec<u8>, u32);

static URLS: [&str; 4] = ["http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz",
                          "http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz"];

fn download(root: &String) -> io::Result<()> {
    let raw_path = PathBuf::from(root).join("raw");
    let processed_path = PathBuf::from(root).join("processed");
    // XXX how to ignore EEXIST?
    fs::create_dir(raw_path.clone())?;
    fs::create_dir(processed_path)?;

    for url in URLS.iter() {
        println!("downloading {}", url);
        let paths: Vec<_> = url.split("/").collect();
        let fname: Vec<_> = paths[5].split("/").collect();
        let file_path = raw_path.join(fname[0]);
        let mut file = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(file_path)?;
        let mut data = Vec::new();
        let mut handle = Easy::new();
        handle.url(url).unwrap();
        {
            let mut transfer = handle.transfer();
            transfer
                .write_function(|new_data| {
                                    data.extend_from_slice(new_data);
                                    Ok(new_data.len())
                                })
                .unwrap();
            transfer.perform().unwrap();
        }
        let mut output = Vec::with_capacity(data.len());
        // do we have a header?
        Decompress::new(false)
            .decompress_vec(data.as_slice(), &mut output, Flush::Finish)?;
        file.write_all(output.as_slice())?;
    }
    println!("Proceeding");
    Ok(())
}

pub struct MNIST {
    pub root: String,
    pub train: bool,
//    pub transform: Option<fn(Vec<u8>)>,
}

#[builder(pattern="owned")]
#[derive(Builder)]
pub struct MNISTArgs {
    root: String,
    #[builder(default="true")]
    train: bool,
    #[builder(default="false")]
    download: bool,
//    #[builder(default="None")]
//    transform: Option<Box<fn(&Vec<u8>)>>,
}

impl MNISTArgsBuilder {
    pub fn done(self) -> MNIST {
        let args = self.build().unwrap();
        MNIST::new(&args)
    }
}

impl MNIST {
    pub fn build(root: &str) -> MNISTArgsBuilder {
        MNISTArgsBuilder::default().root(root.into())
    }
    pub fn new(args: &MNISTArgs) -> Self {
        if args.download {
            download(&args.root);
        }
        MNIST {
            root: args.root.clone(),
            train: args.train,
//            transform: args.transform,
        }
    }
}

impl Index<usize> for MNIST {
    type Output = Sample;
    fn index(&self, idx: usize) -> &Self::Output {
        unimplemented!()
    }
}

impl D::DatasetIntf<Sample> for MNIST {
    fn len(&self) -> usize {
        if self.train { 60000 } else { 10000 }
    }
    fn iter(&self) -> Box<Iterator<Item = Sample>> {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto;

use seccomp::{
    allow_syscall, allow_syscall_if, BpfProgram, Error, SeccompAction, SeccompCmpArgLen as ArgLen,
    SeccompCmpOp::Eq, SeccompCondition as Cond, SeccompError, SeccompFilter, SeccompLevel,
    SeccompRule,
};
use utils::signal::sigrtmin;

#[cfg(target_arch = "aarch64")]
pub const SYS_FTRUNCATE: libc::c_long = 46;
#[cfg(target_arch = "x86_64")]
pub const SYS_FTRUNCATE: libc::c_long = 77;

/// The default filter containing the white listed syscall rules required by `Firecracker` to
/// function.
/// Any non-trivial modification to this allow list needs a proper comment to specify its source
/// or why the sycall/condition is needed.
pub fn default_filter() -> Result<SeccompFilter, Error> {
    Ok(SeccompFilter::new(
        vec![
            // Called by the api thread to receive data on socket
            allow_syscall_if(
                libc::SYS_accept4,
                or![and![Cond::new(
                    3,
                    ArgLen::DWORD,
                    Eq,
                    libc::SOCK_CLOEXEC as u64
                )?],],
            ),
            // Called for expanding the heap
            allow_syscall(libc::SYS_brk),
            // Used for metrics, via the helpers in utils/src/time.rs
            allow_syscall_if(
                libc::SYS_clock_gettime,
                or![and![Cond::new(
                    0,
                    ArgLen::DWORD,
                    Eq,
                    libc::CLOCK_PROCESS_CPUTIME_ID as u64
                )?],],
            ),
            allow_syscall(libc::SYS_close),
            // Needed for vsock
            allow_syscall(libc::SYS_connect),
            allow_syscall(libc::SYS_epoll_ctl),
            allow_syscall(libc::SYS_epoll_pwait),
            #[cfg(all(target_env = "gnu", target_arch = "x86_64"))]
            allow_syscall(libc::SYS_epoll_wait),
            allow_syscall(libc::SYS_exit),
            allow_syscall(libc::SYS_exit_group),
            // Used by snapshotting, drive patching and rescanning
            allow_syscall_if(
                libc::SYS_fcntl,
                or![and![
                    Cond::new(1, ArgLen::DWORD, Eq, super::FCNTL_F_SETFD)?,
                    Cond::new(2, ArgLen::DWORD, Eq, super::FCNTL_FD_CLOEXEC)?,
                ],],
            ),
            // Used for drive patching & rescanning, for reading the local timezone
            allow_syscall(libc::SYS_fstat),
            // Used for snapshotting
            allow_syscall(SYS_FTRUNCATE),
            // Used for synchronization
            allow_syscall_if(
                libc::SYS_futex,
                or![
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAIT_PRIVATE)?],
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAKE_PRIVATE)?],
                    #[cfg(target_env = "gnu")]
                    and![Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        super::FUTEX_CMP_REQUEUE_PRIVATE
                    )?],
                ],
            ),
            // Used by glibc's tgkill
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_getpid),
            allow_syscall_if(libc::SYS_ioctl, super::create_ioctl_seccomp_rule()?),
            // Used by the block device
            allow_syscall(libc::SYS_lseek),
            // Triggered by musl for some customer workloads
            #[cfg(target_env = "musl")]
            allow_syscall_if(
                libc::SYS_madvise,
                or![and![Cond::new(
                    2,
                    ArgLen::DWORD,
                    Eq,
                    libc::MADV_DONTNEED as u64
                )?],],
            ),
            // Used for re-allocating large memory regions, for example vectors
            allow_syscall(libc::SYS_mremap),
            // Used for freeing memory
            allow_syscall(libc::SYS_munmap),
            // Used for reading the timezone in LocalTime::now()
            allow_syscall_if(
                libc::SYS_mmap,
                or![
                    and![Cond::new(3, ArgLen::DWORD, Eq, libc::MAP_SHARED as u64)?],
                    and![Cond::new(
                        3,
                        ArgLen::DWORD,
                        Eq,
                        (libc::MAP_FIXED | libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as u64
                    )?],
                ],
            ),
            #[cfg(target_arch = "x86_64")]
            allow_syscall(libc::SYS_open),
            #[cfg(target_arch = "aarch64")]
            allow_syscall(libc::SYS_openat),
            allow_syscall(libc::SYS_read),
            // Used by the API thread and vsock
            allow_syscall(libc::SYS_recvfrom),
            // SYS_rt_sigreturn is needed in case a fault does occur, so that the signal handler
            // can return. Otherwise we get stuck in a fault loop.
            allow_syscall(libc::SYS_rt_sigreturn),
            // Used by the API thread and vsock
            allow_syscall_if(
                libc::SYS_socket,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::AF_UNIX as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::SOCK_STREAM as u64) | (libc::SOCK_CLOEXEC as u64)
                    )?,
                    Cond::new(2, ArgLen::DWORD, Eq, 0u64)?
                ],],
            ),
            // Used to kick vcpus
            allow_syscall_if(
                libc::SYS_tkill,
                or![and![Cond::new(
                    1,
                    ArgLen::DWORD,
                    Eq,
                    (sigrtmin() + super::super::vstate::vcpu::VCPU_RTSIG_OFFSET) as u64
                )?]],
            ),
            // Used to kick vcpus, on gnu
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_tgkill),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_create,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::CLOCK_MONOTONIC as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::TFD_CLOEXEC as u64) | (libc::TFD_NONBLOCK as u64)
                    )?,
                ],],
            ),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_settime,
                or![and![Cond::new(1, ArgLen::DWORD, Eq, 0u64)?],],
            ),
            allow_syscall(libc::SYS_write),
        ]
        .into_iter()
        .collect(),
        SeccompAction::Trap,
    )?)
}

/// Generate a BPF program based on a seccomp level value.
pub fn get_seccomp_filter(seccomp_level: SeccompLevel) -> Result<BpfProgram, SeccompError> {
    match seccomp_level {
        SeccompLevel::None => Ok(vec![]),
        SeccompLevel::Basic => default_filter()
            .map(|filter| filter.allow_all())
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
        SeccompLevel::Advanced => default_filter()
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
    }
}

#[cfg(test)]
mod tests {
    use super::get_seccomp_filter;
    use seccomp::SeccompLevel;

    #[test]
    fn test_get_seccomp_filter() {
        assert!(get_seccomp_filter(SeccompLevel::None).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Basic).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Advanced).is_ok());
    }
}
<commit_msg>Replaced hardcoded SYS_FTRUNCATE with libc const<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
use std::convert::TryInto;

use seccomp::{
    allow_syscall, allow_syscall_if, BpfProgram, Error, SeccompAction, SeccompCmpArgLen as ArgLen,
    SeccompCmpOp::Eq, SeccompCondition as Cond, SeccompError, SeccompFilter, SeccompLevel,
    SeccompRule,
};
use utils::signal::sigrtmin;

/// The default filter containing the white listed syscall rules required by `Firecracker` to
/// function.
/// Any non-trivial modification to this allow list needs a proper comment to specify its source
/// or why the sycall/condition is needed.
pub fn default_filter() -> Result<SeccompFilter, Error> {
    Ok(SeccompFilter::new(
        vec![
            // Called by the api thread to receive data on socket
            allow_syscall_if(
                libc::SYS_accept4,
                or![and![Cond::new(
                    3,
                    ArgLen::DWORD,
                    Eq,
                    libc::SOCK_CLOEXEC as u64
                )?],],
            ),
            // Called for expanding the heap
            allow_syscall(libc::SYS_brk),
            // Used for metrics, via the helpers in utils/src/time.rs
            allow_syscall_if(
                libc::SYS_clock_gettime,
                or![and![Cond::new(
                    0,
                    ArgLen::DWORD,
                    Eq,
                    libc::CLOCK_PROCESS_CPUTIME_ID as u64
                )?],],
            ),
            allow_syscall(libc::SYS_close),
            // Needed for vsock
            allow_syscall(libc::SYS_connect),
            allow_syscall(libc::SYS_epoll_ctl),
            allow_syscall(libc::SYS_epoll_pwait),
            #[cfg(all(target_env = "gnu", target_arch = "x86_64"))]
            allow_syscall(libc::SYS_epoll_wait),
            allow_syscall(libc::SYS_exit),
            allow_syscall(libc::SYS_exit_group),
            // Used by snapshotting, drive patching and rescanning
            allow_syscall_if(
                libc::SYS_fcntl,
                or![and![
                    Cond::new(1, ArgLen::DWORD, Eq, super::FCNTL_F_SETFD)?,
                    Cond::new(2, ArgLen::DWORD, Eq, super::FCNTL_FD_CLOEXEC)?,
                ],],
            ),
            // Used for drive patching & rescanning, for reading the local timezone
            allow_syscall(libc::SYS_fstat),
            // Used for snapshotting
            allow_syscall(libc::SYS_ftruncate),
            // Used for synchronization
            allow_syscall_if(
                libc::SYS_futex,
                or![
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAIT_PRIVATE)?],
                    and![Cond::new(1, ArgLen::DWORD, Eq, super::FUTEX_WAKE_PRIVATE)?],
                    #[cfg(target_env = "gnu")]
                    and![Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        super::FUTEX_CMP_REQUEUE_PRIVATE
                    )?],
                ],
            ),
            // Used by glibc's tgkill
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_getpid),
            allow_syscall_if(libc::SYS_ioctl, super::create_ioctl_seccomp_rule()?),
            // Used by the block device
            allow_syscall(libc::SYS_lseek),
            // Triggered by musl for some customer workloads
            #[cfg(target_env = "musl")]
            allow_syscall_if(
                libc::SYS_madvise,
                or![and![Cond::new(
                    2,
                    ArgLen::DWORD,
                    Eq,
                    libc::MADV_DONTNEED as u64
                )?],],
            ),
            // Used for re-allocating large memory regions, for example vectors
            allow_syscall(libc::SYS_mremap),
            // Used for freeing memory
            allow_syscall(libc::SYS_munmap),
            // Used for reading the timezone in LocalTime::now()
            allow_syscall_if(
                libc::SYS_mmap,
                or![
                    and![Cond::new(3, ArgLen::DWORD, Eq, libc::MAP_SHARED as u64)?],
                    and![Cond::new(
                        3,
                        ArgLen::DWORD,
                        Eq,
                        (libc::MAP_FIXED | libc::MAP_ANONYMOUS | libc::MAP_PRIVATE) as u64
                    )?],
                ],
            ),
            #[cfg(target_arch = "x86_64")]
            allow_syscall(libc::SYS_open),
            #[cfg(target_arch = "aarch64")]
            allow_syscall(libc::SYS_openat),
            allow_syscall(libc::SYS_read),
            // Used by the API thread and vsock
            allow_syscall(libc::SYS_recvfrom),
            // SYS_rt_sigreturn is needed in case a fault does occur, so that the signal handler
            // can return. Otherwise we get stuck in a fault loop.
            allow_syscall(libc::SYS_rt_sigreturn),
            // Used by the API thread and vsock
            allow_syscall_if(
                libc::SYS_socket,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::AF_UNIX as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::SOCK_STREAM as u64) | (libc::SOCK_CLOEXEC as u64)
                    )?,
                    Cond::new(2, ArgLen::DWORD, Eq, 0u64)?
                ],],
            ),
            // Used to kick vcpus
            allow_syscall_if(
                libc::SYS_tkill,
                or![and![Cond::new(
                    1,
                    ArgLen::DWORD,
                    Eq,
                    (sigrtmin() + super::super::vstate::vcpu::VCPU_RTSIG_OFFSET) as u64
                )?]],
            ),
            // Used to kick vcpus, on gnu
            #[cfg(target_env = "gnu")]
            allow_syscall(libc::SYS_tgkill),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_create,
                or![and![
                    Cond::new(0, ArgLen::DWORD, Eq, libc::CLOCK_MONOTONIC as u64)?,
                    Cond::new(
                        1,
                        ArgLen::DWORD,
                        Eq,
                        (libc::TFD_CLOEXEC as u64) | (libc::TFD_NONBLOCK as u64)
                    )?,
                ],],
            ),
            // Needed for rate limiting
            allow_syscall_if(
                libc::SYS_timerfd_settime,
                or![and![Cond::new(1, ArgLen::DWORD, Eq, 0u64)?],],
            ),
            allow_syscall(libc::SYS_write),
        ]
        .into_iter()
        .collect(),
        SeccompAction::Trap,
    )?)
}

/// Generate a BPF program based on a seccomp level value.
pub fn get_seccomp_filter(seccomp_level: SeccompLevel) -> Result<BpfProgram, SeccompError> {
    match seccomp_level {
        SeccompLevel::None => Ok(vec![]),
        SeccompLevel::Basic => default_filter()
            .map(|filter| filter.allow_all())
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
        SeccompLevel::Advanced => default_filter()
            .and_then(|filter| filter.try_into())
            .map_err(SeccompError::SeccompFilter),
    }
}

#[cfg(test)]
mod tests {
    use super::get_seccomp_filter;
    use seccomp::SeccompLevel;

    #[test]
    fn test_get_seccomp_filter() {
        assert!(get_seccomp_filter(SeccompLevel::None).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Basic).is_ok());
        assert!(get_seccomp_filter(SeccompLevel::Advanced).is_ok());
    }
}
<|endoftext|>


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

#![deny(missing_docs)]

//! Fundamental algebraic structures
//!
//! ~~~notrust
//! |(• ◡•)|ノ〵(❍ᴥ❍⋃)     - "ALGEBRAIC!!!"
//! ~~~
//!
//! For most applications requiring an abstraction over the reals, `ApproxField`
//! should be suffient.
//!
//! # Fundamental algebraic structures
//!
//! Most of these traits also come in an approximate flavor for types that do
//! not satisfy the required properties exactly, but would still benefit from
//! abstractions over the structure in question.
//!
//! ## Identity elements
//!
//! Two traits are provided that allow the definition of the additive and
//! multiplicative identity elements:
//!
//! - `AdditiveIdentity`
//! - `MultiplicativeIdentity`
//!
//! ## Group-like structures
//!
//! These structures are provided for both the addition and multiplication.
//!
//! ~~~notrust
//!               Magma
//!                 |
//!         _______/ \______
//!        /                \
//!   divisiblity      associativity
//!       |                  |
//!       V                  V
//!  Quasigroup          Semigroup
//!       |                  |
//!   identity            identity
//!       |                  |
//!       V                  V
//!     Loop               Monoid
//!       |                  |
//!  associative     invertablility
//!        \______   _______/
//!               \ /
//!                |
//!                V
//!              Group
//!                |
//!          commutativity
//!                |
//!                V
//!           AbelianGroup
//! ~~~
//!
//! The following traits are provided:
//!
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Closure`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Magma`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Quasigroup`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Loop`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Semigroup`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Monoid`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Group`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`AbelianGroup`
//!
//! ## Ring-like structures
//!
//! ~~~notrust
//! AdditiveAbelianGroup     MultiplicativeMonoid
//!           \________   ________/
//!                    \ /
//!                     |
//!                     V
//!                    Ring
//!                     |
//!            commutativity_of_mul
//!                     |
//!                     V
//!              CommutativeRing     MultiplicativeAbelianGroup
//!                      \_______   ___________/
//!                              \ /
//!                               |
//!                               V
//!                             Field
//! ~~~
//!
//! The following traits are provided:
//!
//! - (`Approx`)?`Ring`
//! - (`Approx`)?`CommutativeRing`
//! - (`Approx`)?`Field`
//!
//! # Quickcheck properties
//!
//! Functions are provided to test that algebraic properties like
//! assciociativity and commutativity hold for a given set of arguments.
//!
//! For example:
//!
//! ~~~
//! # use math::structure::ApproxMultiplicativeSemigroup;
//! #[quickcheck]
//! fn prop_mul_is_approx_associative(args: (i32, i32, i32)) -> bool {
//!     ApproxMultiplicativeSemigroup::prop_mul_is_approx_associative(args)
//! }
//! ~~~

pub use self::ident::{AdditiveIdentity, zero};
pub use self::ident::{MultiplicativeIdentity, unit};

pub use self::magma::ApproxAdditiveMagma;
pub use self::magma::ApproxMultiplicativeMagma;
pub use self::magma::AdditiveMagma;
pub use self::magma::MultiplicativeMagma;

pub use self::quasigroup::ApproxAdditiveQuasigroup;
pub use self::quasigroup::ApproxMultiplicativeQuasigroup;
pub use self::quasigroup::AdditiveQuasigroup;
pub use self::quasigroup::MultiplicativeQuasigroup;

pub use self::loop_::ApproxAdditiveLoop;
pub use self::loop_::ApproxMultiplicativeLoop;
pub use self::loop_::AdditiveLoop;
pub use self::loop_::MultiplicativeLoop;

pub use self::semigroup::ApproxAdditiveSemigroup;
pub use self::semigroup::ApproxMultiplicativeSemigroup;
pub use self::semigroup::AdditiveSemigroup;
pub use self::semigroup::MultiplicativeSemigroup;

pub use self::monoid::ApproxAdditiveMonoid;
pub use self::monoid::ApproxMultiplicativeMonoid;
pub use self::monoid::AdditiveMonoid;
pub use self::monoid::MultiplicativeMonoid;

pub use self::group::ApproxAdditiveGroup;
pub use self::group::ApproxMultiplicativeGroup;
pub use self::group::AdditiveGroup;
pub use self::group::MultiplicativeGroup;

// pub use self::abelian::ApproxAdditiveAbelianGroup;
// pub use self::abelian::ApproxMultiplicativeAbelianGroup;
// pub use self::abelian::AdditiveAbelianGroup;
// pub use self::abelian::MultiplicativeAbelianGroup;

// pub use self::ring::ApproxRing;
// pub use self::ring::Ring;
// pub use self::ring::ApproxCommutativeRing;
// pub use self::ring::CommutativeRing;
// pub use self::ring::ApproxField;
// pub use self::ring::Field;

mod ident;

mod magma;
mod quasigroup;
mod loop_;
mod semigroup;
mod monoid;
mod group;
// mod abelian;

// mod ring;
<commit_msg>Fix doc test<commit_after>// Copyright 2013-2014 The Num-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![deny(missing_docs)]

//! Fundamental algebraic structures
//!
//! ~~~notrust
//! |(• ◡•)|ノ〵(❍ᴥ❍⋃)     - "ALGEBRAIC!!!"
//! ~~~
//!
//! For most applications requiring an abstraction over the reals, `ApproxField`
//! should be suffient.
//!
//! # Fundamental algebraic structures
//!
//! Most of these traits also come in an approximate flavor for types that do
//! not satisfy the required properties exactly, but would still benefit from
//! abstractions over the structure in question.
//!
//! ## Identity elements
//!
//! Two traits are provided that allow the definition of the additive and
//! multiplicative identity elements:
//!
//! - `AdditiveIdentity`
//! - `MultiplicativeIdentity`
//!
//! ## Group-like structures
//!
//! These structures are provided for both the addition and multiplication.
//!
//! ~~~notrust
//!               Magma
//!                 |
//!         _______/ \______
//!        /                \
//!   divisiblity      associativity
//!       |                  |
//!       V                  V
//!  Quasigroup          Semigroup
//!       |                  |
//!   identity            identity
//!       |                  |
//!       V                  V
//!     Loop               Monoid
//!       |                  |
//!  associative     invertablility
//!        \______   _______/
//!               \ /
//!                |
//!                V
//!              Group
//!                |
//!          commutativity
//!                |
//!                V
//!           AbelianGroup
//! ~~~
//!
//! The following traits are provided:
//!
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Closure`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Magma`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Quasigroup`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Loop`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Semigroup`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Monoid`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`Group`
//! - (`Approx`)?(`Additive`|`Multiplicative`)`AbelianGroup`
//!
//! ## Ring-like structures
//!
//! ~~~notrust
//! AdditiveAbelianGroup     MultiplicativeMonoid
//!           \________   ________/
//!                    \ /
//!                     |
//!                     V
//!                    Ring
//!                     |
//!            commutativity_of_mul
//!                     |
//!                     V
//!              CommutativeRing     MultiplicativeAbelianGroup
//!                      \_______   ___________/
//!                              \ /
//!                               |
//!                               V
//!                             Field
//! ~~~
//!
//! The following traits are provided:
//!
//! - (`Approx`)?`Ring`
//! - (`Approx`)?`CommutativeRing`
//! - (`Approx`)?`Field`
//!
//! # Quickcheck properties
//!
//! Functions are provided to test that algebraic properties like
//! assciociativity and commutativity hold for a given set of arguments.
//!
//! For example:
//!
//! ~~~
//! # use algebra::structure::ApproxMultiplicativeSemigroup;
//! #[quickcheck]
//! fn prop_mul_is_approx_associative(args: (i32, i32, i32)) -> bool {
//!     ApproxMultiplicativeSemigroup::prop_mul_is_approx_associative(args)
//! }
//! ~~~

pub use self::ident::{AdditiveIdentity, zero};
pub use self::ident::{MultiplicativeIdentity, unit};

pub use self::magma::ApproxAdditiveMagma;
pub use self::magma::ApproxMultiplicativeMagma;
pub use self::magma::AdditiveMagma;
pub use self::magma::MultiplicativeMagma;

pub use self::quasigroup::ApproxAdditiveQuasigroup;
pub use self::quasigroup::ApproxMultiplicativeQuasigroup;
pub use self::quasigroup::AdditiveQuasigroup;
pub use self::quasigroup::MultiplicativeQuasigroup;

pub use self::loop_::ApproxAdditiveLoop;
pub use self::loop_::ApproxMultiplicativeLoop;
pub use self::loop_::AdditiveLoop;
pub use self::loop_::MultiplicativeLoop;

pub use self::semigroup::ApproxAdditiveSemigroup;
pub use self::semigroup::ApproxMultiplicativeSemigroup;
pub use self::semigroup::AdditiveSemigroup;
pub use self::semigroup::MultiplicativeSemigroup;

pub use self::monoid::ApproxAdditiveMonoid;
pub use self::monoid::ApproxMultiplicativeMonoid;
pub use self::monoid::AdditiveMonoid;
pub use self::monoid::MultiplicativeMonoid;

pub use self::group::ApproxAdditiveGroup;
pub use self::group::ApproxMultiplicativeGroup;
pub use self::group::AdditiveGroup;
pub use self::group::MultiplicativeGroup;

// pub use self::abelian::ApproxAdditiveAbelianGroup;
// pub use self::abelian::ApproxMultiplicativeAbelianGroup;
// pub use self::abelian::AdditiveAbelianGroup;
// pub use self::abelian::MultiplicativeAbelianGroup;

// pub use self::ring::ApproxRing;
// pub use self::ring::Ring;
// pub use self::ring::ApproxCommutativeRing;
// pub use self::ring::CommutativeRing;
// pub use self::ring::ApproxField;
// pub use self::ring::Field;

mod ident;

mod magma;
mod quasigroup;
mod loop_;
mod semigroup;
mod monoid;
mod group;
// mod abelian;

// mod ring;
<|endoftext|>