<commit_before>/// A page, can be a blog post or a basic page
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fs::{read_dir};
use std::path::{Path, PathBuf};
use std::result::Result as StdResult;

use tera::{Tera, Context};
use serde::ser::{SerializeStruct, self};
use slug::slugify;

use errors::{Result, ResultExt};
use config::Config;
use front_matter::{FrontMatter, split_content};
use markdown::markdown_to_html;
use utils::{read_file, find_content_components};

/// Looks into the current folder for the path and see if there's anything that is not a .md
/// file. Those will be copied next to the rendered .html file
fn find_related_assets(path: &Path) -> Vec<PathBuf> {
    let mut assets = vec![];

    for entry in read_dir(path).unwrap().filter_map(|e| e.ok()) {
        let entry_path = entry.path();
        if entry_path.is_file() {
            match entry_path.extension() {
                Some(e) => match e.to_str() {
                    Some("md") => continue,
                    _ => assets.push(entry_path.to_path_buf()),
                },
                None => continue,
            }
        }
    }

    assets
}

#[derive(Clone, Debug, PartialEq)]
pub struct Page {
    /// The .md path
    pub file_path: PathBuf,
    /// The .md path, starting from the content directory, with / slashes
    pub relative_path: String,
    /// The parent directory of the file. Is actually the grand parent directory
    /// if it's an asset folder
    pub parent_path: PathBuf,
    /// The name of the .md file
    pub file_name: String,
    /// The directories above our .md file
    /// for example a file at content/kb/solutions/blabla.md will have 2 components:
    /// `kb` and `solutions`
    pub components: Vec<String>,
    /// The actual content of the page, in markdown
    pub raw_content: String,
    /// All the non-md files we found next to the .md file
    pub assets: Vec<PathBuf>,
    /// The HTML rendered of the page
    pub content: String,
    /// The front matter meta-data
    pub meta: FrontMatter,

    /// The slug of that page.
    /// First tries to find the slug in the meta and defaults to filename otherwise
    pub slug: String,
    /// The URL path of the page
    pub path: String,
    /// The full URL for that page
    pub permalink: String,
    /// The summary for the article, defaults to None
    /// When <!-- more --> is found in the text, will take the content up to that part
    /// as summary
    pub summary: Option<String>,

    /// The previous page, by date globally
    pub previous: Option<Box<Page>>,
    /// The previous page, by date only for the section the page is in
    pub previous_in_section: Option<Box<Page>>,
    /// The next page, by date
    pub next: Option<Box<Page>>,
    /// The next page, by date only for the section the page is in
    pub next_in_section: Option<Box<Page>>,
}

impl Page {
    pub fn new(meta: FrontMatter) -> Page {
        Page {
            file_path: PathBuf::new(),
            relative_path: String::new(),
            parent_path: PathBuf::new(),
            file_name: "".to_string(),
            components: vec![],
            raw_content: "".to_string(),
            assets: vec![],
            content: "".to_string(),
            slug: "".to_string(),
            path: "".to_string(),
            permalink: "".to_string(),
            summary: None,
            meta: meta,
            previous: None,
            previous_in_section: None,
            next: None,
            next_in_section: None,
        }
    }

    pub fn has_date(&self) -> bool {
        self.meta.date.is_some()
    }

    /// Get word count and estimated reading time
    pub fn get_reading_analytics(&self) -> (usize, usize) {
        // Only works for latin language but good enough for a start
        let word_count: usize = self.raw_content.split_whitespace().count();

        // https://help.medium.com/hc/en-us/articles/214991667-Read-time
        // 275 seems a bit too high though
        (word_count, (word_count / 200))
    }

    /// Parse a page given the content of the .md file
    /// Files without front matter or with invalid front matter are considered
    /// erroneous
    pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Page> {
        // 1. separate front matter from content
        let (meta, content) = split_content(file_path, content)?;
        let mut page = Page::new(meta);
        page.file_path = file_path.to_path_buf();
        page.parent_path = page.file_path.parent().unwrap().to_path_buf();
        page.raw_content = content;

        let path = Path::new(file_path);
        page.file_name = path.file_stem().unwrap().to_string_lossy().to_string();

        page.slug = {
            if let Some(ref slug) = page.meta.slug {
                slug.trim().to_string()
            } else {
                slugify(page.file_name.clone())
            }
        };
        page.components = find_content_components(&page.file_path);
        page.relative_path = format!("{}/{}.md", page.components.join("/"), page.file_name);

        // 4. Find sections
        // Pages with custom urls exists outside of sections
        if let Some(ref u) = page.meta.url {
            page.path = u.trim().to_string();
        } else if !page.components.is_empty() {
            // If we have a folder with an asset, don't consider it as a component
            if page.file_name == "index" {
                page.components.pop();
                // also set parent_path to grandparent instead
                page.parent_path = page.parent_path.parent().unwrap().to_path_buf();
            }

            // Don't add a trailing slash to sections
            page.path = format!("{}/{}", page.components.join("/"), page.slug);
        } else {
            page.path = page.slug.clone();
        }

        page.permalink = config.make_permalink(&page.path);

        Ok(page)
    }

    /// Read and parse a .md file into a Page struct
    pub fn from_file<P: AsRef<Path>>(path: P, config: &Config) -> Result<Page> {
        let path = path.as_ref();
        let content = read_file(path)?;
        let mut page = Page::parse(path, &content, config)?;
        page.assets = find_related_assets(path.parent().unwrap());

        if !page.assets.is_empty() && page.file_name != "index" {
            bail!("Page `{}` has assets but is not named index.md", path.display());
        }

        Ok(page)

    }

    /// We need access to all pages url to render links relative to content
    /// so that can't happen at the same time as parsing
    pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<()> {
        self.content = markdown_to_html(&self.raw_content, permalinks, tera, config)?;

        if self.raw_content.contains("<!-- more -->") {
            self.summary = Some({
                let summary = self.raw_content.splitn(2, "<!-- more -->").collect::<Vec<&str>>()[0];
                markdown_to_html(summary, permalinks, tera, config)?
            })
        }

        Ok(())
    }

    /// Renders the page using the default layout, unless specified in front-matter
    pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
        let tpl_name = match self.meta.template {
            Some(ref l) => l.to_string(),
            None => "page.html".to_string()
        };

        let mut context = Context::new();
        context.add("config", config);
        context.add("page", self);
        context.add("current_url", &self.permalink);
        context.add("current_path", &self.path);

        tera.render(&tpl_name, &context)
            .chain_err(|| format!("Failed to render page '{}'", self.file_path.display()))
    }
}

impl ser::Serialize for Page {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
        let mut state = serializer.serialize_struct("page", 18)?;
        state.serialize_field("content", &self.content)?;
        state.serialize_field("title", &self.meta.title)?;
        state.serialize_field("description", &self.meta.description)?;
        state.serialize_field("date", &self.meta.date)?;
        state.serialize_field("slug", &self.slug)?;
        state.serialize_field("path", &format!("/{}", self.path))?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("summary", &self.summary)?;
        state.serialize_field("tags", &self.meta.tags)?;
        state.serialize_field("draft", &self.meta.draft)?;
        state.serialize_field("category", &self.meta.category)?;
        state.serialize_field("extra", &self.meta.extra)?;
        let (word_count, reading_time) = self.get_reading_analytics();
        state.serialize_field("word_count", &word_count)?;
        state.serialize_field("reading_time", &reading_time)?;
        state.serialize_field("previous", &self.previous)?;
        state.serialize_field("previous_in_section", &self.previous_in_section)?;
        state.serialize_field("next", &self.next)?;
        state.serialize_field("next_in_section", &self.next_in_section)?;
        state.end()
    }
}

impl PartialOrd for Page {
    fn partial_cmp(&self, other: &Page) -> Option<Ordering> {
        if self.meta.date.is_none() {
            return Some(Ordering::Less);
        }

        if other.meta.date.is_none() {
            return Some(Ordering::Greater);
        }

        let this_date = self.meta.parse_date().unwrap();
        let other_date = other.meta.parse_date().unwrap();

        if this_date > other_date {
            return Some(Ordering::Less);
        }
        if this_date < other_date {
            return Some(Ordering::Greater);
        }

        Some(Ordering::Equal)
    }
}

/// Horribly inefficient way to set previous and next on each pages
/// So many clones
pub fn populate_previous_and_next_pages(input: &[Page], in_section: bool) -> Vec<Page> {
    let pages = input.to_vec();
    let mut res = Vec::new();

    // the input is sorted from most recent to least recent already
    for (i, page) in input.iter().enumerate() {
        let mut new_page = page.clone();

        if new_page.has_date() {
            if i > 0 {
                let next = &pages[i - 1];
                if next.has_date() {
                    if in_section {
                        new_page.next_in_section = Some(Box::new(next.clone()));
                    } else {
                        new_page.next = Some(Box::new(next.clone()));
                    }
                }
            }

            if i < input.len() - 1 {
                let previous = &pages[i + 1];
                if previous.has_date() {
                    if in_section {
                        new_page.previous_in_section = Some(Box::new(previous.clone()));
                    } else {
                        new_page.previous = Some(Box::new(previous.clone()));
                    }
                }
            }
        }
        res.push(new_page);
    }

    res
}

