<commit_before>use std::str::FromStr;
use std::ops::Index;
use std::fs::File;
use std::io::{Error, Write, ErrorKind};
use std::path::Path;
use std::fmt::{self, Display, Formatter};

use timestamp::Timestamp;
use subline::SubLine;
use utils;

#[derive(Debug, Clone, PartialEq)]
pub struct Subtitles {
    pub inner: Vec<SubLine>,
}

impl Subtitles {
    /// Returns the number of elements.
    pub fn len(&self) -> usize {
        self.inner.len()
    }

    /// Construct ```Subtitles``` from given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Subtitles, Error> {
        let mut content = try!(utils::read_file(&path));
        content = utils::prepare(&content);

        if !utils::check(&content) {
            return Err(Error::new(ErrorKind::InvalidData,
                                  "Given file does not match with srt format specification"));
        }
        Ok(try!(Subtitles::from_str(&content)))
    }

    /// Saves ```Subtitles``` into given file path according srt subtitles format.
    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        let mut file = try!(File::create(&path));
        for line in &self.inner {
            try!(write!(&mut file, "{}", line));
        }
        try!(write!(&mut file, "\r\n\r\n"));
        Ok(())
    }

    /// Get ```&SubLine``` by it's index.
    ///
    /// # Panics
    /// Panics if inner structure is broken.
    /// E.g. inner vector is not sorted or SubLine's indices is not consistent.
    pub fn by_index(&self, index: usize) -> Option<&SubLine> {
        let indexed_subline = self.inner.get(index - 1);

        indexed_subline.as_ref().map(|elem| {
            if elem.index != index as u32 {
                panic!("Subtitles's inner structure is broken, inner vector of Subtitles must always be \
            sorted and also unterlying SubLine's indices must be correct");
            }
        });

        indexed_subline
    }

    /// Get ```&mut SubLine``` by it's index.
    ///
    /// # Panics
    /// Panics if inner structure is broken.
    /// E.g. inner vector is not sorted or SubLine's indices is not consistent.
    pub fn by_index_mut(&mut self, index: usize) -> Option<&mut SubLine> {
        let indexed_subline = self.inner.get_mut(index - 1);

        indexed_subline.as_ref().map(|elem| {
            if elem.index != index as u32 {
                panic!("Subtitles's inner structure is broken, inner vector of Subtitles must always be \
            sorted and also unterlying SubLine's indices must be correct");
            }
        });
        indexed_subline
    }

    /// Get ```&SubLine``` for which given ```time```
    /// lies in the range ```start...end``` (inclusive).
    pub fn by_time(&self, time: Timestamp) -> Option<&SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;

        while max >= min {
            guess_index = (max + min) / 2;
            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                return Some(&guess);
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        None
    }

    /// Get ```&mut SubLine``` for which given ```time```
    /// lies in the range ```start...end``` (inclusive).
    pub fn by_time_mut(&mut self, time: Timestamp) -> Option<&mut SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;
        let mut result: usize = 0;
        let mut found = false;

        while max >= min {
            guess_index = (max + min) / 2;

            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                result = guess_index;
                found = true;
                break;
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        if found {
            self.inner.get_mut(result)
        } else {
            None
        }
    }

    /// Get ```&SubLine``` for which given ```time```
    /// lies in the range ```start..start_of_the_next_line``` (exclusive).
    pub fn nearest_by_time(&self, time: Timestamp) -> Option<&SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;

        while max >= min {
            guess_index = (max + min) / 2;
            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                return Some(&guess);
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        self.inner.get(max)
    }

    /// Get ```&mut SubLine``` for which given ```time```
    /// lies in the range ```start..start_of_the_next_line``` (exclusive).
    pub fn nearest_by_time_mut(&mut self, time: Timestamp) -> Option<&mut SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;
        let mut result: usize = 0;
        let mut found_exact = false;

        while max >= min {
            guess_index = (max + min) / 2;

            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                result = guess_index;
                found_exact = true;
                break;
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        if found_exact {
            self.inner.get_mut(result)
        } else {
            self.inner.get_mut(max)
        }
    }

    /// Pushes given line in the end.
    ///
    /// # Panics
    /// Panics if given ```line```'s index is not ```latest_sub_index + 1```.
    /// E.g. pushed line is going to break consistency.
    pub fn push(&mut self, line: SubLine) {
        let last_index: u32 = self.inner.last().map(|line| line.index).unwrap_or(0);
        if line.index != last_index + 1 {
            panic!("Pushed line is going to break consistency, invalid index");
        }
        self.inner.push(line);
    }

    /// Inserts given ```element``` into ```Subtitles```,
    /// shifting all its right element's indices.
    ///
    /// # Panics
    /// Panics if ```element``'s index is greater than the ```Subtitles``` length.
    pub fn insert(&mut self, element: SubLine) {
        let index = (element.index - 1) as usize;
        for line in &mut self.inner[index..] {
            line.index += 1;
        }

        self.inner.insert(index, element);
    }

    /// Removes the last element from a Subtitles and returns it, or None if it is empty.
    pub fn pop(&mut self) -> Option<SubLine> {
        self.inner.pop()
    }
}

impl FromStr for Subtitles {
    type Err = Error;
    /// Construct Subtitles from str.
    ///
    /// Given str must be properly formated:
    /// Newlne styles must be windows like (\r\n).
    /// And in the end of str must be exacly 4 newlines.
    fn from_str(content: &str) -> Result<Subtitles, Error> {
        let mut result = Vec::with_capacity(400);

        for cap in utils::SUBS.captures_iter(&content) {

            let index: u32 = cap.at(1).unwrap().parse().unwrap();

            let start_timestamp: [u32; 4] = [cap.at(2).unwrap().parse().unwrap(),
                                             cap.at(3).unwrap().parse().unwrap(),
                                             cap.at(4).unwrap().parse().unwrap(),
                                             cap.at(5).unwrap().parse().unwrap()];
            let end_timestamp: [u32; 4] = [cap.at(6).unwrap().parse().unwrap(),
                                           cap.at(7).unwrap().parse().unwrap(),
                                           cap.at(8).unwrap().parse().unwrap(),
                                           cap.at(9).unwrap().parse().unwrap()];

            let start = Timestamp::from(&start_timestamp);
            let end = Timestamp::from(&end_timestamp);

            let text = cap.at(10).unwrap().to_owned();

            let line = SubLine {
                index: index,
                text: text,
                start: start,
                end: end,
            };
            result.push(line);
        }
        result.shrink_to_fit();
        Ok(Subtitles::from(result))
    }
}

impl From<Vec<SubLine>> for Subtitles {
    fn from(vec: Vec<SubLine>) -> Subtitles {
        Subtitles { inner: vec }
    }
}

