<commit_before>pub mod bitbucket;
pub mod gist;
pub mod hastebin;
pub mod pastebin;
pub mod pastie;
pub mod sprunge;

use bins::error::*;
use bins::network::download::Downloader;
use bins::network::upload::Uploader;
use bins::network;
use bins::{self, Bins, PasteFile};
use hyper::Url;
use linked_hash_map::LinkedHashMap;
use std::collections::HashMap;
use std::iter::repeat;

pub struct Index {
  pub files: LinkedHashMap<String, Url>
}

impl Index {
  fn repeat_str(string: &str, count: usize) -> String {
    repeat(string).take(count).collect()
  }

  pub fn to_string(&self) -> String {
    let header = format!("{} files", self.files.len());
    let separator = Self::repeat_str("-", header.len());
    let mut body = String::from("");
    for (i, (name, url)) in self.files.iter().enumerate() {
      let number = i + 1;
      body.push_str(&format!("{number}. {name}: {url}\n",
                             number = number,
                             name = name,
                             url = url));
    }
    format!("{}\n{}\n\n{}", header, separator, body)
  }

  pub fn parse<S: Into<String>>(string: S) -> Result<Index> {
    let string = string.into();
    let lines: Vec<&str> = string.split('\n').collect();
    if lines.len() < 4 {
      return Err(ErrorKind::InvalidIndexError.into());
    }
    let mut split = lines.iter().skip(3).filter(|s| !s.trim().is_empty()).map(|s| s.split(": ")).collect::<Vec<_>>();
    let names: Vec<String> = some_or_err!(split.iter_mut()
                                            .map(|s| {
                                              s.nth(0)
                                                .map(|n| n.split(' ').skip(1).collect::<Vec<&str>>().join(" "))
                                            })
                                            .collect(),
                                          ErrorKind::InvalidIndexError.into());
    let url_strings: Vec<String> = some_or_err!(split.iter_mut().map(|s| s.nth(0).map(|s| s.to_owned())).collect(),
                                                ErrorKind::InvalidIndexError.into());
    let urls: Vec<Url> = try!(url_strings.into_iter().map(network::parse_url).collect());
    let urls: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: urls })
  }
}

pub struct RemotePasteFile {
  pub name: String,
  pub url: Url,
  pub contents: Option<String>
}

/// Produce information about HTML content from URLs to HTML content.
pub trait ProduceInfo {
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceInfo for T
  where T: GenerateIndex + ConvertUrlsToRawUrls + Downloader
{
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let raw_url = try!(self.convert_url_to_raw_url(bins, url));
    let mut res = try!(self.download(&bins, &raw_url));
    let content = try!(network::read_response(&mut res));
    let index = Index::parse(content.clone());
    let mut urls: Vec<RemotePasteFile> = Vec::new();
    match index {
      Ok(ref i) => {
        for (name, url) in i.files.clone().into_iter() {
          urls.push(RemotePasteFile {
            name: name.clone(),
            url: url.clone(),
            contents: None
          });
        }
      }
      Err(ref e) => {
        if let ErrorKind::InvalidIndexError = *e.kind() {} else {
          return Err(e.to_string().into());
        }
        let url = url.clone();
        let name = some_or_err!(url.path_segments().and_then(|s| s.last()),
                                "paste url was a root url".into());
        urls.push(RemotePasteFile {
          name: name.to_owned(),
          url: url.clone(),
          contents: Some(content)
        });
      }
    }
    Ok(urls)
  }
}

/// Produce information about raw content from URLs to HTML content.
pub trait ProduceRawInfo {
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_raw_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_raw_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceRawInfo for T
  where T: ConvertUrlsToRawUrls + Downloader + UsesIndices + ProduceInfo
{
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let info = try!(self.produce_info(bins, url));
    info.into_iter()
      .map(|r| {
        let raw_url = try!(self.convert_url_to_raw_url(bins, &r.url));
        Ok(RemotePasteFile { url: raw_url, ..r })
      })
      .collect()
  }
}

pub trait ConvertUrlsToRawUrls {
  fn convert_url_to_raw_url(&self, bins: &Bins, url: &Url) -> Result<Url>;

  fn convert_urls_to_raw_urls(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<Url>> {
    urls.iter().map(|u| self.convert_url_to_raw_url(bins, u)).collect()
  }
}

/// Produce raw content from a URL to HTML content.
pub trait ProduceRawContent: ProduceRawInfo + ProduceInfo + Downloader {
  fn produce_raw_contents(&self, bins: &Bins, url: &Url) -> Result<String> {
    let raw_info = if bins.arguments.urls {
      try!(self.produce_info(bins, url))
    } else {
      try!(self.produce_raw_info(bins, url))
    };
    let raw_info: Vec<RemotePasteFile> = if raw_info.len() > 1 {
      if !bins.arguments.files.is_empty() {
        let mut map: HashMap<String, RemotePasteFile> =
          raw_info.into_iter().map(|r| (r.name.to_lowercase(), r)).collect();
        try!(bins.arguments
          .files
          .iter()
          .map(|s| map.remove(&s.to_lowercase()).ok_or(format!("file {} not found", s)))
          .collect())
      } else if let Some(ref range) = bins.arguments.range {
        let mut numbered_info: HashMap<usize, RemotePasteFile> = raw_info.into_iter()
          .enumerate()
          .collect();
        try!(range.clone()
          .into_iter()
          .map(|n| numbered_info.remove(&n).ok_or(format!("file {} not found", n)))
          .collect())
      } else if bins.arguments.all {
        raw_info
      } else {
        let names = raw_info.into_iter().map(|r| String::from("  ") + &r.name).collect::<Vec<_>>().join("\n");
        return Err(format!("paste had multiple files, but no behavior was specified on the command \
                            line\n\navailable files:\n{}",
                           names)
          .into());
      }
    } else {
      raw_info
    };
    if bins.arguments.raw_urls || bins.arguments.urls {
      return Ok(raw_info.into_iter().map(|r| r.url.as_str().to_owned()).collect::<Vec<_>>().join("\n"));
    }
    let names: Vec<String> = raw_info.iter().map(|p| p.name.clone()).collect();
    let all_contents: Vec<String> = try!(raw_info.iter()
      .map(|p| {
        match p.contents.clone() {
          Some(contents) => Ok(contents),
          None => self.download(&bins, &p.url).and_then(|mut r| network::read_response(&mut r)),
        }
      })
      .collect());
    let files: LinkedHashMap<String, String> = names.into_iter().zip(all_contents.into_iter()).collect();
    Ok(files.into_iter()
      .map(|(name, content)| {
        PasteFile {
          name: name.clone(),
          data: if bins.arguments.number_lines { number_lines(content.clone()) } else { content.clone() }
        }
      })
      .collect::<Vec<PasteFile>>()
      .join())
  }
}

fn number_lines(string: String) -> String {
  let lines: Vec<&str> = string.split("\n").collect();
  let num_lines = lines.len();
  let zeroes: String = repeat(" ").take(num_lines.to_string().len()).collect();
  lines.into_iter().enumerate().map(|(i, l)| {
    let i = i + 1;
    format!("{}{}  {}", &zeroes[0..zeroes.len() - i.to_string().len()], i, l)
  }).collect::<Vec<_>>().join("\n")
}

/// Produce a URL to HTML content from raw content.
pub trait UploadContent: Uploader {
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url>;
}

impl<T> UploadContent for T
  where T: UploadUrl + Uploader
{
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url> {
    let url = try!(network::parse_url(self.get_upload_url()));
    let mut response = try!(self.upload(&url, bins, &content));
    network::parse_url(try!(network::read_response(&mut response)))
  }
}

/// Produce a URL to HTML content from a batch of raw content.
pub trait UploadBatchContent: UploadContent {
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url>;
}

impl<T> UploadBatchContent for T
  where T: GenerateIndex + UploadContent
{
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url> {
    let index = try!(self.generate_index(bins, content));
    self.upload_paste(bins,
                      PasteFile {
                        name: "index.md".to_owned(),
                        data: index.to_string()
                      })
  }
}

pub trait UsesIndices {}

/// Generate an index for multiple files.
pub trait GenerateIndex {
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index>;
}

impl<T> GenerateIndex for T
  where T: UploadContent + UsesIndices
{
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index> {
    let names: Vec<String> = (&content).into_iter().map(|p| p.name.clone()).collect();
    let urls: Vec<Url> = try!(content.into_iter().map(|p| self.upload_paste(bins, p)).collect());
    let uploads: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: uploads })
  }
}

pub trait UploadUrl {
  fn get_upload_url(&self) -> &str;
}