#[cfg(test)]
mod tests {
    use tempdir::TempDir;

    use std::fs::File;

    use super::{find_related_assets};

    #[test]
    fn test_find_related_assets() {
        let tmp_dir = TempDir::new("example").expect("create temp dir");
        File::create(tmp_dir.path().join("index.md")).unwrap();
        File::create(tmp_dir.path().join("example.js")).unwrap();
        File::create(tmp_dir.path().join("graph.jpg")).unwrap();
        File::create(tmp_dir.path().join("fail.png")).unwrap();

        let assets = find_related_assets(tmp_dir.path());
        assert_eq!(assets.len(), 3);
        assert_eq!(assets.iter().filter(|p| p.extension().unwrap() != "md").count(), 3);
        assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "example.js").count(), 1);
        assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "graph.jpg").count(), 1);
        assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "fail.png").count(), 1);
    }
}
<commit_msg>Add asset list in file not name index.md error message<commit_after>/// A page, can be a blog post or a basic page
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fs::{read_dir};
use std::path::{Path, PathBuf};
use std::result::Result as StdResult;

use tera::{Tera, Context};
use serde::ser::{SerializeStruct, self};
use slug::slugify;

use errors::{Result, ResultExt};
use config::Config;
use front_matter::{FrontMatter, split_content};
use markdown::markdown_to_html;
use utils::{read_file, find_content_components};

/// Looks into the current folder for the path and see if there's anything that is not a .md
/// file. Those will be copied next to the rendered .html file
fn find_related_assets(path: &Path) -> Vec<PathBuf> {
    let mut assets = vec![];

    for entry in read_dir(path).unwrap().filter_map(|e| e.ok()) {
        let entry_path = entry.path();
        if entry_path.is_file() {
            match entry_path.extension() {
                Some(e) => match e.to_str() {
                    Some("md") => continue,
                    _ => assets.push(entry_path.to_path_buf()),
                },
                None => continue,
            }
        }
    }

    assets
}

#[derive(Clone, Debug, PartialEq)]
pub struct Page {
    /// The .md path
    pub file_path: PathBuf,
    /// The .md path, starting from the content directory, with / slashes
    pub relative_path: String,
    /// The parent directory of the file. Is actually the grand parent directory
    /// if it's an asset folder
    pub parent_path: PathBuf,
    /// The name of the .md file
    pub file_name: String,
    /// The directories above our .md file
    /// for example a file at content/kb/solutions/blabla.md will have 2 components:
    /// `kb` and `solutions`
    pub components: Vec<String>,
    /// The actual content of the page, in markdown
    pub raw_content: String,
    /// All the non-md files we found next to the .md file
    pub assets: Vec<PathBuf>,
    /// The HTML rendered of the page
    pub content: String,
    /// The front matter meta-data
    pub meta: FrontMatter,

    /// The slug of that page.
    /// First tries to find the slug in the meta and defaults to filename otherwise
    pub slug: String,
    /// The URL path of the page
    pub path: String,
    /// The full URL for that page
    pub permalink: String,
    /// The summary for the article, defaults to None
    /// When <!-- more --> is found in the text, will take the content up to that part
    /// as summary
    pub summary: Option<String>,

    /// The previous page, by date globally
    pub previous: Option<Box<Page>>,
    /// The previous page, by date only for the section the page is in
    pub previous_in_section: Option<Box<Page>>,
    /// The next page, by date
    pub next: Option<Box<Page>>,
    /// The next page, by date only for the section the page is in
    pub next_in_section: Option<Box<Page>>,
}

impl Page {
    pub fn new(meta: FrontMatter) -> Page {
        Page {
            file_path: PathBuf::new(),
            relative_path: String::new(),
            parent_path: PathBuf::new(),
            file_name: "".to_string(),
            components: vec![],
            raw_content: "".to_string(),
            assets: vec![],
            content: "".to_string(),
            slug: "".to_string(),
            path: "".to_string(),
            permalink: "".to_string(),
            summary: None,
            meta: meta,
            previous: None,
            previous_in_section: None,
            next: None,
            next_in_section: None,
        }
    }

    pub fn has_date(&self) -> bool {
        self.meta.date.is_some()
    }

    /// Get word count and estimated reading time
    pub fn get_reading_analytics(&self) -> (usize, usize) {
        // Only works for latin language but good enough for a start
        let word_count: usize = self.raw_content.split_whitespace().count();

        // https://help.medium.com/hc/en-us/articles/214991667-Read-time
        // 275 seems a bit too high though
        (word_count, (word_count / 200))
    }

    /// Parse a page given the content of the .md file
    /// Files without front matter or with invalid front matter are considered
    /// erroneous
    pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Page> {
        // 1. separate front matter from content
        let (meta, content) = split_content(file_path, content)?;
        let mut page = Page::new(meta);
        page.file_path = file_path.to_path_buf();
        page.parent_path = page.file_path.parent().unwrap().to_path_buf();
        page.raw_content = content;

        let path = Path::new(file_path);
        page.file_name = path.file_stem().unwrap().to_string_lossy().to_string();

        page.slug = {
            if let Some(ref slug) = page.meta.slug {
                slug.trim().to_string()
            } else {
                slugify(page.file_name.clone())
            }
        };
        page.components = find_content_components(&page.file_path);
        page.relative_path = format!("{}/{}.md", page.components.join("/"), page.file_name);

        // 4. Find sections
        // Pages with custom urls exists outside of sections
        if let Some(ref u) = page.meta.url {
            page.path = u.trim().to_string();
        } else if !page.components.is_empty() {
            // If we have a folder with an asset, don't consider it as a component
            if page.file_name == "index" {
                page.components.pop();
                // also set parent_path to grandparent instead
                page.parent_path = page.parent_path.parent().unwrap().to_path_buf();
            }

            // Don't add a trailing slash to sections
            page.path = format!("{}/{}", page.components.join("/"), page.slug);
        } else {
            page.path = page.slug.clone();
        }

        page.permalink = config.make_permalink(&page.path);

        Ok(page)
    }

    /// Read and parse a .md file into a Page struct
    pub fn from_file<P: AsRef<Path>>(path: P, config: &Config) -> Result<Page> {
        let path = path.as_ref();
        let content = read_file(path)?;
        let mut page = Page::parse(path, &content, config)?;
        page.assets = find_related_assets(path.parent().unwrap());

        if !page.assets.is_empty() && page.file_name != "index" {
            bail!("Page `{}` has assets ({:?}) but is not named index.md", path.display(), page.assets);
        }

        Ok(page)

    }

    /// We need access to all pages url to render links relative to content
    /// so that can't happen at the same time as parsing
    pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<()> {
        self.content = markdown_to_html(&self.raw_content, permalinks, tera, config)?;

        if self.raw_content.contains("<!-- more -->") {
            self.summary = Some({
                let summary = self.raw_content.splitn(2, "<!-- more -->").collect::<Vec<&str>>()[0];
                markdown_to_html(summary, permalinks, tera, config)?
            })
        }

        Ok(())
    }

    /// Renders the page using the default layout, unless specified in front-matter
    pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
        let tpl_name = match self.meta.template {
            Some(ref l) => l.to_string(),
            None => "page.html".to_string()
        };

        let mut context = Context::new();
        context.add("config", config);
        context.add("page", self);
        context.add("current_url", &self.permalink);
        context.add("current_path", &self.path);

        tera.render(&tpl_name, &context)
            .chain_err(|| format!("Failed to render page '{}'", self.file_path.display()))
    }
}

impl ser::Serialize for Page {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
        let mut state = serializer.serialize_struct("page", 18)?;
        state.serialize_field("content", &self.content)?;
        state.serialize_field("title", &self.meta.title)?;
        state.serialize_field("description", &self.meta.description)?;
        state.serialize_field("date", &self.meta.date)?;
        state.serialize_field("slug", &self.slug)?;
        state.serialize_field("path", &format!("/{}", self.path))?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("summary", &self.summary)?;
        state.serialize_field("tags", &self.meta.tags)?;
        state.serialize_field("draft", &self.meta.draft)?;
        state.serialize_field("category", &self.meta.category)?;
        state.serialize_field("extra", &self.meta.extra)?;
        let (word_count, reading_time) = self.get_reading_analytics();
        state.serialize_field("word_count", &word_count)?;
        state.serialize_field("reading_time", &reading_time)?;
        state.serialize_field("previous", &self.previous)?;
        state.serialize_field("previous_in_section", &self.previous_in_section)?;
        state.serialize_field("next", &self.next)?;
        state.serialize_field("next_in_section", &self.next_in_section)?;
        state.end()
    }
}

