<commit_before>extern crate rustc_serialize;

use std::cmp;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::usize;

pub trait ReprUsize {
    fn from_usize(usize) -> Self;
    fn into_usize(self) -> usize;
}

impl ReprUsize for bool {
    fn into_usize(self) -> usize { self as usize }
    fn from_usize(i: usize) -> bool {
        unsafe { mem::transmute(i as u8) }
    }
}

impl ReprUsize for char {
    fn into_usize(self) -> usize { self as usize }
    fn from_usize(i: usize) -> char {
        unsafe { mem::transmute(i as u32) }
    }
}

macro_rules! impl_predefined_type {
    ($ty: ty) => {
        impl ReprUsize for $ty {
            fn into_usize(self) -> usize { self as usize }
            fn from_usize(i: usize) -> $ty { i as $ty }
        }
    };
}

impl_predefined_type!(u8);
impl_predefined_type!(u16);
impl_predefined_type!(u32);
impl_predefined_type!(u64);
impl_predefined_type!(usize);
impl_predefined_type!(i8);
impl_predefined_type!(i16);
impl_predefined_type!(i32);
impl_predefined_type!(i64);
impl_predefined_type!(isize);

#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]
pub struct BitsVec<T: ReprUsize> {
    inner: Vec<usize>,
    units: usize,
    bits: usize,
    max_bits: usize,
    leftover: usize,
    _marker: PhantomData<T>,
}

impl<T: ReprUsize> BitsVec<T> {
    pub fn new(bits: usize) -> BitsVec<T> {
        let max = usize::MAX.count_ones() as usize;
        // We can store more bits, but then we might need BigInt to get them out!
        assert!(bits < max, "[new] cannot hold more than {} bits at a time", max - 1);

        BitsVec {
            inner: vec![0],
            units: 0,
            bits: bits,
            max_bits: max,
            leftover: max,
            _marker: PhantomData,
        }
    }

    pub fn with_capacity(bits: usize, capacity: usize) -> BitsVec<T> {
        let mut vec = BitsVec::new(bits);
        vec.reserve(capacity);
        vec
    }

    pub fn push(&mut self, value: T) {
        let mut value = value.into_usize();
        assert!(value >> self.bits == 0,
                "[push] input size is more than allowed size ({} >= {})", value, 2usize.pow(self.bits as u32));

        let mut idx = self.inner.len() - 1;
        if self.leftover < self.bits {
            let left = self.bits - self.leftover;
            self.inner[idx] |= value >> left;
            if self.leftover != 0 {     // special case, in which masking would result in zero!
                value &= (1 << left) - 1;
            }

            self.inner.push(0);
            self.leftover = self.max_bits - left;
            idx += 1;
        } else {
            self.leftover -= self.bits;
        }

        value <<= self.leftover;
        self.inner[idx] |= value;
        self.units += 1;
    }

    pub fn get(&self, i: usize) -> T {
        assert!(i < self.units, "[get] index out of bounds ({} >= {})", i, self.units);

        let pos = i * self.bits;
        let idx = pos / self.max_bits;
        let bits = pos % self.max_bits;
        let diff = self.max_bits - bits;
        let mut val = self.inner[idx];
        if bits != 0 {
            val &= (1 << diff) - 1;
        }

        if diff >= self.bits {
            T::from_usize(val >> (diff - self.bits))
        } else {
            let shift = self.bits - diff;
            let last = self.inner[idx + 1] >> (self.max_bits - shift);
            T::from_usize((val << shift) | last)
        }
    }

    pub fn checked_get(&self, i: usize) -> Option<T> {
        if i >= self.units {
            return None
        }

        Some(self.get(i))
    }

    pub fn set(&mut self, i: usize, value: T) {
        let value = value.into_usize();
        assert!(i < self.units, "[set] index out of bounds ({} >= {})", i, self.units);
        assert!(value >> self.bits == 0,
                "[set] input size is more than allowed size ({} >= {})", value, 2usize.pow(self.bits as u32));

        let pos = i * self.bits;
        let idx = pos / self.max_bits;
        let bits = pos % self.max_bits;
        let diff = self.max_bits - bits;
        let mut val = self.inner[idx];

        if diff >= self.bits {
            let shift = diff - self.bits;
            let last = val & ((1 << shift) - 1);
            let mask = if bits == 0 { 0 } else { ((1 << bits) - 1) << diff };   // prevent overflow
            val &= mask;
            val |= value << shift;
            self.inner[idx] = val | last;
        } else {
            let shift = self.bits - diff;
            val &= !((1 << diff) - 1);
            self.inner[idx] = val | (value >> shift);
            let last = value & ((1 << shift) - 1);
            let shift = self.max_bits - shift;
            self.inner[idx + 1] &= (1 << shift) - 1;
            self.inner[idx + 1] |= last << shift;
        }
    }

    pub fn from_iter<I>(bits: usize, iterable: I) -> BitsVec<T>
        where I: Iterator<Item=T>
    {
        let mut vec = BitsVec::new(bits);
        for i in iterable {
            vec.push(i);
        }

        vec
    }

    pub fn len(&self) -> usize {
        self.units
    }

    pub fn is_empty(&self) -> bool {
        self.units == 0
    }

    pub fn reserve(&mut self, additional: usize) {
        self.inner.reserve(additional * self.bits / self.max_bits + 1);
    }

    pub fn clear(&mut self) {
        let bits = self.bits;
        mem::replace(self, BitsVec::new(bits));
    }

    pub fn inner_len(&self) -> usize {
        self.inner.len()
    }

    pub fn iter(&self) -> Iter<T> {
        Iter { vec: self, range: 0..self.units }
    }

    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter { range: 0..self.units, vec: self }
    }
}

impl<T: ReprUsize + Clone> BitsVec<T> {
    pub fn with_elements(bits: usize, length: usize, value: T) -> BitsVec<T> {
        let mut vec = BitsVec::new(bits);
        vec.extend_with_element(length, value);
        vec
    }

    pub fn extend_with_element(&mut self, length: usize, value: T) {
        assert!(length > self.len(), "[extend] final length should be greater than the initial length");
        // Three phases (somewhat inefficient, using safe code and all, but much better than `push`)
        let mut remain = length - self.len();
        self.reserve(remain);

        // 1. Slow push until we get to a common multiple of (self.bits, self.max_bits)
        while self.leftover > 0 {
            self.push(value.clone());
            remain -= 1;
            if remain == 0 {
                return
            }
        }

        // 2. Do the same to a new BitsVec
        let mut temp = BitsVec::new(self.bits);
        temp.reserve(cmp::min(remain, self.max_bits));
        temp.push(value.clone());
        while temp.leftover > 0 && remain > 0 {
            temp.push(value.clone());
        }

        if remain == 0 {
            self.units += temp.units;
            self.inner.extend(&temp.inner);
            return
        }

        // 3. Extend from the new BitsVec
        while remain >= temp.units {
            self.inner.extend(&temp.inner);
            self.units += temp.units;
            remain -= temp.units;
        }

        for _ in 0..remain {
            self.push(value.clone());
        }
    }
}

impl<T: ReprUsize + PartialEq> BitsVec<T> {
    pub fn contains(&self, element: &T) -> bool {
        self.iter().find(|ref i| i == &element).is_some()
    }
}

impl<T: ReprUsize + fmt::Debug> fmt::Debug for BitsVec<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_list().entries(self.iter()).finish()
    }
}

impl<T: ReprUsize> PartialEq for BitsVec<T> {
    fn eq(&self, other: &Self) -> bool {
        if self.units != other.units || self.bits != other.bits {
            return false
        }

        self.inner == other.inner
    }
}

pub struct Iter<'a, T: ReprUsize + 'a> {
    vec: &'a BitsVec<T>,
    range: Range<usize>,
}

impl<'a, T: ReprUsize> IntoIterator for &'a BitsVec<T> {
    type Item = T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.iter()
    }
}

impl<'a, T: ReprUsize> Iterator for Iter<'a, T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        self.range.next().map(|i| self.vec.get(i))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.range.size_hint()
    }
}

impl<'a, T: ReprUsize> DoubleEndedIterator for Iter<'a, T> {
    fn next_back(&mut self) -> Option<T> {
        self.range.next_back().map(|i| self.vec.get(i))
    }
}

impl<'a, T: ReprUsize> ExactSizeIterator for Iter<'a, T> {}

pub struct IntoIter<T: ReprUsize> {
    vec: BitsVec<T>,
    range: Range<usize>,
}

impl<T: ReprUsize> IntoIterator for BitsVec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(self) -> IntoIter<T> {
        self.into_iter()
    }
}

impl<T: ReprUsize> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        self.range.next().map(|i| self.vec.get(i))
    }
}

impl<T: ReprUsize> DoubleEndedIterator for IntoIter<T> {
    fn next_back(&mut self) -> Option<T> {
        self.range.next_back().map(|i| self.vec.get(i))
    }
}

impl<T: ReprUsize> ExactSizeIterator for IntoIter<T> {}

#[cfg(test)]
mod tests {
    use super::{BitsVec, ReprUsize};
    use std::mem;

    #[repr(usize)]
    #[derive(Clone, Debug, Eq, PartialEq)]
    enum TestEnum {
        Value1,
        Value2,
        Value3,
        Value4,
    }

    impl ReprUsize for TestEnum {
        fn into_usize(self) -> usize { self as usize }
        fn from_usize(i: usize) -> Self { unsafe { mem::transmute(i) } }
    }

    #[test]
    fn test_everything_with_enum() {
        let mut vec = BitsVec::with_elements(4, 16, TestEnum::Value4);
        vec.set(0, TestEnum::Value1);
        vec.set(1, TestEnum::Value2);
        vec.set(2, TestEnum::Value3);
        assert_eq!(vec.len(), 16);
        assert_eq!(vec.inner_len(), 1);
        assert_eq!(vec.get(0), TestEnum::Value1);
        assert_eq!(vec.get(1), TestEnum::Value2);
        assert_eq!(vec.get(2), TestEnum::Value3);
        vec.push(TestEnum::Value4);
        for i in 3..vec.len() {
            assert_eq!(vec.get(i), TestEnum::Value4);
        }
    }
}
<commit_msg>Vector truncation<commit_after>extern crate rustc_serialize;