impl<'a> IntoIterator for &'a Subtitles {
    type Item = &'a SubLine;
    type IntoIter = ::std::slice::Iter<'a, SubLine>;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.iter()
    }
}

impl<'a> IntoIterator for &'a mut Subtitles {
    type Item = &'a mut SubLine;
    type IntoIter = ::std::slice::IterMut<'a, SubLine>;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.iter_mut()
    }
}

impl IntoIterator for Subtitles {
    type Item = SubLine;
    type IntoIter = ::std::vec::IntoIter<SubLine>;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.into_iter()
    }
}

impl Display for Subtitles {
    /// Formats Subtitles according srt subtitles format.
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        for line in self {
            try!(write!(f, "{}", line));
        }
        write!(f, "\r\n\r\n")
    }
}

impl Index<usize> for Subtitles {
    type Output = SubLine;
    /// Indexes inner vector. That means that indexing start at 0.
    fn index<'a>(&'a self, index: usize) -> &'a Self::Output {
        &self.inner[index]
    }
}

#[cfg(test)]
mod subtitles_tests {
    use super::*;
    use subline::SubLine;
    use timestamp::Timestamp;
    use utils;
    use std::str::FromStr;

    static PATH: &'static str = "example.srt";

    lazy_static! {
        static ref SUBS: Subtitles = Subtitles::from_file(PATH).unwrap();
    }

    #[test]
    fn from_file() {
        let subs = Subtitles::from_file(PATH).unwrap();

        assert_eq!(subs.len(), 619);
        let latest_sub = SubLine {
            text: "Last".to_owned(),
            index: 619,
            start: Timestamp {
                hours: 1,
                minutes: 6,
                seconds: 40,
                miliseconds: 216,
            },
            end: Timestamp {
                hours: 1,
                minutes: 6,
                seconds: 50,
                miliseconds: 792,
            },
        };
        assert_eq!(&latest_sub, subs.by_index(619).unwrap());
    }

    #[test]
    fn from_str() {
        let mut sub_str = utils::read_file(PATH).unwrap();
        sub_str = utils::prepare(&sub_str);

        let subs = Subtitles::from_str(&sub_str).unwrap();
        let same_subs = Subtitles::from_file(PATH).unwrap();

        assert_eq!(subs, same_subs);
    }

    #[test]
    fn to_string() {
        let mut sub_str = utils::read_file(PATH).unwrap();
        sub_str = utils::prepare(&sub_str);

        let subs = Subtitles::from_str(&sub_str).unwrap();
        let same_subs = Subtitles::from_file(PATH).unwrap();

        sub_str += &"\r\n\r\n";
        assert_eq!(sub_str, subs.to_string());
        assert_eq!(sub_str, same_subs.to_string());
    }

    #[test]
    fn iterator() {
        let mut subs = SUBS.clone();
        let primal_subs = subs.clone();

        for line in &subs {
            let _: &SubLine = line;
        }
        let _: &SubLine = subs.by_index(1).unwrap();

        for line in &mut subs {
            let _: &mut SubLine = line;
            line.text += " lol";
            let offset = Timestamp::new(1, 1, 30, 500);
            line.end += offset;
            line.start += offset;
        }
        println!("{:?}", subs.by_index(1).unwrap().start);
        assert!(subs.by_index(3).unwrap().text.ends_with(" lol"));
        assert!(subs.by_index(10).unwrap().text.ends_with(" lol"));

        let mut new_subs_vec = Vec::with_capacity(subs.len());

        for mut line in subs {
            line.text = line.text.trim_right_matches(" lol").to_owned();
            let offset = Timestamp::new(1, 1, 30, 500);
            line.end -= offset;
            line.start -= offset;
            new_subs_vec.push(line);
        }
        let new_subs = Subtitles::from(new_subs_vec);

        assert_eq!(new_subs, primal_subs);
    }

    #[test]
    fn insert() {
        let mut subs = SUBS.clone();

        let sub14 = subs.by_index(14).unwrap().clone();
        let mut sub15 = subs.by_index(15).unwrap().clone();

        let one_ms = Timestamp::new(0, 0, 0, 1);
        let start = sub14.end + one_ms;
        let end = sub15.start - one_ms;

        let line = SubLine::new(15, "foo bar foo".to_owned(), start, end);
        subs.insert(line.clone());

        let newline = subs.by_index(15).unwrap();
        assert_eq!(newline, &line);

        // sub15's index shifted becomes 16
        sub15.index += 1;
        let new_sub16 = subs.by_index(16).unwrap();
        assert_eq!(new_sub16, &sub15);
    }
}
<commit_msg>Update subtitles.rs<commit_after>use std::str::FromStr;
use std::ops::Index;
use std::fs::File;
use std::io::{Error, Write, ErrorKind};
use std::path::Path;
use std::fmt::{self, Display, Formatter};

use timestamp::Timestamp;
use subline::SubLine;
use utils;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Subtitles {
    pub inner: Vec<SubLine>,
}

impl Subtitles {
    /// Returns the number of elements.
    pub fn len(&self) -> usize {
        self.inner.len()
    }

    /// Construct ```Subtitles``` from given file path.
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Subtitles, Error> {
        let mut content = try!(utils::read_file(&path));
        content = utils::prepare(&content);

        if !utils::check(&content) {
            return Err(Error::new(ErrorKind::InvalidData,
                                  "Given file does not match with srt format specification"));
        }
        Ok(try!(Subtitles::from_str(&content)))
    }