impl PartialOrd for Page {
    fn partial_cmp(&self, other: &Page) -> Option<Ordering> {
        if self.meta.date.is_none() {
            return Some(Ordering::Less);
        }

        if other.meta.date.is_none() {
            return Some(Ordering::Greater);
        }

        let this_date = self.meta.parse_date().unwrap();
        let other_date = other.meta.parse_date().unwrap();

        if this_date > other_date {
            return Some(Ordering::Less);
        }
        if this_date < other_date {
            return Some(Ordering::Greater);
        }

        Some(Ordering::Equal)
    }
}

/// Horribly inefficient way to set previous and next on each pages
/// So many clones
pub fn populate_previous_and_next_pages(input: &[Page], in_section: bool) -> Vec<Page> {
    let pages = input.to_vec();
    let mut res = Vec::new();

    // the input is sorted from most recent to least recent already
    for (i, page) in input.iter().enumerate() {
        let mut new_page = page.clone();

        if new_page.has_date() {
            if i > 0 {
                let next = &pages[i - 1];
                if next.has_date() {
                    if in_section {
                        new_page.next_in_section = Some(Box::new(next.clone()));
                    } else {
                        new_page.next = Some(Box::new(next.clone()));
                    }
                }
            }

            if i < input.len() - 1 {
                let previous = &pages[i + 1];
                if previous.has_date() {
                    if in_section {
                        new_page.previous_in_section = Some(Box::new(previous.clone()));
                    } else {
                        new_page.previous = Some(Box::new(previous.clone()));
                    }
                }
            }
        }
        res.push(new_page);
    }

    res
}

#[cfg(test)]
mod tests {
    use tempdir::TempDir;

    use std::fs::File;

    use super::{find_related_assets};

    #[test]
    fn test_find_related_assets() {
        let tmp_dir = TempDir::new("example").expect("create temp dir");
        File::create(tmp_dir.path().join("index.md")).unwrap();
        File::create(tmp_dir.path().join("example.js")).unwrap();
        File::create(tmp_dir.path().join("graph.jpg")).unwrap();
        File::create(tmp_dir.path().join("fail.png")).unwrap();

        let assets = find_related_assets(tmp_dir.path());
        assert_eq!(assets.len(), 3);
        assert_eq!(assets.iter().filter(|p| p.extension().unwrap() != "md").count(), 3);
        assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "example.js").count(), 1);
        assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "graph.jpg").count(), 1);
        assert_eq!(assets.iter().filter(|p| p.file_name().unwrap() == "fail.png").count(), 1);
    }
}
<|endoftext|>


<commit_before>use error::*;
use ffi::*;
use types::*;

use std::marker::PhantomData;

pub type Plan64 = fftw_plan;
pub type Plan32 = fftwf_plan;

pub trait PlanSpec: Clone + Copy {
    fn validate(self) -> Result<Self>;
    fn destroy(self);
    fn print(self);
}

pub struct Plan<A, B, Plan: PlanSpec> {
    plan: Plan,
    alignment: Alignment,
    phantom: PhantomData<(A, B)>,
}

impl<A, B, P: PlanSpec> Drop for Plan<A, B, P> {
    fn drop(&mut self) {
        self.plan.destroy();
    }
}

pub trait C2CPlan: Sized {
    type Complex;

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex],
        sign: Sign,
        flag: Flag,
    ) -> Result<Self>;

    /// Execute complex-to-complex transform
    fn c2c(
        &mut self,
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex]
    ) -> Result<()>;
}

macro_rules! impl_c2c { ($C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
impl C2CPlan for Plan<$C, $C, $Plan> {
    type Complex = $C;
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex],
        sign: Sign,
        flag: Flag,
    ) -> Result<Self> {
        let plan = excall!{ $plan(
            shape.len() as i32,
            shape.to_cint().as_mut_ptr() as *mut _,
            in_.as_mut_ptr(),
            out.as_mut_ptr(),
            sign as i32, flag.into())
        }.validate()?;
        Ok(Self {
            plan,
            alignment: Alignment::new(in_, out),
            phantom: PhantomData,
        })
    }
    fn c2c(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Complex]) -> Result<()> {
        self.alignment.check(in_, out)?;
        unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
        Ok(())
    }
}
}} // impl_c2c!

impl_c2c!(c64, Plan64; fftw_plan_dft, fftw_execute_dft);
impl_c2c!(c32, Plan32; fftwf_plan_dft, fftwf_execute_dft);

macro_rules! impl_plan_spec {
    ($Plan:ty; $destroy_plan:ident, $print_plan:ident) => {
impl PlanSpec for $Plan {
    fn validate(self) -> Result<Self> {
        if self.is_null() {
            Err(InvalidPlanError::new().into())
        } else {
            Ok(self)
        }
    }
    fn destroy(self) {
        excall!{ $destroy_plan(self) }
    }
    fn print(self) {
        excall!{ $print_plan(self) }
    }
}
}} // impl_plan_spec!

impl_plan_spec!(Plan64; fftw_destroy_plan, fftw_print_plan);
impl_plan_spec!(Plan32; fftwf_destroy_plan, fftwf_print_plan);

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
struct Alignment {
    in_: i32,
    out: i32,
    n_in_: usize,
    n_out: usize,
}

fn alignment_of<T>(a: &[T]) -> i32 {
    unsafe { fftw_alignment_of(a.as_ptr() as *mut _) }
}

impl Alignment {
    fn new<A, B>(in_: &[A], out: &[B]) -> Self {
        Self {
            in_: alignment_of(in_),
            out: alignment_of(out),
            n_in_: in_.len(),
            n_out: out.len(),
        }
    }

    fn check<A, B>(&self, in_: &[A], out: &[B]) -> Result<()> {
        let args = Self::new(in_, out);
        if *self != args {
            Err(InputMismatchError {
                origin: *self,
                args,
            }.into())
        } else {
            Ok(())
        }
    }
}

#[derive(Debug)]
pub struct InputMismatchError {
    origin: Alignment,
    args: Alignment,
}

trait ToCInt {
    fn to_cint(&self) -> Vec<i32>;
}

impl ToCInt for [usize] {
    fn to_cint(&self) -> Vec<i32> {
        self.iter().map(|&x| x as i32).collect()
    }
}
<commit_msg>Implement C2R/R2C<commit_after>use error::*;
use ffi::*;
use types::*;

use std::marker::PhantomData;

pub type Plan64 = fftw_plan;
pub type Plan32 = fftwf_plan;

pub trait PlanSpec: Clone + Copy {
    fn validate(self) -> Result<Self>;
    fn destroy(self);
    fn print(self);
}

pub struct Plan<A, B, Plan: PlanSpec> {
    plan: Plan,
    alignment: Alignment,
    phantom: PhantomData<(A, B)>,
}

impl<A, B, P: PlanSpec> Drop for Plan<A, B, P> {
    fn drop(&mut self) {
        self.plan.destroy();
    }
}

pub trait C2CPlan: Sized {
    type Complex;

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex],
        sign: Sign,
        flag: Flag,
    ) -> Result<Self>;

    /// Execute complex-to-complex transform
    fn c2c(
        &mut self,
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex]
    ) -> Result<()>;
}

pub trait R2CPlan: Sized {
    type Real;
    type Complex;

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Real],
        out: &mut [Self::Complex],
        flag: Flag,
    ) -> Result<Self>;

    /// Execute real-to-complex transform
    fn r2c(
        &mut self,
        in_: &mut [Self::Real],
        out: &mut [Self::Complex]
    ) -> Result<()>;
}

pub trait C2RPlan: Sized {
    type Real;
    type Complex;

    /// Create new plan
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Real],
        flag: Flag,
    ) -> Result<Self>;

    /// Execute complex-to-real transform
    fn c2r(
        &mut self,
        in_: &mut [Self::Complex],
        out: &mut [Self::Real]
    ) -> Result<()>;
}

macro_rules! impl_c2c { ($C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
impl C2CPlan for Plan<$C, $C, $Plan> {
    type Complex = $C;
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Complex],
        sign: Sign,
        flag: Flag,
    ) -> Result<Self> {
        let plan = excall!{ $plan(
            shape.len() as i32,
            shape.to_cint().as_mut_ptr() as *mut _,
            in_.as_mut_ptr(),
            out.as_mut_ptr(),
            sign as i32, flag.into())
        }.validate()?;
        Ok(Self {
            plan,
            alignment: Alignment::new(in_, out),
            phantom: PhantomData,
        })
    }
    fn c2c(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Complex]) -> Result<()> {
        self.alignment.check(in_, out)?;
        unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
        Ok(())
    }
}
}} // impl_c2c!

