<commit_before>use std::fmt;
use std::ops::*;

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)]
pub struct Checked<T>(pub Option<T>);

impl<T> Checked<T> {
    pub fn new(x: T) -> Checked<T> {
        Checked(Some(x))
    }
}

impl<T: fmt::Debug> fmt::Debug for Checked<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Some(ref x) => x.fmt(f),
            None => "overflow".fmt(f),
        }
    }
}

impl<T: fmt::Display> fmt::Display for Checked<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Some(ref x) => x.fmt(f),
            None => "overflow".fmt(f),
        }
    }
}

impl<T> From<T> for Checked<T> {
    fn from(x: T) -> Checked<T> {
        Checked(Some(x))
    }
}

impl<T> From<Option<T>> for Checked<T> {
    fn from(x: Option<T>) -> Checked<T> {
        Checked(x)
    }
}

// I'd like to
// impl<T, U> From<U> where T: From<U> for Checked<T>
// in the obvious way, but that "conflicts" with the default impl From<T> for T.
// This would subsume both the above Froms.

// implements the unary operator "op &T"
// based on "op T" where T is expected to be `Copy`able
macro_rules! forward_ref_unop {
    (impl $imp:ident, $method:ident for $t:ty {}) => {
        impl<'a> $imp for &'a $t {
            type Output = <$t as $imp>::Output;

            fn $method(self) -> <$t as $imp>::Output {
                $imp::$method(*self)
            }
        }
    }
}

// implements binary operators "&T op U", "T op &U", "&T op &U"
// based on "T op U" where T and U are expected to be `Copy`able
macro_rules! forward_ref_binop {
    (impl $imp:ident, $method:ident for $t:ty, $u:ty {}) => {
        impl<'a> $imp<$u> for &'a $t {
            type Output = <$t as $imp<$u>>::Output;

            fn $method(self, other: $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(*self, other)
            }
        }

        impl<'a> $imp<&'a $u> for $t {
            type Output = <$t as $imp<$u>>::Output;

            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(self, *other)
            }
        }

        impl<'a, 'b> $imp<&'a $u> for &'b $t {
            type Output = <$t as $imp<$u>>::Output;

            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(*self, *other)
            }
        }
    }
}

macro_rules! sh_impl {
    ($t:ident, $f:ident) => {
        impl Shl<Checked<$f>> for Checked<$t> {
            type Output = Checked<$t>;

            fn shl(self, other: Checked<$f>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(x.checked_shl(y)),
                    _ => Checked(None),
                }
            }
        }

        impl Shl<$f> for Checked<$t> {
            type Output = Checked<$t>;

            fn shl(self, other: $f) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.checked_shl(other)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shl, shl for Checked<$t>, Checked<$f> {} }
        forward_ref_binop! { impl Shl, shl for Checked<$t>, $f {} }

        impl ShlAssign<$f> for Checked<$t> {
            fn shl_assign(&mut self, other: $f) {
                *self = *self << other;
            }
        }

        impl Shr<Checked<$f>> for Checked<$t> {
            type Output = Checked<$t>;

            fn shr(self, other: Checked<$f>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(x.checked_shr(y)),
                    _ => Checked(None),
                }
            }
        }

        impl Shr<$f> for Checked<$t> {
            type Output = Checked<$t>;

            fn shr(self, other: $f) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.checked_shr(other)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shr, shr for Checked<$t>, Checked<$f> {} }
        forward_ref_binop! { impl Shr, shr for Checked<$t>, $f {} }

        impl ShrAssign<$f> for Checked<$t> {
            fn shr_assign(&mut self, other: $f) {
                *self = *self >> other;
            }
        }
    };
}

macro_rules! sh_impl_reverse {
    ($t:ident, $f:ident) => {
        impl Shl<Checked<$t>> for $f {
            type Output = Checked<$f>;

            fn shl(self, other: Checked<$t>) -> Checked<$f> {
                match other.0 {
                    Some(x) => Checked(self.checked_shl(x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shl, shl for $f, Checked<$t> {} }

        impl Shr<Checked<$t>> for $f {
            type Output = Checked<$f>;

            fn shr(self, other: Checked<$t>) -> Checked<$f> {
                match other.0 {
                    Some(x) => Checked(self.checked_shr(x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shr, shr for $f, Checked<$t> {} }
    };
}

macro_rules! sh_impl_all {
    ($($t:ident)*) => ($(
        // When checked_shX is added for other shift sizes, uncomment some of these.
        // sh_impl! { $t, u8 }
        // sh_impl! { $t, u16 }
        sh_impl! { $t, u32 }
        //sh_impl! { $t, u64 }
        //sh_impl! { $t, usize }

        //sh_impl! { $t, i8 }
        //sh_impl! { $t, i16 }
        //sh_impl! { $t, i32 }
        //sh_impl! { $t, i64 }
        //sh_impl! { $t, isize }

        // sh_impl_reverse! { u8, $t }
        // sh_impl_reverse! { u16, $t }
        sh_impl_reverse! { u32, $t }
        //sh_impl_reverse! { u64, $t }
        //sh_impl_reverse! { usize, $t }

        //sh_impl_reverse! { i8, $t }
        //sh_impl_reverse! { i16, $t }
        //sh_impl_reverse! { i32, $t }
        //sh_impl_reverse! { i64, $t }
        //sh_impl_reverse! { isize, $t }
    )*)
}

sh_impl_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }

// implements unary operators for checked types
macro_rules! impl_unop {
    (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.$checked_method()),
                    None => Checked(None)
                }
            }
        }

        forward_ref_unop! { impl $imp, $method for Checked<$t> {} }
    }
}

// implements unary operators for checked types (with no checked operation)
macro_rules! impl_unop_unchecked {
    (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(Some($op x)),
                    None => Checked(None)
                }
            }
        }

        forward_ref_unop! { impl $imp, $method for Checked<$t> {} }
    }
}

// implements binary operators for checked types
macro_rules! impl_binop {
    (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(x.$checked_method(y)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<$t> for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: $t) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.$checked_method(other)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<Checked<$t>> for $t {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match other.0 {
                    Some(x) => Checked(self.$checked_method(x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} }
        forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} }
        forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} }
    }
}

// implements binary operators for checked types (no checked operation)
macro_rules! impl_binop_unchecked {
    (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(Some(x $op y)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<$t> for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: $t) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(Some(x $op other)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<Checked<$t>> for $t {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match other.0 {
                    Some(x) => Checked(Some(self $op x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} }
        forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} }
        forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} }
    }
}

// implements assignment operators for checked types
macro_rules! impl_binop_assign {
    (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => {
        impl $imp for Checked<$t> {
            fn $method(&mut self, other: Checked<$t>) {
                *self = *self $op other;
            }
        }

        impl $imp<$t> for Checked<$t> {
            fn $method(&mut self, other: $t) {
                *self = *self $op other;
            }
        }
    };
}

macro_rules! checked_impl {
    ($($t:ty)*) => {
        $(
            impl_binop! { impl Add, add, checked_add for $t {} }
            impl_binop_assign! { impl AddAssign, add_assign for $t {+} }
            impl_binop! { impl Sub, sub, checked_sub for $t {} }
            impl_binop_assign! { impl SubAssign, sub_assign for $t {-} }
            impl_binop! { impl Mul, mul, checked_mul for $t {} }
            impl_binop_assign! { impl MulAssign, mul_assign for $t {*} }
            impl_binop! { impl Div, div, checked_div for $t {} }
            impl_binop_assign! { impl DivAssign, div_assign for $t {/} }
            impl_binop! { impl Rem, rem, checked_rem for $t {} }
            impl_binop_assign! { impl RemAssign, rem_assign for $t {%} }
            impl_unop_unchecked! { impl Not, not for $t {!} }
            impl_binop_unchecked! { impl BitXor, bitxor for $t {^} }
            impl_binop_assign! { impl BitXorAssign, bitxor_assign for $t {^} }
            impl_binop_unchecked! { impl BitOr, bitor for $t {|} }
            impl_binop_assign! { impl BitOrAssign, bitor_assign for $t {|} }
            impl_binop_unchecked! { impl BitAnd, bitand for $t {&} }
            impl_binop_assign! { impl BitAndAssign, bitand_assign for $t {&} }
            impl_unop! { impl Neg, neg, checked_neg for $t {} }
        )*
    };
}

checked_impl! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
<commit_msg>Add docs for Checked::new, change name of sh_impl to impl_sh (and variants)<commit_after>use std::fmt;
use std::ops::*;

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)]
pub struct Checked<T>(pub Option<T>);

impl<T> Checked<T> {
    /// Creates a new Checked instance from some sort of integer.
    /// This is essentially equivalent to From\<T\>.
    /// # Examples
    /// ```
    /// use checked::Checked;
    ///
    /// let x = Checked::new(1_000_u32);
    /// let y = Checked::new(1_000_000_u32);
    /// assert_eq!(x * x, y);
    /// ```
    pub fn new(x: T) -> Checked<T> {
        Checked(Some(x))
    }
}

impl<T: fmt::Debug> fmt::Debug for Checked<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Some(ref x) => x.fmt(f),
            None => "overflow".fmt(f),
        }
    }
}

impl<T: fmt::Display> fmt::Display for Checked<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Some(ref x) => x.fmt(f),
            None => "overflow".fmt(f),
        }
    }
}

impl<T> From<T> for Checked<T> {
    fn from(x: T) -> Checked<T> {
        Checked(Some(x))
    }
}

impl<T> From<Option<T>> for Checked<T> {
    fn from(x: Option<T>) -> Checked<T> {
        Checked(x)
    }
}

// I'd like to
// impl<T, U> From<U> where T: From<U> for Checked<T>
// in the obvious way, but that "conflicts" with the default impl From<T> for T.
// This would subsume both the above Froms since Option has the right From impl.

// implements the unary operator "op &T"
// based on "op T" where T is expected to be `Copy`able
macro_rules! forward_ref_unop {
    (impl $imp:ident, $method:ident for $t:ty {}) => {
        impl<'a> $imp for &'a $t {
            type Output = <$t as $imp>::Output;

            fn $method(self) -> <$t as $imp>::Output {
                $imp::$method(*self)
            }
        }
    }
}