pub trait VerifyUrl {
  fn segments(&self, url: &Url) -> Vec<String> {
    let segments = match url.path_segments() {
      Some(s) => s,
      None => return Vec::new(),
    };
    segments.filter(|s| !s.is_empty()).map(|s| s.to_owned()).collect::<Vec<_>>()
  }

  fn verify_url(&self, url: &Url) -> bool;
}

/// A bin, which can upload content in raw form and download content in raw and HTML form.
pub trait Bin: Sync + ProduceInfo + ProduceRawContent + UploadBatchContent + VerifyUrl {
  fn get_name(&self) -> &str;

  fn get_domain(&self) -> &str;
}

trait Join {
  fn join(&self) -> String;
}

impl Join for Vec<PasteFile> {
  fn join(&self) -> String {
    if self.len() == 1 {
      self.get(0).expect("len() == 1, but no first element").data.clone()
    } else {
      self.into_iter().map(|p| format!("--- {} ---\n\n{}", p.name, p.data)).collect::<Vec<String>>().join("\n\n")
    }
  }
}

lazy_static! {
  pub static ref BINS: Vec<Box<Bin>> = {
      vec![
        Box::new(bins::Gist::new()),
        Box::new(bins::Bitbucket::new()),
        Box::new(bins::Sprunge::new()),
        Box::new(bins::Hastebin::new()),
        Box::new(bins::Pastebin::new()),
        Box::new(bins::Pastie::new())
      ]
  };
}

pub fn get_bin_names<'a>() -> Vec<&'a str> {
  BINS.iter().map(|e| e.get_name()).collect()
}

pub fn get_bin_by_name(name: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_name().to_lowercase() == name.to_lowercase())
}

pub fn get_bin_by_domain(domain: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_domain().to_lowercase() == domain.to_lowercase())
}
<commit_msg>refactor: split by char instead of string<commit_after>pub mod bitbucket;
pub mod gist;
pub mod hastebin;
pub mod pastebin;
pub mod pastie;
pub mod sprunge;

use bins::error::*;
use bins::network::download::Downloader;
use bins::network::upload::Uploader;
use bins::network;
use bins::{self, Bins, PasteFile};
use hyper::Url;
use linked_hash_map::LinkedHashMap;
use std::collections::HashMap;
use std::iter::repeat;

pub struct Index {
  pub files: LinkedHashMap<String, Url>
}

impl Index {
  fn repeat_str(string: &str, count: usize) -> String {
    repeat(string).take(count).collect()
  }

  pub fn to_string(&self) -> String {
    let header = format!("{} files", self.files.len());
    let separator = Self::repeat_str("-", header.len());
    let mut body = String::from("");
    for (i, (name, url)) in self.files.iter().enumerate() {
      let number = i + 1;
      body.push_str(&format!("{number}. {name}: {url}\n",
                             number = number,
                             name = name,
                             url = url));
    }
    format!("{}\n{}\n\n{}", header, separator, body)
  }

  pub fn parse<S: Into<String>>(string: S) -> Result<Index> {
    let string = string.into();
    let lines: Vec<&str> = string.split('\n').collect();
    if lines.len() < 4 {
      return Err(ErrorKind::InvalidIndexError.into());
    }
    let mut split = lines.iter().skip(3).filter(|s| !s.trim().is_empty()).map(|s| s.split(": ")).collect::<Vec<_>>();
    let names: Vec<String> = some_or_err!(split.iter_mut()
                                            .map(|s| {
                                              s.nth(0)
                                                .map(|n| n.split(' ').skip(1).collect::<Vec<&str>>().join(" "))
                                            })
                                            .collect(),
                                          ErrorKind::InvalidIndexError.into());
    let url_strings: Vec<String> = some_or_err!(split.iter_mut().map(|s| s.nth(0).map(|s| s.to_owned())).collect(),
                                                ErrorKind::InvalidIndexError.into());
    let urls: Vec<Url> = try!(url_strings.into_iter().map(network::parse_url).collect());
    let urls: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: urls })
  }
}

pub struct RemotePasteFile {
  pub name: String,
  pub url: Url,
  pub contents: Option<String>
}

/// Produce information about HTML content from URLs to HTML content.
pub trait ProduceInfo {
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceInfo for T
  where T: GenerateIndex + ConvertUrlsToRawUrls + Downloader
{
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let raw_url = try!(self.convert_url_to_raw_url(bins, url));
    let mut res = try!(self.download(&bins, &raw_url));
    let content = try!(network::read_response(&mut res));
    let index = Index::parse(content.clone());
    let mut urls: Vec<RemotePasteFile> = Vec::new();
    match index {
      Ok(ref i) => {
        for (name, url) in i.files.clone().into_iter() {
          urls.push(RemotePasteFile {
            name: name.clone(),
            url: url.clone(),
            contents: None
          });
        }
      }
      Err(ref e) => {
        if let ErrorKind::InvalidIndexError = *e.kind() {} else {
          return Err(e.to_string().into());
        }
        let url = url.clone();
        let name = some_or_err!(url.path_segments().and_then(|s| s.last()),
                                "paste url was a root url".into());
        urls.push(RemotePasteFile {
          name: name.to_owned(),
          url: url.clone(),
          contents: Some(content)
        });
      }
    }
    Ok(urls)
  }
}

/// Produce information about raw content from URLs to HTML content.
pub trait ProduceRawInfo {
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_raw_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_raw_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceRawInfo for T
  where T: ConvertUrlsToRawUrls + Downloader + UsesIndices + ProduceInfo
{
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let info = try!(self.produce_info(bins, url));
    info.into_iter()
      .map(|r| {
        let raw_url = try!(self.convert_url_to_raw_url(bins, &r.url));
        Ok(RemotePasteFile { url: raw_url, ..r })
      })
      .collect()
  }
}

pub trait ConvertUrlsToRawUrls {
  fn convert_url_to_raw_url(&self, bins: &Bins, url: &Url) -> Result<Url>;

  fn convert_urls_to_raw_urls(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<Url>> {
    urls.iter().map(|u| self.convert_url_to_raw_url(bins, u)).collect()
  }
}

/// Produce raw content from a URL to HTML content.
pub trait ProduceRawContent: ProduceRawInfo + ProduceInfo + Downloader {
  fn produce_raw_contents(&self, bins: &Bins, url: &Url) -> Result<String> {
    let raw_info = if bins.arguments.urls {
      try!(self.produce_info(bins, url))
    } else {
      try!(self.produce_raw_info(bins, url))
    };
    let raw_info: Vec<RemotePasteFile> = if raw_info.len() > 1 {
      if !bins.arguments.files.is_empty() {
        let mut map: HashMap<String, RemotePasteFile> =
          raw_info.into_iter().map(|r| (r.name.to_lowercase(), r)).collect();
        try!(bins.arguments
          .files
          .iter()
          .map(|s| map.remove(&s.to_lowercase()).ok_or(format!("file {} not found", s)))
          .collect())
      } else if let Some(ref range) = bins.arguments.range {
        let mut numbered_info: HashMap<usize, RemotePasteFile> = raw_info.into_iter()
          .enumerate()
          .collect();
        try!(range.clone()
          .into_iter()
          .map(|n| numbered_info.remove(&n).ok_or(format!("file {} not found", n)))
          .collect())
      } else if bins.arguments.all {
        raw_info
      } else {
        let names = raw_info.into_iter().map(|r| String::from("  ") + &r.name).collect::<Vec<_>>().join("\n");
        return Err(format!("paste had multiple files, but no behavior was specified on the command \
                            line\n\navailable files:\n{}",
                           names)
          .into());
      }
    } else {
      raw_info
    };
    if bins.arguments.raw_urls || bins.arguments.urls {
      return Ok(raw_info.into_iter().map(|r| r.url.as_str().to_owned()).collect::<Vec<_>>().join("\n"));
    }
    let names: Vec<String> = raw_info.iter().map(|p| p.name.clone()).collect();
    let all_contents: Vec<String> = try!(raw_info.iter()
      .map(|p| {
        match p.contents.clone() {
          Some(contents) => Ok(contents),
          None => self.download(&bins, &p.url).and_then(|mut r| network::read_response(&mut r)),
        }
      })
      .collect());
    let files: LinkedHashMap<String, String> = names.into_iter().zip(all_contents.into_iter()).collect();
    Ok(files.into_iter()
      .map(|(name, content)| {
        PasteFile {
          name: name.clone(),
          data: if bins.arguments.number_lines { number_lines(content.clone()) } else { content.clone() }
        }
      })
      .collect::<Vec<PasteFile>>()
      .join())
  }
}

fn number_lines(string: String) -> String {
  let lines: Vec<&str> = string.split('\n').collect();
  let num_lines = lines.len();
  let zeroes: String = repeat(" ").take(num_lines.to_string().len()).collect();
  lines.into_iter().enumerate().map(|(i, l)| {
    let i = i + 1;
    format!("{}{}  {}", &zeroes[0..zeroes.len() - i.to_string().len()], i, l)
  }).collect::<Vec<_>>().join("\n")
}

/// Produce a URL to HTML content from raw content.
pub trait UploadContent: Uploader {
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url>;
}

impl<T> UploadContent for T
  where T: UploadUrl + Uploader
{
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url> {
    let url = try!(network::parse_url(self.get_upload_url()));
    let mut response = try!(self.upload(&url, bins, &content));
    network::parse_url(try!(network::read_response(&mut response)))
  }
}

/// Produce a URL to HTML content from a batch of raw content.
pub trait UploadBatchContent: UploadContent {
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url>;
}

impl<T> UploadBatchContent for T
  where T: GenerateIndex + UploadContent
{
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url> {
    let index = try!(self.generate_index(bins, content));
    self.upload_paste(bins,
                      PasteFile {
                        name: "index.md".to_owned(),
                        data: index.to_string()
                      })
  }
}

pub trait UsesIndices {}

/// Generate an index for multiple files.
pub trait GenerateIndex {
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index>;
}

impl<T> GenerateIndex for T
  where T: UploadContent + UsesIndices
{
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index> {
    let names: Vec<String> = (&content).into_iter().map(|p| p.name.clone()).collect();
    let urls: Vec<Url> = try!(content.into_iter().map(|p| self.upload_paste(bins, p)).collect());
    let uploads: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: uploads })
  }
}