impl_c2c!(c64, Plan64; fftw_plan_dft, fftw_execute_dft);
impl_c2c!(c32, Plan32; fftwf_plan_dft, fftwf_execute_dft);

macro_rules! impl_r2c { ($R:ty, $C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
impl R2CPlan for Plan<$R, $C, $Plan> {
    type Real = $R;
    type Complex = $C;
    fn new(
        shape: &[usize],
        in_: &mut [Self::Real],
        out: &mut [Self::Complex],
        flag: Flag,
    ) -> Result<Self> {
        let plan = excall!{ $plan(
            shape.len() as i32,
            shape.to_cint().as_mut_ptr() as *mut _,
            in_.as_mut_ptr(),
            out.as_mut_ptr(),
            flag.into())
        }.validate()?;
        Ok(Self {
            plan,
            alignment: Alignment::new(in_, out),
            phantom: PhantomData,
        })
    }
    fn r2c(&mut self, in_: &mut [Self::Real], out: &mut [Self::Complex]) -> Result<()> {
        self.alignment.check(in_, out)?;
        unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
        Ok(())
    }
}
}} // impl_r2c!

impl_r2c!(f64, c64, Plan64; fftw_plan_dft_r2c, fftw_execute_dft_r2c);
impl_r2c!(f32, c32, Plan32; fftwf_plan_dft_r2c, fftwf_execute_dft_r2c);

macro_rules! impl_c2r { ($R:ty, $C:ty, $Plan:ty; $plan:ident, $exec:ident) => {
impl C2RPlan for Plan<$C, $R, $Plan> {
    type Real = $R;
    type Complex = $C;
    fn new(
        shape: &[usize],
        in_: &mut [Self::Complex],
        out: &mut [Self::Real],
        flag: Flag,
    ) -> Result<Self> {
        let plan = excall!{ $plan(
            shape.len() as i32,
            shape.to_cint().as_mut_ptr() as *mut _,
            in_.as_mut_ptr(),
            out.as_mut_ptr(),
            flag.into())
        }.validate()?;
        Ok(Self {
            plan,
            alignment: Alignment::new(in_, out),
            phantom: PhantomData,
        })
    }
    fn c2r(&mut self, in_: &mut [Self::Complex], out: &mut [Self::Real]) -> Result<()> {
        self.alignment.check(in_, out)?;
        unsafe { $exec(self.plan, in_.as_mut_ptr(), out.as_mut_ptr()) };
        Ok(())
    }
}
}} // impl_c2r!

impl_c2r!(f64, c64, Plan64; fftw_plan_dft_c2r, fftw_execute_dft_c2r);
impl_c2r!(f32, c32, Plan32; fftwf_plan_dft_c2r, fftwf_execute_dft_c2r);

macro_rules! impl_plan_spec {
    ($Plan:ty; $destroy_plan:ident, $print_plan:ident) => {
impl PlanSpec for $Plan {
    fn validate(self) -> Result<Self> {
        if self.is_null() {
            Err(InvalidPlanError::new().into())
        } else {
            Ok(self)
        }
    }
    fn destroy(self) {
        excall!{ $destroy_plan(self) }
    }
    fn print(self) {
        excall!{ $print_plan(self) }
    }
}
}} // impl_plan_spec!

impl_plan_spec!(Plan64; fftw_destroy_plan, fftw_print_plan);
impl_plan_spec!(Plan32; fftwf_destroy_plan, fftwf_print_plan);

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
struct Alignment {
    in_: i32,
    out: i32,
    n_in_: usize,
    n_out: usize,
}

fn alignment_of<T>(a: &[T]) -> i32 {
    unsafe { fftw_alignment_of(a.as_ptr() as *mut _) }
}

impl Alignment {
    fn new<A, B>(in_: &[A], out: &[B]) -> Self {
        Self {
            in_: alignment_of(in_),
            out: alignment_of(out),
            n_in_: in_.len(),
            n_out: out.len(),
        }
    }

    fn check<A, B>(&self, in_: &[A], out: &[B]) -> Result<()> {
        let args = Self::new(in_, out);
        if *self != args {
            Err(InputMismatchError {
                origin: *self,
                args,
            }.into())
        } else {
            Ok(())
        }
    }
}

#[derive(Debug)]
pub struct InputMismatchError {
    origin: Alignment,
    args: Alignment,
}

trait ToCInt {
    fn to_cint(&self) -> Vec<i32>;
}

impl ToCInt for [usize] {
    fn to_cint(&self) -> Vec<i32> {
        self.iter().map(|&x| x as i32).collect()
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Read};

use collections::BackupSet;
use manifest::Manifest;

pub struct MultiBlockReader<R, I> {
    curr: R,
    iter: I,
}

impl<R, I> Read for MultiBlockReader<R, I>
    where R: Read,
          I: Iterator<Item = R>
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            let len = try!(self.curr.read(buf));
            if len > 0 {
                return Ok(len);
            }
            match self.iter.next() {
                Some(r) => {
                    self.curr = r;
                }
                None => {
                    return Ok(0);
                }
            }
        }
    }
}

pub fn volume_path_of_entry<'a>(chain: &'a BackupSet,
                                manifest: &Manifest,
                                entry_path: &[u8])
                                -> Option<&'a str> {
    let vol_num = try_opt!(manifest.first_volume_of_path(entry_path));
    chain.volume_path(vol_num)
}
<commit_msg>Working on MultiBlockIter<commit_after>use std::cmp::Ordering;
use std::io::{self, Read};

use tar;

use collections::BackupSet;
use manifest::Manifest;

pub struct MultiBlockIter<'a, R: Read + 'a> {
    archive: tar::Entries<'a, R>,
    path: &'a [u8],
}

pub struct MultiReader<R, I> {
    curr: R,
    iter: I,
}

impl<'a, R: Read + 'a> MultiBlockIter<'a, R> {
    pub fn new(vol: tar::Entries<'a, R>, path: &'a [u8]) -> MultiBlockIter<'a, R> {
        MultiBlockIter {
            archive: vol,
            path: path,
        }
    }
}

impl<'a, R: Read + 'a> Iterator for MultiBlockIter<'a, R> {
    type Item = tar::Entry<'a, R>;

    fn next(&mut self) -> Option<Self::Item> {
        for entry in &mut self.archive {
            let entry = try_opt!(entry.ok());
            match entry.path_bytes().as_ref().cmp(self.path) {
                Ordering::Equal => {
                    return Some(entry);
                }
                Ordering::Greater => {
                    if entry.path_bytes().starts_with(self.path) {
                        return Some(entry);
                    }
                }
                Ordering::Less => {
                    return None;
                }
            }
        }
        None
    }
}

impl<R, I> Read for MultiReader<R, I>
    where R: Read,
          I: Iterator<Item = R>
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            let len = try!(self.curr.read(buf));
            if len > 0 {
                return Ok(len);
            }
            match self.iter.next() {
                Some(r) => {
                    self.curr = r;
                }
                None => {
                    return Ok(0);
                }
            }
        }
    }
}

pub fn volume_path_of_entry<'a>(chain: &'a BackupSet,
                                manifest: &Manifest,
                                entry_path: &[u8])
                                -> Option<&'a str> {
    let vol_num = try_opt!(manifest.first_volume_of_path(entry_path));
    chain.volume_path(vol_num)
}
<|endoftext|>


<commit_before>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use nom::*;
use nom::IResult::*;
use nom::Err::*;

use twig::{Twig, Rune, Odor};

#[inline]
pub fn is_lowercase(chr: u8) -> bool {
    chr >= 'a' as u8 && chr <= 'z' as u8
}