// implements binary operators "&T op U", "T op &U", "&T op &U"
// based on "T op U" where T and U are expected to be `Copy`able
macro_rules! forward_ref_binop {
    (impl $imp:ident, $method:ident for $t:ty, $u:ty {}) => {
        impl<'a> $imp<$u> for &'a $t {
            type Output = <$t as $imp<$u>>::Output;

            fn $method(self, other: $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(*self, other)
            }
        }

        impl<'a> $imp<&'a $u> for $t {
            type Output = <$t as $imp<$u>>::Output;

            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(self, *other)
            }
        }

        impl<'a, 'b> $imp<&'a $u> for &'b $t {
            type Output = <$t as $imp<$u>>::Output;

            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(*self, *other)
            }
        }
    }
}

macro_rules! impl_sh {
    ($t:ident, $f:ident) => {
        impl Shl<Checked<$f>> for Checked<$t> {
            type Output = Checked<$t>;

            fn shl(self, other: Checked<$f>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(x.checked_shl(y)),
                    _ => Checked(None),
                }
            }
        }

        impl Shl<$f> for Checked<$t> {
            type Output = Checked<$t>;

            fn shl(self, other: $f) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.checked_shl(other)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shl, shl for Checked<$t>, Checked<$f> {} }
        forward_ref_binop! { impl Shl, shl for Checked<$t>, $f {} }

        impl ShlAssign<$f> for Checked<$t> {
            fn shl_assign(&mut self, other: $f) {
                *self = *self << other;
            }
        }

        impl Shr<Checked<$f>> for Checked<$t> {
            type Output = Checked<$t>;

            fn shr(self, other: Checked<$f>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(x.checked_shr(y)),
                    _ => Checked(None),
                }
            }
        }

        impl Shr<$f> for Checked<$t> {
            type Output = Checked<$t>;

            fn shr(self, other: $f) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.checked_shr(other)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shr, shr for Checked<$t>, Checked<$f> {} }
        forward_ref_binop! { impl Shr, shr for Checked<$t>, $f {} }

        impl ShrAssign<$f> for Checked<$t> {
            fn shr_assign(&mut self, other: $f) {
                *self = *self >> other;
            }
        }
    };
}

macro_rules! impl_sh_reverse {
    ($t:ident, $f:ident) => {
        impl Shl<Checked<$t>> for $f {
            type Output = Checked<$f>;

            fn shl(self, other: Checked<$t>) -> Checked<$f> {
                match other.0 {
                    Some(x) => Checked(self.checked_shl(x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shl, shl for $f, Checked<$t> {} }

        impl Shr<Checked<$t>> for $f {
            type Output = Checked<$f>;

            fn shr(self, other: Checked<$t>) -> Checked<$f> {
                match other.0 {
                    Some(x) => Checked(self.checked_shr(x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl Shr, shr for $f, Checked<$t> {} }
    };
}

macro_rules! impl_sh_all {
    ($($t:ident)*) => ($(
        // When checked_shX is added for other shift sizes, uncomment some of these.
        // impl_sh! { $t, u8 }
        // impl_sh! { $t, u16 }
        impl_sh! { $t, u32 }
        //impl_sh! { $t, u64 }
        //impl_sh! { $t, usize }

        //impl_sh! { $t, i8 }
        //impl_sh! { $t, i16 }
        //impl_sh! { $t, i32 }
        //impl_sh! { $t, i64 }
        //impl_sh! { $t, isize }

        // impl_sh_reverse! { u8, $t }
        // impl_sh_reverse! { u16, $t }
        impl_sh_reverse! { u32, $t }
        //impl_sh_reverse! { u64, $t }
        //impl_sh_reverse! { usize, $t }

        //impl_sh_reverse! { i8, $t }
        //impl_sh_reverse! { i16, $t }
        //impl_sh_reverse! { i32, $t }
        //impl_sh_reverse! { i64, $t }
        //impl_sh_reverse! { isize, $t }
    )*)
}

impl_sh_all! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }

// implements unary operators for checked types
macro_rules! impl_unop {
    (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.$checked_method()),
                    None => Checked(None)
                }
            }
        }

        forward_ref_unop! { impl $imp, $method for Checked<$t> {} }
    }
}

// implements unary operators for checked types (with no checked operation)
macro_rules! impl_unop_unchecked {
    (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(Some($op x)),
                    None => Checked(None)
                }
            }
        }

        forward_ref_unop! { impl $imp, $method for Checked<$t> {} }
    }
}

// implements binary operators for checked types
macro_rules! impl_binop {
    (impl $imp:ident, $method:ident, $checked_method:ident for $t:ty {}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(x.$checked_method(y)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<$t> for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: $t) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(x.$checked_method(other)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<Checked<$t>> for $t {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match other.0 {
                    Some(x) => Checked(self.$checked_method(x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} }
        forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} }
        forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} }
    }
}

// implements binary operators for checked types (no checked operation)
macro_rules! impl_binop_unchecked {
    (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => {
        impl $imp for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match (self.0, other.0) {
                    (Some(x), Some(y)) => Checked(Some(x $op y)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<$t> for Checked<$t> {
            type Output = Checked<$t>;

            fn $method(self, other: $t) -> Checked<$t> {
                match self.0 {
                    Some(x) => Checked(Some(x $op other)),
                    _ => Checked(None),
                }
            }
        }

        impl $imp<Checked<$t>> for $t {
            type Output = Checked<$t>;

            fn $method(self, other: Checked<$t>) -> Checked<$t> {
                match other.0 {
                    Some(x) => Checked(Some(self $op x)),
                    None => Checked(None),
                }
            }
        }

        forward_ref_binop! { impl $imp, $method for Checked<$t>, Checked<$t> {} }
        forward_ref_binop! { impl $imp, $method for Checked<$t>, $t {} }
        forward_ref_binop! { impl $imp, $method for $t, Checked<$t> {} }
    }
}

// implements assignment operators for checked types
macro_rules! impl_binop_assign {
    (impl $imp:ident, $method:ident for $t:ty {$op:tt}) => {
        impl $imp for Checked<$t> {
            fn $method(&mut self, other: Checked<$t>) {
                *self = *self $op other;
            }
        }

        impl $imp<$t> for Checked<$t> {
            fn $method(&mut self, other: $t) {
                *self = *self $op other;
            }
        }
    };
}

macro_rules! checked_impl {
    ($($t:ty)*) => {
        $(
            impl_binop! { impl Add, add, checked_add for $t {} }
            impl_binop_assign! { impl AddAssign, add_assign for $t {+} }
            impl_binop! { impl Sub, sub, checked_sub for $t {} }
            impl_binop_assign! { impl SubAssign, sub_assign for $t {-} }
            impl_binop! { impl Mul, mul, checked_mul for $t {} }
            impl_binop_assign! { impl MulAssign, mul_assign for $t {*} }
            impl_binop! { impl Div, div, checked_div for $t {} }
            impl_binop_assign! { impl DivAssign, div_assign for $t {/} }
            impl_binop! { impl Rem, rem, checked_rem for $t {} }
            impl_binop_assign! { impl RemAssign, rem_assign for $t {%} }
            impl_unop_unchecked! { impl Not, not for $t {!} }
            impl_binop_unchecked! { impl BitXor, bitxor for $t {^} }
            impl_binop_assign! { impl BitXorAssign, bitxor_assign for $t {^} }
            impl_binop_unchecked! { impl BitOr, bitor for $t {|} }
            impl_binop_assign! { impl BitOrAssign, bitor_assign for $t {|} }
            impl_binop_unchecked! { impl BitAnd, bitand for $t {&} }
            impl_binop_assign! { impl BitAndAssign, bitand_assign for $t {&} }
            impl_unop! { impl Neg, neg, checked_neg for $t {} }
        )*
    };
}

checked_impl! { u8 u16 u32 u64 usize i8 i16 i32 i64 isize }
<|endoftext|>


<commit_before>#[cfg(test)]
#[path = "test.rs"]
mod test;

use super::{Opt, Output};
use crate::cfg::{self, CfgValue};
use crate::gen::include::Include;
use crate::syntax::IncludeKind;
use clap::builder::{ArgAction, ValueParser};
use clap::{Arg, Command};
use std::collections::{BTreeMap as Map, BTreeSet as Set};
use std::path::PathBuf;
use std::sync::{Arc, Mutex, PoisonError};
use syn::parse::Parser;

const USAGE: &str = "\
    cxxbridge <input>.rs              Emit .cc file for bridge to stdout
    cxxbridge <input>.rs --header     Emit .h file for bridge to stdout
    cxxbridge --header                Emit \"rust/cxx.h\" header to stdout\
";

const TEMPLATE: &str = "\
{bin} {version}
David Tolnay <dtolnay@gmail.com>
https://github.com/dtolnay/cxx

USAGE:
    {usage}

ARGS:
{positionals}

OPTIONS:
{options}\
";

fn app() -> Command<'static> {
    let mut app = Command::new("cxxbridge")
        .override_usage(USAGE)
        .help_template(TEMPLATE)
        .next_line_help(true)
        .arg(arg_input())
        .arg(arg_cxx_impl_annotations())
        .arg(arg_header())
        .arg(arg_include())
        .arg(arg_output())
        .arg(arg_cfg())
        .mut_arg("help", |a| a.help("Print help information."));
    if let Some(version) = option_env!("CARGO_PKG_VERSION") {
        app = app
            .version(version)
            .mut_arg("version", |a| a.help("Print version information."));
    }
    app
}

const INPUT: &str = "input";
const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
const HEADER: &str = "header";
const INCLUDE: &str = "include";
const OUTPUT: &str = "output";
const CFG: &str = "cfg";

pub(super) fn from_args() -> Opt {
    let matches = app().get_matches();

    let input = matches.get_one::<PathBuf>(INPUT).cloned();
    let cxx_impl_annotations = matches
        .get_one::<String>(CXX_IMPL_ANNOTATIONS)
        .map(String::clone);
    let header = matches.contains_id(HEADER);
    let include = matches
        .get_many::<String>(INCLUDE)
        .unwrap_or_default()
        .map(|include| {
            if include.starts_with('<') && include.ends_with('>') {
                Include {
                    path: include[1..include.len() - 1].to_owned(),
                    kind: IncludeKind::Bracketed,
                }
            } else {
                Include {
                    path: include.to_owned(),
                    kind: IncludeKind::Quoted,
                }
            }
        })
        .collect();

    let mut outputs = Vec::new();
    for path in matches.get_many::<PathBuf>(OUTPUT).unwrap_or_default() {
        outputs.push(if path.as_os_str() == "-" {
            Output::Stdout
        } else {
            Output::File(path.clone())
        });
    }
    if outputs.is_empty() {
        outputs.push(Output::Stdout);
    }

    let mut cfg = Map::new();
    for arg in matches.get_many::<String>(CFG).unwrap_or_default() {
        let (name, value) = cfg::parse.parse_str(arg).unwrap();
        cfg.entry(name).or_insert_with(Set::new).insert(value);
    }

    Opt {
        input,
        header,
        cxx_impl_annotations,
        include,
        outputs,
        cfg,
    }
}

fn arg_input() -> Arg<'static> {
    Arg::new(INPUT)
        .help("Input Rust source file containing #[cxx::bridge].")
        .required_unless_present(HEADER)
        .value_parser(ValueParser::path_buf())
}

fn arg_cxx_impl_annotations() -> Arg<'static> {
    const HELP: &str = "\
Optional annotation for implementations of C++ function wrappers
that may be exposed to Rust. You may for example need to provide
__declspec(dllexport) or __attribute__((visibility(\"default\")))
if Rust code from one shared object or executable depends on
these C++ functions in another.";
    Arg::new(CXX_IMPL_ANNOTATIONS)
        .long(CXX_IMPL_ANNOTATIONS)
        .takes_value(true)
        .value_name("annotation")
        .value_parser(ValueParser::string())
        .help(HELP)
}

fn arg_header() -> Arg<'static> {
    const HELP: &str = "\
Emit header with declarations only. Optional if using `-o` with
a path ending in `.h`.";
    Arg::new(HEADER).long(HEADER).help(HELP)
}

fn arg_include() -> Arg<'static> {
    const HELP: &str = "\
Any additional headers to #include. The cxxbridge tool does not
parse or even require the given paths to exist; they simply go
into the generated C++ code as #include lines.";
    Arg::new(INCLUDE)
        .long(INCLUDE)
        .short('i')
        .takes_value(true)
        .action(ArgAction::Append)
        .value_parser(ValueParser::string())
        .help(HELP)
}

fn arg_output() -> Arg<'static> {
    const HELP: &str = "\
Path of file to write as output. Output goes to stdout if -o is
not specified.";
    Arg::new(OUTPUT)
        .long(OUTPUT)
        .short('o')
        .takes_value(true)
        .action(ArgAction::Append)
        .value_parser(ValueParser::path_buf())
        .help(HELP)
}