use std::cmp;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ops::Range;
use std::usize;

pub trait ReprUsize {
    fn from_usize(usize) -> Self;
    fn into_usize(self) -> usize;
}

impl ReprUsize for bool {
    fn into_usize(self) -> usize { self as usize }
    fn from_usize(i: usize) -> bool {
        unsafe { mem::transmute(i as u8) }
    }
}

impl ReprUsize for char {
    fn into_usize(self) -> usize { self as usize }
    fn from_usize(i: usize) -> char {
        unsafe { mem::transmute(i as u32) }
    }
}

macro_rules! impl_predefined_type {
    ($ty: ty) => {
        impl ReprUsize for $ty {
            fn into_usize(self) -> usize { self as usize }
            fn from_usize(i: usize) -> $ty { i as $ty }
        }
    };
}

impl_predefined_type!(u8);
impl_predefined_type!(u16);
impl_predefined_type!(u32);
impl_predefined_type!(u64);
impl_predefined_type!(usize);
impl_predefined_type!(i8);
impl_predefined_type!(i16);
impl_predefined_type!(i32);
impl_predefined_type!(i64);
impl_predefined_type!(isize);

#[derive(Clone, Hash, RustcEncodable, RustcDecodable)]
pub struct BitsVec<T: ReprUsize> {
    inner: Vec<usize>,
    units: usize,
    bits: usize,
    max_bits: usize,
    leftover: usize,
    _marker: PhantomData<T>,
}

impl<T: ReprUsize> BitsVec<T> {
    pub fn new(bits: usize) -> BitsVec<T> {
        let max = usize::MAX.count_ones() as usize;
        // We can store more bits, but then we might need BigInt to get them out!
        assert!(bits < max, "[new] cannot hold more than {} bits at a time", max - 1);

        BitsVec {
            inner: vec![0],
            units: 0,
            bits: bits,
            max_bits: max,
            leftover: max,
            _marker: PhantomData,
        }
    }

    pub fn with_capacity(bits: usize, capacity: usize) -> BitsVec<T> {
        let mut vec = BitsVec::new(bits);
        vec.reserve(capacity);
        vec
    }

    pub fn push(&mut self, value: T) {
        let mut value = value.into_usize();
        assert!(value >> self.bits == 0,
                "[push] input size is more than allowed size ({} >= {})", value, 2usize.pow(self.bits as u32));

        let mut idx = self.inner.len() - 1;
        if self.leftover < self.bits {
            let left = self.bits - self.leftover;
            self.inner[idx] |= value >> left;
            if self.leftover != 0 {     // special case, in which masking would result in zero!
                value &= (1 << left) - 1;
            }

            self.inner.push(0);
            self.leftover = self.max_bits - left;
            idx += 1;
        } else {
            self.leftover -= self.bits;
        }

        value <<= self.leftover;
        self.inner[idx] |= value;
        self.units += 1;
    }

    pub fn get(&self, i: usize) -> T {
        assert!(i < self.units, "[get] index out of bounds ({} >= {})", i, self.units);

        let pos = i * self.bits;
        let idx = pos / self.max_bits;
        let bits = pos % self.max_bits;
        let diff = self.max_bits - bits;
        let mut val = self.inner[idx];
        if bits != 0 {
            val &= (1 << diff) - 1;
        }

        if diff >= self.bits {
            T::from_usize(val >> (diff - self.bits))
        } else {
            let shift = self.bits - diff;
            let last = self.inner[idx + 1] >> (self.max_bits - shift);
            T::from_usize((val << shift) | last)
        }
    }

    pub fn checked_get(&self, i: usize) -> Option<T> {
        if i >= self.units {
            return None
        }

        Some(self.get(i))
    }

    pub fn set(&mut self, i: usize, value: T) {
        let value = value.into_usize();
        assert!(i < self.units, "[set] index out of bounds ({} >= {})", i, self.units);
        assert!(value >> self.bits == 0,
                "[set] input size is more than allowed size ({} >= {})", value, 2usize.pow(self.bits as u32));

        let pos = i * self.bits;
        let idx = pos / self.max_bits;
        let bits = pos % self.max_bits;
        let diff = self.max_bits - bits;
        let mut val = self.inner[idx];

        if diff >= self.bits {
            let shift = diff - self.bits;
            let last = val & ((1 << shift) - 1);
            let mask = if bits == 0 { 0 } else { ((1 << bits) - 1) << diff };   // prevent overflow
            val &= mask;
            val |= value << shift;
            self.inner[idx] = val | last;
        } else {
            let shift = self.bits - diff;
            val &= !((1 << diff) - 1);
            self.inner[idx] = val | (value >> shift);
            let last = value & ((1 << shift) - 1);
            let shift = self.max_bits - shift;
            self.inner[idx + 1] &= (1 << shift) - 1;
            self.inner[idx + 1] |= last << shift;
        }
    }

    pub fn from_iter<I>(bits: usize, iterable: I) -> BitsVec<T>
        where I: Iterator<Item=T>
    {
        let mut vec = BitsVec::new(bits);
        for i in iterable {
            vec.push(i);
        }

        vec
    }

    pub fn len(&self) -> usize {
        self.units
    }

    pub fn is_empty(&self) -> bool {
        self.units == 0
    }

    pub fn reserve(&mut self, additional: usize) {
        self.inner.reserve(additional * self.bits / self.max_bits + 1);
    }

    pub fn shrink_to_fit(&mut self) {
        self.inner.shrink_to_fit();
    }

    pub fn truncate(&mut self, length: usize) {
        assert!(length < self.units, "length should be smaller for truncation ({} >= {})", length, self.units);
        self.units = length;

        let bits = length * self.bits;
        let mut new_len = bits / self.max_bits;
        let used = bits % self.max_bits;
        if used > 0 {
            new_len += 1;
        }

        self.inner.truncate(new_len);
        if used > 0 {
            self.leftover = self.max_bits - used;
            self.inner[new_len - 1] &= ((1 << used) - 1) << self.leftover;
        } else {
            self.inner.push(0);
            self.leftover = self.max_bits;
        }
    }

    pub fn clear(&mut self) {
        self.truncate(0);
    }

    pub fn inner_len(&self) -> usize {
        self.inner.len()
    }

    pub fn iter(&self) -> Iter<T> {
        Iter { vec: self, range: 0..self.units }
    }

    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter { range: 0..self.units, vec: self }
    }
}

impl<T: ReprUsize + Clone> BitsVec<T> {
    pub fn with_elements(bits: usize, length: usize, value: T) -> BitsVec<T> {
        let mut vec = BitsVec::new(bits);
        vec.extend_with_element(length, value);
        vec
    }

    pub fn extend_with_element(&mut self, length: usize, value: T) {
        assert!(length > self.len(), "[extend] final length should be greater than the initial length");
        // Three phases (somewhat inefficient, using safe code and all, but much better than `push`)
        let mut remain = length - self.len();
        self.reserve(remain);

        // 1. Slow push until we get to a common multiple of (self.bits, self.max_bits)
        while self.leftover > 0 {
            self.push(value.clone());
            remain -= 1;
            if remain == 0 {
                return
            }
        }

        // 2. Do the same to a new BitsVec
        let mut temp = BitsVec::new(self.bits);
        temp.reserve(cmp::min(remain, self.max_bits));
        temp.push(value.clone());
        while temp.leftover > 0 && remain > 0 {
            temp.push(value.clone());
        }

        if remain == 0 {
            self.units += temp.units;
            self.inner.extend(&temp.inner);
            return
        }

        // 3. Extend from the new BitsVec
        while remain >= temp.units {
            self.inner.extend(&temp.inner);
            self.units += temp.units;
            remain -= temp.units;
        }

        for _ in 0..remain {    // remaining valus, if any
            self.push(value.clone());
        }
    }
}

impl<T: ReprUsize + PartialEq> BitsVec<T> {
    pub fn contains(&self, element: &T) -> bool {
        self.iter().find(|ref i| i == &element).is_some()
    }
}

impl<T: ReprUsize + fmt::Debug> fmt::Debug for BitsVec<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_list().entries(self.iter()).finish()
    }
}

impl<T: ReprUsize> PartialEq for BitsVec<T> {
    fn eq(&self, other: &Self) -> bool {
        if self.units != other.units || self.bits != other.bits {
            return false
        }

        self.inner == other.inner
    }
}

pub struct Iter<'a, T: ReprUsize + 'a> {
    vec: &'a BitsVec<T>,
    range: Range<usize>,
}

impl<'a, T: ReprUsize> IntoIterator for &'a BitsVec<T> {
    type Item = T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.iter()
    }
}

impl<'a, T: ReprUsize> Iterator for Iter<'a, T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        self.range.next().map(|i| self.vec.get(i))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.range.size_hint()
    }
}

impl<'a, T: ReprUsize> DoubleEndedIterator for Iter<'a, T> {
    fn next_back(&mut self) -> Option<T> {
        self.range.next_back().map(|i| self.vec.get(i))
    }
}

impl<'a, T: ReprUsize> ExactSizeIterator for Iter<'a, T> {}

pub struct IntoIter<T: ReprUsize> {
    vec: BitsVec<T>,
    range: Range<usize>,
}

impl<T: ReprUsize> IntoIterator for BitsVec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(self) -> IntoIter<T> {
        self.into_iter()
    }
}

impl<T: ReprUsize> Iterator for IntoIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        self.range.next().map(|i| self.vec.get(i))
    }
}

impl<T: ReprUsize> DoubleEndedIterator for IntoIter<T> {
    fn next_back(&mut self) -> Option<T> {
        self.range.next_back().map(|i| self.vec.get(i))
    }
}

impl<T: ReprUsize> ExactSizeIterator for IntoIter<T> {}

#[cfg(test)]
mod tests {
    use super::{BitsVec, ReprUsize};
    use std::mem;

    #[repr(usize)]
    #[derive(Clone, Debug, Eq, PartialEq)]
    enum TestEnum {
        Value1,
        Value2,
        Value3,
        Value4,
    }

    impl ReprUsize for TestEnum {
        fn into_usize(self) -> usize { self as usize }
        fn from_usize(i: usize) -> Self { unsafe { mem::transmute(i) } }
    }