    /// Saves ```Subtitles``` into given file path according srt subtitles format.
    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
        let mut file = try!(File::create(&path));
        for line in &self.inner {
            try!(write!(&mut file, "{}", line));
        }
        try!(write!(&mut file, "\r\n\r\n"));
        Ok(())
    }

    /// Get ```&SubLine``` by it's index.
    ///
    /// # Panics
    /// Panics if inner structure is broken.
    /// E.g. inner vector is not sorted or SubLine's indices is not consistent.
    pub fn by_index(&self, index: usize) -> Option<&SubLine> {
        let indexed_subline = self.inner.get(index - 1);

        indexed_subline.as_ref().map(|elem| {
            if elem.index != index as u32 {
                panic!("Subtitles's inner structure is broken, inner vector of Subtitles must always be \
            sorted and also unterlying SubLine's indices must be correct");
            }
        });

        indexed_subline
    }

    /// Get ```&mut SubLine``` by it's index.
    ///
    /// # Panics
    /// Panics if inner structure is broken.
    /// E.g. inner vector is not sorted or SubLine's indices is not consistent.
    pub fn by_index_mut(&mut self, index: usize) -> Option<&mut SubLine> {
        let indexed_subline = self.inner.get_mut(index - 1);

        indexed_subline.as_ref().map(|elem| {
            if elem.index != index as u32 {
                panic!("Subtitles's inner structure is broken, inner vector of Subtitles must always be \
            sorted and also unterlying SubLine's indices must be correct");
            }
        });
        indexed_subline
    }

    /// Get ```&SubLine``` for which given ```time```
    /// lies in the range ```start...end``` (inclusive).
    pub fn by_time(&self, time: Timestamp) -> Option<&SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;

        while max >= min {
            guess_index = (max + min) / 2;
            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                return Some(&guess);
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        None
    }

    /// Get ```&mut SubLine``` for which given ```time```
    /// lies in the range ```start...end``` (inclusive).
    pub fn by_time_mut(&mut self, time: Timestamp) -> Option<&mut SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;
        let mut result: usize = 0;
        let mut found = false;

        while max >= min {
            guess_index = (max + min) / 2;

            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                result = guess_index;
                found = true;
                break;
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        if found {
            self.inner.get_mut(result)
        } else {
            None
        }
    }

    /// Get ```&SubLine``` for which given ```time```
    /// lies in the range ```start..start_of_the_next_line``` (exclusive).
    pub fn nearest_by_time(&self, time: Timestamp) -> Option<&SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;

        while max >= min {
            guess_index = (max + min) / 2;
            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                return Some(&guess);
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        self.inner.get(max)
    }

    /// Get ```&mut SubLine``` for which given ```time```
    /// lies in the range ```start..start_of_the_next_line``` (exclusive).
    pub fn nearest_by_time_mut(&mut self, time: Timestamp) -> Option<&mut SubLine> {
        let mut min = 0;
        let mut max = self.inner.len() - 1;
        let mut guess_index;
        let mut result: usize = 0;
        let mut found_exact = false;

        while max >= min {
            guess_index = (max + min) / 2;

            let guess = self.inner.get(guess_index).unwrap();

            if (guess.start <= time) && (time <= guess.end) {
                result = guess_index;
                found_exact = true;
                break;
            } else if time > guess.end {
                min = guess_index + 1;
            } else {
                max = guess_index - 1;
            }
        }
        if found_exact {
            self.inner.get_mut(result)
        } else {
            self.inner.get_mut(max)
        }
    }

    /// Pushes given line in the end.
    ///
    /// # Panics
    /// Panics if given ```line```'s index is not ```latest_sub_index + 1```.
    /// E.g. pushed line is going to break consistency.
    pub fn push(&mut self, line: SubLine) {
        let last_index: u32 = self.inner.last().map(|line| line.index).unwrap_or(0);
        if line.index != last_index + 1 {
            panic!("Pushed line is going to break consistency, invalid index");
        }
        self.inner.push(line);
    }

    /// Inserts given ```element``` into ```Subtitles```,
    /// shifting all its right element's indices.
    ///
    /// # Panics
    /// Panics if ```element``'s index is greater than the ```Subtitles``` length.
    pub fn insert(&mut self, element: SubLine) {
        let index = (element.index - 1) as usize;
        for line in &mut self.inner[index..] {
            line.index += 1;
        }

        self.inner.insert(index, element);
    }

    /// Removes the last element from a Subtitles and returns it, or None if it is empty.
    pub fn pop(&mut self) -> Option<SubLine> {
        self.inner.pop()
    }
}

impl FromStr for Subtitles {
    type Err = Error;
    /// Construct Subtitles from str.
    ///
    /// Given str must be properly formated:
    /// Newlne styles must be windows like (\r\n).
    /// And in the end of str must be exacly 4 newlines.
    fn from_str(content: &str) -> Result<Subtitles, Error> {
        let mut result = Vec::with_capacity(400);

        for cap in utils::SUBS.captures_iter(&content) {

            let index: u32 = cap.at(1).unwrap().parse().unwrap();

            let start_timestamp: [u32; 4] = [cap.at(2).unwrap().parse().unwrap(),
                                             cap.at(3).unwrap().parse().unwrap(),
                                             cap.at(4).unwrap().parse().unwrap(),
                                             cap.at(5).unwrap().parse().unwrap()];
            let end_timestamp: [u32; 4] = [cap.at(6).unwrap().parse().unwrap(),
                                           cap.at(7).unwrap().parse().unwrap(),
                                           cap.at(8).unwrap().parse().unwrap(),
                                           cap.at(9).unwrap().parse().unwrap()];

            let start = Timestamp::from(&start_timestamp);
            let end = Timestamp::from(&end_timestamp);

            let text = cap.at(10).unwrap().to_owned();

            let line = SubLine {
                index: index,
                text: text,
                start: start,
                end: end,
            };
            result.push(line);
        }
        result.shrink_to_fit();
        Ok(Subtitles::from(result))
    }
}

impl From<Vec<SubLine>> for Subtitles {
    fn from(vec: Vec<SubLine>) -> Subtitles {
        Subtitles { inner: vec }
    }
}

impl<'a> IntoIterator for &'a Subtitles {
    type Item = &'a SubLine;
    type IntoIter = ::std::slice::Iter<'a, SubLine>;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.iter()
    }
}

impl<'a> IntoIterator for &'a mut Subtitles {
    type Item = &'a mut SubLine;
    type IntoIter = ::std::slice::IterMut<'a, SubLine>;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.iter_mut()
    }
}

impl IntoIterator for Subtitles {
    type Item = SubLine;
    type IntoIter = ::std::vec::IntoIter<SubLine>;

    fn into_iter(self) -> Self::IntoIter {
        self.inner.into_iter()
    }
}

impl Display for Subtitles {
    /// Formats Subtitles according srt subtitles format.
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        for line in self {
            try!(write!(f, "{}", line));
        }
        write!(f, "\r\n\r\n")
    }
}

impl Index<usize> for Subtitles {
    type Output = SubLine;
    /// Indexes inner vector. That means that indexing start at 0.
    fn index<'a>(&'a self, index: usize) -> &'a Self::Output {
        &self.inner[index]
    }
}

#[cfg(test)]
mod subtitles_tests {
    use super::*;
    use subline::SubLine;
    use timestamp::Timestamp;
    use utils;
    use std::str::FromStr;

    static PATH: &'static str = "example.srt";

    lazy_static! {
        static ref SUBS: Subtitles = Subtitles::from_file(PATH).unwrap();
    }

    #[test]
    fn from_file() {
        let subs = Subtitles::from_file(PATH).unwrap();

        assert_eq!(subs.len(), 619);
        let latest_sub = SubLine {
            text: "Last".to_owned(),
            index: 619,
            start: Timestamp {
                hours: 1,
                minutes: 6,
                seconds: 40,
                miliseconds: 216,
            },
            end: Timestamp {
                hours: 1,
                minutes: 6,
                seconds: 50,
                miliseconds: 792,
            },
        };
        assert_eq!(&latest_sub, subs.by_index(619).unwrap());
    }