pub trait UploadUrl {
  fn get_upload_url(&self) -> &str;
}

pub trait VerifyUrl {
  fn segments(&self, url: &Url) -> Vec<String> {
    let segments = match url.path_segments() {
      Some(s) => s,
      None => return Vec::new(),
    };
    segments.filter(|s| !s.is_empty()).map(|s| s.to_owned()).collect::<Vec<_>>()
  }

  fn verify_url(&self, url: &Url) -> bool;
}

/// A bin, which can upload content in raw form and download content in raw and HTML form.
pub trait Bin: Sync + ProduceInfo + ProduceRawContent + UploadBatchContent + VerifyUrl {
  fn get_name(&self) -> &str;

  fn get_domain(&self) -> &str;
}

trait Join {
  fn join(&self) -> String;
}

impl Join for Vec<PasteFile> {
  fn join(&self) -> String {
    if self.len() == 1 {
      self.get(0).expect("len() == 1, but no first element").data.clone()
    } else {
      self.into_iter().map(|p| format!("--- {} ---\n\n{}", p.name, p.data)).collect::<Vec<String>>().join("\n\n")
    }
  }
}

lazy_static! {
  pub static ref BINS: Vec<Box<Bin>> = {
      vec![
        Box::new(bins::Gist::new()),
        Box::new(bins::Bitbucket::new()),
        Box::new(bins::Sprunge::new()),
        Box::new(bins::Hastebin::new()),
        Box::new(bins::Pastebin::new()),
        Box::new(bins::Pastie::new())
      ]
  };
}

pub fn get_bin_names<'a>() -> Vec<&'a str> {
  BINS.iter().map(|e| e.get_name()).collect()
}

pub fn get_bin_by_name(name: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_name().to_lowercase() == name.to_lowercase())
}

pub fn get_bin_by_domain(domain: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_domain().to_lowercase() == domain.to_lowercase())
}
<|endoftext|>


<commit_before>use common::memory::*;
use common::scheduler::*;

use drivers::disk::*;

use programs::common::*;

#[derive(Copy, Clone)]
#[repr(packed)]
pub struct Extent{
    pub block: u64,
    pub length: u64
}

#[repr(packed)]
pub struct Header {
    pub signature: [u8; 8],
    pub version: u32,
    pub name: [u8; 244],
    pub extents: [Extent; 16]
}

#[repr(packed)]
pub struct Node {
    pub name: [u8; 256],
    pub extents: [Extent; 16]
}

impl Clone for Node {
    fn clone(&self) -> Node {
        return Node {
            name: self.name,
            extents: self.extents
        };
    }
}

pub struct FileSystem {
    pub disk: Disk,
    pub header: Header,
    pub nodes: Vec<Node>
}

impl FileSystem {
    pub fn from_disk(disk: Disk) -> FileSystem{
        unsafe{
            let header_ptr: *const Header = alloc_type();
            disk.read(1, 1, header_ptr as usize);
            let header = ptr::read(header_ptr);
            unalloc(header_ptr as usize);

            let mut nodes = Vec::new();
            let node_ptr: *const Node = alloc_type();
            for extent in &header.extents {
                if extent.block > 0 {
                    for node_address in extent.block..extent.block + (extent.length + 511)/512 {
                        disk.read(node_address, 1, node_ptr as usize);
                        nodes.push(ptr::read(node_ptr));
                    }
                }
            }
            unalloc(node_ptr as usize);

            return FileSystem {
                disk:disk,
                header: header,
                nodes: nodes
            };
        }
    }

    pub fn valid(&self) -> bool {
        return self.header.signature[0] == 'R' as u8
            && self.header.signature[1] == 'E' as u8
            && self.header.signature[2] == 'D' as u8
            && self.header.signature[3] == 'O' as u8
            && self.header.signature[4] == 'X' as u8
            && self.header.signature[5] == 'F' as u8
            && self.header.signature[6] == 'S' as u8
            && self.header.signature[7] == '\0' as u8
            && self.header.version == 0xFFFFFFFF;
    }

    pub fn node(&self, filename: &String) -> Option<Node>{
        for node in self.nodes.iter() {
            if String::from_c_slice(&node.name) == *filename {
                return Option::Some(node.clone());
            }
        }

        return Option::None;
    }

    pub fn list(&self, directory: &String) -> Vec<String> {
        let mut ret = Vec::<String>::new();

        for node in self.nodes.iter() {
            let node_name = String::from_c_slice(&node.name);
            if node_name.starts_with(directory.clone()) {
                ret.push(node_name.substr(directory.len(), node_name.len() - directory.len()));
            }
        }

        return ret;
    }
}

pub struct FileResource {
    pub node: Node,
    pub vec: Vec<u8>,
    pub seek: usize
}

impl Resource for FileResource {
    fn url(&self) -> URL {
        return URL::from_string(&String::from_c_slice(&self.node.name));
    }

    fn stat(&self) -> ResourceType {
        return ResourceType::File;
    }

    fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
        let mut i = 0;
        while i < buf.len() && self.seek < self.vec.len() {
            match self.vec.get(self.seek) {
                Option::Some(b) => buf[i] = *b,
                Option::None => ()
            }
            self.seek += 1;
            i += 1;
        }
        return Option::Some(i);
    }

    fn write(&mut self, buf: &[u8]) -> Option<usize> {
        let mut i = 0;
        while i < buf.len() && self.seek < self.vec.len() {
            self.vec.set(self.seek, buf[i]);
            self.seek += 1;
            i += 1;
        }
        while i < buf.len() {
            self.vec.push(buf[i]);
            self.seek += 1;
            i += 1;
        }
        return Option::Some(i);
    }

    fn seek(&mut self, pos: ResourceSeek) -> Option<usize> {
        match pos {
            ResourceSeek::Start(offset) => self.seek = offset,
            ResourceSeek::Current(offset) => self.seek = max(0, self.seek as isize + offset) as usize,
            ResourceSeek::End(offset) => self.seek = max(0, self.vec.len() as isize + offset) as usize
        }
        while self.vec.len() < self.seek {
            self.vec.push(0);
        }
        return Option::Some(self.seek);
    }

    fn flush(&mut self) -> bool {
        return false;
    }
}

pub struct FileScheme {
    pub fs: FileSystem
}

impl SessionItem for FileScheme {
    fn scheme(&self) -> String {
        return "file".to_string();
    }