    #[test]
    fn test_everything_with_enum() {
        let mut vec = BitsVec::with_elements(4, 16, TestEnum::Value4);
        vec.set(0, TestEnum::Value1);
        vec.set(1, TestEnum::Value2);
        vec.set(2, TestEnum::Value3);
        assert_eq!(vec.len(), 16);
        assert_eq!(vec.inner_len(), 1);
        assert_eq!(vec.get(0), TestEnum::Value1);
        assert_eq!(vec.get(1), TestEnum::Value2);
        assert_eq!(vec.get(2), TestEnum::Value3);
        vec.push(TestEnum::Value4);
        for i in 3..vec.len() {
            assert_eq!(vec.get(i), TestEnum::Value4);
        }
    }

    #[test]
    fn test_truncate() {
        let mut vec = BitsVec::with_elements(7, 50, 13);
        vec.truncate(10);
        assert_eq!(vec.inner_len(), 2);
        assert_eq!(vec.get(9), 13);
        vec.push(25);
        assert_eq!(vec.get(10), 25);
        let mut vec = BitsVec::with_elements(8, 20, 50);
        vec.truncate(8);
        assert_eq!(vec.inner_len(), 2);
        assert_eq!(vec.get(7), 50);
        vec.push(20);
        assert_eq!(vec.get(8), 20);
    }
}
<|endoftext|>


<commit_before>extern crate piston_window;

extern crate turtleide;

use canvas::{TurtleCanvas, Command, Radians};

fn main() {
    let canvas = TurtleCanvas::new();

    let dashed = false;
    for i in 0..361 {
        if dashed {
            canvas.apply(Command::Pen {
                enabled: i % 2 == 0,
            });
        }
        canvas.apply(Command::Move {
            distance: 2.,
        });
        canvas.apply(Command::Rotate {
            angle: Radians(1f64.to_radians()),
            clockwise: true,
        });
    }
}

mod canvas {
    use std::thread;
    use std::process;
    use std::time::{Instant, Duration};
    use std::sync::{Mutex, MutexGuard, Arc, TryLockError};
    use std::ops::{Add, Sub, Mul, Div, Rem, Neg};
    use std::f64::consts::PI;

    use piston_window::*;

    use turtleide::Speed;

    #[derive(Default, Clone, Copy, Debug, PartialOrd, PartialEq)]
    pub struct Radians(pub f64);

    impl Radians {
        pub fn cos(self) -> f64 {
            self.0.cos()
        }

        pub fn sin(self) -> f64 {
            self.0.sin()
        }
    }

    impl Add for Radians {
        type Output = Self;

        fn add(self, other: Self) -> Self {
            Radians(self.0 + other.0)
        }
    }

    impl Sub for Radians {
        type Output = Self;

        fn sub(self, other: Self) -> Self {
            Radians(self.0 - other.0)
        }
    }

    impl Mul<f64> for Radians {
        type Output = Self;

        fn mul(self, other: f64) -> Self {
            Radians(self.0 * other)
        }
    }

    impl Div<f64> for Radians {
        type Output = Self;

        fn div(self, other: f64) -> Self {
            Radians(self.0 / other)
        }
    }

    impl Rem<f64> for Radians {
        type Output = Self;

        fn rem(self, other: f64) -> Self {
            Radians(self.0 % other)
        }
    }

    impl Neg for Radians {
        type Output = Self;

        fn neg(self) -> Self {
            Radians(-self.0)
        }
    }

    trait ToCanvasCoordinates : Copy {
        fn to_canvas_coords(self, center: [f64; 2]) -> Self;
    }

    impl ToCanvasCoordinates for [f64; 2] {
        /// Transforms the given local coordinate into a point that can be drawn on the canvas.
        ///
        /// Takes into account the direction of the axis and center when converting
        /// `local` from cartesian coordinates.
        ///
        /// Origin in window is the top left corner and the y-axis goes down instead of up.
        fn to_canvas_coords(self, center: [f64; 2]) -> [f64; 2] {
            [center[0] + self[0], center[1] - self[1]]
        }
    }

    trait AsMillis {
        fn as_millis(&self) -> u64;
    }

    impl AsMillis for Duration {
        fn as_millis(&self) -> u64 {
            self.as_secs() * 1000 + (self.subsec_nanos() / 1_000_000) as u64
        }
    }

    pub enum Command {
        Move {
            distance: f64,
        },
        Rotate {
            angle: Radians,
            clockwise: bool,
        },
        Pen {
            enabled: bool,
        },
    }

    #[derive(Debug, Clone, Copy)]
    pub enum Color {
        Black,
        White,
        Transparent,
    }

    impl From<Color> for types::Color {
        fn from(color: Color) -> Self {
            match color {
                Color::Black => [0., 0., 0., 255.],
                Color::White => [255., 255., 255., 255.],
                Color::Transparent => [0., 0., 0., 0.],
            }
        }
    }

    #[derive(Debug, Clone)]
    struct Path {
        start: [f64; 2],
        end: [f64; 2],
        pen: Pen,
    }

    type Response = Result<(), ()>;

    struct TurtleState {
        pub position: [f64; 2],
        pub heading: Radians,
        pub speed: Speed,
    }

    impl Default for TurtleState {
        fn default() -> TurtleState {
            TurtleState {
                position: Default::default(),
                heading: Radians(90f64.to_radians()),
                speed: Speed::Instant,
            }
        }
    }

    struct DrawingState {
        pen: Pen,
        //pub background: Color,
    }

    #[derive(Debug, Clone)]
    struct Pen {
        pub enabled: bool,
        pub thickness: f64,
        pub color: Color,
    }

    struct Animation {
        kind: AnimationKind,
        speed: Speed,
        start: Instant,
    }

    enum AnimationKind {
        Move {
            path: Path,
        },
        Rotation {
            target_angle: Radians,
            clockwise: bool,
        },
    }

    pub struct TurtleCanvas {
        thread_handle: Option<thread::JoinHandle<()>>,
        command: Arc<Mutex<Option<Command>>>,
    }

    impl TurtleCanvas {
        pub fn new() -> TurtleCanvas {
            let command = Arc::new(Mutex::new(None));

            let command_queue = command.clone();
            let handle = thread::spawn(move || {
                let mut window: PistonWindow = WindowSettings::new(
                    "Turtle IDE", [800, 600]
                ).exit_on_esc(true).build().unwrap();
                let mut paths: Vec<Path> = Vec::new();
                let mut animation: Option<Animation> = None;
                let mut turtle = TurtleState::default();
                let mut drawing = DrawingState {
                    pen: Pen {
                        enabled: true,
                        thickness: 1.,
                        color: Color::Black,
                    },
                };
                // Keeps the lock until the animation is complete
                let mut lock: Option<MutexGuard<_>> = None;
                let mut pending_command: Option<Command> = None;

                while let Some(e) = window.next() {
                    match command_queue.try_lock() {
                        Ok(mut command_lock) => {
                            pending_command = command_lock.take();
                            if pending_command.is_some() {
                                // Start the animation and block until it is complete
                                println!("acquired", );
                                lock = Some(command_lock);
                            }
                        },
                        Ok(_) |
                        Err(TryLockError::WouldBlock) => {}, // Do nothing
                        Err(TryLockError::Poisoned(..)) => break, // Quit
                    }

                    if let Some(command) = pending_command.take() {
                        if animation.is_some() {
                            unreachable!("The main thread did not wait for the animation to complete before sending another command")
                        }
                        match command {
                            Command::Move {distance} => {
                                if distance != 0. {
                                    let start = turtle.position;
                                    let x = distance * turtle.heading.cos();
                                    let y = distance * turtle.heading.sin();
                                    let end = math::add(start, [x, y]);
                                    animation = Some(Animation {
                                        kind: AnimationKind::Move {
                                            path: Path {
                                                start, end,
                                                pen: drawing.pen.clone(),
                                            },
                                        },
                                        speed: turtle.speed,
                                        start: Instant::now(),
                                    });
                                }
                            },
                            Command::Rotate {angle, clockwise} => {
                                if angle != Radians(0.) {
                                    let target_angle = turtle.heading + if clockwise {
                                        -angle
                                    }
                                    else {
                                        angle
                                    };
                                    assert!(target_angle != turtle.heading);
                                    animation = Some(Animation {
                                        kind: AnimationKind::Rotation {
                                            target_angle,
                                            clockwise,
                                        },
                                        speed: turtle.speed,
                                        start: Instant::now(),
                                    });
                                }
                            },
                            Command::Pen {enabled} => {
                                drawing.pen.enabled = enabled;
                                println!("released", );
                                lock.take();
                            },
                        }
                    }

                    window.draw_2d(&e, |c, g| {
                        clear([1.0; 4], g);

                        let view = c.get_view_size();
                        let width = view[0] as f64;
                        let height = view[1] as f64;
                        let center = [width * 0.5, height * 0.5];

                        let mut animation_complete = false;
                        if let Some(Animation {ref kind, ref speed, ref start}) = animation {
                            let elapsed = start.elapsed().as_millis() as f64;

                            match *kind {
                                AnimationKind::Move {ref path} => {
                                    // This code finds the point on the line between start and
                                    // end that we are supposed to be at right now using some
                                    // vector math.
                                    //
                                    // The basic idea is to find the vector that represents the
                                    // line between start and end, normalize it into a direction,
                                    // and then scale that vector to be the size that it should be
                                    // at the elapsed time based on the speed.
                                    let &Path {start, end, ref pen} = path;
                                    let speed = speed.to_absolute(); // px per second
                                    let path_line = math::sub(start, end);
                                    let path_length = math::square_len(path_line).sqrt();
                                    let direction = math::mul_scalar(path_line, 1./path_length);
                                    debug_assert_eq!((math::square_len(direction).sqrt() * 1000.).round(), 1000.);
                                    // (px / sec) * (sec / 1000ms) * ms => px
                                    let offset = math::mul_scalar(direction, speed / 1000. * elapsed);
                                    let offset_length = math::square_len(offset).sqrt();

                                    let current = if speed.is_infinite() || offset_length >= path_length {
                                        paths.push(path.clone());
                                        animation_complete = true;
                                        end
                                    }
                                    else {
                                        math::add(start, offset)
                                    };

                                    turtle.position = current;

                                    let &Pen {thickness, color, enabled} = pen;
                                    if enabled {
                                        let start = start.to_canvas_coords(center);
                                        let current = current.to_canvas_coords(center);

                                        line(color.into(), thickness,
                                            [start[0], start[1], current[0], current[1]],
                                            c.transform, g);
                                    }
                                },
                                AnimationKind::Rotation {target_angle, clockwise} => {
                                    // TODO: Use the returned value in Radians and impl Div for Radians
                                    let speed = Radians(speed.to_rotation());
                                    let target_angle = target_angle % (2.*PI);

                                    let heading = turtle.heading;
                                    let current = (heading + speed / 1000. * elapsed) % (2.*PI);
                                    let current = if speed.0.is_infinite() || current > target_angle {
                                        animation_complete = true;
                                        target_angle
                                    }
                                    else {
                                        let current = if clockwise {
                                            Radians(2.*PI) - current
                                        } else { current };

                                        current
                                    };
                                    assert!(!current.0.is_nan(), "heading became NaN");
                                    turtle.heading = current;
                                },
                            }
                        }
                        if animation_complete {
                            animation = None;
                            println!("released", );
                            lock.take();
                        }

                        for path in &paths {
                            if !path.pen.enabled {
                                continue;
                            }

                            let Path {start, end, ref pen} = *path;
                            let start = start.to_canvas_coords(center);
                            let end = end.to_canvas_coords(center);

                            line(pen.color.into(), pen.thickness,
                                [start[0], start[1], end[0], end[1]],
                                c.transform, g);
                        }

                        // Draw the turtle's shell
                        let cos = turtle.heading.cos();
                        let sin = turtle.heading.sin();
                        let turtle_x = turtle.position[0];
                        let turtle_y = turtle.position[1];
                        let shell: Vec<_> = [
                            [0., 15.],
                            [10., 0.],
                            [0., -15.],
                        ].into_iter().map(|pt| {
                            // Rotate each point by the heading and add the current turtle position
                            let x = cos * pt[0] - sin * pt[1] + turtle_x;
                            let y = sin * pt[0] + cos * pt[1] + turtle_y;
                            [x, y].to_canvas_coords(center)
                        }).collect();

                        polygon(Color::White.into(), &shell, c.transform, g);
                        for i in 0..shell.len() {
                            let start = shell[i];
                            let end = shell[(i + 1) % shell.len()];

                            line(Color::Black.into(), 1.,
                                [start[0], start[1], end[0], end[1]],
                                c.transform, g);
                        }
                    });
                }
            });

            Self {
                thread_handle: Some(handle),
                command,
            }
        }