fn arg_cfg() -> Arg<'static> {
    const HELP: &str = "\
Compilation configuration matching what will be used to build
the Rust side of the bridge.";
    let bool_cfgs = Arc::new(Mutex::new(Map::<String, bool>::new()));
    Arg::new(CFG)
        .long(CFG)
        .takes_value(true)
        .value_name("name=\"value\" | name[=true] | name=false")
        .action(ArgAction::Append)
        .value_parser(move |arg: &str| match cfg::parse.parse_str(arg) {
            Ok((_, CfgValue::Str(_))) => Ok(arg.to_owned()),
            Ok((name, CfgValue::Bool(value))) => {
                let mut bool_cfgs = bool_cfgs.lock().unwrap_or_else(PoisonError::into_inner);
                if let Some(&prev) = bool_cfgs.get(&name) {
                    if prev != value {
                        return Err(format!("cannot have both {0}=false and {0}=true", name));
                    }
                }
                bool_cfgs.insert(name, value);
                Ok(arg.to_owned())
            }
            Err(_) => Err("expected name=\"value\", name=true, or name=false".to_owned()),
        })
        .help(HELP)
}
<commit_msg>Sort cxxbridge flags in alphabetical order<commit_after>#[cfg(test)]
#[path = "test.rs"]
mod test;

use super::{Opt, Output};
use crate::cfg::{self, CfgValue};
use crate::gen::include::Include;
use crate::syntax::IncludeKind;
use clap::builder::{ArgAction, ValueParser};
use clap::{Arg, Command};
use std::collections::{BTreeMap as Map, BTreeSet as Set};
use std::path::PathBuf;
use std::sync::{Arc, Mutex, PoisonError};
use syn::parse::Parser;

const USAGE: &str = "\
    cxxbridge <input>.rs              Emit .cc file for bridge to stdout
    cxxbridge <input>.rs --header     Emit .h file for bridge to stdout
    cxxbridge --header                Emit \"rust/cxx.h\" header to stdout\
";

const TEMPLATE: &str = "\
{bin} {version}
David Tolnay <dtolnay@gmail.com>
https://github.com/dtolnay/cxx

USAGE:
    {usage}

ARGS:
{positionals}

OPTIONS:
{options}\
";

fn app() -> Command<'static> {
    let mut app = Command::new("cxxbridge")
        .override_usage(USAGE)
        .help_template(TEMPLATE)
        .next_line_help(true)
        .arg(arg_input())
        .arg(arg_cfg())
        .arg(arg_cxx_impl_annotations())
        .arg(arg_header())
        .arg(arg_include())
        .arg(arg_output())
        .mut_arg("help", |a| a.help("Print help information."));
    if let Some(version) = option_env!("CARGO_PKG_VERSION") {
        app = app
            .version(version)
            .mut_arg("version", |a| a.help("Print version information."));
    }
    app
}

const INPUT: &str = "input";
const CFG: &str = "cfg";
const CXX_IMPL_ANNOTATIONS: &str = "cxx-impl-annotations";
const HEADER: &str = "header";
const INCLUDE: &str = "include";
const OUTPUT: &str = "output";

pub(super) fn from_args() -> Opt {
    let matches = app().get_matches();

    let input = matches.get_one::<PathBuf>(INPUT).cloned();
    let cxx_impl_annotations = matches
        .get_one::<String>(CXX_IMPL_ANNOTATIONS)
        .map(String::clone);
    let header = matches.contains_id(HEADER);
    let include = matches
        .get_many::<String>(INCLUDE)
        .unwrap_or_default()
        .map(|include| {
            if include.starts_with('<') && include.ends_with('>') {
                Include {
                    path: include[1..include.len() - 1].to_owned(),
                    kind: IncludeKind::Bracketed,
                }
            } else {
                Include {
                    path: include.to_owned(),
                    kind: IncludeKind::Quoted,
                }
            }
        })
        .collect();

    let mut outputs = Vec::new();
    for path in matches.get_many::<PathBuf>(OUTPUT).unwrap_or_default() {
        outputs.push(if path.as_os_str() == "-" {
            Output::Stdout
        } else {
            Output::File(path.clone())
        });
    }
    if outputs.is_empty() {
        outputs.push(Output::Stdout);
    }

    let mut cfg = Map::new();
    for arg in matches.get_many::<String>(CFG).unwrap_or_default() {
        let (name, value) = cfg::parse.parse_str(arg).unwrap();
        cfg.entry(name).or_insert_with(Set::new).insert(value);
    }

    Opt {
        input,
        header,
        cxx_impl_annotations,
        include,
        outputs,
        cfg,
    }
}

fn arg_input() -> Arg<'static> {
    Arg::new(INPUT)
        .help("Input Rust source file containing #[cxx::bridge].")
        .required_unless_present(HEADER)
        .value_parser(ValueParser::path_buf())
}

fn arg_cfg() -> Arg<'static> {
    const HELP: &str = "\
Compilation configuration matching what will be used to build
the Rust side of the bridge.";
    let bool_cfgs = Arc::new(Mutex::new(Map::<String, bool>::new()));
    Arg::new(CFG)
        .long(CFG)
        .takes_value(true)
        .value_name("name=\"value\" | name[=true] | name=false")
        .action(ArgAction::Append)
        .value_parser(move |arg: &str| match cfg::parse.parse_str(arg) {
            Ok((_, CfgValue::Str(_))) => Ok(arg.to_owned()),
            Ok((name, CfgValue::Bool(value))) => {
                let mut bool_cfgs = bool_cfgs.lock().unwrap_or_else(PoisonError::into_inner);
                if let Some(&prev) = bool_cfgs.get(&name) {
                    if prev != value {
                        return Err(format!("cannot have both {0}=false and {0}=true", name));
                    }
                }
                bool_cfgs.insert(name, value);
                Ok(arg.to_owned())
            }
            Err(_) => Err("expected name=\"value\", name=true, or name=false".to_owned()),
        })
        .help(HELP)
}

fn arg_cxx_impl_annotations() -> Arg<'static> {
    const HELP: &str = "\
Optional annotation for implementations of C++ function wrappers
that may be exposed to Rust. You may for example need to provide
__declspec(dllexport) or __attribute__((visibility(\"default\")))
if Rust code from one shared object or executable depends on
these C++ functions in another.";
    Arg::new(CXX_IMPL_ANNOTATIONS)
        .long(CXX_IMPL_ANNOTATIONS)
        .takes_value(true)
        .value_name("annotation")
        .value_parser(ValueParser::string())
        .help(HELP)
}

fn arg_header() -> Arg<'static> {
    const HELP: &str = "\
Emit header with declarations only. Optional if using `-o` with
a path ending in `.h`.";
    Arg::new(HEADER).long(HEADER).help(HELP)
}

fn arg_include() -> Arg<'static> {
    const HELP: &str = "\
Any additional headers to #include. The cxxbridge tool does not
parse or even require the given paths to exist; they simply go
into the generated C++ code as #include lines.";
    Arg::new(INCLUDE)
        .long(INCLUDE)
        .short('i')
        .takes_value(true)
        .action(ArgAction::Append)
        .value_parser(ValueParser::string())
        .help(HELP)
}

fn arg_output() -> Arg<'static> {
    const HELP: &str = "\
Path of file to write as output. Output goes to stdout if -o is
not specified.";
    Arg::new(OUTPUT)
        .long(OUTPUT)
        .short('o')
        .takes_value(true)
        .action(ArgAction::Append)
        .value_parser(ValueParser::path_buf())
        .help(HELP)
}
<|endoftext|>


<commit_before>/* Decoder */

use cpu::Op8;
use cpu::Op8::*;
use cpu::Cond;
use cpu::ops::Cont;
use cpu::ops::Cont::*;
use cpu::ops::Instruction;
use cpu::ops::Instruction::*;
use cpu::registers::Reg8::*;
use cpu::registers::Reg16::*;
use cpu::IndirectAddr;

// TODO: add documentation for the macros
macro_rules! imm_type {
    (A   -> $i: ident) => { Ind(IndirectAddr::Imm8($i)) };
    (I   -> $i: ident) => { Imm($i) };
    (I16 -> $i: ident) => { $i };
    (A16 -> $i: ident) => { $i }
}

macro_rules! partial_type {
    (A)   => { Partial8  };
    (I)   => { Partial8  };
    (I16) => { Partial16 };
    (A16) => { Partial16 }
}