/// Match at least two spaces or one newline.
pub fn long_space(input: &[u8]) -> IResult<&[u8], &[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for item in input.iter() {
        if *item == '\t' as u8 {
            // Physical tabs are banned.
            // XXX: Should have own ErrorKind tag.
            return Error(Position(ErrorKind::MultiSpace, input));
        }

        if *item == '\n' as u8 {
            spaces += 2;
        } else if *item == ' ' as u8 {
            spaces += 1;
        }

        if *item != ' ' as u8 && *item != '\r' as u8 && *item != '\n' as u8 {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Error(Position(ErrorKind::MultiSpace, input))
    } else {
        Done(&input[idx..], &input[0..idx])
    }
}

pub fn ident(input:&[u8]) -> IResult<&[u8], &[u8]> {
    for (idx, item) in input.iter().enumerate() {
        if idx == 0 {
            // TODO: Should we only accept lowercase chars?
            if !is_alphabetic(*item) {
                return Error(Position(ErrorKind::Alpha, input))
            }
        } else {
            if !is_alphabetic(*item) && !is_digit(*item) && *item != '-' as u8 {
                return Done(&input[idx..], &input[0..idx])
            }
        }
    }
    Error(Position(ErrorKind::Alpha, input))
}

named!(comment<&[u8]>,
    chain!(
        tag!("::") ~
        x: take_until_and_consume!("\n"),
        || { x }
    )
);

// A valid gap is any sequence of long spaces and comments.
named!(gap< Vec<&[u8]> >,
    many1!(
        alt!(
            long_space
          | comment
        )
    )
);

// TODO: Handle separator dots
// TODO: Handle other odors than ud.
named!(ud<Twig>,
  map_res!(
    map_res!(
      map_res!(
        digit,
        str::from_utf8
      ),
      FromStr::from_str
    ),
    |x| Ok::<Twig, ()>(Twig::Cell(
            box Twig::Rune(Rune::dtzy),
            box Twig::Atom(Odor::ud, x)))
  )
);

// TODO: Don't have two different-named identifier parsers that differ just on
// having the from_utf8...
/// Parse an identifier name.
named!(id<&str>,
    map_res!(
        ident,
        str::from_utf8
    )
);

/// Terminator for an arbitrary-length tall rune.
named!(tall_terminator<()>,
    chain!(
        gap ~
        tag!("=="),
      || ()
    )
);

macro_rules! tall_rune_args {
    ($first: tt) => {
        chain!(
            x: $first ~
            gap,
         || { x }
         )
    };
    ($first: tt, $($rest: tt),+) => {
        chain!(
            x: $first ~
            gap ~
            xs: tall_rune_args!($($rest),*),
         || { Twig::Cell(box x, box xs) }
         )
    };
}

macro_rules! wide_rune_args {
    ($first: tt) => {
        chain!(
            x: $first ~
            tag!(")"),
         || { x }
         )
    };
    ($first: tt, $($rest: tt),+) => {
        chain!(
            x: $first ~
            tag!(" ") ~
            xs: wide_rune_args!($($rest),*),
         || { Twig::Cell(box x, box xs) }
         )
    };
}

/// A standard rune that may have either a wide or a tall form.
macro_rules! rune {
    ($name:ident, $($parser: tt),+) => {
        chain!(
            tag!(Rune::$name.glyph()) ~
            args: alt!(
                chain!(
                    tag!("(") ~
                    args: wide_rune_args($($parser),*),
                    || { args }
                ) |
                chain!(
                    gap ~
                    args: tall_rune_args($($parser),*),
                    || { args }
                )
            ),
            || { Twig::Cell(box Twig::Rune(Rune::$name), box args) }
        )
    }
}

/// Parse a Hoon expression into an AST.
named!(pub ream<Twig>,
    alt!(
        rune!(brhp, ream) |
        ud
        /*
      | Brhp
      | Dtls
      | Dtts
      | Ktts
      | Tsgr
      | Tsls
      | Wtcl
      */

        // TODO: Rest of hoon
    )
);

/*
named!(p<(Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream),
            || { Box::new(p) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream,
                || { Box::new(p) }),
            tag!(")"))
    )
);

named!(pq<(Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream,
                || { (Box::new(p), Box::new(q)) }),
            tag!(")"))
    )
);

named!(pqr<(Box<Twig>, Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream) ~
            r: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q), Box::new(r)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream ~
                space ~
                r: ream,
                || { (Box::new(p), Box::new(q), Box::new(r)) }),
            tag!(")"))
    )
);

/// Regular rune with 1 argument
macro_rules! rune1 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               p: p,
               || {
                 Twig::$id(p)
               }
            )
        );
    }
}

/// Regular rune with 2 arguments
macro_rules! rune2 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pq,
               || {
                 let (p, q) = a.clone();
                 Twig::$id(p, q)
               }
            )
        );
    }
}

/// Regular rune with 3 arguments
macro_rules! rune3 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pqr,
               || {
                 let (p, q, r) = a.clone();
                 Twig::$id(p, q, r)
               }
            )
        );
    }
}

named!(wing<Wing>,
    chain!(
        x: id ~
        mut xs: many0!(
            chain!(
                tag!(".") ~
                x: id,
                || { x.to_string() }
            )
        ),
        || {
            xs.insert(0, x.to_string());
            xs
        }
    )
);

rune1!(Brhp, "|-");
rune1!(Dtls, ".+");
rune2!(Dtts, ".=");
rune2!(Ktts, "^=");
rune2!(Tsgr, "=>");
rune2!(Tsls, "=+");
rune3!(Wtcl, "?:");
*/

/*
named!(Cnts<Twig>,
    // TODO: Tall form
    delimited!(
        tag!("%=("),
        chain!(
            p: wing ~
            space ~
            q1: wing ~
            space ~
            q2: ream ~
            rs: many0!(
                chain!(
                    tag!(",") ~
                    space ~
                    r1: wing ~
                    space ~
                    r2: ream,
                || { (r1, r2) }
                )
            ),

            || {
                rs.insert(0, (q1, q2));
                Twig::Cnts(p, rs)
            }),
        tag!(")"))
);
*/

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

    #[test]
    fn test_parse_gap() {
        assert!(gap(&b"  "[..]).is_done());
        assert!(gap(&b"    "[..]).is_done());
        assert!(gap(&b"\n"[..]).is_done());
        assert!(gap(&b"\n  "[..]).is_done());
        assert!(gap(&b"  \n  "[..]).is_done());
        assert!(!gap(&b" "[..]).is_done());
    }
}
<commit_msg>Apparently you need an extra magic parameter for parser macros<commit_after>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use nom::*;
use nom::IResult::*;
use nom::Err::*;

use twig::{Twig, Rune, Odor};

#[inline]
pub fn is_lowercase(chr: u8) -> bool {
    chr >= 'a' as u8 && chr <= 'z' as u8
}