    #[test]
    fn from_str() {
        let mut sub_str = utils::read_file(PATH).unwrap();
        sub_str = utils::prepare(&sub_str);

        let subs = Subtitles::from_str(&sub_str).unwrap();
        let same_subs = Subtitles::from_file(PATH).unwrap();

        assert_eq!(subs, same_subs);
    }

    #[test]
    fn to_string() {
        let mut sub_str = utils::read_file(PATH).unwrap();
        sub_str = utils::prepare(&sub_str);

        let subs = Subtitles::from_str(&sub_str).unwrap();
        let same_subs = Subtitles::from_file(PATH).unwrap();

        sub_str += &"\r\n\r\n";
        assert_eq!(sub_str, subs.to_string());
        assert_eq!(sub_str, same_subs.to_string());
    }

    #[test]
    fn iterator() {
        let mut subs = SUBS.clone();
        let primal_subs = subs.clone();

        for line in &subs {
            let _: &SubLine = line;
        }
        let _: &SubLine = subs.by_index(1).unwrap();

        for line in &mut subs {
            let _: &mut SubLine = line;
            line.text += " lol";
            let offset = Timestamp::new(1, 1, 30, 500);
            line.end += offset;
            line.start += offset;
        }
        println!("{:?}", subs.by_index(1).unwrap().start);
        assert!(subs.by_index(3).unwrap().text.ends_with(" lol"));
        assert!(subs.by_index(10).unwrap().text.ends_with(" lol"));

        let mut new_subs_vec = Vec::with_capacity(subs.len());

        for mut line in subs {
            line.text = line.text.trim_right_matches(" lol").to_owned();
            let offset = Timestamp::new(1, 1, 30, 500);
            line.end -= offset;
            line.start -= offset;
            new_subs_vec.push(line);
        }
        let new_subs = Subtitles::from(new_subs_vec);

        assert_eq!(new_subs, primal_subs);
    }

    #[test]
    fn insert() {
        let mut subs = SUBS.clone();

        let sub14 = subs.by_index(14).unwrap().clone();
        let mut sub15 = subs.by_index(15).unwrap().clone();

        let one_ms = Timestamp::new(0, 0, 0, 1);
        let start = sub14.end + one_ms;
        let end = sub15.start - one_ms;

        let line = SubLine::new(15, "foo bar foo".to_owned(), start, end);
        subs.insert(line.clone());

        let newline = subs.by_index(15).unwrap();
        assert_eq!(newline, &line);

        // sub15's index shifted becomes 16
        sub15.index += 1;
        let new_sub16 = subs.by_index(16).unwrap();
        assert_eq!(new_sub16, &sub15);
    }
}
<|endoftext|>


<commit_before>use {Error, Result, from_ffi};
use errno::Errno;
use libc::c_int;
use std::os::unix::io::RawFd;

mod ffi {
    use libc::{c_int};
    use super::EpollEvent;

    extern {
        pub fn epoll_create(size: c_int) -> c_int;
        pub fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *const EpollEvent) -> c_int;
        pub fn epoll_wait(epfd: c_int, events: *mut EpollEvent, max_events: c_int, timeout: c_int) -> c_int;
    }
}

bitflags!(
    #[repr(C)]
    flags EpollEventKind: u32 {
        const EPOLLIN = 0x001,
        const EPOLLPRI = 0x002,
        const EPOLLOUT = 0x004,
        const EPOLLRDNORM = 0x040,
        const EPOLLRDBAND = 0x080,
        const EPOLLWRNORM = 0x100,
        const EPOLLWRBAND = 0x200,
        const EPOLLMSG = 0x400,
        const EPOLLERR = 0x008,
        const EPOLLHUP = 0x010,
        const EPOLLRDHUP = 0x2000,
        const EPOLLWAKEUP = 1 << 29,
        const EPOLLONESHOT = 1 << 30,
        const EPOLLET = 1 << 31
    }
);

#[derive(Clone, Copy)]
#[repr(C)]
pub enum EpollOp {
    EpollCtlAdd = 1,
    EpollCtlDel = 2,
    EpollCtlMod = 3
}

#[cfg(all(target_os = "android", not(target_arch = "x86_64")))]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct EpollEvent {
    pub events: EpollEventKind,
    pub data: u64
}

#[cfg(all(target_os = "android", not(target_arch = "x86_64")))]
#[test]
fn test_epoll_event_size() {
    use std::mem::size_of;
    assert_eq!(size_of::<EpollEvent>(), 16);
}

#[cfg(any(not(target_os = "android"), target_arch = "x86_64"))]
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct EpollEvent {
    pub events: EpollEventKind,
    pub data: u64
}

#[inline]
pub fn epoll_create() -> Result<RawFd> {
    let res = unsafe { ffi::epoll_create(1024) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res)
}

#[inline]
pub fn epoll_ctl(epfd: RawFd, op: EpollOp, fd: RawFd, event: &EpollEvent) -> Result<()> {
    let res = unsafe { ffi::epoll_ctl(epfd, op as c_int, fd, event as *const EpollEvent) };
    from_ffi(res)
}

#[inline]
pub fn epoll_wait(epfd: RawFd, events: &mut [EpollEvent], timeout_ms: isize) -> Result<usize> {
    let res = unsafe {
        ffi::epoll_wait(epfd, events.as_mut_ptr(), events.len() as c_int, timeout_ms as c_int)
    };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res as usize)
}
<commit_msg>Fix the struct packing of EpollEvent<commit_after>use {Error, Result, from_ffi};
use errno::Errno;
use libc::c_int;
use std::os::unix::io::RawFd;

mod ffi {
    use libc::{c_int};
    use super::EpollEvent;

    extern {
        pub fn epoll_create(size: c_int) -> c_int;
        pub fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *const EpollEvent) -> c_int;
        pub fn epoll_wait(epfd: c_int, events: *mut EpollEvent, max_events: c_int, timeout: c_int) -> c_int;
    }
}

bitflags!(
    #[repr(C)]
    flags EpollEventKind: u32 {
        const EPOLLIN = 0x001,
        const EPOLLPRI = 0x002,
        const EPOLLOUT = 0x004,
        const EPOLLRDNORM = 0x040,
        const EPOLLRDBAND = 0x080,
        const EPOLLWRNORM = 0x100,
        const EPOLLWRBAND = 0x200,
        const EPOLLMSG = 0x400,
        const EPOLLERR = 0x008,
        const EPOLLHUP = 0x010,
        const EPOLLRDHUP = 0x2000,
        const EPOLLWAKEUP = 1 << 29,
        const EPOLLONESHOT = 1 << 30,
        const EPOLLET = 1 << 31
    }
);