    fn open(&mut self, url: &URL) -> Box<Resource>{
        let path = url.path();
        if path.len() == 0 || path.ends_with("/".to_string()) {
            let mut list = String::new();
            let mut dirs: Vec<String> = Vec::new();

            for file in self.fs.list(&path).iter() {
                let line;
                match file.find("/".to_string()) {
                    Option::Some(index) => {
                        let dirname = file.substr(0, index + 1);
                        let mut found = false;
                        for dir in dirs.iter() {
                            if dirname == *dir {
                                found = true;
                                break;
                            }
                        }
                        if found {
                            line = String::new();
                        }else{
                            line = dirname.clone();
                            dirs.push(dirname);
                        }
                    },
                    Option::None => line = file.clone()
                }
                if line.len() > 0 {
                    if list.len() > 0 {
                        list = list + '\n' + line;
                    }else{
                        list = line;
                    }
                }
            }

            return box VecResource::new(url.clone(), ResourceType::Dir, list.to_utf8());
        }else{
            match self.fs.node(&path) {
                Option::Some(node) => {
                    let mut vec: Vec<u8> = Vec::new();
                    if node.extents[0].block > 0 && node.extents[0].length > 0 {
                        unsafe {
                            let data = alloc(node.extents[0].length as usize);
                            if data > 0 {
                                let reenable = start_no_ints();

                                self.fs.disk.read(node.extents[0].block, ((node.extents[0].length + 511)/512) as u16, data);

                                end_no_ints(reenable);

                                vec = Vec {
                                    data: data as *mut u8,
                                    length: node.extents[0].length as usize
                                };
                            }
                        }
                    }

                    return box FileResource {
                        node: node,
                        vec: vec,
                        seek: 0
                    };
                },
                Option::None => return box NoneResource
            }
        }
    }
}
<commit_msg>WIP Insert nodes<commit_after>use common::memory::*;
use common::scheduler::*;

use drivers::disk::*;

use programs::common::*;

#[derive(Copy, Clone)]
#[repr(packed)]
pub struct Extent{
    pub block: u64,
    pub length: u64
}

#[repr(packed)]
pub struct Header {
    pub signature: [u8; 8],
    pub version: u32,
    pub name: [u8; 244],
    pub extents: [Extent; 16]
}

#[repr(packed)]
pub struct Node {
    pub name: [u8; 256],
    pub extents: [Extent; 16]
}

impl Clone for Node {
    fn clone(&self) -> Node {
        return Node {
            name: self.name,
            extents: self.extents
        };
    }
}

pub struct FileSystem {
    pub disk: Disk,
    pub header: Header,
    pub nodes: Vec<Node>
}

impl FileSystem {
    pub fn from_disk(disk: Disk) -> FileSystem{
        unsafe{
            let header_ptr: *const Header = alloc_type();
            disk.read(1, 1, header_ptr as usize);
            let header = ptr::read(header_ptr);
            unalloc(header_ptr as usize);

            let mut nodes = Vec::new();
            let node_ptr: *const Node = alloc_type();
            for extent in &header.extents {
                if extent.block > 0 {
                    for node_address in extent.block..extent.block + (extent.length + 511)/512 {
                        disk.read(node_address, 1, node_ptr as usize);
                        nodes.push(ptr::read(node_ptr));
                    }
                }
            }
            unalloc(node_ptr as usize);

            return FileSystem {
                disk:disk,
                header: header,
                nodes: nodes
            };
        }
    }

    pub fn valid(&self) -> bool {
        return self.header.signature[0] == 'R' as u8
            && self.header.signature[1] == 'E' as u8
            && self.header.signature[2] == 'D' as u8
            && self.header.signature[3] == 'O' as u8
            && self.header.signature[4] == 'X' as u8
            && self.header.signature[5] == 'F' as u8
            && self.header.signature[6] == 'S' as u8
            && self.header.signature[7] == '\0' as u8
            && self.header.version == 0xFFFFFFFF;
    }

    pub fn node(&self, filename: &String) -> Option<Node>{
        for node in self.nodes.iter() {
            if String::from_c_slice(&node.name) == *filename {
                return Option::Some(node.clone());
            }
        }

        return Option::None;
    }

    pub fn list(&self, directory: &String) -> Vec<String> {
        let mut ret = Vec::<String>::new();

        for node in self.nodes.iter() {
            let node_name = String::from_c_slice(&node.name);
            if node_name.starts_with(directory.clone()) {
                ret.push(node_name.substr(directory.len(), node_name.len() - directory.len()));
            }
        }

        return ret;
    }
}

pub struct FileResource {
    pub node: Node,
    pub vec: Vec<u8>,
    pub seek: usize
}

impl Resource for FileResource {
    fn url(&self) -> URL {
        return URL::from_string(&String::from_c_slice(&self.node.name));
    }

    fn stat(&self) -> ResourceType {
        return ResourceType::File;
    }

    fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
        let mut i = 0;
        while i < buf.len() && self.seek < self.vec.len() {
            match self.vec.get(self.seek) {
                Option::Some(b) => buf[i] = *b,
                Option::None => ()
            }
            self.seek += 1;
            i += 1;
        }
        return Option::Some(i);
    }

    fn write(&mut self, buf: &[u8]) -> Option<usize> {
        let mut i = 0;
        while i < buf.len() && self.seek < self.vec.len() {
            self.vec.set(self.seek, buf[i]);
            self.seek += 1;
            i += 1;
        }
        while i < buf.len() {
            self.vec.push(buf[i]);
            self.seek += 1;
            i += 1;
        }
        return Option::Some(i);
    }

    fn seek(&mut self, pos: ResourceSeek) -> Option<usize> {
        match pos {
            ResourceSeek::Start(offset) => self.seek = offset,
            ResourceSeek::Current(offset) => self.seek = max(0, self.seek as isize + offset) as usize,
            ResourceSeek::End(offset) => self.seek = max(0, self.vec.len() as isize + offset) as usize
        }
        while self.vec.len() < self.seek {
            self.vec.push(0);
        }
        return Option::Some(self.seek);
    }

    fn flush(&mut self) -> bool {
        return false;
    }
}

pub struct FileScheme {
    pub fs: FileSystem
}

impl SessionItem for FileScheme {
    fn scheme(&self) -> String {
        return "file".to_string();
    }