/// Match at least two spaces or one newline.
pub fn long_space(input: &[u8]) -> IResult<&[u8], &[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for item in input.iter() {
        if *item == '\t' as u8 {
            // Physical tabs are banned.
            // XXX: Should have own ErrorKind tag.
            return Error(Position(ErrorKind::MultiSpace, input));
        }

        if *item == '\n' as u8 {
            spaces += 2;
        } else if *item == ' ' as u8 {
            spaces += 1;
        }

        if *item != ' ' as u8 && *item != '\r' as u8 && *item != '\n' as u8 {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Error(Position(ErrorKind::MultiSpace, input))
    } else {
        Done(&input[idx..], &input[0..idx])
    }
}

pub fn ident(input:&[u8]) -> IResult<&[u8], &[u8]> {
    for (idx, item) in input.iter().enumerate() {
        if idx == 0 {
            // TODO: Should we only accept lowercase chars?
            if !is_alphabetic(*item) {
                return Error(Position(ErrorKind::Alpha, input))
            }
        } else {
            if !is_alphabetic(*item) && !is_digit(*item) && *item != '-' as u8 {
                return Done(&input[idx..], &input[0..idx])
            }
        }
    }
    Error(Position(ErrorKind::Alpha, input))
}

named!(comment<&[u8]>,
    chain!(
        tag!("::") ~
        x: take_until_and_consume!("\n"),
        || { x }
    )
);

// A valid gap is any sequence of long spaces and comments.
named!(gap< Vec<&[u8]> >,
    many1!(
        alt!(
            long_space
          | comment
        )
    )
);

// TODO: Handle separator dots
// TODO: Handle other odors than ud.
named!(ud<Twig>,
  map_res!(
    map_res!(
      map_res!(
        digit,
        str::from_utf8
      ),
      FromStr::from_str
    ),
    |x| Ok::<Twig, ()>(Twig::Cell(
            box Twig::Rune(Rune::dtzy),
            box Twig::Atom(Odor::ud, x)))
  )
);

// TODO: Don't have two different-named identifier parsers that differ just on
// having the from_utf8...
/// Parse an identifier name.
named!(id<&str>,
    map_res!(
        ident,
        str::from_utf8
    )
);

/// Terminator for an arbitrary-length tall rune.
named!(tall_terminator<()>,
    chain!(
        gap ~
        tag!("=="),
      || ()
    )
);

macro_rules! tall_rune_args {
    ($i: expr, $first: tt) => {
        chain!($i,
            x: $first ~
            gap,
         || { x }
         )
    };
    ($i: expr, $first: tt, $($rest: tt),+) => {
        chain!($i,
            x: $first ~
            gap ~
            xs: tall_rune_args!($($rest),*),
         || { Twig::Cell(box x, box xs) }
         )
    };
}

macro_rules! wide_rune_args {
    ($i: expr, $first: tt) => {
        chain!($i,
            x: $first ~
            tag!(")"),
         || { x }
         )
    };
    ($i: expr, $first: tt, $($rest: tt),+) => {
        chain!($i,
            x: $first ~
            tag!(" ") ~
            xs: wide_rune_args!($($rest),*),
         || { Twig::Cell(box x, box xs) }
         )
    };
}

/// A standard rune that may have either a wide or a tall form.
macro_rules! rune {
    ($i: expr, $name:ident, $($parser: tt),+) => {
        chain!($i,
            tag!(Rune::$name.glyph()) ~
            args: alt!(
                chain!(
                    tag!("(") ~
                    args: wide_rune_args!($($parser),*),
                    || { args }
                ) |
                chain!(
                    gap ~
                    args: tall_rune_args!($($parser),*),
                    || { args }
                )
            ),
            || { Twig::Cell(box Twig::Rune(Rune::$name), box args) }
        )
    }
}

/// Parse a Hoon expression into an AST.
named!(pub ream<Twig>,
    alt!(
        rune!(brhp, ream) |
        ud
        /*
      | Brhp
      | Dtls
      | Dtts
      | Ktts
      | Tsgr
      | Tsls
      | Wtcl
      */

        // TODO: Rest of hoon
    )
);

/*
named!(p<(Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream),
            || { Box::new(p) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream,
                || { Box::new(p) }),
            tag!(")"))
    )
);

named!(pq<(Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream,
                || { (Box::new(p), Box::new(q)) }),
            tag!(")"))
    )
);

named!(pqr<(Box<Twig>, Box<Twig>, Box<Twig>)>,
    alt!(
        chain!(
            p: preceded!(gap, ream) ~
            q: preceded!(gap, ream) ~
            r: preceded!(gap, ream),
            || { (Box::new(p), Box::new(q), Box::new(r)) })
        | delimited!(
            tag!("("),
            chain!(
                p: ream ~
                space ~
                q: ream ~
                space ~
                r: ream,
                || { (Box::new(p), Box::new(q), Box::new(r)) }),
            tag!(")"))
    )
);

/// Regular rune with 1 argument
macro_rules! rune1 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               p: p,
               || {
                 Twig::$id(p)
               }
            )
        );
    }
}

/// Regular rune with 2 arguments
macro_rules! rune2 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pq,
               || {
                 let (p, q) = a.clone();
                 Twig::$id(p, q)
               }
            )
        );
    }
}

/// Regular rune with 3 arguments
macro_rules! rune3 {
    ($id:ident, $rune:expr) => {
        named!($id<Twig>,
           chain!(
               tag!($rune) ~
               a: pqr,
               || {
                 let (p, q, r) = a.clone();
                 Twig::$id(p, q, r)
               }
            )
        );
    }
}

named!(wing<Wing>,
    chain!(
        x: id ~
        mut xs: many0!(
            chain!(
                tag!(".") ~
                x: id,
                || { x.to_string() }
            )
        ),
        || {
            xs.insert(0, x.to_string());
            xs
        }
    )
);

rune1!(Brhp, "|-");
rune1!(Dtls, ".+");
rune2!(Dtts, ".=");
rune2!(Ktts, "^=");
rune2!(Tsgr, "=>");
rune2!(Tsls, "=+");
rune3!(Wtcl, "?:");
*/

/*
named!(Cnts<Twig>,
    // TODO: Tall form
    delimited!(
        tag!("%=("),
        chain!(
            p: wing ~
            space ~
            q1: wing ~
            space ~
            q2: ream ~
            rs: many0!(
                chain!(
                    tag!(",") ~
                    space ~
                    r1: wing ~
                    space ~
                    r2: ream,
                || { (r1, r2) }
                )
            ),

            || {
                rs.insert(0, (q1, q2));
                Twig::Cnts(p, rs)
            }),
        tag!(")"))
);
*/

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

    #[test]
    fn test_parse_gap() {
        assert!(gap(&b"  "[..]).is_done());
        assert!(gap(&b"    "[..]).is_done());
        assert!(gap(&b"\n"[..]).is_done());
        assert!(gap(&b"\n  "[..]).is_done());
        assert!(gap(&b"  \n  "[..]).is_done());
        assert!(!gap(&b" "[..]).is_done());
    }
}
<|endoftext|>


<commit_before>use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};

use crate::attributes::Attributes;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{ElementResult, SetAttributes};
use crate::error::*;
use crate::node::Node;
use crate::number_list::{NumberList, NumberListLength};
use crate::parsers::{Parse, ParseValue};
use crate::surface_utils::{
    iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
};
use crate::util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// Color matrix operation types.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum OperationType {
    Matrix,
    Saturate,
    HueRotate,
    LuminanceToAlpha,
}

impl Default for OperationType {
    fn default() -> Self {
        OperationType::Matrix
    }
}

/// The `feColorMatrix` filter primitive.
pub struct FeColorMatrix {
    base: PrimitiveWithInput,
    matrix: Matrix5<f64>,
}

impl Default for FeColorMatrix {
    /// Constructs a new `ColorMatrix` with empty properties.
    #[inline]
    fn default() -> FeColorMatrix {
        FeColorMatrix {
            base: PrimitiveWithInput::new::<Self>(),
            matrix: Matrix5::identity(),
        }
    }
}

#[rustfmt::skip]
impl SetAttributes for FeColorMatrix {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        self.base.set_attributes(attrs)?;

        // First, determine the operation type.
        let mut operation_type = Default::default();
        for (attr, value) in attrs
            .iter()
            .filter(|(attr, _)| attr.expanded() == expanded_name!("", "type"))
        {
            operation_type = attr.parse(value)?;
        }

        // Now read the matrix correspondingly.
        // LuminanceToAlpha doesn't accept any matrix.
        if operation_type == OperationType::LuminanceToAlpha {
            self.matrix = {
                Matrix5::new(
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.2125, 0.7154, 0.0721, 0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 1.0,
                )
            };
        } else {
            for (attr, value) in attrs
                .iter()
                .filter(|(attr, _)| attr.expanded() == expanded_name!("", "values"))
            {
                let new_matrix = match operation_type {
                    OperationType::LuminanceToAlpha => unreachable!(),
                    OperationType::Matrix => {
                        let NumberList(v) =
                            NumberList::parse_str(value, NumberListLength::Exact(20))
                                .attribute(attr)?;
                        let matrix = Matrix4x5::from_row_slice(&v);
                        let mut matrix = matrix.fixed_resize(0.0);
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                    OperationType::Saturate => {
                        let s: f64 = attr.parse_and_validate(value, |s| {
                            if s < 0.0 || s > 1.0 {
                                Err(ValueErrorKind::value_error("expected value from 0 to 1"))
                            } else {
                                Ok(s)
                            }
                        })?;

                        Matrix5::new(
                            0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
                            0.0,               0.0,               0.0,               1.0, 0.0,
                            0.0,               0.0,               0.0,               0.0, 1.0,
                        )
                    }
                    OperationType::HueRotate => {
                        let degrees: f64 = attr.parse(value)?;
                        let (sin, cos) = degrees.to_radians().sin_cos();

                        let a = Matrix3::new(
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                        );

                        let b = Matrix3::new(
                             0.787, -0.715, -0.072,
                            -0.213,  0.285, -0.072,
                            -0.213, -0.715,  0.928,
                        );

                        let c = Matrix3::new(
                            -0.213, -0.715,  0.928,
                             0.143,  0.140, -0.283,
                            -0.787,  0.715,  0.072,
                        );

                        let top_left = a + b * cos + c * sin;

                        let mut matrix = top_left.fixed_resize(0.0);
                        matrix[(3, 3)] = 1.0;
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                };

                self.matrix = new_matrix;
            }
        }

        Ok(())
    }
}

impl FilterEffect for FeColorMatrix {
    fn render(
        &self,
        node: &Node,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx, node.parent().as_ref())?
            .add_input(&input)
            .into_irect(draw_ctx);

        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input.surface().surface_type(),
        )?;

        surface.modify(&mut |data, stride| {
            for (x, y, pixel) in Pixels::within(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;

                let pixel_vec = if alpha == 0.0 {
                    Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
                } else {
                    Vector5::new(
                        f64::from(pixel.r) / 255f64 / alpha,
                        f64::from(pixel.g) / 255f64 / alpha,
                        f64::from(pixel.b) / 255f64 / alpha,
                        alpha,
                        1.0,
                    )
                };
                let mut new_pixel_vec = Vector5::zeros();
                self.matrix.mul_to(&pixel_vec, &mut new_pixel_vec);

                let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);

                let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;

                let output_pixel = Pixel {
                    r: premultiply(new_pixel_vec[0]),
                    g: premultiply(new_pixel_vec[1]),
                    b: premultiply(new_pixel_vec[2]),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: surface.share()?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl Parse for OperationType {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "matrix" => OperationType::Matrix,
            "saturate" => OperationType::Saturate,
            "hueRotate" => OperationType::HueRotate,
            "luminanceToAlpha" => OperationType::LuminanceToAlpha,
        )?)
    }
}
<commit_msg>color_matrix: parse saturate as f64<commit_after>use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};