        pub fn apply(&self, command: Command) {
            self.command.lock().map(|mut cmd| {
                println!("{:?}", cmd.is_some());
                *cmd = Some(command);
                println!("placed command", );
            }).map_err(|_| {
                // The connection has been closed so the window was closed
                // or an error occurred on that thread
                process::exit(0);
            });
        }
    }

    impl Drop for TurtleCanvas {
        fn drop(&mut self) {
            if let Some(handle) = self.thread_handle.take() {
                handle.join().unwrap();
            }
        }
    }
}
<commit_msg>Revert "Failed attempt at making things faster using shared memory"<commit_after>extern crate piston_window;

extern crate turtleide;

use canvas::{TurtleCanvas, Command, Radians};

fn main() {
    let canvas = TurtleCanvas::new();

    let dashed = false;
    for i in 0..361 {
        if dashed {
            canvas.apply(Command::Pen {
                enabled: i % 2 == 0,
            });
        }
        canvas.apply(Command::Move {
            distance: 2.,
        });
        canvas.apply(Command::Rotate {
            angle: Radians(1f64.to_radians()),
            clockwise: true,
        });
    }
}

mod canvas {
    use std::thread;
    use std::process;
    use std::time::{Instant, Duration};
    use std::sync::mpsc::{self, TryRecvError};
    use std::ops::{Add, Sub, Mul, Div, Rem, Neg};
    use std::f64::consts::PI;

    use piston_window::*;

    use turtleide::Speed;

    #[derive(Default, Clone, Copy, Debug, PartialOrd, PartialEq)]
    pub struct Radians(pub f64);

    impl Radians {
        pub fn cos(self) -> f64 {
            self.0.cos()
        }

        pub fn sin(self) -> f64 {
            self.0.sin()
        }
    }

    impl Add for Radians {
        type Output = Self;

        fn add(self, other: Self) -> Self {
            Radians(self.0 + other.0)
        }
    }

    impl Sub for Radians {
        type Output = Self;

        fn sub(self, other: Self) -> Self {
            Radians(self.0 - other.0)
        }
    }

    impl Mul<f64> for Radians {
        type Output = Self;

        fn mul(self, other: f64) -> Self {
            Radians(self.0 * other)
        }
    }

    impl Div<f64> for Radians {
        type Output = Self;

        fn div(self, other: f64) -> Self {
            Radians(self.0 / other)
        }
    }

    impl Rem<f64> for Radians {
        type Output = Self;

        fn rem(self, other: f64) -> Self {
            Radians(self.0 % other)
        }
    }

    impl Neg for Radians {
        type Output = Self;

        fn neg(self) -> Self {
            Radians(-self.0)
        }
    }

    trait ToCanvasCoordinates : Copy {
        fn to_canvas_coords(self, center: [f64; 2]) -> Self;
    }

    impl ToCanvasCoordinates for [f64; 2] {
        /// Transforms the given local coordinate into a point that can be drawn on the canvas.
        ///
        /// Takes into account the direction of the axis and center when converting
        /// `local` from cartesian coordinates.
        ///
        /// Origin in window is the top left corner and the y-axis goes down instead of up.
        fn to_canvas_coords(self, center: [f64; 2]) -> [f64; 2] {
            [center[0] + self[0], center[1] - self[1]]
        }
    }

    trait AsMillis {
        fn as_millis(&self) -> u64;
    }

    impl AsMillis for Duration {
        fn as_millis(&self) -> u64 {
            self.as_secs() * 1000 + (self.subsec_nanos() / 1_000_000) as u64
        }
    }

    pub enum Command {
        Move {
            distance: f64,
        },
        Rotate {
            angle: Radians,
            clockwise: bool,
        },
        Pen {
            enabled: bool,
        },
    }

    #[derive(Debug, Clone, Copy)]
    pub enum Color {
        Black,
        White,
        Transparent,
    }

    impl From<Color> for types::Color {
        fn from(color: Color) -> Self {
            match color {
                Color::Black => [0., 0., 0., 255.],
                Color::White => [255., 255., 255., 255.],
                Color::Transparent => [0., 0., 0., 0.],
            }
        }
    }

    #[derive(Debug, Clone)]
    struct Path {
        start: [f64; 2],
        end: [f64; 2],
        pen: Pen,
    }

    type Response = Result<(), ()>;

    struct TurtleState {
        pub position: [f64; 2],
        pub heading: Radians,
        pub speed: Speed,
    }

    impl Default for TurtleState {
        fn default() -> TurtleState {
            TurtleState {
                position: Default::default(),
                heading: Radians(90f64.to_radians()),
                speed: Speed::Instant,
            }
        }
    }

    struct DrawingState {
        pen: Pen,
        //pub background: Color,
    }

    #[derive(Debug, Clone)]
    struct Pen {
        pub enabled: bool,
        pub thickness: f64,
        pub color: Color,
    }

    struct Animation {
        kind: AnimationKind,
        speed: Speed,
        start: Instant,
    }

    enum AnimationKind {
        Move {
            path: Path,
        },
        Rotation {
            target_angle: Radians,
            clockwise: bool,
        },
    }

    pub struct TurtleCanvas {
        thread_handle: Option<thread::JoinHandle<()>>,
        transmitter: mpsc::Sender<Command>,
        receiver: mpsc::Receiver<Response>,
    }