#[derive(Clone, Copy)]
#[repr(C)]
pub enum EpollOp {
    EpollCtlAdd = 1,
    EpollCtlDel = 2,
    EpollCtlMod = 3
}

#[cfg(not(target_arch = "x86_64"))]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct EpollEvent {
    pub events: EpollEventKind,
    pub data: u64
}

#[cfg(target_arch = "x86_64")]
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct EpollEvent {
    pub events: EpollEventKind,
    pub data: u64
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[test]
fn test_epoll_event_size() {
    use std::mem::size_of;
    assert_eq!(size_of::<EpollEvent>(), 12);
}

#[cfg(target_arch = "arm")]
#[test]
fn test_epoll_event_size() {
    use std::mem::size_of;
    assert_eq!(size_of::<EpollEvent>(), 16);
}

#[inline]
pub fn epoll_create() -> Result<RawFd> {
    let res = unsafe { ffi::epoll_create(1024) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res)
}

#[inline]
pub fn epoll_ctl(epfd: RawFd, op: EpollOp, fd: RawFd, event: &EpollEvent) -> Result<()> {
    let res = unsafe { ffi::epoll_ctl(epfd, op as c_int, fd, event as *const EpollEvent) };
    from_ffi(res)
}

#[inline]
pub fn epoll_wait(epfd: RawFd, events: &mut [EpollEvent], timeout_ms: isize) -> Result<usize> {
    let res = unsafe {
        ffi::epoll_wait(epfd, events.as_mut_ptr(), events.len() as c_int, timeout_ms as c_int)
    };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res as usize)
}
<|endoftext|>


<commit_before>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

enum FilterMode {
    Bytes(usize),
    Lines(usize),
}

struct Settings {
    mode: FilterMode,
    sleep_msec: u32,
    beginning: bool,
    follow: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: FilterMode::Lines(10),
            sleep_msec: 1000,
            beginning: false,
            follow: false,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { settings.mode = FilterMode::Lines(n); args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    settings.follow = given_options.opt_present("f");
    if settings.follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { settings.sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                settings.beginning = true;
                slice = &slice[1..];
            }
            match parse_size(slice) {
                Some(m) => settings.mode = FilterMode::Lines(m),
                None => {
                    show_error!("invalid number of lines ({})", slice);
                    return 1;
                }
            }
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    settings.beginning = true;
                    slice = &slice[1..];
                }
                match parse_size(slice) {
                    Some(m) => settings.mode = FilterMode::Bytes(m),
                    None => {
                        show_error!("invalid number of bytes ({})", slice);
                        return 1;
                    }
                }
            }
            None => { }
        }
    };

    let files = given_options.free;

    if files.is_empty() {
        let mut buffer = BufReader::new(stdin());
        tail(&mut buffer, &settings);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in &files {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            let mut buffer = BufReader::new(reader);
            tail(&mut buffer, &settings);
        }
    }

    0
}

fn parse_size(mut size_slice: &str) -> Option<usize> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000usize
        } else {
            1024usize
        };
    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' => 1usize,
                'M' => 2usize,
                'G' => 3usize,
                'T' => 4usize,
                'P' => 5usize,
                'E' => 6usize,
                'Z' => 7usize,
                'Y' => 8usize,
                'b' => {
                    base = 512usize;
                    1usize
                }
                _ => {
                    has_suffix = false;
                    0usize
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0usize
        };

    let mut multiplier = 1usize;
    for _ in 0usize .. exponent {
        multiplier *= base;
    }
    if base == 1000usize && exponent == 0usize {
        // sole B is not a valid suffix
        None
    } else {
        let value: Option<usize> = size_slice.parse().ok();
        match value {
            Some(v) => Some(multiplier * v),
            _ => None
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<usize> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

fn tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
    // Read through each line/char and store them in a ringbuffer that always
    // contains count lines/chars. When reaching the end of file, output the
    // data in the ringbuf.
    match settings.mode {
        FilterMode::Lines(mut count) => {
            let mut ringbuf: VecDeque<String> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = String::new();
                match reader.read_line(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_string(&mut stdout, datum);
            }
        },
        FilterMode::Bytes(mut count) => {
            let mut ringbuf: VecDeque<u8> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = [0; 1];
                match reader.read(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum[0]);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_byte(&mut stdout, datum);
            }
        }
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while settings.follow {
        sleep(Duration::new(0, settings.sleep_msec*1000));
        loop {
            let mut datum = String::new();
            match reader.read_line(&mut datum) {
                Ok(0) => break,
                Ok(_) => print!("{}", datum),
                Err(err) => panic!(err)
            }
        }
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &str) {
    print!("{}", s);
}

fn version() {
    println!("{} {}", NAME, VERSION);
}
<commit_msg>tail: Take ownership of the provided BufReader<T><commit_after>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

enum FilterMode {
    Bytes(usize),
    Lines(usize),
}

struct Settings {
    mode: FilterMode,
    sleep_msec: u32,
    beginning: bool,
    follow: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: FilterMode::Lines(10),
            sleep_msec: 1000,
            beginning: false,
            follow: false,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { settings.mode = FilterMode::Lines(n); args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    settings.follow = given_options.opt_present("f");
    if settings.follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { settings.sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                settings.beginning = true;
                slice = &slice[1..];
            }
            match parse_size(slice) {
                Some(m) => settings.mode = FilterMode::Lines(m),
                None => {
                    show_error!("invalid number of lines ({})", slice);
                    return 1;
                }
            }
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    settings.beginning = true;
                    slice = &slice[1..];
                }
                match parse_size(slice) {
                    Some(m) => settings.mode = FilterMode::Bytes(m),
                    None => {
                        show_error!("invalid number of bytes ({})", slice);
                        return 1;
                    }
                }
            }
            None => { }
        }
    };

    let files = given_options.free;

    if files.is_empty() {
        let buffer = BufReader::new(stdin());
        tail(buffer, &settings);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in &files {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            let buffer = BufReader::new(reader);
            tail(buffer, &settings);
        }
    }

    0
}