    fn open(&mut self, url: &URL) -> Box<Resource>{
        let path = url.path();
        if path.len() == 0 || path.ends_with("/".to_string()) {
            let mut list = String::new();
            let mut dirs: Vec<String> = Vec::new();

            for file in self.fs.list(&path).iter() {
                let line;
                match file.find("/".to_string()) {
                    Option::Some(index) => {
                        let dirname = file.substr(0, index + 1);
                        let mut found = false;
                        for dir in dirs.iter() {
                            if dirname == *dir {
                                found = true;
                                break;
                            }
                        }
                        if found {
                            line = String::new();
                        }else{
                            line = dirname.clone();
                            dirs.push(dirname);
                        }
                    },
                    Option::None => line = file.clone()
                }
                if line.len() > 0 {
                    if list.len() > 0 {
                        list = list + '\n' + line;
                    }else{
                        list = line;
                    }
                }
            }

            return box VecResource::new(url.clone(), ResourceType::Dir, list.to_utf8());
        }else{
            match self.fs.node(&path) {
                Option::Some(node) => {
                    let mut vec: Vec<u8> = Vec::new();
                    if node.extents[0].block > 0 && node.extents[0].length > 0 {
                        unsafe {
                            let data = alloc(node.extents[0].length as usize);
                            if data > 0 {
                                let reenable = start_no_ints();

                                self.fs.disk.read(node.extents[0].block, ((node.extents[0].length + 511)/512) as u16, data);

                                end_no_ints(reenable);

                                vec = Vec {
                                    data: data as *mut u8,
                                    length: node.extents[0].length as usize
                                };
                            }
                        }
                    }

                    return box FileResource {
                        node: node,
                        vec: vec,
                        seek: 0
                    };
                },
                Option::None => {
                    d("Creating ");
                    path.d();
                    dl();

                    let mut name = [0; 256];
                    for i in 0..256 {
                        //TODO: UTF8
                        let b = path[i] as u8;
                        name[i] = b;
                        if b == 0 {
                            break;
                        }
                    }

                    let node = Node {
                        name: name,
                        extents: [Extent { block: 0, length: 0 }; 16]
                    };

                    //TODO: Sync to disk
                    let mut node_i = 0;
                    while node_i < self.fs.nodes.len() {
                        let mut cmp = 0;

                        if let Option::Some(other_node) = self.fs.nodes.get(node_i) {
                            for i in 0..256 {
                                if other_node.name[i] != node.name[i] {
                                    cmp = other_node.name[i] as isize - node.name[i] as isize;
                                    break;
                                }
                            }
                        }

                        if cmp >= 0 {
                            break;
                        }

                        node_i += 1;
                    }
                    d("Insert at ");
                    dd(node_i);
                    dl();
                    self.fs.nodes.insert(node_i, node.clone());

                    return box FileResource {
                        node: node,
                        vec: Vec::new(),
                        seek: 0
                    };
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Combinators applying parsers in sequence

#[macro_use]
mod macros;

use crate::error::ParseError;
use crate::internal::{IResult, Parser};

/// Gets an object from the first parser,
/// then gets another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::pair;
/// use nom::bytes::complete::tag;
///
/// let mut parser = pair(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abcefghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn pair<I, O1, O2, E: ParseError<I>, F, G>(
  mut first: F,
  mut second: G,
) -> impl FnMut(I) -> IResult<I, (O1, O2), E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first.parse(input)?;
    second.parse(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn pairc<I, O1, O2, E: ParseError<I>, F, G>(
  input: I,
  first: F,
  second: G,
) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  pair(first, second)(input)
}

/// Matches an object from the first parser and discards it,
/// then gets an object from the second parser.
///
/// # Arguments
/// * `first` The opening parser.
/// * `second` The second parser to get object.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::preceded;
/// use nom::bytes::complete::tag;
///
/// let mut parser = preceded(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", "efg")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "efg")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn preceded<I, O1, O2, E: ParseError<I>, F, G>(
  mut first: F,
  mut second: G,
) -> impl FnMut(I) -> IResult<I, O2, E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
{
  move |input: I| {
    let (input, _) = first.parse(input)?;
    second.parse(input)
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn precededc<I, O1, O2, E: ParseError<I>, F, G>(
  input: I,
  first: F,
  second: G,
) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  preceded(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the second parser and discards it.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to match an object.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::terminated;
/// use nom::bytes::complete::tag;
///
/// let mut parser = terminated(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", "abc")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "abc")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn terminated<I, O1, O2, E: ParseError<I>, F, G>(
  mut first: F,
  mut second: G,
) -> impl FnMut(I) -> IResult<I, O1, E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first.parse(input)?;
    second.parse(input).map(|(i, _)| (i, o1))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn terminatedc<I, O1, O2, E: ParseError<I>, F, G>(
  input: I,
  first: F,
  second: G,
) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  terminated(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the sep_parser and discards it,
/// then gets another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::separated_pair;
/// use nom::bytes::complete::tag;
///
/// let mut parser = separated_pair(tag("abc"), tag("|"), tag("efg"));
///
/// assert_eq!(parser("abc|efg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn separated_pair<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  mut first: F,
  mut sep: G,
  mut second: H,
) -> impl FnMut(I) -> IResult<I, (O1, O3), E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
  H: Parser<I, O3, E>,
{
  move |input: I| {
    let (input, o1) = first.parse(input)?;
    let (input, _) = sep.parse(input)?;
    second.parse(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn separated_pairc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  input: I,
  first: F,
  sep: G,
  second: H,
) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  separated_pair(first, sep, second)(input)
}

/// Matches an object from the first parser,
/// then gets an object from the sep_parser,
/// then matches another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::delimited;
/// use nom::bytes::complete::tag;
///
/// let mut parser = delimited(tag("abc"), tag("|"), tag("efg"));
///
/// assert_eq!(parser("abc|efg"), Ok(("", "|")));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", "|")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn delimited<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  mut first: F,
  mut sep: G,
  mut second: H,
) -> impl FnMut(I) -> IResult<I, O2, E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
  H: Parser<I, O3, E>,
{
  move |input: I| {
    let (input, _) = first.parse(input)?;
    let (input, o2) = sep.parse(input)?;
    second.parse(input).map(|(i, _)| (i, o2))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn delimitedc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  input: I,
  first: F,
  sep: G,
  second: H,
) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  delimited(first, sep, second)(input)
}

/// Helper trait for the tuple combinator.
///
/// This trait is implemented for tuples of parsers of up to 21 elements.
pub trait Tuple<I, O, E> {
  /// Parses the input and returns a tuple of results of each parser.
  fn parse(&mut self, input: I) -> IResult<I, O, E>;
}

impl<Input, Output, Error: ParseError<Input>, F: Parser<Input, Output, Error>>
  Tuple<Input, (Output,), Error> for (F,)
{
  fn parse(&mut self, input: Input) -> IResult<Input, (Output,), Error> {
    self.0.parse(input).map(|(i, o)| (i, (o,)))
  }
}

macro_rules! tuple_trait(
  ($name1:ident $ty1:ident, $name2: ident $ty2:ident, $($name:ident $ty:ident),*) => (
    tuple_trait!(__impl $name1 $ty1, $name2 $ty2; $($name $ty),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident, $($name2:ident $ty2:ident),*) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait!(__impl $($name $ty),+ , $name1 $ty1; $($name2 $ty2),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait_impl!($($name $ty),+, $name1 $ty1);
  );
);

macro_rules! tuple_trait_impl(
  ($($name:ident $ty: ident),+) => (
    impl<
      Input: Clone, $($ty),+ , Error: ParseError<Input>,
      $($name: Parser<Input, $ty, Error>),+
    > Tuple<Input, ( $($ty),+ ), Error> for ( $($name),+ ) {

      fn parse(&mut self, input: Input) -> IResult<Input, ( $($ty),+ ), Error> {
        tuple_trait_inner!(0, self, input, (), $($name)+)

      }
    }
  );
);

macro_rules! tuple_trait_inner(
  ($it:tt, $self:expr, $input:expr, (), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it.parse($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ( o ), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it.parse($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ($($parsed)* , o), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident) => ({
    let (i, o) = $self.$it.parse($input.clone())?;

    Ok((i, ($($parsed)* , o)))
  });
);

tuple_trait!(FnA A, FnB B, FnC C, FnD D, FnE E, FnF F, FnG G, FnH H, FnI I, FnJ J, FnK K, FnL L,
  FnM M, FnN N, FnO O, FnP P, FnQ Q, FnR R, FnS S, FnT T, FnU U);

///Aapplies a tuple of parsers one by one and returns their results as a tuple.
///
/// ```rust
/// # use nom::{Err, error::ErrorKind};
/// use nom::sequence::tuple;
/// use nom::character::complete::{alpha1, digit1};
/// let mut parser = tuple((alpha1, digit1, alpha1));
///
/// assert_eq!(parser("abc123def"), Ok(("", ("abc", "123", "def"))));
/// assert_eq!(parser("123def"), Err(Err::Error(("123def", ErrorKind::Alpha))));
/// ```
pub fn tuple<I, O, E: ParseError<I>, List: Tuple<I, O, E>>(
  mut l: List,
) -> impl FnMut(I) -> IResult<I, O, E> {
  move |i: I| l.parse(i)
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn single_element_tuples() {
    use crate::character::complete::alpha1;
    use crate::{error::ErrorKind, Err};

    let mut parser = tuple((alpha1,));
    assert_eq!(parser("abc123def"), Ok(("123def", ("abc",))));
    assert_eq!(
      parser("123def"),
      Err(Err::Error(("123def", ErrorKind::Alpha)))
    );
  }
}
<commit_msg>Fix typo<commit_after>//! Combinators applying parsers in sequence

#[macro_use]
mod macros;

use crate::error::ParseError;
use crate::internal::{IResult, Parser};

/// Gets an object from the first parser,
/// then gets another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::pair;
/// use nom::bytes::complete::tag;
///
/// let mut parser = pair(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abcefghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn pair<I, O1, O2, E: ParseError<I>, F, G>(
  mut first: F,
  mut second: G,
) -> impl FnMut(I) -> IResult<I, (O1, O2), E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first.parse(input)?;
    second.parse(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn pairc<I, O1, O2, E: ParseError<I>, F, G>(
  input: I,
  first: F,
  second: G,
) -> IResult<I, (O1, O2), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  pair(first, second)(input)
}

/// Matches an object from the first parser and discards it,
/// then gets an object from the second parser.
///
/// # Arguments
/// * `first` The opening parser.
/// * `second` The second parser to get object.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::preceded;
/// use nom::bytes::complete::tag;
///
/// let mut parser = preceded(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", "efg")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "efg")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn preceded<I, O1, O2, E: ParseError<I>, F, G>(
  mut first: F,
  mut second: G,
) -> impl FnMut(I) -> IResult<I, O2, E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
{
  move |input: I| {
    let (input, _) = first.parse(input)?;
    second.parse(input)
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn precededc<I, O1, O2, E: ParseError<I>, F, G>(
  input: I,
  first: F,
  second: G,
) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  preceded(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the second parser and discards it.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `second` The second parser to match an object.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::terminated;
/// use nom::bytes::complete::tag;
///
/// let mut parser = terminated(tag("abc"), tag("efg"));
///
/// assert_eq!(parser("abcefg"), Ok(("", "abc")));
/// assert_eq!(parser("abcefghij"), Ok(("hij", "abc")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn terminated<I, O1, O2, E: ParseError<I>, F, G>(
  mut first: F,
  mut second: G,
) -> impl FnMut(I) -> IResult<I, O1, E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
{
  move |input: I| {
    let (input, o1) = first.parse(input)?;
    second.parse(input).map(|(i, _)| (i, o1))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn terminatedc<I, O1, O2, E: ParseError<I>, F, G>(
  input: I,
  first: F,
  second: G,
) -> IResult<I, O1, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
{
  terminated(first, second)(input)
}

/// Gets an object from the first parser,
/// then matches an object from the sep_parser and discards it,
/// then gets another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::separated_pair;
/// use nom::bytes::complete::tag;
///
/// let mut parser = separated_pair(tag("abc"), tag("|"), tag("efg"));
///
/// assert_eq!(parser("abc|efg"), Ok(("", ("abc", "efg"))));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", ("abc", "efg"))));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn separated_pair<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  mut first: F,
  mut sep: G,
  mut second: H,
) -> impl FnMut(I) -> IResult<I, (O1, O3), E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
  H: Parser<I, O3, E>,
{
  move |input: I| {
    let (input, o1) = first.parse(input)?;
    let (input, _) = sep.parse(input)?;
    second.parse(input).map(|(i, o2)| (i, (o1, o2)))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn separated_pairc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  input: I,
  first: F,
  sep: G,
  second: H,
) -> IResult<I, (O1, O3), E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  separated_pair(first, sep, second)(input)
}

/// Matches an object from the first parser,
/// then gets an object from the sep_parser,
/// then matches another object from the second parser.
///
/// # Arguments
/// * `first` The first parser to apply.
/// * `sep` The separator parser to apply.
/// * `second` The second parser to apply.
/// ```rust
/// # use nom::{Err, error::ErrorKind, Needed};
/// # use nom::Needed::Size;
/// use nom::sequence::delimited;
/// use nom::bytes::complete::tag;
///
/// let mut parser = delimited(tag("abc"), tag("|"), tag("efg"));
///
/// assert_eq!(parser("abc|efg"), Ok(("", "|")));
/// assert_eq!(parser("abc|efghij"), Ok(("hij", "|")));
/// assert_eq!(parser(""), Err(Err::Error(("", ErrorKind::Tag))));
/// assert_eq!(parser("123"), Err(Err::Error(("123", ErrorKind::Tag))));
/// ```
pub fn delimited<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  mut first: F,
  mut sep: G,
  mut second: H,
) -> impl FnMut(I) -> IResult<I, O2, E>
where
  F: Parser<I, O1, E>,
  G: Parser<I, O2, E>,
  H: Parser<I, O3, E>,
{
  move |input: I| {
    let (input, _) = first.parse(input)?;
    let (input, o2) = sep.parse(input)?;
    second.parse(input).map(|(i, _)| (i, o2))
  }
}

// this implementation is used for type inference issues in macros
#[doc(hidden)]
pub fn delimitedc<I, O1, O2, O3, E: ParseError<I>, F, G, H>(
  input: I,
  first: F,
  sep: G,
  second: H,
) -> IResult<I, O2, E>
where
  F: Fn(I) -> IResult<I, O1, E>,
  G: Fn(I) -> IResult<I, O2, E>,
  H: Fn(I) -> IResult<I, O3, E>,
{
  delimited(first, sep, second)(input)
}

/// Helper trait for the tuple combinator.
///
/// This trait is implemented for tuples of parsers of up to 21 elements.
pub trait Tuple<I, O, E> {
  /// Parses the input and returns a tuple of results of each parser.
  fn parse(&mut self, input: I) -> IResult<I, O, E>;
}

impl<Input, Output, Error: ParseError<Input>, F: Parser<Input, Output, Error>>
  Tuple<Input, (Output,), Error> for (F,)
{
  fn parse(&mut self, input: Input) -> IResult<Input, (Output,), Error> {
    self.0.parse(input).map(|(i, o)| (i, (o,)))
  }
}

macro_rules! tuple_trait(
  ($name1:ident $ty1:ident, $name2: ident $ty2:ident, $($name:ident $ty:ident),*) => (
    tuple_trait!(__impl $name1 $ty1, $name2 $ty2; $($name $ty),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident, $($name2:ident $ty2:ident),*) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait!(__impl $($name $ty),+ , $name1 $ty1; $($name2 $ty2),*);
  );
  (__impl $($name:ident $ty: ident),+; $name1:ident $ty1:ident) => (
    tuple_trait_impl!($($name $ty),+);
    tuple_trait_impl!($($name $ty),+, $name1 $ty1);
  );
);

macro_rules! tuple_trait_impl(
  ($($name:ident $ty: ident),+) => (
    impl<
      Input: Clone, $($ty),+ , Error: ParseError<Input>,
      $($name: Parser<Input, $ty, Error>),+
    > Tuple<Input, ( $($ty),+ ), Error> for ( $($name),+ ) {

      fn parse(&mut self, input: Input) -> IResult<Input, ( $($ty),+ ), Error> {
        tuple_trait_inner!(0, self, input, (), $($name)+)

      }
    }
  );
);

macro_rules! tuple_trait_inner(
  ($it:tt, $self:expr, $input:expr, (), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it.parse($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ( o ), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident $($id:ident)+) => ({
    let (i, o) = $self.$it.parse($input.clone())?;

    succ!($it, tuple_trait_inner!($self, i, ($($parsed)* , o), $($id)+))
  });
  ($it:tt, $self:expr, $input:expr, ($($parsed:tt)*), $head:ident) => ({
    let (i, o) = $self.$it.parse($input.clone())?;

    Ok((i, ($($parsed)* , o)))
  });
);

tuple_trait!(FnA A, FnB B, FnC C, FnD D, FnE E, FnF F, FnG G, FnH H, FnI I, FnJ J, FnK K, FnL L,
  FnM M, FnN N, FnO O, FnP P, FnQ Q, FnR R, FnS S, FnT T, FnU U);

///Applies a tuple of parsers one by one and returns their results as a tuple.
///
/// ```rust
/// # use nom::{Err, error::ErrorKind};
/// use nom::sequence::tuple;
/// use nom::character::complete::{alpha1, digit1};
/// let mut parser = tuple((alpha1, digit1, alpha1));
///
/// assert_eq!(parser("abc123def"), Ok(("", ("abc", "123", "def"))));
/// assert_eq!(parser("123def"), Err(Err::Error(("123def", ErrorKind::Alpha))));
/// ```
pub fn tuple<I, O, E: ParseError<I>, List: Tuple<I, O, E>>(
  mut l: List,
) -> impl FnMut(I) -> IResult<I, O, E> {
  move |i: I| l.parse(i)
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn single_element_tuples() {
    use crate::character::complete::alpha1;
    use crate::{error::ErrorKind, Err};

    let mut parser = tuple((alpha1,));
    assert_eq!(parser("abc123def"), Ok(("123def", ("abc",))));
    assert_eq!(
      parser("123def"),
      Err(Err::Error(("123def", ErrorKind::Alpha)))
    );
  }
}
<|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/. */

//! The task that handles all rendering/painting.

use buffer_map::BufferMap;
use display_list::optimizer::DisplayListOptimizer;
use display_list::DisplayList;
use font_context::FontContext;
use render_context::RenderContext;

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers;
use native;
use rustrt::task;
use rustrt::task::TaskOpts;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerId};
use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry;
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use sync::Arc;
use font_cache_task::FontCacheTask;

/// Information about a layer that layout sends to the painting task.
pub struct RenderLayer {
    /// A per-pipeline ID describing this layer that should be stable across reflows.
    pub id: LayerId,
    /// The display list describing the contents of this layer.
    pub display_list: Arc<DisplayList>,
    /// The position of the layer in pixels.
    pub position: Rect<uint>,
    /// The color of the background in this layer. Used for unrendered content.
    pub background_color: Color,
    /// The scrolling policy of this layer.
    pub scroll_policy: ScrollPolicy,
}

pub struct ReRenderRequest {
    pub buffer_requests: Vec<BufferRequest>,
    pub scale: f32,
    pub layer_id: LayerId,
    pub epoch: Epoch,
}

pub enum Msg {
    RenderMsg(SmallVec1<RenderLayer>),
    ReRenderMsg(Vec<ReRenderRequest>),
    UnusedBufferMsg(Vec<Box<LayerBuffer>>),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Option<Sender<()>>),
}

#[deriving(Clone)]
pub struct RenderChan(Sender<Msg>);

impl RenderChan {
    pub fn new() -> (Receiver<Msg>, RenderChan) {
        let (chan, port) = channel();
        (port, RenderChan(chan))
    }

    pub fn send(&self, msg: Msg) {
        let &RenderChan(ref chan) = self;
        assert!(chan.send_opt(msg).is_ok(), "RenderChan.send: render port closed")
    }

    pub fn send_opt(&self, msg: Msg) -> Result<(), Msg> {
        let &RenderChan(ref chan) = self;
        chan.send_opt(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
pub enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C> {
    id: PipelineId,
    port: Receiver<Msg>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: Box<FontContext>,
    opts: Opts,

    /// A channel to the time profiler.
    time_profiler_chan: TimeProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layers to be rendered.
    render_layers: SmallVec1<RenderLayer>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

fn initialize_layers<C:RenderListener>(
                     compositor: &mut C,
                     pipeline_id: PipelineId,
                     epoch: Epoch,
                     render_layers: &[RenderLayer]) {
    let metadata = render_layers.iter().map(|render_layer| {
        LayerMetadata {
            id: render_layer.id,
            position: render_layer.position,
            background_color: render_layer.background_color,
            scroll_policy: render_layer.scroll_policy,
        }
    }).collect();
    compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
}

impl<C:RenderListener + Send> RenderTask<C> {
    pub fn create(id: PipelineId,
                  port: Receiver<Msg>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  font_cache_task: FontCacheTask,
                  failure_msg: Failure,
                  opts: Opts,
                  time_profiler_chan: TimeProfilerChan,
                  shutdown_chan: Sender<()>) {

        let ConstellationChan(c) = constellation_chan.clone();
        let fc = font_cache_task.clone();

        let mut task_opts = TaskOpts::new();
        task_opts.name = Some("RenderTask".into_maybe_owned());
        task_opts.on_exit = Some(proc(result: task::Result) {
            match result {
                Ok(()) => {},
                Err(..) => {
                    c.send(FailureMsg(failure_msg));
                }
            }
        });
        native::task::spawn_opts(task_opts, proc() {
            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: box FontContext::new(fc.clone()),
                    opts: opts,
                    time_profiler_chan: time_profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layers: SmallVec1::new(),

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                match render_task.native_graphics_context.as_ref() {
                    Some(ctx) => render_task.buffer_map.clear(ctx),
                    None => (),
                }
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layers) => {
                    self.epoch.next();
                    self.render_layers = render_layers;

                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    initialize_layers(&mut self.compositor,
                                      self.id,
                                      self.epoch,
                                      self.render_layers.as_slice());
                }
                ReRenderMsg(requests) => {
                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        self.compositor.rerendermsg_discarded();
                        continue;
                    }

                    let mut replies = Vec::new();
                    for ReRenderRequest { buffer_requests, scale, layer_id, epoch }
                          in requests.move_iter() {
                        if self.epoch == epoch {
                            self.render(&mut replies, buffer_requests, scale, layer_id);
                        } else {
                            debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                        }
                    }

                    debug!("render_task: returning surfaces");
                    self.compositor.paint(self.id, self.epoch, replies);
                }
                UnusedBufferMsg(unused_buffers) => {
                    for buffer in unused_buffers.move_iter().rev() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;

                    // Here we assume that the main layer—the layer responsible for the page size—
                    // is the first layer. This is a pretty fragile assumption. It will be fixed
                    // once we use the layers-based scrolling infrastructure for all scrolling.
                    if self.render_layers.len() > 1 {
                        self.epoch.next();
                        initialize_layers(&mut self.compositor,
                                          self.id,
                                          self.epoch,
                                          self.render_layers.as_slice());
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.map(|ch| ch.send(()));
                    break;
                }
            }
        }
    }

    /// Renders one layer and sends the tiles back to the layer.
    fn render(&mut self,
              replies: &mut Vec<(LayerId, Box<LayerBufferSet>)>,
              tiles: Vec<BufferRequest>,
              scale: f32,
              layer_id: LayerId) {
        time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = vec!();

            // Find the appropriate render layer.
            let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
                Some(render_layer) => render_layer,
                None => return,
            };

            self.compositor.set_render_state(RenderingRenderState);

            // Divide up the layer into tiles.
            for tile in tiles.iter() {
                // Optimize the display list for this tile.
                let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect);
                let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(),
                                                          page_rect_au);
                let display_list = optimizer.optimize();

                let width = tile.screen_rect.size.width;
                let height = tile.screen_rect.size.height;

                let size = Size2D(width as i32, height as i32);
                let draw_target = match self.graphics_context {
                    CpuGraphicsContext => {
                        DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                    }
                    GpuGraphicsContext => {
                        // FIXME(pcwalton): Cache the components of draw targets
                        // (texture color buffer, renderbuffers) instead of recreating them.
                        let draw_target =
                            DrawTarget::new_with_fbo(self.opts.render_backend,
                                                     native_graphics_context!(self),
                                                     size,
                                                     B8G8R8A8);
                        draw_target.make_current();
                        draw_target
                    }
                };

                {
                    // Build the render context.
                    let mut ctx = RenderContext {
                        draw_target: &draw_target,
                        font_ctx: &mut self.font_ctx,
                        opts: &self.opts,
                        page_rect: tile.page_rect,
                        screen_rect: tile.screen_rect,
                    };

                    // Apply the translation to render the tile we want.
                    let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                    let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                    let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                  -(tile.page_rect.origin.y) as AzFloat);
                    let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
                                                  -(render_layer.position.origin.y as AzFloat));

                    ctx.draw_target.set_transform(&matrix);

                    // Clear the buffer.
                    ctx.clear();

                    // Draw the display list.
                    profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
                        display_list.draw_into_context(&mut ctx);
                        ctx.draw_target.flush();
                    });
                }

                // Extract the texture from the draw target and place it into its slot in the
                // buffer. If using CPU rendering, upload it first.
                //
                // FIXME(pcwalton): We should supply the texture and native surface *to* the
                // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                let buffer = match self.graphics_context {
                    CpuGraphicsContext => {
                        let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                            Some(buffer) => {
                                let mut buffer = buffer;
                                buffer.rect = tile.page_rect;
                                buffer.screen_pos = tile.screen_rect;
                                buffer.resolution = scale;
                                buffer.native_surface.mark_wont_leak();
                                buffer.painted_with_cpu = true;
                                buffer
                            }
                            None => {
                                // Create an empty native surface. We mark it as not leaking
                                // in case it dies in transit to the compositor task.
                                let mut native_surface: NativeSurface =
                                    layers::platform::surface::NativeSurfaceMethods::new(
                                        native_graphics_context!(self),
                                        Size2D(width as i32, height as i32),
                                        width as i32 * 4);
                                native_surface.mark_wont_leak();

                                box LayerBuffer {
                                    native_surface: native_surface,
                                    rect: tile.page_rect,
                                    screen_pos: tile.screen_rect,
                                    resolution: scale,
                                    stride: (width * 4) as uint,
                                    painted_with_cpu: true,
                                }
                            }
                        };

                        draw_target.snapshot().get_data_surface().with_data(|data| {
                            buffer.native_surface.upload(native_graphics_context!(self), data);
                            debug!("RENDERER uploading to native surface {:d}",
                                   buffer.native_surface.get_id() as int);
                        });

                        buffer
                    }
                    GpuGraphicsContext => {
                        draw_target.make_current();
                        let StolenGLResources {
                            surface: native_surface
                        } = draw_target.steal_gl_resources().unwrap();

                        // We mark the native surface as not leaking in case the surfaces
                        // die on their way to the compositor task.
                        let mut native_surface: NativeSurface =
                            NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                        native_surface.mark_wont_leak();

                        box LayerBuffer {
                            native_surface: native_surface,
                            rect: tile.page_rect,
                            screen_pos: tile.screen_rect,
                            resolution: scale,
                            stride: (width * 4) as uint,
                            painted_with_cpu: false,
                        }
                    }
                };

                new_buffers.push(buffer);
            }

            let layer_buffer_set = box LayerBufferSet {
                buffers: new_buffers,
            };

            replies.push((render_layer.id, layer_buffer_set));
            self.compositor.set_render_state(IdleRenderState);
        })
    }
}