    impl TurtleCanvas {
        pub fn new() -> TurtleCanvas {
            let (drawing_tx, drawing_rx) = mpsc::channel();
            let (main_tx, main_rx) = mpsc::channel();

            let handle = thread::spawn(move || {
                let mut window: PistonWindow = WindowSettings::new(
                    "Turtle IDE", [800, 600]
                ).exit_on_esc(true).build().unwrap();
                let mut paths: Vec<Path> = Vec::new();
                let mut animation: Option<Animation> = None;
                let mut turtle = TurtleState::default();
                let mut drawing = DrawingState {
                    pen: Pen {
                        enabled: true,
                        thickness: 1.,
                        color: Color::Black,
                    },
                };

                while let Some(e) = window.next() {
                    match drawing_rx.try_recv() {
                        Ok(command) => {
                            if animation.is_some() {
                                unreachable!("The main thread did not wait for the animation to complete before sending another command")
                            }
                            match command {
                                Command::Move {distance} => {
                                    if distance != 0. {
                                        let start = turtle.position;
                                        let x = distance * turtle.heading.cos();
                                        let y = distance * turtle.heading.sin();
                                        let end = math::add(start, [x, y]);
                                        animation = Some(Animation {
                                            kind: AnimationKind::Move {
                                                path: Path {
                                                    start, end,
                                                    pen: drawing.pen.clone(),
                                                },
                                            },
                                            speed: turtle.speed,
                                            start: Instant::now(),
                                        });
                                    }
                                },
                                Command::Rotate {angle, clockwise} => {
                                    if angle != Radians(0.) {
                                        let target_angle = turtle.heading + if clockwise {
                                            -angle
                                        }
                                        else {
                                            angle
                                        };
                                        assert!(target_angle != turtle.heading);
                                        animation = Some(Animation {
                                            kind: AnimationKind::Rotation {
                                                target_angle,
                                                clockwise,
                                            },
                                            speed: turtle.speed,
                                            start: Instant::now(),
                                        });
                                    }
                                },
                                Command::Pen {enabled} => {
                                    drawing.pen.enabled = enabled;
                                    main_tx.send(Ok(())).unwrap();
                                },
                            }
                        },
                        Err(TryRecvError::Empty) => {}, // Do nothing
                        Err(TryRecvError::Disconnected) => break, // Quit
                    }

                    window.draw_2d(&e, |c, g| {
                        clear([1.0; 4], g);

                        let view = c.get_view_size();
                        let width = view[0] as f64;
                        let height = view[1] as f64;
                        let center = [width * 0.5, height * 0.5];

                        let mut animation_complete = false;
                        if let Some(Animation {ref kind, ref speed, ref start}) = animation {
                            let elapsed = start.elapsed().as_millis() as f64;

                            match *kind {
                                AnimationKind::Move {ref path} => {
                                    // This code finds the point on the line between start and
                                    // end that we are supposed to be at right now using some
                                    // vector math.
                                    //
                                    // The basic idea is to find the vector that represents the
                                    // line between start and end, normalize it into a direction,
                                    // and then scale that vector to be the size that it should be
                                    // at the elapsed time based on the speed.
                                    let &Path {start, end, ref pen} = path;
                                    let speed = speed.to_absolute(); // px per second
                                    let path_line = math::sub(start, end);
                                    let path_length = math::square_len(path_line).sqrt();
                                    let direction = math::mul_scalar(path_line, 1./path_length);
                                    debug_assert_eq!((math::square_len(direction).sqrt() * 1000.).round(), 1000.);
                                    // (px / sec) * (sec / 1000ms) * ms => px
                                    let offset = math::mul_scalar(direction, speed / 1000. * elapsed);
                                    let offset_length = math::square_len(offset).sqrt();

                                    let current = if speed.is_infinite() || offset_length >= path_length {
                                        paths.push(path.clone());
                                        animation_complete = true;
                                        end
                                    }
                                    else {
                                        math::add(start, offset)
                                    };

                                    turtle.position = current;

                                    let &Pen {thickness, color, enabled} = pen;
                                    if enabled {
                                        let start = start.to_canvas_coords(center);
                                        let current = current.to_canvas_coords(center);

                                        line(color.into(), thickness,
                                            [start[0], start[1], current[0], current[1]],
                                            c.transform, g);
                                    }
                                },
                                AnimationKind::Rotation {target_angle, clockwise} => {
                                    // TODO: Use the returned value in Radians and impl Div for Radians
                                    let speed = Radians(speed.to_rotation());
                                    let target_angle = target_angle % (2.*PI);

                                    let heading = turtle.heading;
                                    let current = (heading + speed / 1000. * elapsed) % (2.*PI);
                                    let current = if speed.0.is_infinite() || current > target_angle {
                                        animation_complete = true;
                                        target_angle
                                    }
                                    else {
                                        let current = if clockwise {
                                            Radians(2.*PI) - current
                                        } else { current };

                                        current
                                    };
                                    assert!(!current.0.is_nan(), "heading became NaN");
                                    turtle.heading = current;
                                },
                            }
                        }
                        if animation_complete {
                            animation = None;
                            main_tx.send(Ok(())).unwrap();
                        }

                        for path in &paths {
                            if !path.pen.enabled {
                                continue;
                            }

                            let Path {start, end, ref pen} = *path;
                            let start = start.to_canvas_coords(center);
                            let end = end.to_canvas_coords(center);

                            line(pen.color.into(), pen.thickness,
                                [start[0], start[1], end[0], end[1]],
                                c.transform, g);
                        }

                        // Draw the turtle's shell
                        let cos = turtle.heading.cos();
                        let sin = turtle.heading.sin();
                        let turtle_x = turtle.position[0];
                        let turtle_y = turtle.position[1];
                        let shell: Vec<_> = [
                            [0., 15.],
                            [10., 0.],
                            [0., -15.],
                        ].into_iter().map(|pt| {
                            // Rotate each point by the heading and add the current turtle position
                            let x = cos * pt[0] - sin * pt[1] + turtle_x;
                            let y = sin * pt[0] + cos * pt[1] + turtle_y;
                            [x, y].to_canvas_coords(center)
                        }).collect();

                        polygon(Color::White.into(), &shell, c.transform, g);
                        for i in 0..shell.len() {
                            let start = shell[i];
                            let end = shell[(i + 1) % shell.len()];

                            line(Color::Black.into(), 1.,
                                [start[0], start[1], end[0], end[1]],
                                c.transform, g);
                        }
                    });
                }
            });

            Self {
                thread_handle: Some(handle),
                transmitter: drawing_tx,
                receiver: main_rx,
            }
        }

        pub fn apply(&self, command: Command) {
            let result = self.transmitter.send(command).map_err(|_| ());
            let result = result.and_then(|_| {
                // Wait for the drawing animation to complete
                self.receiver.recv().map_err(|_| ())
            });
            match result {
                Ok(_) => {},
                Err(_) => {
                    // The connection has been closed so the window was closed
                    // or an error occurred on that thread
                    process::exit(0);
                },
            }
        }
    }

    impl Drop for TurtleCanvas {
        fn drop(&mut self) {
            if let Some(handle) = self.thread_handle.take() {
                handle.join().unwrap();
            }
        }
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use rustc::ty;
use rustc::hir::*;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use syntax::parse::token::InternedString;
use syntax::util::small_vector::SmallVector;
use utils::{SpanlessEq, SpanlessHash};
use utils::{get_parent_expr, in_macro, span_lint_and_then, span_note_and_lint, snippet};

/// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is
/// `Warn` by default.
///
/// **Why is this bad?** This is probably a copy & paste error.
///
/// **Known problems:** Hopefully none.
///
/// **Example:** `if a == b { .. } else if a == b { .. }`
declare_lint! {
    pub IFS_SAME_COND,
    Warn,
    "consecutive `ifs` with the same condition"
}

/// **What it does:** This lint checks for `if/else` with the same body as the *then* part and the
/// *else* part. This lint is `Warn` by default.
///
/// **Why is this bad?** This is probably a copy & paste error.
///
/// **Known problems:** Hopefully none.
///
/// **Example:** `if .. { 42 } else { 42 }`
declare_lint! {
    pub IF_SAME_THEN_ELSE,
    Warn,
    "if with the same *then* and *else* blocks"
}

/// **What it does:** This lint checks for `match` with identical arm bodies.
///
/// **Why is this bad?** This is probably a copy & paste error. If arm bodies are the same on
/// purpose, you can factor them
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust,ignore
/// match foo {
///     Bar => bar(),
///     Quz => quz(),
///     Baz => bar(), // <= oops
/// }
/// ```
///
/// This should probably be
/// ```rust,ignore
/// match foo {
///     Bar => bar(),
///     Quz => quz(),
///     Baz => baz(), // <= fixed
/// }
/// ```
///
/// or if the original code was not a typo:
/// ```rust,ignore
/// match foo {
///     Bar | Baz => bar(), // <= shows the intent better
///     Quz => quz(),
/// }
/// ```
declare_lint! {
    pub MATCH_SAME_ARMS,
    Warn,
    "`match` with identical arm bodies"
}

#[derive(Copy, Clone, Debug)]
pub struct CopyAndPaste;

impl LintPass for CopyAndPaste {
    fn get_lints(&self) -> LintArray {
        lint_array![IFS_SAME_COND, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]
    }
}

impl LateLintPass for CopyAndPaste {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if !in_macro(cx, expr.span) {
            // skip ifs directly in else, it will be checked in the parent if
            if let Some(&Expr { node: ExprIf(_, _, Some(ref else_expr)), .. }) = get_parent_expr(cx, expr) {
                if else_expr.id == expr.id {
                    return;
                }
            }

            let (conds, blocks) = if_sequence(expr);
            lint_same_then_else(cx, blocks.as_slice());
            lint_same_cond(cx, conds.as_slice());
            lint_match_arms(cx, expr);
        }
    }
}

/// Implementation of `IF_SAME_THEN_ELSE`.
fn lint_same_then_else(cx: &LateContext, blocks: &[&Block]) {
    let hash: &Fn(&&Block) -> u64 = &|block| -> u64 {
        let mut h = SpanlessHash::new(cx);
        h.hash_block(block);
        h.finish()
    };

    let eq: &Fn(&&Block, &&Block) -> bool = &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) };

    if let Some((i, j)) = search_same(blocks, hash, eq) {
        span_note_and_lint(cx,
                           IF_SAME_THEN_ELSE,
                           j.span,
                           "this `if` has identical blocks",
                           i.span,
                           "same as this");
    }
}

/// Implementation of `IFS_SAME_COND`.
fn lint_same_cond(cx: &LateContext, conds: &[&Expr]) {
    let hash: &Fn(&&Expr) -> u64 = &|expr| -> u64 {
        let mut h = SpanlessHash::new(cx);
        h.hash_expr(expr);
        h.finish()
    };

    let eq: &Fn(&&Expr, &&Expr) -> bool = &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };

    if let Some((i, j)) = search_same(conds, hash, eq) {
        span_note_and_lint(cx,
                           IFS_SAME_COND,
                           j.span,
                           "this `if` has the same condition as a previous if",
                           i.span,
                           "same as this");
    }
}

/// Implementation if `MATCH_SAME_ARMS`.
fn lint_match_arms(cx: &LateContext, expr: &Expr) {
    let hash = |arm: &Arm| -> u64 {
        let mut h = SpanlessHash::new(cx);
        h.hash_expr(&arm.body);
        h.finish()
    };

    let eq = |lhs: &Arm, rhs: &Arm| -> bool {
        // Arms with a guard are ignored, those can’t always be merged together
        lhs.guard.is_none() && rhs.guard.is_none() &&
            SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
            // all patterns should have the same bindings
            bindings(cx, &lhs.pats[0]) == bindings(cx, &rhs.pats[0])
    };

    if let ExprMatch(_, ref arms, MatchSource::Normal) = expr.node {
        if let Some((i, j)) = search_same(arms, hash, eq) {
            span_lint_and_then(cx,
                               MATCH_SAME_ARMS,
                               j.body.span,
                               "this `match` has identical arm bodies",
                               |db| {
                db.span_note(i.body.span, "same as this");

                // Note: this does not use `span_suggestion` on purpose: there is no clean way to
                // remove the other arm. Building a span and suggest to replace it to "" makes an
                // even more confusing error message. Also in order not to make up a span for the
                // whole pattern, the suggestion is only shown when there is only one pattern. The
                // user should know about `|` if they are already using it…

                if i.pats.len() == 1 && j.pats.len() == 1 {
                    let lhs = snippet(cx, i.pats[0].span, "<pat1>");
                    let rhs = snippet(cx, j.pats[0].span, "<pat2>");
                    db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
                }
            });
        }
    }
}