macro_rules! instr {
    ($mnem: ident) => {
        Done($mnem)
    };
    ($mnem: ident $t: ident#) => {
        partial_type!($t)(Box::new(move |addr| $mnem(imm_type!($t -> addr))))
    };
    ($mnem: ident $op1: expr) => {
        Done($mnem($op1))
    };
    ($mnem: ident $t: ident#, $op2: expr) => {
        partial_type!($t)(Box::new(|addr| $mnem(imm_type!($t -> addr), $op2)))
    };
    ($mnem: ident $op1: expr, $t: ident#) => {
        partial_type!($t)(Box::new(move |addr| $mnem($op1, imm_type!($t -> addr))))
    };
    ($mnem: ident $op1: expr, $op2: expr) => {
        Done($mnem($op1, $op2))
    };
}

// Auxiliary macro for decoding instructions of the form:
// | opcode(2) | imm_op(3) | op(3)
//
// These happen a lot in the CB-prefixed instructions
macro_rules! decode_cb_block {
    ($opcode: expr, $instr: ident) => ({
        const OP: u8 = 0x7;
        let op = to_operand($opcode & OP);
        let imm_op = ($opcode >> 3) & OP;
        $instr(imm_op, op)
    })
}

// Auxiliary macro for decoding instructions of the form:
// | opcode(4) | SWITCH_BIT(1) | op(3)
//
// Where the SWITCH_BIT decides which of the two instructions to call.
// These are relatively common, since operands are encoded in 3 bits (op),
// so single-operand instructions only use up 8 mappings.
// Since their opcodes are 4 bits, there are still 8 mappings, which are
// shared with another instruction.
macro_rules! decode_row {
    ($opcode: expr, $instr_set: ident, $instr_unset: ident) => ({
        const SWITCH_BIT: u8 = 0x4;
        let op = to_operand($opcode & 0x7 as u8);
        if $opcode & SWITCH_BIT > 0 {
            $instr_set(op)
        } else {
            $instr_unset(op)
        }
    })
}

fn to_operand(operand: u8) -> Op8 {
    match operand {
        0x0 => Reg(B),
        0x1 => Reg(C),
        0x2 => Reg(D),
        0x3 => Reg(E),
        0x4 => Reg(F),
        0x5 => Reg(L),
        0x6 => Ind(IndirectAddr::HL),
        0x7 => Reg(A),
        _   => panic!("Invalid operand")
    }
}

pub fn decode_cb(code: u8) -> Instruction {
    match code {
        0x00...0x0F => decode_row!(code, RRC, RLC),
        0x10...0x1F => decode_row!(code, RL, RR),
        0x20...0x2F => decode_row!(code, SLA, SRA),
        0x30...0x3F => decode_row!(code, SRL, SWAP),
        0x40...0x7F => decode_cb_block!(code, BIT),
        0x80...0xBF => decode_cb_block!(code, RES),
        0xC0...0xFF => decode_cb_block!(code, SET),
        _ => panic!("warning: Unknown opcode 0xCB{:02x}", code)
    }
}

// count the number of elements within square brackets
macro_rules! count {
    ([]) => {0};
    ([$e: tt $(,$es: tt)*]) => {1 + count!([$($es),*])}
}

// [1, 0, 1, 0] => 10
macro_rules! assemble_bits {
    ([$e: expr $(,$es: expr)*]) => {assemble_bits_acc!([$($es),*],$e)}
}

// helper for assemble_bits
macro_rules! assemble_bits_acc {
    ([], $acc: expr) => {$acc};

    ([$e: expr $(,$es: expr)*], $acc: expr) => {
        assemble_bits_acc!([$($es),*], ($acc << 1) | $e)
    }
}