<commit_msg>Move RenderState manipulation out of a loop so that we thrash it less<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/. */

//! The task that handles all rendering/painting.

use buffer_map::BufferMap;
use display_list::optimizer::DisplayListOptimizer;
use display_list::DisplayList;
use font_context::FontContext;
use render_context::RenderContext;

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers;
use native;
use rustrt::task;
use rustrt::task::TaskOpts;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerId};
use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry;
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use sync::Arc;
use font_cache_task::FontCacheTask;

/// Information about a layer that layout sends to the painting task.
pub struct RenderLayer {
    /// A per-pipeline ID describing this layer that should be stable across reflows.
    pub id: LayerId,
    /// The display list describing the contents of this layer.
    pub display_list: Arc<DisplayList>,
    /// The position of the layer in pixels.
    pub position: Rect<uint>,
    /// The color of the background in this layer. Used for unrendered content.
    pub background_color: Color,
    /// The scrolling policy of this layer.
    pub scroll_policy: ScrollPolicy,
}

pub struct ReRenderRequest {
    pub buffer_requests: Vec<BufferRequest>,
    pub scale: f32,
    pub layer_id: LayerId,
    pub epoch: Epoch,
}

pub enum Msg {
    RenderMsg(SmallVec1<RenderLayer>),
    ReRenderMsg(Vec<ReRenderRequest>),
    UnusedBufferMsg(Vec<Box<LayerBuffer>>),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Option<Sender<()>>),
}