/// Return the list of condition expressions and the list of blocks in a sequence of `if/else`.
/// Eg. would return `([a, b], [c, d, e])` for the expression
/// `if a { c } else if b { d } else { e }`.
fn if_sequence(mut expr: &Expr) -> (SmallVector<&Expr>, SmallVector<&Block>) {
    let mut conds = SmallVector::zero();
    let mut blocks = SmallVector::zero();

    while let ExprIf(ref cond, ref then_block, ref else_expr) = expr.node {
        conds.push(&**cond);
        blocks.push(&**then_block);

        if let Some(ref else_expr) = *else_expr {
            expr = else_expr;
        } else {
            break;
        }
    }

    // final `else {..}`
    if !blocks.is_empty() {
        if let ExprBlock(ref block) = expr.node {
            blocks.push(&**block);
        }
    }

    (conds, blocks)
}

/// Return the list of bindings in a pattern.
fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<InternedString, ty::Ty<'tcx>> {
    fn bindings_impl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat, map: &mut HashMap<InternedString, ty::Ty<'tcx>>) {
        match pat.node {
            PatKind::Box(ref pat) |
            PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
            PatKind::TupleStruct(_, ref pats, _) => {
                for pat in pats {
                    bindings_impl(cx, pat, map);
                }
            }
            PatKind::Binding(_, ref ident, ref as_pat) => {
                if let Entry::Vacant(v) = map.entry(ident.node.as_str()) {
                    v.insert(cx.tcx.pat_ty(pat));
                }
                if let Some(ref as_pat) = *as_pat {
                    bindings_impl(cx, as_pat, map);
                }
            }
            PatKind::Struct(_, ref fields, _) => {
                for pat in fields {
                    bindings_impl(cx, &pat.node.pat, map);
                }
            }
            PatKind::Tuple(ref fields, _) => {
                for pat in fields {
                    bindings_impl(cx, pat, map);
                }
            }
            PatKind::Vec(ref lhs, ref mid, ref rhs) => {
                for pat in lhs {
                    bindings_impl(cx, pat, map);
                }
                if let Some(ref mid) = *mid {
                    bindings_impl(cx, mid, map);
                }
                for pat in rhs {
                    bindings_impl(cx, pat, map);
                }
            }
            PatKind::Lit(..) |
            PatKind::QPath(..) |
            PatKind::Range(..) |
            PatKind::Wild |
            PatKind::Path(..) => (),
        }
    }

    let mut result = HashMap::new();
    bindings_impl(cx, pat, &mut result);
    result
}

fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Option<(&T, &T)>
    where Hash: Fn(&T) -> u64,
          Eq: Fn(&T, &T) -> bool
{
    // common cases
    if exprs.len() < 2 {
        return None;
    } else if exprs.len() == 2 {
        return if eq(&exprs[0], &exprs[1]) {
            Some((&exprs[0], &exprs[1]))
        } else {
            None
        };
    }

    let mut map: HashMap<_, Vec<&_>> = HashMap::with_capacity(exprs.len());

    for expr in exprs {
        match map.entry(hash(expr)) {
            Entry::Occupied(o) => {
                for o in o.get() {
                    if eq(o, expr) {
                        return Some((o, expr));
                    }
                }
            }
            Entry::Vacant(v) => {
                v.insert(vec![expr]);
            }
        }
    }

    None
}
<commit_msg>Link to known issues for `MATCH_SAME_ARMS`<commit_after>use rustc::lint::*;
use rustc::ty;
use rustc::hir::*;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use syntax::parse::token::InternedString;
use syntax::util::small_vector::SmallVector;
use utils::{SpanlessEq, SpanlessHash};
use utils::{get_parent_expr, in_macro, span_lint_and_then, span_note_and_lint, snippet};

/// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is
/// `Warn` by default.
///
/// **Why is this bad?** This is probably a copy & paste error.
///
/// **Known problems:** Hopefully none.
///
/// **Example:** `if a == b { .. } else if a == b { .. }`
declare_lint! {
    pub IFS_SAME_COND,
    Warn,
    "consecutive `ifs` with the same condition"
}

/// **What it does:** This lint checks for `if/else` with the same body as the *then* part and the
/// *else* part. This lint is `Warn` by default.
///
/// **Why is this bad?** This is probably a copy & paste error.
///
/// **Known problems:** Hopefully none.
///
/// **Example:** `if .. { 42 } else { 42 }`
declare_lint! {
    pub IF_SAME_THEN_ELSE,
    Warn,
    "if with the same *then* and *else* blocks"
}

/// **What it does:** This lint checks for `match` with identical arm bodies.
///
/// **Why is this bad?** This is probably a copy & paste error. If arm bodies are the same on
/// purpose, you can factor them
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
///
/// **Known problems:** False positive possible with order dependent `match`
/// (see issue [#860](https://github.com/Manishearth/rust-clippy/issues/860)).
///
/// **Example:**
/// ```rust,ignore
/// match foo {
///     Bar => bar(),
///     Quz => quz(),
///     Baz => bar(), // <= oops
/// }
/// ```
///
/// This should probably be
/// ```rust,ignore
/// match foo {
///     Bar => bar(),
///     Quz => quz(),
///     Baz => baz(), // <= fixed
/// }
/// ```
///
/// or if the original code was not a typo:
/// ```rust,ignore
/// match foo {
///     Bar | Baz => bar(), // <= shows the intent better
///     Quz => quz(),
/// }
/// ```
declare_lint! {
    pub MATCH_SAME_ARMS,
    Warn,
    "`match` with identical arm bodies"
}

#[derive(Copy, Clone, Debug)]
pub struct CopyAndPaste;

impl LintPass for CopyAndPaste {
    fn get_lints(&self) -> LintArray {
        lint_array![IFS_SAME_COND, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]
    }
}

impl LateLintPass for CopyAndPaste {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if !in_macro(cx, expr.span) {
            // skip ifs directly in else, it will be checked in the parent if
            if let Some(&Expr { node: ExprIf(_, _, Some(ref else_expr)), .. }) = get_parent_expr(cx, expr) {
                if else_expr.id == expr.id {
                    return;
                }
            }

            let (conds, blocks) = if_sequence(expr);
            lint_same_then_else(cx, blocks.as_slice());
            lint_same_cond(cx, conds.as_slice());
            lint_match_arms(cx, expr);
        }
    }
}

/// Implementation of `IF_SAME_THEN_ELSE`.
fn lint_same_then_else(cx: &LateContext, blocks: &[&Block]) {
    let hash: &Fn(&&Block) -> u64 = &|block| -> u64 {
        let mut h = SpanlessHash::new(cx);
        h.hash_block(block);
        h.finish()
    };

    let eq: &Fn(&&Block, &&Block) -> bool = &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) };

    if let Some((i, j)) = search_same(blocks, hash, eq) {
        span_note_and_lint(cx,
                           IF_SAME_THEN_ELSE,
                           j.span,
                           "this `if` has identical blocks",
                           i.span,
                           "same as this");
    }
}

/// Implementation of `IFS_SAME_COND`.
fn lint_same_cond(cx: &LateContext, conds: &[&Expr]) {
    let hash: &Fn(&&Expr) -> u64 = &|expr| -> u64 {
        let mut h = SpanlessHash::new(cx);
        h.hash_expr(expr);
        h.finish()
    };

    let eq: &Fn(&&Expr, &&Expr) -> bool = &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };

    if let Some((i, j)) = search_same(conds, hash, eq) {
        span_note_and_lint(cx,
                           IFS_SAME_COND,
                           j.span,
                           "this `if` has the same condition as a previous if",
                           i.span,
                           "same as this");
    }
}

/// Implementation if `MATCH_SAME_ARMS`.
fn lint_match_arms(cx: &LateContext, expr: &Expr) {
    let hash = |arm: &Arm| -> u64 {
        let mut h = SpanlessHash::new(cx);
        h.hash_expr(&arm.body);
        h.finish()
    };

    let eq = |lhs: &Arm, rhs: &Arm| -> bool {
        // Arms with a guard are ignored, those can’t always be merged together
        lhs.guard.is_none() && rhs.guard.is_none() &&
            SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
            // all patterns should have the same bindings
            bindings(cx, &lhs.pats[0]) == bindings(cx, &rhs.pats[0])
    };

    if let ExprMatch(_, ref arms, MatchSource::Normal) = expr.node {
        if let Some((i, j)) = search_same(arms, hash, eq) {
            span_lint_and_then(cx,
                               MATCH_SAME_ARMS,
                               j.body.span,
                               "this `match` has identical arm bodies",
                               |db| {
                db.span_note(i.body.span, "same as this");

                // Note: this does not use `span_suggestion` on purpose: there is no clean way to
                // remove the other arm. Building a span and suggest to replace it to "" makes an
                // even more confusing error message. Also in order not to make up a span for the
                // whole pattern, the suggestion is only shown when there is only one pattern. The
                // user should know about `|` if they are already using it…

                if i.pats.len() == 1 && j.pats.len() == 1 {
                    let lhs = snippet(cx, i.pats[0].span, "<pat1>");
                    let rhs = snippet(cx, j.pats[0].span, "<pat2>");
                    db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
                }
            });
        }
    }
}

/// Return the list of condition expressions and the list of blocks in a sequence of `if/else`.
/// Eg. would return `([a, b], [c, d, e])` for the expression
/// `if a { c } else if b { d } else { e }`.
fn if_sequence(mut expr: &Expr) -> (SmallVector<&Expr>, SmallVector<&Block>) {
    let mut conds = SmallVector::zero();
    let mut blocks = SmallVector::zero();

    while let ExprIf(ref cond, ref then_block, ref else_expr) = expr.node {
        conds.push(&**cond);
        blocks.push(&**then_block);

        if let Some(ref else_expr) = *else_expr {
            expr = else_expr;
        } else {
            break;
        }
    }

    // final `else {..}`
    if !blocks.is_empty() {
        if let ExprBlock(ref block) = expr.node {
            blocks.push(&**block);
        }
    }

    (conds, blocks)
}