fn parse_size(mut size_slice: &str) -> Option<usize> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000usize
        } else {
            1024usize
        };
    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' => 1usize,
                'M' => 2usize,
                'G' => 3usize,
                'T' => 4usize,
                'P' => 5usize,
                'E' => 6usize,
                'Z' => 7usize,
                'Y' => 8usize,
                'b' => {
                    base = 512usize;
                    1usize
                }
                _ => {
                    has_suffix = false;
                    0usize
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0usize
        };

    let mut multiplier = 1usize;
    for _ in 0usize .. exponent {
        multiplier *= base;
    }
    if base == 1000usize && exponent == 0usize {
        // sole B is not a valid suffix
        None
    } else {
        let value: Option<usize> = size_slice.parse().ok();
        match value {
            Some(v) => Some(multiplier * v),
            _ => None
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<usize> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

fn tail<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
    // Read through each line/char and store them in a ringbuffer that always
    // contains count lines/chars. When reaching the end of file, output the
    // data in the ringbuf.
    match settings.mode {
        FilterMode::Lines(mut count) => {
            let mut ringbuf: VecDeque<String> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = String::new();
                match reader.read_line(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_string(&mut stdout, datum);
            }
        },
        FilterMode::Bytes(mut count) => {
            let mut ringbuf: VecDeque<u8> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = [0; 1];
                match reader.read(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum[0]);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in &ringbuf {
                print_byte(&mut stdout, datum);
            }
        }
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while settings.follow {
        sleep(Duration::new(0, settings.sleep_msec*1000));
        loop {
            let mut datum = String::new();
            match reader.read_line(&mut datum) {
                Ok(0) => break,
                Ok(_) => print!("{}", datum),
                Err(err) => panic!(err)
            }
        }
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &str) {
    print!("{}", s);
}

fn version() {
    println!("{} {}", NAME, VERSION);
}
<|endoftext|>


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

use byteorder::{LittleEndian, ReadBytesExt};
use euclid::Matrix4D;
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, RendererFrame};
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcReceiver};
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, RenderNotifier};
use webrender_traits::{PipelineId, WebGLContextId, ScrollLayerId};
use batch::new_id;
use device::TextureId;
use offscreen_gl_context::{NativeGLContext, GLContext, ColorAttachmentType, NativeGLContextMethods, NativeGLContextHandle};

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    payload_rx: IpcBytesReceiver,
    payload_tx: IpcBytesSender,
    result_tx: Sender<ResultMsg>,

    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
    current_bound_webgl_context_id: Option<WebGLContextId>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               payload_rx: IpcBytesReceiver,
               payload_tx: IpcBytesSender,
               result_tx: Sender<ResultMsg>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                dummy_mask_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            payload_rx: payload_rx,
            payload_tx: payload_tx,
            result_tx: result_tx,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
            current_bound_webgl_context_id: None,
        }
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            let msg = self.api_rx.recv();
            match msg {
                Ok(msg) => {
                    match msg {
                        ApiMsg::AddRawFont(id, bytes) => {
                            profile_counters.font_templates.inc(bytes.len());
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                        }
                        ApiMsg::AddNativeFont(id, native_font_handle) => {
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Native(native_font_handle));
                        }
                        ApiMsg::AddImage(id, width, height, format, bytes) => {
                            profile_counters.image_templates.inc(bytes.len());
                            self.resource_cache.add_image_template(id,
                                                                   width,
                                                                   height,
                                                                   format,
                                                                   bytes);
                        }
                        ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                            self.resource_cache.update_image_template(id,
                                                                      width,
                                                                      height,
                                                                      format,
                                                                      bytes);
                        }
                        ApiMsg::DeleteImage(id) => {
                            self.resource_cache.delete_image_template(id);
                        }
                        ApiMsg::CloneApi(sender) => {
                            let result = self.next_namespace_id;

                            let IdNamespace(id_namespace) = self.next_namespace_id;
                            self.next_namespace_id = IdNamespace(id_namespace + 1);

                            sender.send(result).unwrap();
                        }
                        ApiMsg::SetRootStackingContext(stacking_context_id,
                                                       background_color,
                                                       epoch,
                                                       pipeline_id,
                                                       viewport_size,
                                                       stacking_contexts,
                                                       display_lists,
                                                       auxiliary_lists_descriptor) => {
                            for (id, stacking_context) in stacking_contexts.into_iter() {
                                self.scene.add_stacking_context(id,
                                                                pipeline_id,
                                                                epoch,
                                                                stacking_context);
                            }

                            let mut leftover_auxiliary_data = vec![];
                            let mut auxiliary_data;
                            loop {
                                auxiliary_data = self.payload_rx.recv().unwrap();
                                {
                                    let mut payload_reader = Cursor::new(&auxiliary_data[..]);
                                    let payload_stacking_context_id =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    let payload_epoch =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    if payload_epoch == epoch.0 &&
                                            payload_stacking_context_id == stacking_context_id.0 {
                                        break
                                    }
                                }
                                leftover_auxiliary_data.push(auxiliary_data)
                            }
                            for leftover_auxiliary_data in leftover_auxiliary_data {
                                self.payload_tx.send(&leftover_auxiliary_data[..]).unwrap()
                            }

                            let mut auxiliary_data = Cursor::new(&mut auxiliary_data[8..]);
                            for (display_list_id,
                                 display_list_descriptor) in display_lists.into_iter() {
                                let mut built_display_list_data =
                                    vec![0; display_list_descriptor.size()];
                                auxiliary_data.read_exact(&mut built_display_list_data[..])
                                              .unwrap();
                                let built_display_list =
                                    BuiltDisplayList::from_data(built_display_list_data,
                                                                display_list_descriptor);
                                self.scene.add_display_list(display_list_id,
                                                            pipeline_id,
                                                            epoch,
                                                            built_display_list,
                                                            &mut self.resource_cache);
                            }

                            let mut auxiliary_lists_data =
                                vec![0; auxiliary_lists_descriptor.size()];
                            auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                            let auxiliary_lists =
                                AuxiliaryLists::from_data(auxiliary_lists_data,
                                                          auxiliary_lists_descriptor);
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_stacking_context(pipeline_id,
                                                                     epoch,
                                                                     stacking_context_id,
                                                                     background_color,
                                                                     viewport_size,
                                                                     &mut self.resource_cache,
                                                                     auxiliary_lists);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::SetRootPipeline(pipeline_id) => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.scene.set_root_pipeline_id(pipeline_id);

                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::Scroll(delta, cursor, move_phase) => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.frame.scroll(delta, cursor, move_phase);
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::TickScrollingBounce => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.frame.tick_scrolling_bounce_animations();
                                self.render()
                            });

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                            // First, find the specific layer that contains the point.
                            let point = point / self.device_pixel_ratio;
                            if let (Some(root_pipeline_id), Some(root_scroll_layer_id)) =
                                    (self.scene.root_pipeline_id,
                                     self.frame.root_scroll_layer_id) {
                                if let Some(scroll_layer_id) =
                                        self.frame.get_scroll_layer(&point, root_scroll_layer_id) {
                                    if let Some(layer) = self.frame.layers.get(&scroll_layer_id) {
                                        // Now, because we send a *pipeline ID*, not a layer ID, as
                                        // a response, we need the translated point to be relative
                                        // to the origin of that pipeline in the scene. So we need
                                        // to find the root layer for the pipeline. (We don't send
                                        // layer IDs because layer IDs are internal to WebRender;
                                        // Servo won't know what to do with them.)
                                        if let Some(layer_id) =
                                                self.frame.root_scroll_layer_for_pipeline(
                                                    layer.pipeline_id) {
                                            if let Some(layer) = self.frame.layers.get(&layer_id) {
                                                let point = point - layer.world_origin -
                                                    layer.scrolling.offset;
                                                tx.send((point, layer.pipeline_id)).unwrap();
                                                continue
                                            }
                                        }
                                    }
                                }
                            }
                            tx.send((point, PipelineId(0, 0))).unwrap()
                        }
                        ApiMsg::GetScrollLayerState(tx) => {
                            tx.send(self.frame.get_scroll_layer_state(self.device_pixel_ratio))
                              .unwrap()
                        }
                        ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                            if let Some(ref handle) = self.webrender_context_handle {
                                match GLContext::<NativeGLContext>::new(size, attributes, ColorAttachmentType::Texture, Some(handle)) {
                                    Ok(ctx) => {
                                        let id = WebGLContextId(new_id());

                                        let (real_size, texture_id) = {
                                            let draw_buffer = ctx.borrow_draw_buffer().unwrap();
                                            (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
                                        };

                                        let limits = ctx.borrow_limits().clone();

                                        self.webgl_contexts.insert(id, ctx);

                                        self.resource_cache
                                            .add_webgl_texture(id, TextureId(texture_id), real_size);

                                        tx.send(Ok((id, limits))).unwrap();
                                    },
                                    Err(msg) => {
                                        tx.send(Err(msg.to_owned())).unwrap();
                                    }
                                }
                            } else {
                                tx.send(Err("Not implemented yet".to_owned())).unwrap();
                            }
                        }
                        ApiMsg::WebGLCommand(context_id, command) => {
                            // TODO: Buffer the commands and only apply them here if they need to
                            // be synchronous.
                            let ctx = self.webgl_contexts.get(&context_id).unwrap();
                            ctx.make_current().unwrap();
                            command.apply(ctx);
                            self.current_bound_webgl_context_id = Some(context_id);
                        }
                    }
                }
                Err(..) => {
                    break;
                }
            }
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        if let Some(id) = self.current_bound_webgl_context_id {
            self.webgl_contexts.get(&id).unwrap().unbind().unwrap();
        }

        self.frame.create(&self.scene,
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes,
                          self.device_pixel_ratio);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        let frame = self.frame.build(&mut self.resource_cache,
                                     &mut self.thread_pool,
                                     self.device_pixel_ratio);

        let pending_update = self.resource_cache.pending_updates();
        if !pending_update.updates.is_empty() {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let pending_updates = self.frame.pending_updates();
        let msg = ResultMsg::NewFrame(frame, pending_updates, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }
}