#[deriving(Clone)]
pub struct RenderChan(Sender<Msg>);

impl RenderChan {
    pub fn new() -> (Receiver<Msg>, RenderChan) {
        let (chan, port) = channel();
        (port, RenderChan(chan))
    }

    pub fn send(&self, msg: Msg) {
        let &RenderChan(ref chan) = self;
        assert!(chan.send_opt(msg).is_ok(), "RenderChan.send: render port closed")
    }

    pub fn send_opt(&self, msg: Msg) -> Result<(), Msg> {
        let &RenderChan(ref chan) = self;
        chan.send_opt(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
pub enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C> {
    id: PipelineId,
    port: Receiver<Msg>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: Box<FontContext>,
    opts: Opts,

    /// A channel to the time profiler.
    time_profiler_chan: TimeProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layers to be rendered.
    render_layers: SmallVec1<RenderLayer>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

fn initialize_layers<C:RenderListener>(
                     compositor: &mut C,
                     pipeline_id: PipelineId,
                     epoch: Epoch,
                     render_layers: &[RenderLayer]) {
    let metadata = render_layers.iter().map(|render_layer| {
        LayerMetadata {
            id: render_layer.id,
            position: render_layer.position,
            background_color: render_layer.background_color,
            scroll_policy: render_layer.scroll_policy,
        }
    }).collect();
    compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
}

impl<C:RenderListener + Send> RenderTask<C> {
    pub fn create(id: PipelineId,
                  port: Receiver<Msg>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  font_cache_task: FontCacheTask,
                  failure_msg: Failure,
                  opts: Opts,
                  time_profiler_chan: TimeProfilerChan,
                  shutdown_chan: Sender<()>) {

        let ConstellationChan(c) = constellation_chan.clone();
        let fc = font_cache_task.clone();

        let mut task_opts = TaskOpts::new();
        task_opts.name = Some("RenderTask".into_maybe_owned());
        task_opts.on_exit = Some(proc(result: task::Result) {
            match result {
                Ok(()) => {},
                Err(..) => {
                    c.send(FailureMsg(failure_msg));
                }
            }
        });
        native::task::spawn_opts(task_opts, proc() {
            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: box FontContext::new(fc.clone()),
                    opts: opts,
                    time_profiler_chan: time_profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layers: SmallVec1::new(),

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                match render_task.native_graphics_context.as_ref() {
                    Some(ctx) => render_task.buffer_map.clear(ctx),
                    None => (),
                }
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layers) => {
                    self.epoch.next();
                    self.render_layers = render_layers;

                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    initialize_layers(&mut self.compositor,
                                      self.id,
                                      self.epoch,
                                      self.render_layers.as_slice());
                }
                ReRenderMsg(requests) => {
                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        self.compositor.rerendermsg_discarded();
                        continue;
                    }

                    self.compositor.set_render_state(RenderingRenderState);

                    let mut replies = Vec::new();
                    for ReRenderRequest { buffer_requests, scale, layer_id, epoch }
                          in requests.move_iter() {
                        if self.epoch == epoch {
                            self.render(&mut replies, buffer_requests, scale, layer_id);
                        } else {
                            debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                        }
                    }

                    self.compositor.set_render_state(IdleRenderState);

                    debug!("render_task: returning surfaces");
                    self.compositor.paint(self.id, self.epoch, replies);
                }
                UnusedBufferMsg(unused_buffers) => {
                    for buffer in unused_buffers.move_iter().rev() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;

                    // Here we assume that the main layer—the layer responsible for the page size—
                    // is the first layer. This is a pretty fragile assumption. It will be fixed
                    // once we use the layers-based scrolling infrastructure for all scrolling.
                    if self.render_layers.len() > 1 {
                        self.epoch.next();
                        initialize_layers(&mut self.compositor,
                                          self.id,
                                          self.epoch,
                                          self.render_layers.as_slice());
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.map(|ch| ch.send(()));
                    break;
                }
            }
        }
    }

    /// Renders one layer and sends the tiles back to the layer.
    fn render(&mut self,
              replies: &mut Vec<(LayerId, Box<LayerBufferSet>)>,
              tiles: Vec<BufferRequest>,
              scale: f32,
              layer_id: LayerId) {
        time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = vec!();

            // Find the appropriate render layer.
            let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
                Some(render_layer) => render_layer,
                None => return,
            };

            // Divide up the layer into tiles.
            for tile in tiles.iter() {
                // Optimize the display list for this tile.
                let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect);
                let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(),
                                                          page_rect_au);
                let display_list = optimizer.optimize();

                let width = tile.screen_rect.size.width;
                let height = tile.screen_rect.size.height;

                let size = Size2D(width as i32, height as i32);
                let draw_target = match self.graphics_context {
                    CpuGraphicsContext => {
                        DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                    }
                    GpuGraphicsContext => {
                        // FIXME(pcwalton): Cache the components of draw targets
                        // (texture color buffer, renderbuffers) instead of recreating them.
                        let draw_target =
                            DrawTarget::new_with_fbo(self.opts.render_backend,
                                                     native_graphics_context!(self),
                                                     size,
                                                     B8G8R8A8);
                        draw_target.make_current();
                        draw_target
                    }
                };

                {
                    // Build the render context.
                    let mut ctx = RenderContext {
                        draw_target: &draw_target,
                        font_ctx: &mut self.font_ctx,
                        opts: &self.opts,
                        page_rect: tile.page_rect,
                        screen_rect: tile.screen_rect,
                    };

                    // Apply the translation to render the tile we want.
                    let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                    let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                    let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                  -(tile.page_rect.origin.y) as AzFloat);
                    let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
                                                  -(render_layer.position.origin.y as AzFloat));

                    ctx.draw_target.set_transform(&matrix);

                    // Clear the buffer.
                    ctx.clear();

                    // Draw the display list.
                    profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
                        display_list.draw_into_context(&mut ctx);
                        ctx.draw_target.flush();
                    });
                }

                // Extract the texture from the draw target and place it into its slot in the
                // buffer. If using CPU rendering, upload it first.
                //
                // FIXME(pcwalton): We should supply the texture and native surface *to* the
                // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                let buffer = match self.graphics_context {
                    CpuGraphicsContext => {
                        let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                            Some(buffer) => {
                                let mut buffer = buffer;
                                buffer.rect = tile.page_rect;
                                buffer.screen_pos = tile.screen_rect;
                                buffer.resolution = scale;
                                buffer.native_surface.mark_wont_leak();
                                buffer.painted_with_cpu = true;
                                buffer
                            }
                            None => {
                                // Create an empty native surface. We mark it as not leaking
                                // in case it dies in transit to the compositor task.
                                let mut native_surface: NativeSurface =
                                    layers::platform::surface::NativeSurfaceMethods::new(
                                        native_graphics_context!(self),
                                        Size2D(width as i32, height as i32),
                                        width as i32 * 4);
                                native_surface.mark_wont_leak();

                                box LayerBuffer {
                                    native_surface: native_surface,
                                    rect: tile.page_rect,
                                    screen_pos: tile.screen_rect,
                                    resolution: scale,
                                    stride: (width * 4) as uint,
                                    painted_with_cpu: true,
                                }
                            }
                        };

                        draw_target.snapshot().get_data_surface().with_data(|data| {
                            buffer.native_surface.upload(native_graphics_context!(self), data);
                            debug!("RENDERER uploading to native surface {:d}",
                                   buffer.native_surface.get_id() as int);
                        });

                        buffer
                    }
                    GpuGraphicsContext => {
                        draw_target.make_current();
                        let StolenGLResources {
                            surface: native_surface
                        } = draw_target.steal_gl_resources().unwrap();

                        // We mark the native surface as not leaking in case the surfaces
                        // die on their way to the compositor task.
                        let mut native_surface: NativeSurface =
                            NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                        native_surface.mark_wont_leak();

                        box LayerBuffer {
                            native_surface: native_surface,
                            rect: tile.page_rect,
                            screen_pos: tile.screen_rect,
                            resolution: scale,
                            stride: (width * 4) as uint,
                            painted_with_cpu: false,
                        }
                    }
                };

                new_buffers.push(buffer);
            }

            let layer_buffer_set = box LayerBufferSet {
                buffers: new_buffers,
            };

            replies.push((render_layer.id, layer_buffer_set));
        })
    }
}

<|endoftext|>