macro_rules! mask_aux {
    ($acc: expr, [#$n: ident $size: expr]) => {$acc << $size};
    ($acc: expr, $t: tt) => {assemble_bits_acc!($t, $acc)};
}

macro_rules! assemble_mask {
    ($acc: expr,) => {$acc};
    ($acc: expr, $t: tt $($ts: tt)*) => {
        assemble_mask!(mask_aux!($acc, $t), $($ts)*)
    }
}

macro_rules! wildcard_aux {
    ($acc: expr, [#$n: ident $size: expr]) => {$acc << $size};
    ($acc: expr, $t: tt) => {{
        let shift = count!($t);
        ($acc << shift) | ((1 << shift) - 1)
    }};
}

macro_rules! wildcard_mask {
    ($acc: expr,) => {$acc};
    ($acc: expr, $t: tt $($ts: tt)*) => {
        wildcard_mask!(wildcard_aux!($acc, $t), $($ts)*)
    }
}

macro_rules! op_shift {
    () => {0};
    ([#$n: ident $size: expr] $($ts: tt)*) => {$size + op_shift!($($ts)*)};
    ($t: tt $($ts: tt)*) => {count!($t) + op_shift!($($ts)*)}
}

macro_rules! transform {
    ($op: expr, () => ($leaf: expr)) => {$leaf};
    ($op: expr, () => [$($leaf: tt)+]) => {instr!($($leaf)+)};

    ($op: expr, ([#$n: ident $size: expr] $($ts: tt)*) => $leaf: tt) => {{
        let shift = op_shift!($($ts)*);
        let mask  = ((1 << $size) - 1) << shift;
        let $n = to_operand(($op & mask) >> shift);

        transform!($op, ($($ts)*) => $leaf)
    }};

    ($op: expr, ($t: tt $($ts: tt)*) => $leaf: tt) => {{
        transform!($op, ($($ts)*) => $leaf)
    }};
}

macro_rules! matches {
    ($op: expr, #$e: expr) => {$op == $e};
    ($op: expr, $($t: tt)+) => {{
        let mask = assemble_mask!(0, $($t)+);
        let wildcard = wildcard_mask!(0, $($t)+);
        $op & wildcard == mask
    }};
}

macro_rules! match_rule {
    ($op: expr, $(($($t: tt)+) => $e: tt,)+) => {
        $( if matches!($op, $($t)+) {
                transform!($op, ($($t)+) => $e)
            } else
        )+ {panic!("No match: {}", $op)}
    }
}

pub fn decode(opcode: u8) -> Cont<Instruction> {
    match_rule!(opcode,

        (#0x76) => [HALT],
        ([0,1] [#to 3] [#from 3]) => [LD to, from],
        ([1,0,0,0,1] [#op 3]) => [ADC op],
        ([1,0,0,0,0] [#op 3]) => [ADD op],
        ([1,0,0,1,1] [#op 3]) => [SBC op],
        ([1,0,0,1,0] [#op 3]) => [SUB op],
        ([1,0,1,0,1] [#op 3]) => [XOR op],
        ([1,0,1,0,0] [#op 3]) => [AND op],
        ([1,0,1,1,1] [#op 3]) => [OR op],
        ([1,0,1,1,0] [#op 3]) => [CP op],
        ([1,1] [#op 3] [1,1,1]) => [RST op],
        ([0,0] [#dest 3] [1,1,0]) => [LD dest, I#],

        (#0x01) => [LD16 BC, I16#],
        (#0x11) => [LD16 DE, I16#],
        (#0x21) => [LD16 HL, I16#],
        (#0x31) => [LD16 SP, I16#],
        (#0x27) => [DAA],
        (#0x2F) => [CPL],
        (#0x3F) => [CCF],
        (#0x37) => [SCF],
        (#0x00) => [NOP],
        (#0x10) => [STOP],
        (#0xF3) => [DI],
        (#0xFB) => [EI],
        (#0x1A) => [LD Reg(A), Ind(IndirectAddr::DE)],
        (#0x02) => [LD Ind(IndirectAddr::BC), Reg(A)],
        (#0x12) => [LD Ind(IndirectAddr::DE), Reg(A)],
        (#0xEA) => [LD A#, Reg(A)],
        (#0xFA) => [LD Reg(A), A#],
        (#0xF2) => [LD Ind(IndirectAddr::C), Reg(A)],
        (#0xE2) => [LD Reg(A), Ind(IndirectAddr::C)],
        (#0x3A) => [LDD Reg(A), Ind(IndirectAddr::HL)],
        (#0x32) => [LDD Ind(IndirectAddr::HL), Reg(A)],
        (#0x2A) => [LDI Reg(A), Ind(IndirectAddr::HL)],
        (#0x22) => [LDI Ind(IndirectAddr::HL), Reg(A)],
        (#0x0A) => [LD Reg(A), Ind(IndirectAddr::BC)],

        (#0xE0) => [LDH A#, Reg(A)],
        (#0xF0) => [LDH Reg(A), A#],
        // 16-bit load
        (#0x01) => [LD16 BC, I16#],
        (#0x11) => [LD16 DE, I16#],
        (#0x21) => [LD16 HL, I16#],
        (#0x31) => [LD16 SP, I16#],

        (#0xF9) => [LDSPHL],

        (#0xF8) => [LDHL I#], // TODO: immediate should be signed!

        (#0x08) => [LDSP A16#],

        (#0xF5) => [PUSH AF],
        (#0xC5) => [PUSH BC],
        (#0xD5) => [PUSH DE],
        (#0xE5) => [PUSH HL],

        (#0xF1) => [POP AF],
        (#0xC1) => [POP BC],
        (#0xD1) => [POP DE],
        (#0xE1) => [POP HL],

        (#0xC6) => [ADD I#],
        (#0xCE) => [ADC I#],

        (#0xD6) => [SUB I#],
        (#0xDE) => [SBC I#],

        (#0xE6) => [AND I#],
        (#0xEE) => [XOR I#],

        (#0xF6) => [OR I#],
        (#0xFE) => [CP I#],

        (#0x04) => [INC Reg(B)],
        (#0x0C) => [INC Reg(C)],
        (#0x14) => [INC Reg(D)],
        (#0x1C) => [INC Reg(E)],
        (#0x24) => [INC Reg(F)],
        (#0x2C) => [INC Reg(L)],
        (#0x3C) => [INC Reg(A)],
        (#0x34) => [INC Ind(IndirectAddr::HL)],

        (#0x05) => [DEC Reg(B)],
        (#0x0D) => [DEC Reg(C)],
        (#0x15) => [DEC Reg(D)],
        (#0x1D) => [DEC Reg(E)],
        (#0x25) => [DEC Reg(F)],
        (#0x2D) => [DEC Reg(L)],
        (#0x3D) => [DEC Reg(A)],
        (#0x35) => [DEC Ind(IndirectAddr::HL)],

        (#0x09) => [ADDHL BC],
        (#0x19) => [ADDHL DE],
        (#0x29) => [ADDHL HL],
        (#0x39) => [ADDHL SP],
        (#0xE8) => [ADDSP I#],

        (#0x03) => [INC16 BC],
        (#0x13) => [INC16 DE],
        (#0x23) => [INC16 HL],
        (#0x33) => [INC16 SP],

        (#0x0B) => [DEC16 BC],
        (#0x1B) => [DEC16 DE],
        (#0x2B) => [DEC16 HL],
        (#0x3B) => [DEC16 SP],

        // rotates and shifts
        (#0x0F) => [RRCA],
        (#0x1f) => [RRA],
        (#0xCB) => (Partial8(Box::new(|cb| decode_cb(cb)))),

        // control
        (#0xC3) => [JP A#, Cond::None],
        (#0xC2) => [JP A#, Cond::NZ],
        (#0xCA) => [JP A#, Cond::Z],
        (#0xD2) => [JP A#, Cond::NC],
        (#0xDA) => [JP A#, Cond::C],
        (#0xE9) => [JP Ind(IndirectAddr::HL), Cond::None],

        (#0x18) => [JR A#, Cond::None],
        (#0x20) => [JR A#, Cond::NZ],
        (#0x28) => [JR A#, Cond::Z],
        (#0x30) => [JR A#, Cond::NC],
        (#0x38) => [JR A#, Cond::C],

        (#0xCD) => [CALL A#, Cond::None],
        (#0xC4) => [CALL A#, Cond::NZ],
        (#0xCC) => [CALL A#, Cond::Z],
        (#0xD4) => [CALL A#, Cond::NC],
        (#0xDC) => [CALL A#, Cond::C],

        (#0xC9) => [RET Cond::None],
        (#0xC0) => [RET Cond::NZ],
        (#0xC8) => [RET Cond::Z],
        (#0xD0) => [RET Cond::NC],
        (#0xD8) => [RET Cond::C],

        (#0xD9) => [RETI],
        (#0x07) => [RLCA],
        (#0x17) => [RLCA],

        // UNMAPPED:
        (#0xD3) => [NOP],
        (#0xDB) => [NOP],
        (#0xDD) => [NOP],
        (#0xE3) => [NOP],
        (#0xE4) => [NOP],
        (#0xEB) => [NOP],
        (#0xEC) => [NOP],
        (#0xED) => [NOP],
        (#0xF4) => [NOP],
        (#0xFC) => [NOP],
        (#0xFD) => [NOP],
    )
}
<commit_msg>decode: Use bit-level pattern matching for all instr<commit_after>/* Decoder */

use cpu::Op8;
use cpu::Op8::*;
use cpu::Cond;
use cpu::ops::Cont;
use cpu::ops::Cont::*;
use cpu::ops::Instruction;
use cpu::ops::Instruction::*;
use cpu::registers::Reg8::*;
use cpu::registers::Reg16;
use cpu::registers::Reg16::*;
use cpu::IndirectAddr;

// TODO: add documentation for the macros
macro_rules! imm_type {
    (A   -> $i: ident) => { Ind(IndirectAddr::Imm8($i)) };
    (I   -> $i: ident) => { Imm($i) };
    (I16 -> $i: ident) => { $i };
    (A16 -> $i: ident) => { $i }
}

macro_rules! partial_type {
    (A)   => { Partial8  };
    (I)   => { Partial8  };
    (I16) => { Partial16 };
    (A16) => { Partial16 }
}

macro_rules! instr {
    ($mnem: ident) => {
        Done($mnem)
    };
    ($mnem: ident $t: ident#) => {
        partial_type!($t)(Box::new(move |addr| $mnem(imm_type!($t -> addr))))
    };
    ($mnem: ident $op1: expr) => {
        Done($mnem($op1))
    };
    ($mnem: ident $t: ident#, $op2: expr) => {
        partial_type!($t)(Box::new(move |addr| $mnem(imm_type!($t -> addr), $op2)))
    };
    ($mnem: ident $op1: expr, $t: ident#) => {
        partial_type!($t)(Box::new(move |addr| $mnem($op1, imm_type!($t -> addr))))
    };
    ($mnem: ident $op1: expr, $op2: expr) => {
        Done($mnem($op1, $op2))
    };
}

// count the number of elements within square brackets
macro_rules! count {
    ([]) => {0};
    ([$e: tt $(,$es: tt)*]) => {1 + count!([$($es),*])}
}

// [1, 0, 1, 0] => 10
macro_rules! assemble_bits {
    ([$e: expr $(,$es: expr)*]) => {assemble_bits_acc!([$($es),*],$e)}
}

// helper for assemble_bits
macro_rules! assemble_bits_acc {
    ([], $acc: expr) => {$acc};

    ([$e: expr $(,$es: expr)*], $acc: expr) => {
        assemble_bits_acc!([$($es),*], ($acc << 1) | $e)
    }
}

macro_rules! mask_aux {
    ($acc: expr, [#$n: ident $size: expr]) => {$acc << $size};
    ($acc: expr, $t: tt) => {assemble_bits_acc!($t, $acc)};
}

macro_rules! assemble_mask {
    ($acc: expr,) => {$acc};
    ($acc: expr, $t: tt $($ts: tt)*) => {
        assemble_mask!(mask_aux!($acc, $t), $($ts)*)
    }
}

macro_rules! wildcard_aux {
    ($acc: expr, [#$n: ident $size: expr]) => {$acc << $size};
    ($acc: expr, $t: tt) => {{
        let shift = count!($t);
        ($acc << shift) | ((1 << shift) - 1)
    }};
}

macro_rules! wildcard_mask {
    ($acc: expr,) => {$acc};
    ($acc: expr, $t: tt $($ts: tt)*) => {
        wildcard_mask!(wildcard_aux!($acc, $t), $($ts)*)
    }
}

macro_rules! op_shift {
    () => {0};
    ([#$n: ident $size: expr] $($ts: tt)*) => {$size + op_shift!($($ts)*)};
    ($t: tt $($ts: tt)*) => {count!($t) + op_shift!($($ts)*)}
}

macro_rules! transform {
    ($op: expr, () => ($leaf: expr)) => {$leaf};
    ($op: expr, () => [$($leaf: tt)+]) => {instr!($($leaf)+)};

    ($op: expr, ([#$n: ident $size: expr] $($ts: tt)*) => $leaf: tt) => {{
        let shift = op_shift!($($ts)*);
        let mask  = ((1 << $size) - 1) << shift;
        let $n = ($op & mask) >> shift;

        transform!($op, ($($ts)*) => $leaf)
    }};

    ($op: expr, ($t: tt $($ts: tt)*) => $leaf: tt) => {{
        transform!($op, ($($ts)*) => $leaf)
    }};
}

macro_rules! matches {
    ($op: expr, #$e: expr) => {$op == $e};
    ($op: expr, $($t: tt)+) => {{
        let mask = assemble_mask!(0, $($t)+);
        let wildcard = wildcard_mask!(0, $($t)+);
        $op & wildcard == mask
    }};
}

macro_rules! match_rule {
    ($op: expr, $(($($t: tt)+) => $e: tt,)+) => {
        $( if matches!($op, $($t)+) {
                transform!($op, ($($t)+) => $e)
            } else
        )+ {panic!("No match: {}", $op)}
    }
}

fn reg8(operand: u8) -> Op8 {
    match operand {
        0x0 => Reg(B),
        0x1 => Reg(C),
        0x2 => Reg(D),
        0x3 => Reg(E),
        0x4 => Reg(F),
        0x5 => Reg(L),
        0x6 => Ind(IndirectAddr::HL),
        0x7 => Reg(A),
        _   => panic!("Invalid operand")
    }
}

// PUSH and POP might use the AF register instead of SP
// AF and SP are mutually exclusive for a given type of instruction
fn reg16(operand: u8, has_af: bool) -> Reg16 {
    match operand {
         0x0 => BC,
         0x1 => DE,
         0x2 => HL,
         0x3 if has_af => AF,
         0x3 => SP,
         _   => panic!("Invalid operand")
    }
}

fn cond(operand: u8) -> Cond {
    match operand {
        0x0 => Cond::NZ,
        0x1 => Cond::Z,
        0x2 => Cond::NC,
        0x3 => Cond::C,
        _   => panic!("Invalid operand")
    }
}

pub fn decode_cb(code: u8) -> Instruction {
    let cont = match_rule!(code,
        // | opcode(5) | op(3)
        ([0,0,0,0,0] [#op 3]) => [RLC  reg8(op)],
        ([0,0,0,0,1] [#op 3]) => [RRC  reg8(op)],
        ([0,0,0,1,0] [#op 3]) => [RR   reg8(op)],
        ([0,0,0,1,1] [#op 3]) => [RL   reg8(op)],
        ([0,0,1,0,0] [#op 3]) => [SRA  reg8(op)],
        ([0,0,1,0,1] [#op 3]) => [SLA  reg8(op)],
        ([0,0,1,1,0] [#op 3]) => [SWAP reg8(op)],
        ([0,0,1,1,1] [#op 3]) => [SRL  reg8(op)],
        // | opcode(2) | imm_op(3) | op(3)
        ([0,1] [#imm_op 3] [#op 3]) => [BIT imm_op, reg8(op)],
        ([1,0] [#imm_op 3] [#op 3]) => [RES imm_op, reg8(op)],
        ([1,1] [#imm_op 3] [#op 3]) => [SET imm_op, reg8(op)],
    );
    match cont {
        Done(i) => i,
        _       => panic!("CB instructions should not take immediate data")
    }
}

pub fn decode(opcode: u8) -> Cont<Instruction> {
    match_rule!(opcode,
        (#0x76) => [HALT],

        ([0,0] [#dest 3] [1,1,0])   => [LD  reg8(dest), I#],
        ([0,0] [#op   3] [1,0,0])   => [INC reg8(op)],
        ([0,0] [#op   3] [1,0,1])   => [DEC reg8(op)],
        ([0,1] [#to   3] [#from 3]) => [LD  reg8(to), reg8(from)],
        ([1,1] [#op   3] [1,1,1])   => [RST reg8(op)],

        ([0,0,1] [#op 2] [0,0,0])   => [JR   A#, cond(op)],
        ([1,1,0] [#op 2] [0,0,0])   => [RET      cond(op)],
        ([1,1,0] [#op 2] [0,1,0])   => [JP   A#, cond(op)],
        ([1,1,0] [#op 2] [1,0,0])   => [CALL A#, cond(op)],

        ([1,0,0,0,0] [#op 3])       => [ADD reg8(op)],
        ([1,0,0,0,1] [#op 3])       => [ADC reg8(op)],
        ([1,0,0,1,0] [#op 3])       => [SUB reg8(op)],
        ([1,0,0,1,1] [#op 3])       => [SBC reg8(op)],
        ([1,0,1,0,0] [#op 3])       => [AND reg8(op)],
        ([1,0,1,0,1] [#op 3])       => [XOR reg8(op)],
        ([1,0,1,1,0] [#op 3])       => [CP  reg8(op)],
        ([1,0,1,1,1] [#op 3])       => [OR  reg8(op)],

        // 16 bit
        ([0,0] [#op 2] [0,0,0,1])   => [LD16  reg16(op, false), I16#],
        ([0,0] [#op 2] [0,0,1,1])   => [INC16 reg16(op, false)],
        ([0,0] [#op 2] [1,0,0,1])   => [ADDHL reg16(op, false)],
        ([0,0] [#op 2] [1,0,1,1])   => [DEC16 reg16(op, false)],
        ([1,1] [#op 2] [0,0,0,1])   => [POP   reg16(op, true)],
        ([1,1] [#op 2] [0,1,0,1])   => [PUSH  reg16(op, true)],

        (#0xE8) => [ADDSP I#],

        // control
        (#0x18) => [JR A#, Cond::None],
        (#0xC3) => [JP A#, Cond::None],
        (#0xC9) => [RET Cond::None],
        (#0xCD) => [CALL A#, Cond::None],
        (#0xE9) => [JP Ind(IndirectAddr::HL), Cond::None],

        (#0x27) => [DAA],
        (#0x2F) => [CPL],
        (#0x3F) => [CCF],
        (#0x37) => [SCF],
        (#0x00) => [NOP],
        (#0x10) => [STOP],
        (#0xF3) => [DI],
        (#0xFB) => [EI],

        (#0x02) => [LD Ind(IndirectAddr::BC), Reg(A)],
        (#0x12) => [LD Ind(IndirectAddr::DE), Reg(A)],
        (#0xEA) => [LD A#, Reg(A)],
        (#0xF2) => [LD Ind(IndirectAddr::C), Reg(A)],

        (#0x1A) => [LD Reg(A), Ind(IndirectAddr::DE)],
        (#0xFA) => [LD Reg(A), A#],
        (#0xE2) => [LD Reg(A), Ind(IndirectAddr::C)],
        (#0x0A) => [LD Reg(A), Ind(IndirectAddr::BC)],

        (#0x3A) => [LDD Reg(A), Ind(IndirectAddr::HL)],
        (#0x32) => [LDD Ind(IndirectAddr::HL), Reg(A)],

        (#0x2A) => [LDI Reg(A), Ind(IndirectAddr::HL)],
        (#0x22) => [LDI Ind(IndirectAddr::HL), Reg(A)],

        (#0xE0) => [LDH A#, Reg(A)],
        (#0xF0) => [LDH Reg(A), A#],
        (#0xF9) => [LDSPHL],
        (#0xF8) => [LDHL I#], // TODO: immediate should be signed!
        (#0x08) => [LDSP A16#],

        (#0xC6) => [ADD I#],
        (#0xCE) => [ADC I#],
        (#0xD6) => [SUB I#],
        (#0xDE) => [SBC I#],
        (#0xE6) => [AND I#],
        (#0xEE) => [XOR I#],
        (#0xF6) => [OR  I#],
        (#0xFE) => [CP  I#],

        // rotates and shifts
        (#0x0F) => [RRCA],
        (#0x1f) => [RRA],
        (#0xCB) => (Partial8(Box::new(|cb| decode_cb(cb)))),

        (#0xD9) => [RETI],
        (#0x07) => [RLCA],
        (#0x17) => [RLCA],

        // UNMAPPED:
        (#0xD3) => [NOP],
        (#0xDB) => [NOP],
        (#0xDD) => [NOP],
        (#0xE3) => [NOP],
        (#0xE4) => [NOP],
        (#0xEB) => [NOP],
        (#0xEC) => [NOP],
        (#0xED) => [NOP],
        (#0xF4) => [NOP],
        (#0xFC) => [NOP],
        (#0xFD) => [NOP],
    )
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin 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.

//! Seeds a server with initial peers on first start and keep monitoring
//! peer counts to connect to more if neeed. Seedin strategy is
//! configurable with either no peers, a user-defined list or a preset
//! list of DNS records (the default).

use chrono::prelude::Utc;
use chrono::{Duration, MIN_DATE};
use rand::{thread_rng, Rng};
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::{mpsc, Arc};
use std::{cmp, str, thread, time};

use crate::core::global;
use crate::p2p;
use crate::p2p::ChainAdapter;
use crate::pool::DandelionConfig;
use crate::util::{Mutex, StopState};

// DNS Seeds with contact email associated
const MAINNET_DNS_SEEDS: &'static [&'static str] = &[
	"mainnet.seed.grin-tech.org",      // igno.peverell@protonmail.com
	"mainnet.seed.grin.icu",           // gary.peverell@protonmail.com
	"mainnet.seed.713.mw",             // jasper@713.mw
	"mainnet.seed.grin.lesceller.com", // q.lesceller@gmail.com
	"mainnet.seed.grin.prokapi.com",   // hendi@prokapi.com
];
const FLOONET_DNS_SEEDS: &'static [&'static str] = &[
	"floonet.seed.grin-tech.org",      // igno.peverell@protonmail.com
	"floonet.seed.grin.icu",           // gary.peverell@protonmail.com
	"floonet.seed.713.mw",             // jasper@713.mw
	"floonet.seed.grin.lesceller.com", // q.lesceller@gmail.com
	"floonet.seed.grin.prokapi.com",   // hendi@prokapi.com
];

pub fn connect_and_monitor(
	p2p_server: Arc<p2p::Server>,
	capabilities: p2p::Capabilities,
	dandelion_config: DandelionConfig,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr> + Send>,
	preferred_peers: Option<Vec<SocketAddr>>,
	stop_state: Arc<Mutex<StopState>>,
) {
	let _ = thread::Builder::new()
		.name("seed".to_string())
		.spawn(move || {
			let peers = p2p_server.peers.clone();

			// open a channel with a listener that connects every peer address sent below
			// max peer count
			let (tx, rx) = mpsc::channel();

			// check seeds first
			connect_to_seeds_and_preferred_peers(
				peers.clone(),
				tx.clone(),
				seed_list,
				preferred_peers.clone(),
			);

			let mut prev = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_expire_check = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_ping = Utc::now();
			let mut start_attempt = 0;

			loop {
				if stop_state.lock().is_stopped() {
					break;
				}

				// Pause egress peer connection request. Only for tests.
				if stop_state.lock().is_paused() {
					thread::sleep(time::Duration::from_secs(1));
					continue;
				}

				// Check for and remove expired peers from the storage
				if Utc::now() - prev_expire_check > Duration::hours(1) {
					peers.remove_expired();

					prev_expire_check = Utc::now();
				}

				// make several attempts to get peers as quick as possible
				// with exponential backoff
				if Utc::now() - prev > Duration::seconds(cmp::min(20, 1 << start_attempt)) {
					// try to connect to any address sent to the channel
					listen_for_addrs(peers.clone(), p2p_server.clone(), capabilities, &rx);

					// monitor additional peers if we need to add more
					monitor_peers(
						peers.clone(),
						p2p_server.config.clone(),
						tx.clone(),
						preferred_peers.clone(),
					);

					update_dandelion_relay(peers.clone(), dandelion_config.clone());

					prev = Utc::now();
					start_attempt = cmp::min(6, start_attempt + 1);
				}

				// Ping connected peers on every 10s to monitor peers.
				if Utc::now() - prev_ping > Duration::seconds(10) {
					let total_diff = peers.total_difficulty();
					let total_height = peers.total_height();
					peers.check_all(total_diff, total_height);
					prev_ping = Utc::now();
				}

				thread::sleep(time::Duration::from_secs(1));
			}
		});
}

fn monitor_peers(
	peers: Arc<p2p::Peers>,
	config: p2p::P2PConfig,
	tx: mpsc::Sender<SocketAddr>,
	preferred_peers_list: Option<Vec<SocketAddr>>,
) {
	// regularly check if we need to acquire more peers  and if so, gets
	// them from db
	let total_count = peers.all_peers().len();
	let mut healthy_count = 0;
	let mut banned_count = 0;
	let mut defuncts = vec![];

	for x in peers.all_peers() {
		match x.flags {
			p2p::State::Banned => {
				let interval = Utc::now().timestamp() - x.last_banned;
				// Unban peer
				if interval >= config.ban_window() {
					peers.unban_peer(&x.addr);
					debug!(
						"monitor_peers: unbanned {} after {} seconds",
						x.addr, interval
					);
				} else {
					banned_count += 1;
				}
			}
			p2p::State::Healthy => healthy_count += 1,
			p2p::State::Defunct => defuncts.push(x),
		}
	}

	debug!(
		"monitor_peers: on {}:{}, {} connected ({} most_work). \
		 all {} = {} healthy + {} banned + {} defunct",
		config.host,
		config.port,
		peers.peer_count(),
		peers.most_work_peers().len(),
		total_count,
		healthy_count,
		banned_count,
		defuncts.len(),
	);

	// maintenance step first, clean up p2p server peers
	peers.clean_peers(config.peer_max_count() as usize);

	// not enough peers, getting more from db
	if peers.peer_count() >= config.peer_min_preferred_count() {
		return;
	}

	// loop over connected peers
	// ask them for their list of peers
	let mut connected_peers: Vec<SocketAddr> = vec![];
	for p in peers.connected_peers() {
		trace!(
			"monitor_peers: {}:{} ask {} for more peers",
			config.host,
			config.port,
			p.info.addr,
		);
		let _ = p.send_peer_request(p2p::Capabilities::PEER_LIST);
		connected_peers.push(p.info.addr)
	}

	// Attempt to connect to preferred peers if there is some
	match preferred_peers_list {
		Some(preferred_peers) => {
			for p in preferred_peers {
				if !connected_peers.is_empty() {
					if !connected_peers.contains(&p) {
						tx.send(p).unwrap();
					}
				} else {
					tx.send(p).unwrap();
				}
			}
		}
		None => debug!("monitor_peers: no preferred peers"),
	}

	// take a random defunct peer and mark it healthy: over a long period any
	// peer will see another as defunct eventually, gives us a chance to retry
	if defuncts.len() > 0 {
		thread_rng().shuffle(&mut defuncts);
		let _ = peers.update_state(defuncts[0].addr, p2p::State::Healthy);
	}

	// find some peers from our db
	// and queue them up for a connection attempt
	let new_peers = peers.find_peers(
		p2p::State::Healthy,
		p2p::Capabilities::UNKNOWN,
		config.peer_max_count() as usize,
	);

	for p in new_peers.iter().filter(|p| !peers.is_known(&p.addr)) {
		trace!(
			"monitor_peers: on {}:{}, queue to soon try {}",
			config.host,
			config.port,
			p.addr,
		);
		tx.send(p.addr).unwrap();
	}
}

fn update_dandelion_relay(peers: Arc<p2p::Peers>, dandelion_config: DandelionConfig) {
	// Dandelion Relay Updater
	let dandelion_relay = peers.get_dandelion_relay();
	if dandelion_relay.is_empty() {
		debug!("monitor_peers: no dandelion relay updating");
		peers.update_dandelion_relay();
	} else {
		for last_added in dandelion_relay.keys() {
			let dandelion_interval = Utc::now().timestamp() - last_added;
			if dandelion_interval >= dandelion_config.relay_secs.unwrap() as i64 {
				debug!("monitor_peers: updating expired dandelion relay");
				peers.update_dandelion_relay();
			}
		}
	}
}

// Check if we have any pre-existing peer in db. If so, start with those,
// otherwise use the seeds provided.
fn connect_to_seeds_and_preferred_peers(
	peers: Arc<p2p::Peers>,
	tx: mpsc::Sender<SocketAddr>,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr>>,
	peers_preferred_list: Option<Vec<SocketAddr>>,
) {
	// check if we have some peers in db
	// look for peers that are able to give us other peers (via PEER_LIST capability)
	let peers = peers.find_peers(p2p::State::Healthy, p2p::Capabilities::PEER_LIST, 100);

	// if so, get their addresses, otherwise use our seeds
	let mut peer_addrs = if peers.len() > 3 {
		peers.iter().map(|p| p.addr).collect::<Vec<_>>()
	} else {
		seed_list()
	};

	// If we have preferred peers add them to the connection
	match peers_preferred_list {
		Some(mut peers_preferred) => peer_addrs.append(&mut peers_preferred),
		None => trace!("No preferred peers"),
	};

	if peer_addrs.len() == 0 {
		warn!("No seeds were retrieved.");
	}

	// connect to this first set of addresses
	for addr in peer_addrs {
		tx.send(addr).unwrap();
	}
}

/// Regularly poll a channel receiver for new addresses and initiate a
/// connection if the max peer count isn't exceeded. A request for more
/// peers is also automatically sent after connection.
fn listen_for_addrs(
	peers: Arc<p2p::Peers>,
	p2p: Arc<p2p::Server>,
	capab: p2p::Capabilities,
	rx: &mpsc::Receiver<SocketAddr>,
) {
	if peers.peer_count() >= p2p.config.peer_max_count() {
		// clean the rx messages to avoid accumulating
		for _ in rx.try_iter() {}
		return;
	}

	for addr in rx.try_iter() {
		let peers_c = peers.clone();
		let p2p_c = p2p.clone();
		let _ = thread::Builder::new()
			.name("peer_connect".to_string())
			.spawn(move || match p2p_c.connect(&addr) {
				Ok(p) => {
					let _ = p.send_peer_request(capab);
					let _ = peers_c.update_state(addr, p2p::State::Healthy);
				}
				Err(_) => {
					let _ = peers_c.update_state(addr, p2p::State::Defunct);
				}
			});
	}
}

pub fn dns_seeds() -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		let mut addresses: Vec<SocketAddr> = vec![];
		let net_seeds = if global::is_floonet() {
			FLOONET_DNS_SEEDS
		} else {
			MAINNET_DNS_SEEDS
		};
		for dns_seed in net_seeds {
			let temp_addresses = addresses.clone();
			debug!("Retrieving seed nodes from dns {}", dns_seed);
			match (dns_seed.to_owned(), 0).to_socket_addrs() {
				Ok(addrs) => addresses.append(
					&mut (addrs
						.map(|mut addr| {
							addr.set_port(13414);
							addr
						})
						.filter(|addr| !temp_addresses.contains(addr))
						.collect()),
				),
				Err(e) => debug!("Failed to resolve seed {:?} got error {:?}", dns_seed, e),
			}
		}
		debug!("Retrieved seed addresses: {:?}", addresses);
		addresses
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>) -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(move || {
		addrs_str
			.iter()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>()
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn preferred_peers(addrs_str: Vec<String>) -> Option<Vec<SocketAddr>> {
	if addrs_str.is_empty() {
		None
	} else {
		Some(
			addrs_str
				.iter()
				.map(|s| s.parse().unwrap())
				.collect::<Vec<_>>(),
		)
	}
}
<commit_msg>fix: avoid duplicate connection (#2361)<commit_after>// Copyright 2018 The Grin 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.

//! Seeds a server with initial peers on first start and keep monitoring
//! peer counts to connect to more if neeed. Seedin strategy is
//! configurable with either no peers, a user-defined list or a preset
//! list of DNS records (the default).

use chrono::prelude::{DateTime, Utc};
use chrono::{Duration, MIN_DATE};
use rand::{thread_rng, Rng};
use std::collections::HashMap;
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::{mpsc, Arc};
use std::{cmp, str, thread, time};

use crate::core::global;
use crate::p2p;
use crate::p2p::ChainAdapter;
use crate::pool::DandelionConfig;
use crate::util::{Mutex, StopState};

// DNS Seeds with contact email associated
const MAINNET_DNS_SEEDS: &'static [&'static str] = &[
	"mainnet.seed.grin-tech.org",      // igno.peverell@protonmail.com
	"mainnet.seed.grin.icu",           // gary.peverell@protonmail.com
	"mainnet.seed.713.mw",             // jasper@713.mw
	"mainnet.seed.grin.lesceller.com", // q.lesceller@gmail.com
	"mainnet.seed.grin.prokapi.com",   // hendi@prokapi.com
];
const FLOONET_DNS_SEEDS: &'static [&'static str] = &[
	"floonet.seed.grin-tech.org",      // igno.peverell@protonmail.com
	"floonet.seed.grin.icu",           // gary.peverell@protonmail.com
	"floonet.seed.713.mw",             // jasper@713.mw
	"floonet.seed.grin.lesceller.com", // q.lesceller@gmail.com
	"floonet.seed.grin.prokapi.com",   // hendi@prokapi.com
];

pub fn connect_and_monitor(
	p2p_server: Arc<p2p::Server>,
	capabilities: p2p::Capabilities,
	dandelion_config: DandelionConfig,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr> + Send>,
	preferred_peers: Option<Vec<SocketAddr>>,
	stop_state: Arc<Mutex<StopState>>,
) {
	let _ = thread::Builder::new()
		.name("seed".to_string())
		.spawn(move || {
			let peers = p2p_server.peers.clone();

			// open a channel with a listener that connects every peer address sent below
			// max peer count
			let (tx, rx) = mpsc::channel();

			// check seeds first
			connect_to_seeds_and_preferred_peers(
				peers.clone(),
				tx.clone(),
				seed_list,
				preferred_peers.clone(),
			);

			let mut prev = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_expire_check = MIN_DATE.and_hms(0, 0, 0);
			let mut prev_ping = Utc::now();
			let mut start_attempt = 0;

			let mut connecting_history: HashMap<SocketAddr, DateTime<Utc>> = HashMap::new();

			loop {
				if stop_state.lock().is_stopped() {
					break;
				}

				// Pause egress peer connection request. Only for tests.
				if stop_state.lock().is_paused() {
					thread::sleep(time::Duration::from_secs(1));
					continue;
				}

				// Check for and remove expired peers from the storage
				if Utc::now() - prev_expire_check > Duration::hours(1) {
					peers.remove_expired();

					prev_expire_check = Utc::now();
				}

				// make several attempts to get peers as quick as possible
				// with exponential backoff
				if Utc::now() - prev > Duration::seconds(cmp::min(20, 1 << start_attempt)) {
					// try to connect to any address sent to the channel
					listen_for_addrs(
						peers.clone(),
						p2p_server.clone(),
						capabilities,
						&rx,
						&mut connecting_history,
					);

					// monitor additional peers if we need to add more
					monitor_peers(
						peers.clone(),
						p2p_server.config.clone(),
						tx.clone(),
						preferred_peers.clone(),
					);

					update_dandelion_relay(peers.clone(), dandelion_config.clone());

					prev = Utc::now();
					start_attempt = cmp::min(6, start_attempt + 1);
				}

				// Ping connected peers on every 10s to monitor peers.
				if Utc::now() - prev_ping > Duration::seconds(10) {
					let total_diff = peers.total_difficulty();
					let total_height = peers.total_height();
					peers.check_all(total_diff, total_height);
					prev_ping = Utc::now();
				}

				thread::sleep(time::Duration::from_secs(1));
			}
		});
}

fn monitor_peers(
	peers: Arc<p2p::Peers>,
	config: p2p::P2PConfig,
	tx: mpsc::Sender<SocketAddr>,
	preferred_peers_list: Option<Vec<SocketAddr>>,
) {
	// regularly check if we need to acquire more peers  and if so, gets
	// them from db
	let total_count = peers.all_peers().len();
	let mut healthy_count = 0;
	let mut banned_count = 0;
	let mut defuncts = vec![];

	for x in peers.all_peers() {
		match x.flags {
			p2p::State::Banned => {
				let interval = Utc::now().timestamp() - x.last_banned;
				// Unban peer
				if interval >= config.ban_window() {
					peers.unban_peer(&x.addr);
					debug!(
						"monitor_peers: unbanned {} after {} seconds",
						x.addr, interval
					);
				} else {
					banned_count += 1;
				}
			}
			p2p::State::Healthy => healthy_count += 1,
			p2p::State::Defunct => defuncts.push(x),
		}
	}

	debug!(
		"monitor_peers: on {}:{}, {} connected ({} most_work). \
		 all {} = {} healthy + {} banned + {} defunct",
		config.host,
		config.port,
		peers.peer_count(),
		peers.most_work_peers().len(),
		total_count,
		healthy_count,
		banned_count,
		defuncts.len(),
	);

	// maintenance step first, clean up p2p server peers
	peers.clean_peers(config.peer_max_count() as usize);

	// not enough peers, getting more from db
	if peers.peer_count() >= config.peer_min_preferred_count() {
		return;
	}

	// loop over connected peers
	// ask them for their list of peers
	let mut connected_peers: Vec<SocketAddr> = vec![];
	for p in peers.connected_peers() {
		trace!(
			"monitor_peers: {}:{} ask {} for more peers",
			config.host,
			config.port,
			p.info.addr,
		);
		let _ = p.send_peer_request(p2p::Capabilities::PEER_LIST);
		connected_peers.push(p.info.addr)
	}

	// Attempt to connect to preferred peers if there is some
	match preferred_peers_list {
		Some(preferred_peers) => {
			for p in preferred_peers {
				if !connected_peers.is_empty() {
					if !connected_peers.contains(&p) {
						tx.send(p).unwrap();
					}
				} else {
					tx.send(p).unwrap();
				}
			}
		}
		None => debug!("monitor_peers: no preferred peers"),
	}

	// take a random defunct peer and mark it healthy: over a long period any
	// peer will see another as defunct eventually, gives us a chance to retry
	if defuncts.len() > 0 {
		thread_rng().shuffle(&mut defuncts);
		let _ = peers.update_state(defuncts[0].addr, p2p::State::Healthy);
	}

	// find some peers from our db
	// and queue them up for a connection attempt
	let new_peers = peers.find_peers(
		p2p::State::Healthy,
		p2p::Capabilities::UNKNOWN,
		config.peer_max_count() as usize,
	);

	for p in new_peers.iter().filter(|p| !peers.is_known(&p.addr)) {
		trace!(
			"monitor_peers: on {}:{}, queue to soon try {}",
			config.host,
			config.port,
			p.addr,
		);
		tx.send(p.addr).unwrap();
	}
}

fn update_dandelion_relay(peers: Arc<p2p::Peers>, dandelion_config: DandelionConfig) {
	// Dandelion Relay Updater
	let dandelion_relay = peers.get_dandelion_relay();
	if dandelion_relay.is_empty() {
		debug!("monitor_peers: no dandelion relay updating");
		peers.update_dandelion_relay();
	} else {
		for last_added in dandelion_relay.keys() {
			let dandelion_interval = Utc::now().timestamp() - last_added;
			if dandelion_interval >= dandelion_config.relay_secs.unwrap() as i64 {
				debug!("monitor_peers: updating expired dandelion relay");
				peers.update_dandelion_relay();
			}
		}
	}
}

// Check if we have any pre-existing peer in db. If so, start with those,
// otherwise use the seeds provided.
fn connect_to_seeds_and_preferred_peers(
	peers: Arc<p2p::Peers>,
	tx: mpsc::Sender<SocketAddr>,
	seed_list: Box<dyn Fn() -> Vec<SocketAddr>>,
	peers_preferred_list: Option<Vec<SocketAddr>>,
) {
	// check if we have some peers in db
	// look for peers that are able to give us other peers (via PEER_LIST capability)
	let peers = peers.find_peers(p2p::State::Healthy, p2p::Capabilities::PEER_LIST, 100);

	// if so, get their addresses, otherwise use our seeds
	let mut peer_addrs = if peers.len() > 3 {
		peers.iter().map(|p| p.addr).collect::<Vec<_>>()
	} else {
		seed_list()
	};

	// If we have preferred peers add them to the connection
	match peers_preferred_list {
		Some(mut peers_preferred) => peer_addrs.append(&mut peers_preferred),
		None => trace!("No preferred peers"),
	};

	if peer_addrs.len() == 0 {
		warn!("No seeds were retrieved.");
	}

	// connect to this first set of addresses
	for addr in peer_addrs {
		tx.send(addr).unwrap();
	}
}

/// Regularly poll a channel receiver for new addresses and initiate a
/// connection if the max peer count isn't exceeded. A request for more
/// peers is also automatically sent after connection.
fn listen_for_addrs(
	peers: Arc<p2p::Peers>,
	p2p: Arc<p2p::Server>,
	capab: p2p::Capabilities,
	rx: &mpsc::Receiver<SocketAddr>,
	connecting_history: &mut HashMap<SocketAddr, DateTime<Utc>>,
) {
	if peers.peer_count() >= p2p.config.peer_max_count() {
		// clean the rx messages to avoid accumulating
		for _ in rx.try_iter() {}
		return;
	}

	let connect_min_interval = 30;
	for addr in rx.try_iter() {
		// ignore the duplicate connecting to same peer within 30 seconds
		let now = Utc::now();
		if let Some(last_connect_time) = connecting_history.get(&addr) {
			if *last_connect_time + Duration::seconds(connect_min_interval) > now {
				debug!(
					"peer_connect: ignore a duplicate request to {}. previous connecting time: {}",
					addr,
					last_connect_time.format("%H:%M:%S%.3f").to_string(),
				);
				continue;
			} else {
				*connecting_history.get_mut(&addr).unwrap() = now;
			}
		}
		connecting_history.insert(addr, now);

		let peers_c = peers.clone();
		let p2p_c = p2p.clone();
		let _ = thread::Builder::new()
			.name("peer_connect".to_string())
			.spawn(move || match p2p_c.connect(&addr) {
				Ok(p) => {
					let _ = p.send_peer_request(capab);
					let _ = peers_c.update_state(addr, p2p::State::Healthy);
				}
				Err(_) => {
					let _ = peers_c.update_state(addr, p2p::State::Defunct);
				}
			});
	}

	// shrink the connecting history.
	// put a threshold here to avoid frequent shrinking in every call
	if connecting_history.len() > 100 {
		let now = Utc::now();
		let old: Vec<_> = connecting_history
			.iter()
			.filter(|&(_, t)| *t + Duration::seconds(connect_min_interval) < now)
			.map(|(s, _)| s.clone())
			.collect();
		for addr in old {
			connecting_history.remove(&addr);
		}
	}
}

pub fn dns_seeds() -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(|| {
		let mut addresses: Vec<SocketAddr> = vec![];
		let net_seeds = if global::is_floonet() {
			FLOONET_DNS_SEEDS
		} else {
			MAINNET_DNS_SEEDS
		};
		for dns_seed in net_seeds {
			let temp_addresses = addresses.clone();
			debug!("Retrieving seed nodes from dns {}", dns_seed);
			match (dns_seed.to_owned(), 0).to_socket_addrs() {
				Ok(addrs) => addresses.append(
					&mut (addrs
						.map(|mut addr| {
							addr.set_port(13414);
							addr
						})
						.filter(|addr| !temp_addresses.contains(addr))
						.collect()),
				),
				Err(e) => debug!("Failed to resolve seed {:?} got error {:?}", dns_seed, e),
			}
		}
		debug!("Retrieved seed addresses: {:?}", addresses);
		addresses
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn predefined_seeds(addrs_str: Vec<String>) -> Box<dyn Fn() -> Vec<SocketAddr> + Send> {
	Box::new(move || {
		addrs_str
			.iter()
			.map(|s| s.parse().unwrap())
			.collect::<Vec<_>>()
	})
}

/// Convenience function when the seed list is immediately known. Mostly used
/// for tests.
pub fn preferred_peers(addrs_str: Vec<String>) -> Option<Vec<SocketAddr>> {
	if addrs_str.is_empty() {
		None
	} else {
		Some(
			addrs_str
				.iter()
				.map(|s| s.parse().unwrap())
				.collect::<Vec<_>>(),
		)
	}
}
<|endoftext|>


<commit_before>use std::io::{self, Read, Write};
use std::net::SocketAddr;
use std::time::Duration;

use hyper::net::{SslClient, HttpStream, NetworkStream};
use native_tls::{ClientBuilder, TlsStream as NativeTlsStream, HandshakeError};

pub struct TlsClient(ClientBuilder);

impl TlsClient {
    pub fn new() -> ::Result<TlsClient> {
        ClientBuilder::new()
            .map(TlsClient)
            .map_err(|e| ::Error::Http(::hyper::Error::Ssl(Box::new(e))))
    }
}

impl SslClient for TlsClient {
    type Stream = TlsStream;

    fn wrap_client(&self, stream: HttpStream, host: &str) -> ::hyper::Result<Self::Stream> {
        self.0.handshake(host, stream).map(TlsStream).map_err(|e| {
            match e {
                HandshakeError::Failure(e) => ::hyper::Error::Ssl(Box::new(e)),
                HandshakeError::Interrupted(..) => {
                    // while using hyper 0.9, this won't happen, because the
                    // socket is in blocking mode. once we move to hyper 0.10,
                    // much of this `tls` module will go away anyways
                    unreachable!("TlsClient::handshake Interrupted")
                }
            }
        })
    }
}

pub struct TlsStream(NativeTlsStream<HttpStream>);

impl Read for TlsStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for TlsStream {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.0.write(data)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl Clone for TlsStream {
    fn clone(&self) -> TlsStream {
        unreachable!("TlsStream::clone is never used for the Client")
    }
}

impl NetworkStream for TlsStream {
    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
        self.0.get_mut().peer_addr()
    }

    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.0.get_ref().set_read_timeout(dur)
    }

    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.0.get_ref().set_write_timeout(dur)
    }
}
<commit_msg>Update to reflect latest native-tls API<commit_after>use std::io::{self, Read, Write};
use std::net::SocketAddr;
use std::time::Duration;

use hyper::net::{SslClient, HttpStream, NetworkStream};
use native_tls::{TlsConnector, TlsStream as NativeTlsStream, HandshakeError};

pub struct TlsClient(TlsConnector);

impl TlsClient {
    pub fn new() -> ::Result<TlsClient> {
        TlsConnector::builder()
            .and_then(|c| c.build())
            .map(TlsClient)
            .map_err(|e| ::Error::Http(::hyper::Error::Ssl(Box::new(e))))
    }
}

impl SslClient for TlsClient {
    type Stream = TlsStream;

    fn wrap_client(&self, stream: HttpStream, host: &str) -> ::hyper::Result<Self::Stream> {
        self.0.connect(host, stream).map(TlsStream).map_err(|e| {
            match e {
                HandshakeError::Failure(e) => ::hyper::Error::Ssl(Box::new(e)),
                HandshakeError::Interrupted(..) => {
                    // while using hyper 0.9, this won't happen, because the
                    // socket is in blocking mode. once we move to hyper 0.10,
                    // much of this `tls` module will go away anyways
                    unreachable!("TlsClient::handshake Interrupted")
                }
            }
        })
    }
}

pub struct TlsStream(NativeTlsStream<HttpStream>);

impl Read for TlsStream {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for TlsStream {
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        self.0.write(data)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl Clone for TlsStream {
    fn clone(&self) -> TlsStream {
        unreachable!("TlsStream::clone is never used for the Client")
    }
}

impl NetworkStream for TlsStream {
    fn peer_addr(&mut self) -> io::Result<SocketAddr> {
        self.0.get_mut().peer_addr()
    }

    fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.0.get_ref().set_read_timeout(dur)
    }

    fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
        self.0.get_ref().set_write_timeout(dur)
    }
}
<|endoftext|>