<commit_msg>Auto merge of #298 - pcwalton:flood-gates, r=glennw<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use byteorder::{LittleEndian, ReadBytesExt};
use euclid::Matrix4D;
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, RendererFrame};
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcReceiver};
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::mem;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace, RenderNotifier};
use webrender_traits::{PipelineId, WebGLContextId, ScrollLayerId};
use batch::new_id;
use device::TextureId;
use offscreen_gl_context::{NativeGLContext, GLContext, ColorAttachmentType, NativeGLContextMethods, NativeGLContextHandle};

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    payload_rx: IpcBytesReceiver,
    payload_tx: IpcBytesSender,
    result_tx: Sender<ResultMsg>,

    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
    current_bound_webgl_context_id: Option<WebGLContextId>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               payload_rx: IpcBytesReceiver,
               payload_tx: IpcBytesSender,
               result_tx: Sender<ResultMsg>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               dummy_mask_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                dummy_mask_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            payload_rx: payload_rx,
            payload_tx: payload_tx,
            result_tx: result_tx,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
            current_bound_webgl_context_id: None,
        }
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            // Preprocess messages to guard against message floods.
            let mut msgs = vec![];
            let mut index_of_last_set_root_stacking_context_msg = None;
            let mut msg = match self.api_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break,
            };
            loop {
                match msg {
                    ApiMsg::SetRootStackingContext(..) => {
                        index_of_last_set_root_stacking_context_msg = Some(msgs.len())
                    }
                    _ => {}
                }
                msgs.push(msg);
                msg = match self.api_rx.try_recv() {
                    Err(_) => break,
                    Ok(msg) => msg,
                }
            }

            for (i, msg) in mem::replace(&mut msgs, vec![]).into_iter().enumerate() {
                match msg {
                    ApiMsg::SetRootStackingContext(..) if Some(i) !=
                            index_of_last_set_root_stacking_context_msg => {
                        // We're getting flooded with new frames. Ignore all but the last one.
                    }
                    _ => msgs.push(msg),
                }
            }

            // Process the messages that survived.
            for msg in msgs {
                match msg {
                    ApiMsg::AddRawFont(id, bytes) => {
                        profile_counters.font_templates.inc(bytes.len());
                        self.resource_cache
                            .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                    }
                    ApiMsg::AddNativeFont(id, native_font_handle) => {
                        self.resource_cache
                            .add_font_template(id, FontTemplate::Native(native_font_handle));
                    }
                    ApiMsg::AddImage(id, width, height, format, bytes) => {
                        profile_counters.image_templates.inc(bytes.len());
                        self.resource_cache.add_image_template(id,
                                                               width,
                                                               height,
                                                               format,
                                                               bytes);
                    }
                    ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                        self.resource_cache.update_image_template(id,
                                                                  width,
                                                                  height,
                                                                  format,
                                                                  bytes);
                    }
                    ApiMsg::DeleteImage(id) => {
                        self.resource_cache.delete_image_template(id);
                    }
                    ApiMsg::CloneApi(sender) => {
                        let result = self.next_namespace_id;

                        let IdNamespace(id_namespace) = self.next_namespace_id;
                        self.next_namespace_id = IdNamespace(id_namespace + 1);

                        sender.send(result).unwrap();
                    }
                    ApiMsg::SetRootStackingContext(stacking_context_id,
                                                   background_color,
                                                   epoch,
                                                   pipeline_id,
                                                   viewport_size,
                                                   stacking_contexts,
                                                   display_lists,
                                                   auxiliary_lists_descriptor) => {
                        for (id, stacking_context) in stacking_contexts.into_iter() {
                            self.scene.add_stacking_context(id,
                                                            pipeline_id,
                                                            epoch,
                                                            stacking_context);
                        }

                        let mut leftover_auxiliary_data = vec![];
                        let mut auxiliary_data;
                        loop {
                            auxiliary_data = self.payload_rx.recv().unwrap();
                            {
                                let mut payload_reader = Cursor::new(&auxiliary_data[..]);
                                let payload_stacking_context_id =
                                    payload_reader.read_u32::<LittleEndian>().unwrap();
                                let payload_epoch =
                                    payload_reader.read_u32::<LittleEndian>().unwrap();
                                if payload_epoch == epoch.0 &&
                                        payload_stacking_context_id ==
                                            stacking_context_id.0 {
                                    break
                                }
                            }
                            leftover_auxiliary_data.push(auxiliary_data)
                        }
                        for leftover_auxiliary_data in leftover_auxiliary_data {
                            self.payload_tx.send(&leftover_auxiliary_data[..]).unwrap()
                        }

                        let mut auxiliary_data = Cursor::new(&mut auxiliary_data[8..]);
                        for (display_list_id,
                             display_list_descriptor) in display_lists.into_iter() {
                            let mut built_display_list_data =
                                vec![0; display_list_descriptor.size()];
                            auxiliary_data.read_exact(&mut built_display_list_data[..])
                                          .unwrap();
                            let built_display_list =
                                BuiltDisplayList::from_data(built_display_list_data,
                                                            display_list_descriptor);
                            self.scene.add_display_list(display_list_id,
                                                        pipeline_id,
                                                        epoch,
                                                        built_display_list,
                                                        &mut self.resource_cache);
                        }

                        let mut auxiliary_lists_data =
                            vec![0; auxiliary_lists_descriptor.size()];
                        auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                        let auxiliary_lists =
                            AuxiliaryLists::from_data(auxiliary_lists_data,
                                                      auxiliary_lists_descriptor);
                        let frame = profile_counters.total_time.profile(|| {
                            self.scene.set_root_stacking_context(pipeline_id,
                                                                 epoch,
                                                                 stacking_context_id,
                                                                 background_color,
                                                                 viewport_size,
                                                                 &mut self.resource_cache,
                                                                 auxiliary_lists);

                            self.build_scene();
                            self.render()
                        });

                        self.publish_frame(frame, &mut profile_counters);
                    }
                    ApiMsg::SetRootPipeline(pipeline_id) => {
                        let frame = profile_counters.total_time.profile(|| {
                            self.scene.set_root_pipeline_id(pipeline_id);

                            self.build_scene();
                            self.render()
                        });

                        self.publish_frame(frame, &mut profile_counters);
                    }
                    ApiMsg::Scroll(delta, cursor, move_phase) => {
                        let frame = profile_counters.total_time.profile(|| {
                            self.frame.scroll(delta, cursor, move_phase);
                            self.render()
                        });

                        self.publish_frame(frame, &mut profile_counters);
                    }
                    ApiMsg::TickScrollingBounce => {
                        let frame = profile_counters.total_time.profile(|| {
                            self.frame.tick_scrolling_bounce_animations();
                            self.render()
                        });

                        self.publish_frame(frame, &mut profile_counters);
                    }
                    ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                        // First, find the specific layer that contains the point.
                        let point = point / self.device_pixel_ratio;
                        if let (Some(root_pipeline_id), Some(root_scroll_layer_id)) =
                                (self.scene.root_pipeline_id,
                                 self.frame.root_scroll_layer_id) {
                            if let Some(scroll_layer_id) =
                                    self.frame.get_scroll_layer(&point,
                                                                root_scroll_layer_id) {
                                if let Some(layer) = self.frame
                                                         .layers
                                                         .get(&scroll_layer_id) {
                                    // Now, because we send a *pipeline ID*, not a layer
                                    // ID, as a response, we need the translated point to
                                    // be relative to the origin of that pipeline in the
                                    // scene. So we need to find the root layer for the
                                    // pipeline. (We don't send layer IDs because layer IDs
                                    // are internal to WebRender; Servo won't know what to
                                    // do with them.)
                                    if let Some(layer_id) =
                                            self.frame.root_scroll_layer_for_pipeline(
                                                layer.pipeline_id) {
                                        if let Some(layer) = self.frame
                                                                 .layers
                                                                     .get(&layer_id) {
                                            let point = point - layer.world_origin -
                                                layer.scrolling.offset;
                                            tx.send((point, layer.pipeline_id)).unwrap();
                                            continue
                                        }
                                    }
                                }
                            }
                        }
                        tx.send((point, PipelineId(0, 0))).unwrap()
                    }
                    ApiMsg::GetScrollLayerState(tx) => {
                        tx.send(self.frame.get_scroll_layer_state(self.device_pixel_ratio))
                          .unwrap()
                    }
                    ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                        if let Some(ref handle) = self.webrender_context_handle {
                            match GLContext::<NativeGLContext>::new(
                                    size,
                                    attributes,
                                    ColorAttachmentType::Texture,
                                    Some(handle)) {
                                Ok(ctx) => {
                                    let id = WebGLContextId(new_id());

                                    let (real_size, texture_id) = {
                                        let draw_buffer = ctx.borrow_draw_buffer()
                                                             .unwrap();
                                        (draw_buffer.size(),
                                         draw_buffer.get_bound_texture_id().unwrap())
                                    };

                                    let limits = ctx.borrow_limits().clone();

                                    self.webgl_contexts.insert(id, ctx);

                                    self.resource_cache
                                        .add_webgl_texture(id,
                                                           TextureId(texture_id),
                                                           real_size);

                                    tx.send(Ok((id, limits))).unwrap();
                                },
                                Err(msg) => {
                                    tx.send(Err(msg.to_owned())).unwrap();
                                }
                            }
                        } else {
                            tx.send(Err("Not implemented yet".to_owned())).unwrap();
                        }
                    }
                    ApiMsg::WebGLCommand(context_id, command) => {
                        // TODO: Buffer the commands and only apply them here if they need
                        // to be synchronous.
                        let ctx = self.webgl_contexts.get(&context_id).unwrap();
                        ctx.make_current().unwrap();
                        command.apply(ctx);
                        self.current_bound_webgl_context_id = Some(context_id);
                    }
                }
            }
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        if let Some(id) = self.current_bound_webgl_context_id {
            self.webgl_contexts.get(&id).unwrap().unbind().unwrap();
        }

        self.frame.create(&self.scene,
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes,
                          self.device_pixel_ratio);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        let frame = self.frame.build(&mut self.resource_cache,
                                     &mut self.thread_pool,
                                     self.device_pixel_ratio);

        let pending_update = self.resource_cache.pending_updates();
        if !pending_update.updates.is_empty() {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let pending_updates = self.frame.pending_updates();
        let msg = ResultMsg::NewFrame(frame, pending_updates, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }
}

<|endoftext|>