/// Return the list of bindings in a pattern.
fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<InternedString, ty::Ty<'tcx>> {
    fn bindings_impl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat, map: &mut HashMap<InternedString, ty::Ty<'tcx>>) {
        match pat.node {
            PatKind::Box(ref pat) |
            PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
            PatKind::TupleStruct(_, ref pats, _) => {
                for pat in pats {
                    bindings_impl(cx, pat, map);
                }
            }
            PatKind::Binding(_, ref ident, ref as_pat) => {
                if let Entry::Vacant(v) = map.entry(ident.node.as_str()) {
                    v.insert(cx.tcx.pat_ty(pat));
                }
                if let Some(ref as_pat) = *as_pat {
                    bindings_impl(cx, as_pat, map);
                }
            }
            PatKind::Struct(_, ref fields, _) => {
                for pat in fields {
                    bindings_impl(cx, &pat.node.pat, map);
                }
            }
            PatKind::Tuple(ref fields, _) => {
                for pat in fields {
                    bindings_impl(cx, pat, map);
                }
            }
            PatKind::Vec(ref lhs, ref mid, ref rhs) => {
                for pat in lhs {
                    bindings_impl(cx, pat, map);
                }
                if let Some(ref mid) = *mid {
                    bindings_impl(cx, mid, map);
                }
                for pat in rhs {
                    bindings_impl(cx, pat, map);
                }
            }
            PatKind::Lit(..) |
            PatKind::QPath(..) |
            PatKind::Range(..) |
            PatKind::Wild |
            PatKind::Path(..) => (),
        }
    }

    let mut result = HashMap::new();
    bindings_impl(cx, pat, &mut result);
    result
}

fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Option<(&T, &T)>
    where Hash: Fn(&T) -> u64,
          Eq: Fn(&T, &T) -> bool
{
    // common cases
    if exprs.len() < 2 {
        return None;
    } else if exprs.len() == 2 {
        return if eq(&exprs[0], &exprs[1]) {
            Some((&exprs[0], &exprs[1]))
        } else {
            None
        };
    }

    let mut map: HashMap<_, Vec<&_>> = HashMap::with_capacity(exprs.len());

    for expr in exprs {
        match map.entry(hash(expr)) {
            Entry::Occupied(o) => {
                for o in o.get() {
                    if eq(o, expr) {
                        return Some((o, expr));
                    }
                }
            }
            Entry::Vacant(v) => {
                v.insert(vec![expr]);
            }
        }
    }

    None
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;
use std::fmt;

pub struct Cartridge(pub Vec<u8>);

impl Cartridge {

    /// Load rom file
    pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Cartridge> {
        let mut rom: Vec<u8> = Vec::new();
        let mut file = File::open(path)?;
        let _ = file.read_to_end(&mut rom)?;
        println!("ROM LOADED");
        Ok(Cartridge(rom))
    }

    /// Read a byte from the rom
    pub fn read_byte(&self, addr: usize) -> u8 {
        self.0[addr]
    }

    /// Read a range of bytes from the rom
    pub fn read_range(&self, addr: usize, end: usize) -> Vec<u8> {
        let mut a: Vec<u8> = Vec::new();
        for x in addr..end {
            a.push(self.0[x])
        }
        a
    }

    /// Read the rom's title
    pub fn read_title(&self) -> String {
        let mut title = String::with_capacity(16);
        for letter in self.read_range(0x0134, 0x0143) {
            if letter == 0 {
                break;
            }
            title.push(letter as char)
        }
        title
    }

}

impl fmt::Debug for Cartridge {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "=== ROM DEBUG ===\n\
            > title game {}\n\
            > cartridge type: {:#x}\n\
            > ROM size: {:#x}\n\
            > RAM size: {:#x}",
            self.read_title(),
            self.read_byte(0x0147),
            self.read_byte(0x148),
            self.read_byte(0x0149))
    }
}

#[test]
fn get_name() {
    let rom = Cartridge::new("./rom/super_mario.gb").unwrap();
    assert_eq!(rom.read_title(), "SUPER MARIOLAND");
}

<commit_msg>better type and change range to tuples<commit_after>use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::io;
use std::fmt;

const TITLE: (u16, u16) = (0x0134, 0x0143);
const CARTRIDGE_TYPE: u16 = 0x0147;
const ROM_SIZE: u16 = 0x0148;
const RAM_SIZE: u16 = 0x0149;

pub struct Cartridge(Vec<u8>);

impl Cartridge {

    /// Load rom file
    pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Cartridge> {
        let mut rom: Vec<u8> = Vec::new();
        let mut file = File::open(path)?;
        let _ = file.read_to_end(&mut rom)?;
        Ok(Cartridge(rom))
    }

    /// Read a byte from the rom
    pub fn read_byte(&self, addr: u16) -> u8 {
        self.0[addr as usize]
    }

    /// Read a range of bytes from the rom
    pub fn read_range(&self, addr: (u16, u16)) -> Vec<u8> {
        let mut a: Vec<u8> = Vec::new();
        for x in addr.0..addr.1 {
            a.push(self.0[x as usize])
        }
        a
    }

    /// Read the rom's title
    pub fn read_title(&self) -> String {
        let mut title = String::with_capacity(16);
        for letter in self.read_range(TITLE) {
            if letter == 0 {
                break;
            }
            title.push(letter as char)
        }
        title
    }

}

impl fmt::Debug for Cartridge {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "=== ROM DEBUG ===\n\
            > title game {}\n\
            > cartridge type: {:#x}\n\
            > ROM size: {:#x}\n\
            > RAM size: {:#x}",
            self.read_title(),
            self.read_byte(CARTRIDGE_TYPE),
            self.read_byte(ROM_SIZE),
            self.read_byte(RAM_SIZE))
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017-2018 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};

use futures::{Future, Poll, Sink, Stream};
use futures::future;
use futures::future::Executor;
use futures::sync::{mpsc, oneshot};

use error;
use resp;
use resp::FromResp;
use super::connect::{connect, ClientConnection};

fn err<S>(msg: S) -> Box<Future<Item = (), Error = error::Error> + Send>
where
    S: Into<String>,
{
    Box::new(future::err(error::internal(msg)))
}

/// Used for Redis's PUBSUB functionality.
///
/// Returns a future that resolves to a `PubsubConnection`.
pub fn pubsub_connect<E>(
    addr: &SocketAddr,
    executor: E,
) -> Box<Future<Item = PubsubConnection, Error = error::Error>>
where
    E: Executor<Box<Future<Item = (), Error = ()> + Send>> + 'static,
{
    let pubsub_con = connect(addr)
        .map_err(|e| e.into())
        .and_then(move |connection| {
            let ClientConnection { sender, receiver } = connection;
            let (out_tx, out_rx) = mpsc::unbounded();

            let sender = Box::new(
                sender
                    .sink_map_err(|e| error!("Sending socket error: {}", e))
                    .send_all(out_rx.map_err(|()| error!("Outgoing message channel error")))
                    .map(|_| debug!("Send all completed successfully")),
            ) as Box<Future<Item = (), Error = ()> + Send>;

            let subs = Arc::new(Mutex::new(PubsubSubscriptions {
                pending: HashMap::new(),
                confirmed: HashMap::new(),
            }));
            let subs_reader = subs.clone();

            let receiver_raw = receiver.for_each(move |msg| {
                let (message_type, topic, msg) = match msg {
                    resp::RespValue::Array(mut messages) => match (
                        messages.pop(),
                        messages.pop(),
                        messages.pop(),
                        messages.pop(),
                    ) {
                        (Some(msg), Some(topic), Some(message_type), None) => {
                            match (msg, String::from_resp(topic), message_type) {
                                (msg, Ok(topic), resp::RespValue::BulkString(bytes)) => {
                                    (bytes, topic, msg)
                                }
                                _ => return err("RESP Message structure invalid"),
                            }
                        }
                        _ => {
                            return err(format!(
                                "Incorrect number of records in PUBSUB message: {}",
                                messages.len()
                            ))
                        }
                    },
                    _ => return err("Incorrect message type"),
                };
                let mut subs = subs_reader.lock().expect("Lock is tainted");
                if message_type == b"subscribe" {
                    if let Some((tx, notification_tx)) = subs.pending.remove(&topic) {
                        subs.confirmed.insert(topic, tx);
                        match notification_tx.send(()) {
                            Ok(()) => {
                                let ok =
                                    future::ok(()).map_err(|_: ()| error::internal("unreachable"));
                                Box::new(ok)
                            }
                            Err(()) => err("Cannot send notification of subscription"),
                        }
                    } else {
                        err("Received subscribe notification that wasn't expected")
                    }
                } else if message_type == b"unsubscribe" {
                    if let Some(_) = subs.confirmed.remove(&topic) {
                        let result = if subs.confirmed.is_empty() {
                            future::err(error::Error::EndOfStream)
                        } else {
                            future::ok(())
                        };
                        Box::new(result)
                    } else {
                        err("Receieved unsubscription notification from a topic we're not subscribed to")
                    }
                } else if message_type == b"message" {
                    match subs.confirmed.get(&topic) {
                        Some(tx) => {
                            let future = tx.clone().send(msg).map(|_| ()).map_err(|e| e.into());
                            Box::new(future)
                        }
                        None => {
                            let ok = future::ok(()).map_err(|_: ()| error::internal("unreachable"));
                            Box::new(ok)
                        }
                    }
                } else {
                    return err(format!("Unexpected message type: {:?}", message_type));
                }
            });
            let receiver = Box::new(
                receiver_raw
                    .then(|result| match result {
                        Ok(_) => future::ok(()),
                        Err(error::Error::EndOfStream) => future::ok(()),
                        Err(e) => future::err(e),
                    })
                    .map(|_| debug!("End of PUBSUB connection, successful"))
                    .map_err(|e| error!("PUBSUB Receiver error: {}", e)),
            ) as Box<Future<Item = (), Error = ()> + Send>;

            match executor
                .execute(sender)
                .and_then(|_| executor.execute(receiver))
            {
                Ok(()) => future::ok(PubsubConnection {
                    out_tx: out_tx,
                    subscriptions: subs,
                }),
                Err(e) => future::err(error::internal(format!(
                    "Cannot execute background task: {:?}",
                    e
                ))),
            }
        });
    Box::new(pubsub_con)
}

struct PubsubSubscriptions {
    pending: HashMap<String, (mpsc::Sender<resp::RespValue>, oneshot::Sender<()>)>,
    confirmed: HashMap<String, mpsc::Sender<resp::RespValue>>,
}