use crate::attributes::Attributes;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{ElementResult, SetAttributes};
use crate::error::*;
use crate::node::Node;
use crate::number_list::{NumberList, NumberListLength};
use crate::parsers::{Parse, ParseValue};
use crate::surface_utils::{
    iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
};
use crate::util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{FilterEffect, FilterError, PrimitiveWithInput};

/// Color matrix operation types.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
enum OperationType {
    Matrix,
    Saturate,
    HueRotate,
    LuminanceToAlpha,
}

impl Default for OperationType {
    fn default() -> Self {
        OperationType::Matrix
    }
}

/// The `feColorMatrix` filter primitive.
pub struct FeColorMatrix {
    base: PrimitiveWithInput,
    matrix: Matrix5<f64>,
}

impl Default for FeColorMatrix {
    /// Constructs a new `ColorMatrix` with empty properties.
    #[inline]
    fn default() -> FeColorMatrix {
        FeColorMatrix {
            base: PrimitiveWithInput::new::<Self>(),
            matrix: Matrix5::identity(),
        }
    }
}

#[rustfmt::skip]
impl SetAttributes for FeColorMatrix {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        self.base.set_attributes(attrs)?;

        // First, determine the operation type.
        let mut operation_type = Default::default();
        for (attr, value) in attrs
            .iter()
            .filter(|(attr, _)| attr.expanded() == expanded_name!("", "type"))
        {
            operation_type = attr.parse(value)?;
        }

        // Now read the matrix correspondingly.
        // LuminanceToAlpha doesn't accept any matrix.
        if operation_type == OperationType::LuminanceToAlpha {
            self.matrix = {
                Matrix5::new(
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 0.0,
                    0.2125, 0.7154, 0.0721, 0.0, 0.0,
                    0.0,    0.0,    0.0,    0.0, 1.0,
                )
            };
        } else {
            for (attr, value) in attrs
                .iter()
                .filter(|(attr, _)| attr.expanded() == expanded_name!("", "values"))
            {
                let new_matrix = match operation_type {
                    OperationType::LuminanceToAlpha => unreachable!(),
                    OperationType::Matrix => {
                        let NumberList(v) =
                            NumberList::parse_str(value, NumberListLength::Exact(20))
                                .attribute(attr)?;
                        let matrix = Matrix4x5::from_row_slice(&v);
                        let mut matrix = matrix.fixed_resize(0.0);
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                    OperationType::Saturate => {
                        let s: f64 = attr.parse(value)?;

                        Matrix5::new(
                            0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
                            0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
                            0.0,               0.0,               0.0,               1.0, 0.0,
                            0.0,               0.0,               0.0,               0.0, 1.0,
                        )
                    }
                    OperationType::HueRotate => {
                        let degrees: f64 = attr.parse(value)?;
                        let (sin, cos) = degrees.to_radians().sin_cos();

                        let a = Matrix3::new(
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                            0.213, 0.715, 0.072,
                        );

                        let b = Matrix3::new(
                             0.787, -0.715, -0.072,
                            -0.213,  0.285, -0.072,
                            -0.213, -0.715,  0.928,
                        );

                        let c = Matrix3::new(
                            -0.213, -0.715,  0.928,
                             0.143,  0.140, -0.283,
                            -0.787,  0.715,  0.072,
                        );

                        let top_left = a + b * cos + c * sin;

                        let mut matrix = top_left.fixed_resize(0.0);
                        matrix[(3, 3)] = 1.0;
                        matrix[(4, 4)] = 1.0;
                        matrix
                    }
                };

                self.matrix = new_matrix;
            }
        }

        Ok(())
    }
}

impl FilterEffect for FeColorMatrix {
    fn render(
        &self,
        node: &Node,
        ctx: &FilterContext,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let input = self.base.get_input(ctx, acquired_nodes, draw_ctx)?;
        let bounds = self
            .base
            .get_bounds(ctx, node.parent().as_ref())?
            .add_input(&input)
            .into_irect(draw_ctx);

        let mut surface = ExclusiveImageSurface::new(
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
            input.surface().surface_type(),
        )?;

        surface.modify(&mut |data, stride| {
            for (x, y, pixel) in Pixels::within(input.surface(), bounds) {
                let alpha = f64::from(pixel.a) / 255f64;

                let pixel_vec = if alpha == 0.0 {
                    Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
                } else {
                    Vector5::new(
                        f64::from(pixel.r) / 255f64 / alpha,
                        f64::from(pixel.g) / 255f64 / alpha,
                        f64::from(pixel.b) / 255f64 / alpha,
                        alpha,
                        1.0,
                    )
                };
                let mut new_pixel_vec = Vector5::zeros();
                self.matrix.mul_to(&pixel_vec, &mut new_pixel_vec);

                let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);

                let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;

                let output_pixel = Pixel {
                    r: premultiply(new_pixel_vec[0]),
                    g: premultiply(new_pixel_vec[1]),
                    b: premultiply(new_pixel_vec[2]),
                    a: ((new_alpha * 255f64) + 0.5) as u8,
                };

                data.set_pixel(stride, output_pixel, x, y);
            }
        });

        Ok(FilterResult {
            name: self.base.result.clone(),
            output: FilterOutput {
                surface: surface.share()?,
                bounds,
            },
        })
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        true
    }
}

impl Parse for OperationType {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parse_identifiers!(
            parser,
            "matrix" => OperationType::Matrix,
            "saturate" => OperationType::Saturate,
            "hueRotate" => OperationType::HueRotate,
            "luminanceToAlpha" => OperationType::LuminanceToAlpha,
        )?)
    }
}
<|endoftext|>


<commit_before>use libc;

pub fn pid() -> i32 {
    unsafe { libc::getpid() }
}
<commit_msg>Add documentation to util.rs<commit_after>use libc;

/// Return the process-id (pid) of the current process
pub fn pid() -> i32 {
    unsafe { libc::getpid() }
}
<|endoftext|>


<commit_before>use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;

struct SineWave(pub f64);

impl Fn<(f64, ), f64> for SineWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SineWave(frequency) = *self;
        FloatMath::sin(t * frequency * 2.0 * PI)
    }
}

struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SineWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor()
    }
}
<commit_msg>Rust breaking change: Copy trait is now opt-in<commit_after>use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;

#[deriving(Copy)]
struct SineWave(pub f64);

impl Fn<(f64, ), f64> for SineWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SineWave(frequency) = *self;
        FloatMath::sin(t * frequency * 2.0 * PI)
    }
}

#[deriving(Copy)]
struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SineWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

#[deriving(Copy)]
struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor()
    }
}
<|endoftext|>


<commit_before>use native::task;
use std::comm::channel;
use test::BenchHarness;

pub struct WorkUnit<ARG, RV> {
    arg: ARG,
    rv: Sender<RV>,
}

pub enum MessageToWorker<ARG, RV> {
    Work(WorkUnit<ARG, RV>),
    Halt,
}

enum MessageToDispatcher<ARG, RV> {
    Dispatch(WorkUnit<ARG, RV>),
    HaltAll,
    RegisterWorker(Sender<Sender<Sender<MessageToWorker<ARG, RV>>>>),
}

pub struct WorkQueue<ARG, RV> {
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

pub struct WorkQueueProxy<ARG, RV> {
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

impl<ARG:Send, RV:Send> WorkQueue<ARG, RV> {
    pub fn create() -> WorkQueue<ARG, RV> {
        let (dispatcher, dispatcher_inbox) = channel::<MessageToDispatcher<ARG, RV>>();

        // dispatcher
        task::spawn(proc() {
            let (want_work, idle_worker) = channel::<Sender<MessageToWorker<ARG, RV>>>();
            let mut worker_count = 0;
            let inbox = dispatcher_inbox;
            let idle_worker = idle_worker;
            loop {
                match inbox.recv() {
                    Dispatch(work_item) => {
                        idle_worker.recv().send(Work(work_item));
                    },
                    RegisterWorker(want_idle_sender) => {
                        worker_count += 1;
                        want_idle_sender.send(want_work.clone());
                    }
                    HaltAll => {
                        while worker_count > 0 {
                            idle_worker.recv().send(Halt);
                            worker_count -= 1;
                        }
                        return;
                    },
                };
            }
        });
        return WorkQueue{dispatcher: dispatcher};
    }

    pub fn proxy(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }

    pub fn register_worker(&self) -> Sender<Sender<MessageToWorker<ARG, RV>>> {
        let (reg_s, reg_r) = channel::<Sender<Sender<MessageToWorker<ARG, RV>>>>();
        self.dispatcher.send(RegisterWorker(reg_s));
        return reg_r.recv();
    }

    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch(WorkUnit{arg: arg, rv: rv}));
        return wait_for_rv;
    }
}

// rustc complais "cannot implement a destructor on a structure with
// type parameters", but our destruction is safe, we only send
// a simple message on a channel.
#[unsafe_destructor]
impl<ARG:Send, RV:Send> Drop for WorkQueue<ARG, RV> {
    fn drop(&mut self) {
        self.dispatcher.send(HaltAll);
    }
}

impl<ARG:Send, RV:Send> WorkQueueProxy<ARG, RV> {
    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch(WorkUnit{arg: arg, rv: rv}));
        return wait_for_rv;
    }
}

impl<ARG:Send, RV:Send> Clone for WorkQueueProxy<ARG, RV> {
    fn clone(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }
}

#[cfg(test)]
fn spawn_test_worker(queue: &WorkQueue<int, int>) {
    let want_work = queue.register_worker();
    task::spawn(proc() {
        let want_work = want_work;
        loop {
            let (idle, get_work_unit) = channel::<MessageToWorker<int, int>>();
            want_work.send(idle);
            let work_unit = match get_work_unit.recv() {
                Work(wu) => wu,
                Halt     => return
            };
            let rv = work_unit.arg * 2;
            work_unit.rv.send(rv);
        }
    });
}

#[test]
fn test_queue() {
    let queue = WorkQueue::<int, int>::create();
    for _ in range(0, 3) {
        spawn_test_worker(&queue);
    }
    let mut promise_list: ~[Receiver<int>] = ~[];
    for c in range(0, 10) {
        let rv = queue.push(c);
        promise_list.push(rv);
    }
    let return_list = promise_list.map(|promise| promise.recv());
    assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
}

#[test]
fn test_enqueue_from_tasks() {
    let queue = WorkQueue::<int, int>::create();
    for _ in range(0, 3) {
        spawn_test_worker(&queue);
    }
    let mut promise_list: ~[Receiver<int>] = ~[];
    let queue_proxy = queue.proxy();
    for c in range(0, 10) {
        let queue_proxy_clone = queue_proxy.clone();
        let (done, promise) = channel::<int>();
        promise_list.push(promise);
        task::spawn(proc() {
            let done = done;
            let queue = queue_proxy_clone;
            let rv = queue.push(c);
            done.send(rv.recv());
        });
    }
    let return_list = promise_list.map(|promise| promise.recv());
    assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
}

#[bench]
fn bench_1000_tasks_4_threads(b: &mut BenchHarness) {
    let queue = WorkQueue::<int, int>::create();
    for _ in range(0, 4) {
        spawn_test_worker(&queue);
    }
    b.iter(|| {
        let mut promise_list: ~[Receiver<int>] = ~[];
        for _ in range(0, 1000) {
            let rv = queue.push(1);
            promise_list.push(rv);
        }
        let _ = promise_list.map(|promise| promise.recv());
    });
}
<commit_msg>benchmark for spawning workers<commit_after>use native::task;
use std::comm::channel;
use test::BenchHarness;

pub struct WorkUnit<ARG, RV> {
    arg: ARG,
    rv: Sender<RV>,
}

pub enum MessageToWorker<ARG, RV> {
    Work(WorkUnit<ARG, RV>),
    Halt,
}

enum MessageToDispatcher<ARG, RV> {
    Dispatch(WorkUnit<ARG, RV>),
    HaltAll,
    RegisterWorker(Sender<Sender<Sender<MessageToWorker<ARG, RV>>>>),
}

pub struct WorkQueue<ARG, RV> {
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

pub struct WorkQueueProxy<ARG, RV> {
    dispatcher: Sender<MessageToDispatcher<ARG, RV>>,
}

impl<ARG:Send, RV:Send> WorkQueue<ARG, RV> {
    pub fn create() -> WorkQueue<ARG, RV> {
        let (dispatcher, dispatcher_inbox) = channel::<MessageToDispatcher<ARG, RV>>();

        // dispatcher
        task::spawn(proc() {
            let (want_work, idle_worker) = channel::<Sender<MessageToWorker<ARG, RV>>>();
            let mut worker_count = 0;
            let inbox = dispatcher_inbox;
            let idle_worker = idle_worker;
            loop {
                match inbox.recv() {
                    Dispatch(work_item) => {
                        idle_worker.recv().send(Work(work_item));
                    },
                    RegisterWorker(want_idle_sender) => {
                        worker_count += 1;
                        want_idle_sender.send(want_work.clone());
                    }
                    HaltAll => {
                        while worker_count > 0 {
                            idle_worker.recv().send(Halt);
                            worker_count -= 1;
                        }
                        return;
                    },
                };
            }
        });
        return WorkQueue{dispatcher: dispatcher};
    }

    pub fn proxy(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }

    pub fn register_worker(&self) -> Sender<Sender<MessageToWorker<ARG, RV>>> {
        let (reg_s, reg_r) = channel::<Sender<Sender<MessageToWorker<ARG, RV>>>>();
        self.dispatcher.send(RegisterWorker(reg_s));
        return reg_r.recv();
    }

    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch(WorkUnit{arg: arg, rv: rv}));
        return wait_for_rv;
    }
}

// rustc complais "cannot implement a destructor on a structure with
// type parameters", but our destruction is safe, we only send
// a simple message on a channel.
#[unsafe_destructor]
impl<ARG:Send, RV:Send> Drop for WorkQueue<ARG, RV> {
    fn drop(&mut self) {
        self.dispatcher.send(HaltAll);
    }
}

impl<ARG:Send, RV:Send> WorkQueueProxy<ARG, RV> {
    pub fn push(&self, arg: ARG) -> Receiver<RV> {
        let (rv, wait_for_rv) = channel::<RV>();
        self.dispatcher.send(Dispatch(WorkUnit{arg: arg, rv: rv}));
        return wait_for_rv;
    }
}

impl<ARG:Send, RV:Send> Clone for WorkQueueProxy<ARG, RV> {
    fn clone(&self) -> WorkQueueProxy<ARG, RV> {
        return WorkQueueProxy{dispatcher: self.dispatcher.clone()};
    }
}

#[cfg(test)]
fn spawn_test_worker(queue: &WorkQueue<int, int>) {
    let want_work = queue.register_worker();
    task::spawn(proc() {
        let want_work = want_work;
        loop {
            let (idle, get_work_unit) = channel::<MessageToWorker<int, int>>();
            want_work.send(idle);
            let work_unit = match get_work_unit.recv() {
                Work(wu) => wu,
                Halt     => return
            };
            let rv = work_unit.arg * 2;
            work_unit.rv.send(rv);
        }
    });
}

#[test]
fn test_queue() {
    let queue = WorkQueue::<int, int>::create();
    for _ in range(0, 3) {
        spawn_test_worker(&queue);
    }
    let mut promise_list: ~[Receiver<int>] = ~[];
    for c in range(0, 10) {
        let rv = queue.push(c);
        promise_list.push(rv);
    }
    let return_list = promise_list.map(|promise| promise.recv());
    assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
}

#[test]
fn test_enqueue_from_tasks() {
    let queue = WorkQueue::<int, int>::create();
    for _ in range(0, 3) {
        spawn_test_worker(&queue);
    }
    let mut promise_list: ~[Receiver<int>] = ~[];
    let queue_proxy = queue.proxy();
    for c in range(0, 10) {
        let queue_proxy_clone = queue_proxy.clone();
        let (done, promise) = channel::<int>();
        promise_list.push(promise);
        task::spawn(proc() {
            let done = done;
            let queue = queue_proxy_clone;
            let rv = queue.push(c);
            done.send(rv.recv());
        });
    }
    let return_list = promise_list.map(|promise| promise.recv());
    assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
}

#[bench]
fn bench_1000_tasks_4_threads(b: &mut BenchHarness) {
    let queue = WorkQueue::<int, int>::create();
    for _ in range(0, 4) {
        spawn_test_worker(&queue);
    }
    b.iter(|| {
        let mut promise_list: ~[Receiver<int>] = ~[];
        for _ in range(0, 1000) {
            let rv = queue.push(1);
            promise_list.push(rv);
        }
        let _ = promise_list.map(|promise| promise.recv());
    });
}

#[bench]
fn bench_100_workers(b: &mut BenchHarness) {
    b.iter(|| {
        let queue = WorkQueue::<int, int>::create();
        for _ in range(0, 100) {
            spawn_test_worker(&queue);
        }
    });
}
<|endoftext|>