#[derive(Clone)]
pub struct PubsubConnection {
    out_tx: mpsc::UnboundedSender<resp::RespValue>,
    subscriptions: Arc<Mutex<PubsubSubscriptions>>,
}

impl PubsubConnection {
    /// Subscribes to a particular PUBSUB topic.
    ///
    /// Returns a future that resolves to a `Stream` that contains all the messages published on
    /// that particular topic.
    pub fn subscribe<T: Into<String>>(
        &self,
        topic: T,
    ) -> Box<Future<Item = PubsubStream, Error = error::Error>> {
        let topic = topic.into();
        let mut subs = self.subscriptions.lock().expect("Lock is tainted");

        // TODO - check arbitrary buffer size
        let (tx, rx) = mpsc::channel(10);
        let stream = Box::new(rx) as Box<Stream<Item = resp::RespValue, Error = ()>>;
        if subs.confirmed.contains_key(&topic) || subs.pending.contains_key(&topic) {
            return Box::new(future::err(error::internal("Already subscribed")));
        }

        let (notification_tx, notification_rx) = oneshot::channel();
        let subscribe_msg = resp_array!["SUBSCRIBE", &topic];
        subs.pending.insert(topic.clone(), (tx, notification_tx));
        let new_out_tx = self.out_tx.clone();
        match self.out_tx.unbounded_send(subscribe_msg) {
            Ok(_) => {
                let done = notification_rx
                    .map(|_| PubsubStream::new(topic, stream, new_out_tx))
                    .map_err(|e| e.into());
                Box::new(done)
            }
            Err(e) => Box::new(future::err(e.into())),
        }
    }
}

pub struct PubsubStream {
    topic: String,
    underlying: Box<Stream<Item = resp::RespValue, Error = ()>>,
    out_tx: mpsc::UnboundedSender<resp::RespValue>,
}

impl PubsubStream {
    fn new<S>(topic: String, stream: S, out_tx: mpsc::UnboundedSender<resp::RespValue>) -> Self
    where
        S: Stream<Item = resp::RespValue, Error = ()> + 'static,
    {
        PubsubStream {
            topic: topic,
            underlying: Box::new(stream),
            out_tx: out_tx,
        }
    }
}

impl Stream for PubsubStream {
    type Item = resp::RespValue;
    type Error = ();

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        self.underlying.poll()
    }
}

impl Drop for PubsubStream {
    fn drop(&mut self) {
        let send_f = self.out_tx
            .clone()
            .send(resp_array!["UNSUBSCRIBE", &self.topic]);
        match send_f.wait() {
            Ok(_) => debug!("Send unsubscribe command for topic: {}", self.topic),
            Err(e) => error!(
                "Failed to send unsubscribe command for topic: {}, error: {}",
                self.topic, e
            ),
        }
    }
}
<commit_msg>Minor changes to make PUBSUB compatible with threads<commit_after>/*
 * Copyright 2017-2018 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};

use futures::{Future, Poll, Sink, Stream};
use futures::future;
use futures::future::Executor;
use futures::sync::{mpsc, oneshot};

use error;
use resp;
use resp::FromResp;
use super::connect::{connect, ClientConnection};

fn err<S>(msg: S) -> Box<Future<Item = (), Error = error::Error> + Send>
where
    S: Into<String>,
{
    Box::new(future::err(error::internal(msg)))
}

/// Used for Redis's PUBSUB functionality.
///
/// Returns a future that resolves to a `PubsubConnection`.
pub fn pubsub_connect<E>(
    addr: &SocketAddr,
    executor: E,
) -> Box<Future<Item = PubsubConnection, Error = error::Error>>
where
    E: Executor<Box<Future<Item = (), Error = ()> + Send>> + 'static,
{
    let pubsub_con = connect(addr)
        .map_err(|e| e.into())
        .and_then(move |connection| {
            let ClientConnection { sender, receiver } = connection;
            let (out_tx, out_rx) = mpsc::unbounded();

            let sender = Box::new(
                sender
                    .sink_map_err(|e| error!("Sending socket error: {}", e))
                    .send_all(out_rx.map_err(|()| error!("Outgoing message channel error")))
                    .map(|_| debug!("Send all completed successfully")),
            ) as Box<Future<Item = (), Error = ()> + Send>;

            let subs = Arc::new(Mutex::new(PubsubSubscriptions {
                pending: HashMap::new(),
                confirmed: HashMap::new(),
            }));
            let subs_reader = subs.clone();

            let receiver_raw = receiver.for_each(move |msg| {
                let (message_type, topic, msg) = match msg {
                    resp::RespValue::Array(mut messages) => match (
                        messages.pop(),
                        messages.pop(),
                        messages.pop(),
                        messages.pop(),
                    ) {
                        (Some(msg), Some(topic), Some(message_type), None) => {
                            match (msg, String::from_resp(topic), message_type) {
                                (msg, Ok(topic), resp::RespValue::BulkString(bytes)) => {
                                    (bytes, topic, msg)
                                }
                                _ => return err("RESP Message structure invalid"),
                            }
                        }
                        _ => {
                            return err(format!(
                                "Incorrect number of records in PUBSUB message: {}",
                                messages.len()
                            ))
                        }
                    },
                    _ => return err("Incorrect message type"),
                };
                let mut subs = subs_reader.lock().expect("Lock is tainted");
                if message_type == b"subscribe" {
                    if let Some((tx, notification_tx)) = subs.pending.remove(&topic) {
                        subs.confirmed.insert(topic, tx);
                        match notification_tx.send(()) {
                            Ok(()) => {
                                let ok =
                                    future::ok(()).map_err(|_: ()| error::internal("unreachable"));
                                Box::new(ok)
                            }
                            Err(()) => err("Cannot send notification of subscription"),
                        }
                    } else {
                        err("Received subscribe notification that wasn't expected")
                    }
                } else if message_type == b"unsubscribe" {
                    if let Some(_) = subs.confirmed.remove(&topic) {
                        let result = if subs.confirmed.is_empty() {
                            future::err(error::Error::EndOfStream)
                        } else {
                            future::ok(())
                        };
                        Box::new(result)
                    } else {
                        err("Receieved unsubscription notification from a topic we're not subscribed to")
                    }
                } else if message_type == b"message" {
                    match subs.confirmed.get(&topic) {
                        Some(tx) => {
                            let future = tx.clone().send(msg).map(|_| ()).map_err(|e| e.into());
                            Box::new(future)
                        }
                        None => {
                            let ok = future::ok(()).map_err(|_: ()| error::internal("unreachable"));
                            Box::new(ok)
                        }
                    }
                } else {
                    return err(format!("Unexpected message type: {:?}", message_type));
                }
            });
            let receiver = Box::new(
                receiver_raw
                    .then(|result| match result {
                        Ok(_) => future::ok(()),
                        Err(error::Error::EndOfStream) => future::ok(()),
                        Err(e) => future::err(e),
                    })
                    .map(|_| debug!("End of PUBSUB connection, successful"))
                    .map_err(|e| error!("PUBSUB Receiver error: {}", e)),
            ) as Box<Future<Item = (), Error = ()> + Send>;

            match executor
                .execute(sender)
                .and_then(|_| executor.execute(receiver))
            {
                Ok(()) => future::ok(PubsubConnection {
                    out_tx: out_tx,
                    subscriptions: subs,
                }),
                Err(e) => future::err(error::internal(format!(
                    "Cannot execute background task: {:?}",
                    e
                ))),
            }
        });
    Box::new(pubsub_con)
}

struct PubsubSubscriptions {
    pending: HashMap<String, (mpsc::Sender<resp::RespValue>, oneshot::Sender<()>)>,
    confirmed: HashMap<String, mpsc::Sender<resp::RespValue>>,
}

#[derive(Clone)]
pub struct PubsubConnection {
    out_tx: mpsc::UnboundedSender<resp::RespValue>,
    subscriptions: Arc<Mutex<PubsubSubscriptions>>,
}

impl PubsubConnection {
    /// Subscribes to a particular PUBSUB topic.
    ///
    /// Returns a future that resolves to a `Stream` that contains all the messages published on
    /// that particular topic.
    pub fn subscribe<T: Into<String>>(
        &self,
        topic: T,
    ) -> Box<Future<Item = PubsubStream, Error = error::Error> + Send> {
        let topic = topic.into();
        let mut subs = self.subscriptions.lock().expect("Lock is tainted");

        // TODO - check arbitrary buffer size
        let (tx, rx) = mpsc::channel(10);
        if subs.confirmed.contains_key(&topic) || subs.pending.contains_key(&topic) {
            return Box::new(future::err(error::internal("Already subscribed")));
        }

        let (notification_tx, notification_rx) = oneshot::channel();
        let subscribe_msg = resp_array!["SUBSCRIBE", &topic];
        subs.pending.insert(topic.clone(), (tx, notification_tx));
        let new_out_tx = self.out_tx.clone();
        match self.out_tx.unbounded_send(subscribe_msg) {
            Ok(_) => {
                let done = notification_rx
                    .map(|_| PubsubStream::new(topic, rx, new_out_tx))
                    .map_err(|e| e.into());
                Box::new(done)
            }
            Err(e) => Box::new(future::err(e.into())),
        }
    }
}

pub struct PubsubStream {
    topic: String,
    underlying: mpsc::Receiver<resp::RespValue>,
    out_tx: mpsc::UnboundedSender<resp::RespValue>,
}

impl PubsubStream {
    fn new(
        topic: String,
        stream: mpsc::Receiver<resp::RespValue>,
        out_tx: mpsc::UnboundedSender<resp::RespValue>,
    ) -> Self {
        PubsubStream {
            topic: topic,
            underlying: stream,
            out_tx: out_tx,
        }
    }
}

impl Stream for PubsubStream {
    type Item = resp::RespValue;
    type Error = ();

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        self.underlying.poll()
    }
}

impl Drop for PubsubStream {
    fn drop(&mut self) {
        let send_f = self.out_tx
            .clone()
            .send(resp_array!["UNSUBSCRIBE", &self.topic]);
        match send_f.wait() {
            Ok(_) => debug!("Send unsubscribe command for topic: {}", self.topic),
            Err(e) => error!(
                "Failed to send unsubscribe command for topic: {}, error: {}",
                self.topic, e
            ),
        }
    }
}
<|endoftext|>