<commit_before>extern crate regex;

use regex::Regex;
use std::collections::HashSet;
use std::io::{self, Write};

use super::{Compile, Name, ParseError, Path, Statement, Template};
use self::runtime::RUNTIME;

mod runtime;

/// A program is the final result of Mustache AST to Ruby extension source
/// translation that is presented to the main compiler driver for output.
///
/// It contains all external rendering functions generated by the translator,
/// which are exposed to Ruby code after the extension is compiled.
#[derive(Debug)]
pub struct Program {
    global: Scope,
}

impl Program {
    fn new() -> Self {
        Program { global: Scope::new(Name::new("global")) }
    }

    fn merge(&mut self, scope: Scope) -> &mut Self {
        self.global.merge(scope);
        self
    }
}

impl Compile for Program {
    /// Writes the final translated source code to an output buffer.
    ///
    /// This emits fully-formed Ruby extension source code that may be input
    /// into a mkmf build process, creating a dynamically loadable shared
    /// object file.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        // Emit runtime preamble.
        writeln!(buf, "{}", RUNTIME)?;

        // Emit string content declarations.
        for string in &self.global.strings {
            writeln!(buf, "{};", string.decl)?;
        }

        writeln!(buf, "")?;

        // Emit function declarations.
        for fun in &self.global.functions {
            writeln!(buf, "{};", fun.decl)?;
        }

        writeln!(buf, "")?;

        // Emit function definitions.
        for fun in &self.global.functions {
            fun.emit(buf)?
        }

        // Emit public render function.
        let renders: Vec<_> = self.global.functions.iter().filter_map(|f| f.invoke_if()).collect();
        writeln!(buf,
                 r#"static VALUE render(VALUE self, VALUE name, VALUE context) {{
                        const char *ptr = StringValuePtr(name);
                        const long length = RSTRING_LEN(name);
                        VALUE buf = rb_str_buf_new(0);
                        VALUE stack = rb_ary_new_from_args(1, context);
                        {}
                        else {{
                            rb_raise(rb_eArgError, "Template not found");
                        }}
                        return buf;
                    }}"#,
                 renders.join(" else "))?;

        writeln!(buf, "")?;

        // Emit initialize function.
        writeln!(buf, "static void initialize() {{")?;
        for string in &self.global.strings {
            string.emit(buf)?;
        }
        writeln!(buf, "}}")
    }
}

/// A store for functions created by the translation process of an input
/// template to source code output.
///
/// Scopes have name generators that are used in function naming, providing a
/// stable name that other scopes may rely on for partial template function
/// calls.
///
/// After each template is translated into a scope they are merged into a
/// Program's global scope for final output.
#[derive(Debug)]
struct Scope {
    name: Name,
    functions: Vec<Function>,
    strings: Vec<StaticString>,
}

impl Scope {
    fn new(name: Name) -> Self {
        Scope {
            name: name,
            functions: Vec::new(),
            strings: Vec::new(),
        }
    }

    /// Combines this scope's function definitions with another's.
    fn merge(&mut self, mut other: Scope) -> &mut Self {
        self.functions.append(&mut other.functions);
        self.strings.append(&mut other.strings);
        self
    }

    /// Advances the scope's name generator to the next unique identifier. This
    /// should be called before descending another level in the recursive
    /// tree translation process.
    fn next(&mut self) -> &mut Self {
        self.name.next();
        self
    }

    /// Adds a function to this scope.
    fn register(&mut self, fun: Function) {
        self.functions.push(fun);
    }

    /// Adds a constant string value to this scope.
    fn content(&mut self, string: StaticString) {
        self.strings.push(string);
    }

    /// Returns the template path used to generate function names in this
    /// scope (e.g. "includes/header").
    fn base_name(&self) -> String {
        self.name.base.clone()
    }
}

#[derive(Debug)]
struct StaticString {
    name: String,
    decl: String,
    value: String,
    length: usize,
}

impl StaticString {
    /// Writes the raw content string global to the buffer.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        writeln!(buf, "{{")?;
        writeln!(buf, "static const char* content = \"{}\";", self.value)?;
        writeln!(buf,
                 "{} = rb_str_new_static(content, {});",
                 self.name,
                 self.length)?;
        writeln!(buf, "rb_global_variable(&{});", self.name)?;
        writeln!(buf, "}}")
    }
}

#[derive(Debug)]
struct Function {
    name: String,
    decl: String,
    body: Vec<String>,
    export: Option<String>,
}

impl Function {
    /// Writes the function definition to the buffer.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        writeln!(buf, "{} {{", self.decl)?;
        for node in &self.body {
            writeln!(buf, "{}", node)?;
        }
        writeln!(buf, "}}\n")
    }

    /// Builds a conditional statement to call the function if the template
    /// name matches the function's exported name, like "includes/header".
    fn invoke_if(&self) -> Option<String> {
        if self.export.is_none() {
            return None;
        }

        let export = self.export.as_ref().unwrap();
        Some(format!("if (length == {len} && strncmp(ptr, \"{path}\", {len}) == 0) {{
                          {fun}(buf, stack);
                      }}",
                     len = export.len(),
                     path = export,
                     fun = self.name))
    }
}

/// Recursively walks the AST, translating Mustache statement tree nodes into
/// the corresponding Ruby extension source code.
///
/// Sections are extracted into top-level functions paired with a function
/// call at the location the section appeared in the template. Partials are
/// similarly translated into a function call which is expected to be provided
/// by another template in the final tree.
fn transform(scope: &mut Scope, node: &Statement) -> Option<String> {
    match *node {
        Statement::Program(ref block) => {
            let id = scope.name.id();

            // Build private render function.
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let render = Function {
                name: format!("render_{}", id),
                decl: format!("static void render_{}(VALUE buf, VALUE stack)", id),
                body: children,
                export: Some(scope.base_name()),
            };

            scope.register(render);
            None
        }
        Statement::Section(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let name = format!("section_{}", scope.next().name);
            let fun = Function {
                decl: format!("static void {}(VALUE buf, VALUE stack)", name),
                name: name,
                body: children,
                export: None,
            };

            let call = format!("{{ {} section(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Inverted(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let name = format!("section_{}", scope.next().name);
            let fun = Function {
                decl: format!("static void {}(VALUE buf, VALUE stack)", name),
                name: name,
                body: children,
                export: None,
            };

            let call = format!("{{ {} inverted(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Partial(ref name, ref padding) => {
            let name = Name::new(name);
            Some(format!("render_{}(buf, stack);", name.id()))
        }
        Statement::Comment(_) => None,
        Statement::Content(ref text) => {
            let content = clean(text);

            let name = format!("content_{}", scope.next().name);
            let string = StaticString {
                decl: format!("static VALUE {}", name),
                name: name,
                value: content,
                length: text.len(),
            };

            let append = format!("rb_str_buf_append(buf, {});", string.name);

            scope.content(string);
            Some(append)
        }
        Statement::Variable(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, true); }}", path))
        }
        Statement::Html(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, false); }}", path))
        }
    }
}

/// Transforms the AST of each parsed template into a source code tree
/// and links each template together into a single executable program.
pub fn link(templates: &Vec<Template>) -> Result<Program, ParseError> {
    validate(templates)?;

    let mut program = Program::new();
    templates.iter()
        .map(|template| {
            let mut scope = Scope::new(template.name());
            transform(&mut scope, &template.tree);
            scope
        })
        .fold(&mut program, |program, scope| program.merge(scope));

    Ok(program)
}

/// Ensures all templates may be linked together into an executable.
///
/// This method checks that all partial template paths are provided by
/// another template. For example, a `{{>include/header}}` partial invocation
/// must be provided by an `include/header.mustache` template file.
///
/// Partials can be considered function calls, so the function must be defined.
fn validate(templates: &Vec<Template>) -> Result<(), ParseError> {
    let all: HashSet<_> = templates.iter().map(|temp| &temp.name).collect();

    for template in templates {
        let names: HashSet<_> = template.tree.partials().into_iter().collect();
        let missing = &names - &all;
        if !missing.is_empty() {
            let name = missing.into_iter().next().unwrap();
            return Err(ParseError::UnknownPartial(name.clone(), template.path.clone()));
        }
    }

    Ok(())
}

/// Replaces string literal characters considered invalid inside a cstr with
/// their escaped counterparts.
fn clean(text: &str) -> String {
    let re = Regex::new(r"\\").unwrap();
    let text = re.replace_all(&text, "\\\\");

    let re = Regex::new(r"\r").unwrap();
    let text = re.replace_all(&text, "\\r");

    let re = Regex::new(r"\n").unwrap();
    let text = re.replace_all(&text, "\\n");

    let re = Regex::new(r#"["]"#).unwrap();
    re.replace_all(&text, "\\\"").into_owned()
}

/// Transforms a Mustache variable key path into the source code to build a
/// Ruby array. At runtime, each key in the array is recursively processed to
/// find the replacement text for a Mustache expression.
fn path_ary(path: &Path) -> String {
    let args = path.keys
        .iter()
        .map(|key| format!("\"{}\"", key))
        .collect::<Vec<String>>()
        .join(", ");

    format!("static const struct path path = {{ .keys = {{ {} }}, .length = {} }};",
            args,
            path.keys.len())
}

#[cfg(test)]
mod tests {
    use super::{link, transform, Scope};
    use super::super::{Name, ParseError, Statement, Template};
    use std::path::{Path, PathBuf};

    #[test]
    fn validates_valid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/robot"), None);
        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Ok(_) => (),
            Err(e) => panic!("Must link valid partials: {}", e),
        }
    }

    #[test]
    fn validates_invalid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/unknown"), None);

        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Err(ParseError::UnknownPartial(ref name, ref path)) => {
                assert_eq!("machines/unknown", name);
                assert_eq!(Path::new("app/templates/machines/robots.mustache"), path);
            }
            _ => panic!("Must enforce partial references"),
        }
    }

    #[test]
    fn transforms_tree_into_functions() {
        let text = "
            {{> includes/header }}

                {{# robots}}

{{ name.first }}

                {{/ robots}}
                {{^ robots}}
                    {{! else clause }}
                    No robots
                {{/ robots}}

            {{> includes/footer }}
            {{{ unescaped.html }}}
        ";

        match Statement::parse(text) {
            Ok(tree) => {
                let mut scope = Scope::new(Name::new("machines/robot"));
                transform(&mut scope, &tree);

                // One for each section, private render, and exported template function.
                let names: Vec<_> = scope.functions.iter().map(|fun| &fun.name).collect();
                assert_eq!(vec!["section_machines_robot12",
                                "section_machines_robot17",
                                "render_machines_robot"],
                           names);

                // Single exported function name.
                let exports: Vec<_> =
                    scope.functions.iter().filter_map(|fun| fun.export.as_ref()).collect();
                assert_eq!(vec!["machines/robot"], exports);
            }
            Err(e) => panic!("Failed to parse tree: {}", e),
        }
    }
}
<commit_msg>Set initial buffer size<commit_after>extern crate regex;

use regex::Regex;
use std::collections::HashSet;
use std::io::{self, Write};

use super::{Compile, Name, ParseError, Path, Statement, Template};
use self::runtime::RUNTIME;

mod runtime;

/// A program is the final result of Mustache AST to Ruby extension source
/// translation that is presented to the main compiler driver for output.
///
/// It contains all external rendering functions generated by the translator,
/// which are exposed to Ruby code after the extension is compiled.
#[derive(Debug)]
pub struct Program {
    global: Scope,
}

impl Program {
    fn new() -> Self {
        Program { global: Scope::new(Name::new("global")) }
    }

    fn merge(&mut self, scope: Scope) -> &mut Self {
        self.global.merge(scope);
        self
    }
}

impl Compile for Program {
    /// Writes the final translated source code to an output buffer.
    ///
    /// This emits fully-formed Ruby extension source code that may be input
    /// into a mkmf build process, creating a dynamically loadable shared
    /// object file.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        // Emit runtime preamble.
        writeln!(buf, "{}", RUNTIME)?;

        // Emit string content declarations.
        for string in &self.global.strings {
            writeln!(buf, "{};", string.decl)?;
        }

        writeln!(buf, "")?;

        // Emit function declarations.
        for fun in &self.global.functions {
            writeln!(buf, "{};", fun.decl)?;
        }

        writeln!(buf, "")?;

        // Emit function definitions.
        for fun in &self.global.functions {
            fun.emit(buf)?
        }

        // Emit public render function.
        let renders: Vec<_> = self.global.functions.iter().filter_map(|f| f.invoke_if()).collect();
        writeln!(buf,
                 r#"static VALUE render(VALUE self, VALUE name, VALUE context) {{
                        const char *ptr = StringValuePtr(name);
                        const long length = RSTRING_LEN(name);
                        VALUE buf = rb_str_buf_new(2048);
                        VALUE stack = rb_ary_new_from_args(1, context);
                        {}
                        else {{
                            rb_raise(rb_eArgError, "Template not found");
                        }}
                        return buf;
                    }}"#,
                 renders.join(" else "))?;

        writeln!(buf, "")?;

        // Emit initialize function.
        writeln!(buf, "static void initialize() {{")?;
        for string in &self.global.strings {
            string.emit(buf)?;
        }
        writeln!(buf, "}}")
    }
}

/// A store for functions created by the translation process of an input
/// template to source code output.
///
/// Scopes have name generators that are used in function naming, providing a
/// stable name that other scopes may rely on for partial template function
/// calls.
///
/// After each template is translated into a scope they are merged into a
/// Program's global scope for final output.
#[derive(Debug)]
struct Scope {
    name: Name,
    functions: Vec<Function>,
    strings: Vec<StaticString>,
}

impl Scope {
    fn new(name: Name) -> Self {
        Scope {
            name: name,
            functions: Vec::new(),
            strings: Vec::new(),
        }
    }

    /// Combines this scope's function definitions with another's.
    fn merge(&mut self, mut other: Scope) -> &mut Self {
        self.functions.append(&mut other.functions);
        self.strings.append(&mut other.strings);
        self
    }

    /// Advances the scope's name generator to the next unique identifier. This
    /// should be called before descending another level in the recursive
    /// tree translation process.
    fn next(&mut self) -> &mut Self {
        self.name.next();
        self
    }

    /// Adds a function to this scope.
    fn register(&mut self, fun: Function) {
        self.functions.push(fun);
    }

    /// Adds a constant string value to this scope.
    fn content(&mut self, string: StaticString) {
        self.strings.push(string);
    }

    /// Returns the template path used to generate function names in this
    /// scope (e.g. "includes/header").
    fn base_name(&self) -> String {
        self.name.base.clone()
    }
}

#[derive(Debug)]
struct StaticString {
    name: String,
    decl: String,
    value: String,
    length: usize,
}

impl StaticString {
    /// Writes the raw content string global to the buffer.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        writeln!(buf, "{{")?;
        writeln!(buf, "static const char* content = \"{}\";", self.value)?;
        writeln!(buf,
                 "{} = rb_str_new_static(content, {});",
                 self.name,
                 self.length)?;
        writeln!(buf, "rb_global_variable(&{});", self.name)?;
        writeln!(buf, "}}")
    }
}

#[derive(Debug)]
struct Function {
    name: String,
    decl: String,
    body: Vec<String>,
    export: Option<String>,
}

impl Function {
    /// Writes the function definition to the buffer.
    fn emit(&self, buf: &mut Write) -> io::Result<()> {
        writeln!(buf, "{} {{", self.decl)?;
        for node in &self.body {
            writeln!(buf, "{}", node)?;
        }
        writeln!(buf, "}}\n")
    }

    /// Builds a conditional statement to call the function if the template
    /// name matches the function's exported name, like "includes/header".
    fn invoke_if(&self) -> Option<String> {
        if self.export.is_none() {
            return None;
        }

        let export = self.export.as_ref().unwrap();
        Some(format!("if (length == {len} && strncmp(ptr, \"{path}\", {len}) == 0) {{
                          {fun}(buf, stack);
                      }}",
                     len = export.len(),
                     path = export,
                     fun = self.name))
    }
}

/// Recursively walks the AST, translating Mustache statement tree nodes into
/// the corresponding Ruby extension source code.
///
/// Sections are extracted into top-level functions paired with a function
/// call at the location the section appeared in the template. Partials are
/// similarly translated into a function call which is expected to be provided
/// by another template in the final tree.
fn transform(scope: &mut Scope, node: &Statement) -> Option<String> {
    match *node {
        Statement::Program(ref block) => {
            let id = scope.name.id();

            // Build private render function.
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let render = Function {
                name: format!("render_{}", id),
                decl: format!("static void render_{}(VALUE buf, VALUE stack)", id),
                body: children,
                export: Some(scope.base_name()),
            };

            scope.register(render);
            None
        }
        Statement::Section(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let name = format!("section_{}", scope.next().name);
            let fun = Function {
                decl: format!("static void {}(VALUE buf, VALUE stack)", name),
                name: name,
                body: children,
                export: None,
            };

            let call = format!("{{ {} section(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Inverted(ref path, ref block) => {
            let children = block.statements
                .iter()
                .filter_map(|stmt| transform(scope.next(), stmt))
                .collect();

            let name = format!("section_{}", scope.next().name);
            let fun = Function {
                decl: format!("static void {}(VALUE buf, VALUE stack)", name),
                name: name,
                body: children,
                export: None,
            };

            let call = format!("{{ {} inverted(buf, stack, &path, {}); }}",
                               path_ary(path),
                               fun.name);

            scope.register(fun);
            Some(call)
        }
        Statement::Partial(ref name, ref padding) => {
            let name = Name::new(name);
            Some(format!("render_{}(buf, stack);", name.id()))
        }
        Statement::Comment(_) => None,
        Statement::Content(ref text) => {
            let content = clean(text);

            let name = format!("content_{}", scope.next().name);
            let string = StaticString {
                decl: format!("static VALUE {}", name),
                name: name,
                value: content,
                length: text.len(),
            };

            let append = format!("rb_str_buf_append(buf, {});", string.name);

            scope.content(string);
            Some(append)
        }
        Statement::Variable(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, true); }}", path))
        }
        Statement::Html(ref path) => {
            let path = path_ary(path);
            Some(format!("{{ {} append_value(buf, stack, &path, false); }}", path))
        }
    }
}

/// Transforms the AST of each parsed template into a source code tree
/// and links each template together into a single executable program.
pub fn link(templates: &Vec<Template>) -> Result<Program, ParseError> {
    validate(templates)?;

    let mut program = Program::new();
    templates.iter()
        .map(|template| {
            let mut scope = Scope::new(template.name());
            transform(&mut scope, &template.tree);
            scope
        })
        .fold(&mut program, |program, scope| program.merge(scope));

    Ok(program)
}

/// Ensures all templates may be linked together into an executable.
///
/// This method checks that all partial template paths are provided by
/// another template. For example, a `{{>include/header}}` partial invocation
/// must be provided by an `include/header.mustache` template file.
///
/// Partials can be considered function calls, so the function must be defined.
fn validate(templates: &Vec<Template>) -> Result<(), ParseError> {
    let all: HashSet<_> = templates.iter().map(|temp| &temp.name).collect();

    for template in templates {
        let names: HashSet<_> = template.tree.partials().into_iter().collect();
        let missing = &names - &all;
        if !missing.is_empty() {
            let name = missing.into_iter().next().unwrap();
            return Err(ParseError::UnknownPartial(name.clone(), template.path.clone()));
        }
    }

    Ok(())
}

/// Replaces string literal characters considered invalid inside a cstr with
/// their escaped counterparts.
fn clean(text: &str) -> String {
    let re = Regex::new(r"\\").unwrap();
    let text = re.replace_all(&text, "\\\\");

    let re = Regex::new(r"\r").unwrap();
    let text = re.replace_all(&text, "\\r");

    let re = Regex::new(r"\n").unwrap();
    let text = re.replace_all(&text, "\\n");

    let re = Regex::new(r#"["]"#).unwrap();
    re.replace_all(&text, "\\\"").into_owned()
}

/// Transforms a Mustache variable key path into the source code to build a
/// Ruby array. At runtime, each key in the array is recursively processed to
/// find the replacement text for a Mustache expression.
fn path_ary(path: &Path) -> String {
    let args = path.keys
        .iter()
        .map(|key| format!("\"{}\"", key))
        .collect::<Vec<String>>()
        .join(", ");

    format!("static const struct path path = {{ .keys = {{ {} }}, .length = {} }};",
            args,
            path.keys.len())
}

#[cfg(test)]
mod tests {
    use super::{link, transform, Scope};
    use super::super::{Name, ParseError, Statement, Template};
    use std::path::{Path, PathBuf};

    #[test]
    fn validates_valid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/robot"), None);
        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Ok(_) => (),
            Err(e) => panic!("Must link valid partials: {}", e),
        }
    }

    #[test]
    fn validates_invalid_partial_reference() {
        let base = PathBuf::from("app/templates");
        let path = PathBuf::from("app/templates/machines/robots.mustache");
        let tree = Statement::Partial(String::from("machines/unknown"), None);

        let master = Template::new(&base, path, tree);

        let path = PathBuf::from("app/templates/machines/robot.mustache");
        let tree = Statement::Content(String::from("hubot"));
        let detail = Template::new(&base, path, tree);

        let templates = vec![master, detail];
        match link(&templates) {
            Err(ParseError::UnknownPartial(ref name, ref path)) => {
                assert_eq!("machines/unknown", name);
                assert_eq!(Path::new("app/templates/machines/robots.mustache"), path);
            }
            _ => panic!("Must enforce partial references"),
        }
    }

    #[test]
    fn transforms_tree_into_functions() {
        let text = "
            {{> includes/header }}

                {{# robots}}

{{ name.first }}

                {{/ robots}}
                {{^ robots}}
                    {{! else clause }}
                    No robots
                {{/ robots}}

            {{> includes/footer }}
            {{{ unescaped.html }}}
        ";

        match Statement::parse(text) {
            Ok(tree) => {
                let mut scope = Scope::new(Name::new("machines/robot"));
                transform(&mut scope, &tree);

                // One for each section, private render, and exported template function.
                let names: Vec<_> = scope.functions.iter().map(|fun| &fun.name).collect();
                assert_eq!(vec!["section_machines_robot12",
                                "section_machines_robot17",
                                "render_machines_robot"],
                           names);

                // Single exported function name.
                let exports: Vec<_> =
                    scope.functions.iter().filter_map(|fun| fun.export.as_ref()).collect();
                assert_eq!(vec!["machines/robot"], exports);
            }
            Err(e) => panic!("Failed to parse tree: {}", e),
        }
    }
}
<|endoftext|>


<commit_before>use common::*;
use data::value::*;
use data::packet::Packet;
use std::sync::Arc;
use std::io::Write;
use std::any::Any;

use error::*;

pub trait Muxer {
    fn configure(&mut self) -> Result<()>;
    fn write_header(&mut self, buf: &mut Vec<u8>) -> Result<()>;
    fn write_packet(&mut self, buf: &mut Vec<u8>, pkt: Arc<Packet>) -> Result<()>;
    fn write_trailer(&mut self, buf: &mut Vec<u8>) -> Result<()>;

    fn set_global_info(&mut self, info: GlobalInfo) -> Result<()>;
    fn set_option<'a>(&mut self, key: &str, val: Value<'a>) -> Result<()>;
}

pub struct Context {
    muxer: Box<Muxer>,
    writer: Box<Write>,
    buf: Vec<u8>,
    pub user_private: Option<Box<Any + Send + Sync>>,
}

impl Context {
    pub fn new<W: Write + 'static>(muxer: Box<Muxer>, writer: Box<W>) -> Self {
        Context {
            muxer: muxer,
            writer: writer,
            buf: Vec::new(),
            user_private: None,
        }
    }

    pub fn configure(&mut self) -> Result<()> {
        self.muxer.configure()
    }

    pub fn write_header(&mut self) -> Result<usize> {
        self.muxer.write_header(&mut self.buf)?;
        //FIXME: we should have proper management of the buffer's index
        match self.writer.write_all(&self.buf) {
          Ok(()) => {
            let len = self.buf.len();
            self.buf.clear();
            Ok(len)
          },
          Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn write_packet(&mut self, pkt: Arc<Packet>) -> Result<usize> {
        self.muxer.write_packet(&mut self.buf, pkt)?;
        //FIXME: we should have proper management of the buffer's index
        match self.writer.write_all(&self.buf) {
          Ok(()) => {
            let len = self.buf.len();
            self.buf.clear();
            Ok(len)
          },
          Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn write_trailer(&mut self) -> Result<usize> {
        self.muxer.write_trailer(&mut self.buf)?;
        //FIXME: we should have proper management of the buffer's index
        match self.writer.write_all(&self.buf) {
          Ok(()) => {
            let len = self.buf.len();
            self.buf.clear();
            Ok(len)
          },
          Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn set_global_info(&mut self, info: GlobalInfo) -> Result<()> {
        self.muxer.set_global_info(info)
    }

    pub fn set_option<'a, V>(&mut self, key: &str, val: V) -> Result<()>
        where V: Into<Value<'a>> {
        self.muxer.set_option(key, val.into())
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    pub name: &'static str,
    pub demuxer: &'static str,
    pub description: &'static str,
    pub extensions: &'static [&'static str],
    pub mime: &'static [&'static str],
}

pub trait Descriptor {
    fn create(&self) -> Box<Muxer>;
    fn describe<'a>(&'a self) -> &'a Descr;
}

pub trait Lookup {
    fn by_name(&self, name: &str) -> Option<&'static Descriptor>;
}

impl Lookup for [&'static Descriptor] {
    fn by_name(&self, name: &str) -> Option<&'static Descriptor> {
        self.iter()
            .find(|&&d| d.describe().name == name)
            .map(|v| *v)
    }
}
<commit_msg>format: Make sure the muxer context is Send<commit_after>use common::*;
use data::value::*;
use data::packet::Packet;
use std::sync::Arc;
use std::io::Write;
use std::any::Any;

use error::*;

pub trait Muxer: Send {
    fn configure(&mut self) -> Result<()>;
    fn write_header(&mut self, buf: &mut Vec<u8>) -> Result<()>;
    fn write_packet(&mut self, buf: &mut Vec<u8>, pkt: Arc<Packet>) -> Result<()>;
    fn write_trailer(&mut self, buf: &mut Vec<u8>) -> Result<()>;

    fn set_global_info(&mut self, info: GlobalInfo) -> Result<()>;
    fn set_option<'a>(&mut self, key: &str, val: Value<'a>) -> Result<()>;
}

pub struct Context {
    muxer: Box<Muxer + Send>,
    writer: Box<Write + Send>,
    buf: Vec<u8>,
    pub user_private: Option<Box<Any + Send + Sync>>,
}

impl Context {
    pub fn new<W: Write + 'static + Send>(muxer: Box<Muxer + Send>, writer: Box<W>) -> Self {
        Context {
            muxer: muxer,
            writer: writer,
            buf: Vec::new(),
            user_private: None,
        }
    }

    pub fn configure(&mut self) -> Result<()> {
        self.muxer.configure()
    }

    pub fn write_header(&mut self) -> Result<usize> {
        self.muxer.write_header(&mut self.buf)?;
        //FIXME: we should have proper management of the buffer's index
        match self.writer.write_all(&self.buf) {
          Ok(()) => {
            let len = self.buf.len();
            self.buf.clear();
            Ok(len)
          },
          Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn write_packet(&mut self, pkt: Arc<Packet>) -> Result<usize> {
        self.muxer.write_packet(&mut self.buf, pkt)?;
        //FIXME: we should have proper management of the buffer's index
        match self.writer.write_all(&self.buf) {
          Ok(()) => {
            let len = self.buf.len();
            self.buf.clear();
            Ok(len)
          },
          Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn write_trailer(&mut self) -> Result<usize> {
        self.muxer.write_trailer(&mut self.buf)?;
        //FIXME: we should have proper management of the buffer's index
        match self.writer.write_all(&self.buf) {
          Ok(()) => {
            let len = self.buf.len();
            self.buf.clear();
            Ok(len)
          },
          Err(e) => Err(Error::Io(e)),
        }
    }

    pub fn set_global_info(&mut self, info: GlobalInfo) -> Result<()> {
        self.muxer.set_global_info(info)
    }

    pub fn set_option<'a, V>(&mut self, key: &str, val: V) -> Result<()>
        where V: Into<Value<'a>> {
        self.muxer.set_option(key, val.into())
    }
}

#[derive(Clone, Debug, PartialEq)]
pub struct Descr {
    pub name: &'static str,
    pub demuxer: &'static str,
    pub description: &'static str,
    pub extensions: &'static [&'static str],
    pub mime: &'static [&'static str],
}

pub trait Descriptor {
    fn create(&self) -> Box<Muxer>;
    fn describe<'a>(&'a self) -> &'a Descr;
}

pub trait Lookup {
    fn by_name(&self, name: &str) -> Option<&'static Descriptor>;
}

impl Lookup for [&'static Descriptor] {
    fn by_name(&self, name: &str) -> Option<&'static Descriptor> {
        self.iter()
            .find(|&&d| d.describe().name == name)
            .map(|v| *v)
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use logger::ImagLogger;

use libimagstore::store::Store;

#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;
        use std::error::Error;

        use libimagstore::hook::position::HookPosition;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");

        Runtime::init_logger(is_debugging, is_verbose);

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let cfg = match Configuration::new(&rtp) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                let cause : Option<Box<Error>> = Some(Box::new(e));
                return Err(RuntimeError::new(RuntimeErrorKind::Instantiate, cause));
            } else {
                trace_error(&e);
                None
            },

            Ok(cfg) => Some(cfg),
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath, store_config).map(|mut store| {
            // If we are debugging, generate hooks for all positions
            if is_debugging {
                let hooks = vec![
                    (DebugHook::new(HookPosition::PreCreate), HookPosition::PreCreate),
                    (DebugHook::new(HookPosition::PostCreate), HookPosition::PostCreate),
                    (DebugHook::new(HookPosition::PreRetrieve), HookPosition::PreRetrieve),
                    (DebugHook::new(HookPosition::PostRetrieve), HookPosition::PostRetrieve),
                    (DebugHook::new(HookPosition::PreUpdate), HookPosition::PreUpdate),
                    (DebugHook::new(HookPosition::PostUpdate), HookPosition::PostUpdate),
                    (DebugHook::new(HookPosition::PreDelete), HookPosition::PreDelete),
                    (DebugHook::new(HookPosition::PostDelete), HookPosition::PostDelete),
                ];

                // Put all debug hooks into the aspect "debug".
                // If it fails, trace the error and warn, but continue.
                for (hook, position) in hooks {
                    if let Err(e) = store.register_hook(position, &String::from("debug"), Box::new(hook)) {
                        if e.err_type() == StoreErrorKind::HookRegisterError {
                            trace_error_dbg(&e);
                            warn!("Registering debug hook with store failed");
                        } else {
                            trace_error(&e);
                        };
                    }
                }
            }

            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err(|e| {
            RuntimeError::new(RuntimeErrorKind::Instantiate, Some(Box::new(e)))
        })
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name("verbosity")
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("debugging")
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("config")
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("runtimepath")
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("storepath")
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("editor")
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))
    }

    /**
     * Initialize the internal logger
     */
    fn init_logger(is_debugging: bool, is_verbose: bool) {
        let lvl = if is_debugging {
            LogLevelFilter::Debug
        } else if is_verbose {
            LogLevelFilter::Info
        } else {
            LogLevelFilter::Error
        };

        log::set_logger(|max_log_lvl| {
            max_log_lvl.set(lvl);
            debug!("Init logger with {}", lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        })
        .map_err(|_| {
            panic!("Could not setup logger");
        })
        .ok();
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the configuration object
     */
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

<commit_msg>Only warn if there is no config file<commit_after>use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use logger::ImagLogger;

use libimagstore::store::Store;

#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;
        use std::error::Error;

        use libimagstore::hook::position::HookPosition;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");

        Runtime::init_logger(is_debugging, is_verbose);

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let cfg = match Configuration::new(&rtp) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                let cause : Option<Box<Error>> = Some(Box::new(e));
                return Err(RuntimeError::new(RuntimeErrorKind::Instantiate, cause));
            } else {
                warn!("No config file found.");
                warn!("Continuing without configuration file");
                None
            },

            Ok(cfg) => Some(cfg),
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath, store_config).map(|mut store| {
            // If we are debugging, generate hooks for all positions
            if is_debugging {
                let hooks = vec![
                    (DebugHook::new(HookPosition::PreCreate), HookPosition::PreCreate),
                    (DebugHook::new(HookPosition::PostCreate), HookPosition::PostCreate),
                    (DebugHook::new(HookPosition::PreRetrieve), HookPosition::PreRetrieve),
                    (DebugHook::new(HookPosition::PostRetrieve), HookPosition::PostRetrieve),
                    (DebugHook::new(HookPosition::PreUpdate), HookPosition::PreUpdate),
                    (DebugHook::new(HookPosition::PostUpdate), HookPosition::PostUpdate),
                    (DebugHook::new(HookPosition::PreDelete), HookPosition::PreDelete),
                    (DebugHook::new(HookPosition::PostDelete), HookPosition::PostDelete),
                ];

                // Put all debug hooks into the aspect "debug".
                // If it fails, trace the error and warn, but continue.
                for (hook, position) in hooks {
                    if let Err(e) = store.register_hook(position, &String::from("debug"), Box::new(hook)) {
                        if e.err_type() == StoreErrorKind::HookRegisterError {
                            trace_error_dbg(&e);
                            warn!("Registering debug hook with store failed");
                        } else {
                            trace_error(&e);
                        };
                    }
                }
            }

            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err(|e| {
            RuntimeError::new(RuntimeErrorKind::Instantiate, Some(Box::new(e)))
        })
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name("verbosity")
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("debugging")
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("config")
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("runtimepath")
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("storepath")
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("editor")
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))
    }

    /**
     * Initialize the internal logger
     */
    fn init_logger(is_debugging: bool, is_verbose: bool) {
        let lvl = if is_debugging {
            LogLevelFilter::Debug
        } else if is_verbose {
            LogLevelFilter::Info
        } else {
            LogLevelFilter::Error
        };

        log::set_logger(|max_log_lvl| {
            max_log_lvl.set(lvl);
            debug!("Init logger with {}", lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        })
        .map_err(|_| {
            panic!("Could not setup logger");
        })
        .ok();
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the configuration object
     */
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

<|endoftext|>


<commit_before>use std;
import io::{writer_util, reader_util};
import std::map;
import map::hashmap;
import result;

export rowreader, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inquotedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mut offset : uint,
    mut buffers : [[char]],
    mut state : state,
    mut trailing_nl : bool,
    mut terminating : bool
};

type row = {
    fields : [ fieldtype ]
};

type bufferdescr = {
    escaped: bool,
    sb: uint,
    eb: uint,
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow(&row: [str]) -> bool;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        new_reader_readlen(f, delim, quote, 1024u)
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mut offset : 0u,
        mut buffers : [],
        mut state : fieldstart(false),
        mut trailing_nl : false,
        mut terminating: false
    }
}

impl of rowiter for rowreader {
    fn readrow(&row: [str]) -> bool {
        fn unescape(escaped: [char]) -> [char] {
            let mut r : [char] = [];
            vec::reserve(r, vec::len(escaped));
            let mut in_q = false;
            for c in escaped {
                if in_q {
                    assert(c == '"');
                    in_q = false;
                } else {
                    in_q = c == '"';
                    r += [c];
                }
            }
            ret r;
        }
        fn statestr(state: state) -> str {
            alt state {
                fieldstart(after_delim) {
                    #fmt("fieldstart : after_delim %b", after_delim)
                }
                infield(b,o) {
                    #fmt("field : %u %u", b, o)
                }
                inquotedfield(b, o) {
                    #fmt("inquotedfield : %u %u", b, o)
                }
                inquote(b, o) {
                    #fmt("inquote : %u %u", b, o)
                }
            }
        }
        fn row_from_buf(self: rowreader, &fields: [fieldtype]) -> bool {
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let mut eb = vec::len(self.buffers) - 1u;
                let mut sb = sb, so = so, eo = eo;
                if escaped {
                    so += 1u;
                    if so > vec::len(self.buffers[sb]) {
                        sb += 1u;
                        so = vec::len(self.buffers[sb]) - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = vec::len(self.buffers[eb]) - 1u;
                    }
                }
                bufferfield({ escaped: escaped, sb: sb, eb: eb, start: so, end: eo })
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf = self.buffers[cbuffer];
            while self.offset < vec::len(buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                #debug("got '%c' | %s", c, statestr(self.state));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        #debug("fieldstart : after_delim %b", after_delim);
                        if c == self.quote {
                            self.state = inquotedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [emptyfield];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [emptyfield];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        #debug("field : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                        }
                    }
                    inquotedfield(b, o) {
                        #debug("inquotedfield : %u %u", b, o);
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                    }
                    inquote(b, o) {
                        #debug("inquote : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                            ret true;
                        } else if c == self.quote {
                            self.state = inquotedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                #debug("now %s", statestr(self.state));
            }
            ret false;
        }
        self.state = fieldstart(false);
        let mut do_read = vec::len(self.buffers) == 0u;
        let mut fields = [];

        while !self.terminating {
            if do_read {
                let mut data = self.f.read_chars(self.readlen);
                //log(error, ("aa", str::from_chars(data)));
                if vec::len(data) == 0u {
                    if !self.trailing_nl {
                        self.terminating = true;
                        data = ['\n'];
                    } else {
                        ret false;
                    }
                }
                // this is horrible, but it avoids the whole parser needing
                // to know about \r.
                data = vec::filter(data) { |c| c != '\r' };
                let data_len = vec::len(data);
                if data_len == 0u {
                    cont;
                }
                //log(error, ("here", str::from_chars(data)));
                self.trailing_nl = data[data_len - 1u] == '\n';
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, fields) {
                let l = vec::len(fields);
                vec::reserve(row, l);
                row = vec::map(fields) { |field|
                    alt field {
                        emptyfield() { "" }
                        bufferfield(desc) {
                            let mut buf = [];
                            {
                                let mut i = desc.sb;
                                while i <= desc.eb {
                                    let from = if (i == desc.sb)
                                        { desc.start } else { 0u };
                                    let to = if (i == desc.eb)
                                        { desc.end } else { vec::len(self.buffers[i]) };
                                    buf += vec::slice(self.buffers[i], from, to);
                                    i = i + 1u;
                                }
                            }
                            if desc.escaped {
                                buf = unescape(buf);
                            }
                            str::from_chars(buf)
                        }
                    }
                };
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                fields = [];
                ret true;
            }
            do_read = true;
        }
        ret false;
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    let mut fields : [str] = [];
    // can reuse, we're just shoving new vals in same cols..
    let m : map::hashmap<str, str> = map::str_hash();
    let ncols = vec::len(cols);
    while r.readrow(fields) {
        if vec::len(fields) != ncols {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        let mut col = 0u;
        vec::iter(fields) { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        hashmap_iter_cols(r, row, f);
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        let cols : [str] = vec::map(row, hmap);
        if !hver(cols) {
            ret;
        }
        hashmap_iter_cols(r, cols, f);
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(s: str, mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(s);
            let r = mk(f);
            let mut i = 0u;
            loop {
                let mut row: [str] = [];
                if !r.readrow(row) {
                    break;
                }
                let expect = expected[i];
                assert(vec::len(row) == vec::len(expect));
                let mut j = 0u;
                while j < row.len() {
                    assert(row[j] == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(s: str) {
            // test default reader params
            chk(s) { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test default constructor
            chk(s) { |inp|
                new_reader(inp, ',', '"')
            };
            // test continuations over read buffers
            let mut j = 1u;
            while j < str::len(s) {
                chk(s) { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
            ret;
        };
        // so we can test trailing newline case, testdata
        // must not end in \n - leave off the last newline
        runchecks(testdata);
        runchecks(str::replace(testdata, "\n", "\r\n"));
        if !str::ends_with(testdata, "\n") {
            runchecks(testdata+"\n");
            runchecks(str::replace(testdata+"\n", "\n", "\r\n"));
        }
    }

    #[test]
    fn test_simple() {
        rowmatch("a,b,c,d\n1,2,3,4",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn test_trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn test_leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn test_quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_with_comma() {
        rowmatch("\"1,2\"",
                 [["1,2"]])
    }

    #[test]
    fn test_quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn test_blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<commit_msg>quite a decent speedup<commit_after>use std;
import io::{writer_util, reader_util};
import std::map;
import map::hashmap;
import result;

export rowreader, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inquotedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mut offset : uint,
    mut buffers : [[char]],
    mut state : state,
    mut trailing_nl : bool,
    mut terminating : bool
};

type bufferdescr = {
    escaped: bool,
    sb: uint,
    eb: uint,
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow(&row: [str]) -> bool;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        new_reader_readlen(f, delim, quote, 1024u)
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mut offset : 0u,
        mut buffers : [],
        mut state : fieldstart(false),
        mut trailing_nl : false,
        mut terminating: false
    }
}

fn statestr(state: state) -> str {
    alt state {
        fieldstart(after_delim) {
            #fmt("fieldstart : after_delim %b", after_delim)
        }
        infield(b,o) {
            #fmt("field : %u %u", b, o)
        }
        inquotedfield(b, o) {
            #fmt("inquotedfield : %u %u", b, o)
        }
        inquote(b, o) {
            #fmt("inquote : %u %u", b, o)
        }
    }
}

fn unescape(escaped: [char]) -> [char] {
    let mut r : [char] = [];
    vec::reserve(r, vec::len(escaped));
    let mut in_q = false;
    for c in escaped {
        if in_q {
            assert(c == '"');
            in_q = false;
        } else {
            in_q = c == '"';
            r += [c];
        }
    }
    ret r;
}

impl of rowiter for rowreader {
    fn readrow(&row: [str]) -> bool {
        fn row_from_buf(self: rowreader, &fields: [str]) -> bool {
            fn decode(buffers: [[char]], field: fieldtype) -> str {
                alt field {
                    emptyfield() { "" }
                    bufferfield(desc) {
                        let mut buf = [];
                        {
                            let mut i = desc.sb;
                            while i <= desc.eb {
                                let from = if (i == desc.sb)
                                    { desc.start } else { 0u };
                                let to = if (i == desc.eb)
                                    { desc.end } else { vec::len(buffers[i]) };
                                buf += vec::slice(buffers[i], from, to);
                                i = i + 1u;
                            }
                        }
                        if desc.escaped {
                            buf = unescape(buf);
                        }
                        str::from_chars(buf)
                    }
                }
            }
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let mut eb = vec::len(self.buffers) - 1u;
                let mut sb = sb, so = so, eo = eo;
                if escaped {
                    so += 1u;
                    if so > vec::len(self.buffers[sb]) {
                        sb += 1u;
                        so = vec::len(self.buffers[sb]) - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = vec::len(self.buffers[eb]) - 1u;
                    }
                }
                bufferfield({ escaped: escaped, sb: sb, eb: eb, start: so, end: eo })
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf = self.buffers[cbuffer];
            while self.offset < vec::len(buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                #debug("got '%c' | %s", c, statestr(self.state));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        #debug("fieldstart : after_delim %b", after_delim);
                        if c == self.quote {
                            self.state = inquotedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [decode(self.buffers, emptyfield)];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, emptyfield)];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        #debug("field : %u %u", b, o);
                        if c == '\n' {
                            fields += [decode(self.buffers, new_bufferfield(self, false, b, o, coffset))];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, new_bufferfield(self, false, b, o, coffset))];
                        }
                    }
                    inquotedfield(b, o) {
                        #debug("inquotedfield : %u %u", b, o);
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                    }
                    inquote(b, o) {
                        #debug("inquote : %u %u", b, o);
                        if c == '\n' {
                            fields += [decode(self.buffers, new_bufferfield(self, true, b, o, coffset))];
                            ret true;
                        } else if c == self.quote {
                            self.state = inquotedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [decode(self.buffers, new_bufferfield(self, true, b, o, coffset))];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                #debug("now %s", statestr(self.state));
            }
            ret false;
        }
        self.state = fieldstart(false);
        let mut do_read = vec::len(self.buffers) == 0u;

        while !self.terminating {
            if do_read {
                let mut data = self.f.read_chars(self.readlen);
                //log(error, ("aa", str::from_chars(data)));
                if vec::len(data) == 0u {
                    if !self.trailing_nl {
                        self.terminating = true;
                        data = ['\n'];
                    } else {
                        ret false;
                    }
                }
                // this is horrible, but it avoids the whole parser needing
                // to know about \r.
                data = vec::filter(data) { |c| c != '\r' };
                let data_len = vec::len(data);
                if data_len == 0u {
                    cont;
                }
                self.trailing_nl = data[data_len - 1u] == '\n';
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, row) {
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                ret true;
            }
            do_read = true;
        }
        ret false;
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    let mut fields : [str] = [];
    // can reuse, we're just shoving new vals in same cols..
    let m : map::hashmap<str, str> = map::str_hash();
    let ncols = vec::len(cols);
    while r.readrow(fields) {
        if vec::len(fields) != ncols {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        let mut col = 0u;
        vec::iter(fields) { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        hashmap_iter_cols(r, row, f);
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let mut row: [str] = [];
    if r.readrow(row) {
        let cols : [str] = vec::map(row, hmap);
        if !hver(cols) {
            ret;
        }
        hashmap_iter_cols(r, cols, f);
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(s: str, mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(s);
            let r = mk(f);
            let mut i = 0u;
            loop {
                let mut row: [str] = [];
                if !r.readrow(row) {
                    break;
                }
                let expect = expected[i];
                assert(vec::len(row) == vec::len(expect));
                let mut j = 0u;
                while j < row.len() {
                    assert(row[j] == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(s: str) {
            // test default reader params
            chk(s) { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test default constructor
            chk(s) { |inp|
                new_reader(inp, ',', '"')
            };
            // test continuations over read buffers
            let mut j = 1u;
            while j < str::len(s) {
                chk(s) { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
            ret;
        };
        // so we can test trailing newline case, testdata
        // must not end in \n - leave off the last newline
        runchecks(testdata);
        runchecks(str::replace(testdata, "\n", "\r\n"));
        if !str::ends_with(testdata, "\n") {
            runchecks(testdata+"\n");
            runchecks(str::replace(testdata+"\n", "\n", "\r\n"));
        }
    }

    #[test]
    fn simple() {
        rowmatch("a,b,c,d\n1,2,3,4",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn quote_with_comma() {
        rowmatch("\"1,2\"",
                 [["1,2"]])
    }

    #[test]
    fn quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<|endoftext|>


<commit_before>extern mod std;

use libc::c_char;
use libc::c_int;
use libc::size_t;
use libc::c_ulong;
use libc::c_void;
use ptr::null;
use ptr::addr_of;
use ptr::mut_addr_of;
use str::as_c_str;

struct mpz_struct {
  _mp_alloc: c_int,
  _mp_size: c_int,
  _mp_d: *c_void
}

type mpz_srcptr = *const mpz_struct;
type mpz_ptr = *mut mpz_struct;

extern mod gmp {
  fn __gmpz_init(x: mpz_ptr);
  fn __gmpz_init_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_clear(x: mpz_ptr);
  fn __gmpz_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_get_str(str: *c_char, base: c_int, op: mpz_srcptr) -> *c_char;
  fn __gmpz_sizeinbase(op: mpz_srcptr, base: c_int) -> size_t;
  fn __gmpz_cmp(op: mpz_srcptr, op2: mpz_srcptr) -> c_int;
  fn __gmpz_cmp_ui(op1: mpz_srcptr, op2: c_ulong) -> c_int;
  fn __gmpz_add(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_sub(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_mul(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_neg(rop: mpz_ptr, op: mpz_srcptr);
  fn __gmpz_tdiv_q(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
  fn __gmpz_mod(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
}

use gmp::*;

pub struct Mpz {
  priv mpz: mpz_struct,

  drop {
    __gmpz_clear(mut_addr_of(&self.mpz));
  }
}

impl Mpz {
  fn set_str(&self, s: &str, base: int) -> bool {
    let mpz = mut_addr_of(&self.mpz);
    let r = as_c_str(s, { |s| __gmpz_set_str(mpz, s, base as c_int) });
    r == 0
  }

  fn size_in_base(&self, base: int) -> uint {
    __gmpz_sizeinbase(addr_of(&self.mpz), base as c_int) as uint
  }
}

impl Mpz: cmp::Eq {
  pure fn eq(other: &Mpz) -> bool unsafe {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) == 0
  }
  pure fn ne(other: &Mpz) -> bool unsafe {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) != 0
  }
}

impl Mpz: cmp::Ord {
  pure fn lt(other: &Mpz) -> bool unsafe {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) < 0
  }
  pure fn le(other: &Mpz) -> bool unsafe {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) <= 0
  }
  pure fn gt(other: &Mpz) -> bool unsafe {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) > 0
  }
  pure fn ge(other: &Mpz) -> bool unsafe {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) >= 0
  }
}

impl Mpz: num::Num {
  pure fn add(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_add(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn sub(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_sub(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn mul(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_mul(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn div(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_tdiv_q(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn modulo(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_mod(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn neg() -> Mpz unsafe {
    let res = init();
    __gmpz_neg(mut_addr_of(&res.mpz), addr_of(&self.mpz));
    res
  }
  pure fn to_int() -> int {
    fail ~"not implemented";
  }
  static pure fn from_int(other: int) -> Mpz unsafe {
    fail ~"not implemented";
  }
}

impl Mpz : from_str::FromStr {
  static fn from_str(s: &str) -> Option<Mpz> {
    let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
    if as_c_str(s, { |s| __gmpz_init_set_str(mut_addr_of(&mpz), s, 10) }) == 0 {
      Some(Mpz { mpz: mpz })
    } else {
      __gmpz_clear(mut_addr_of(&mpz));
      None
    }
  }
}

impl Mpz : to_str::ToStr {
  pure fn to_str() -> ~str unsafe {
    let length = self.size_in_base(10) + 2;
    let dst = vec::to_mut(vec::from_elem(length, '0'));
    let pdst = vec::raw::to_ptr(dst);

    str::raw::from_c_str(__gmpz_get_str(pdst as *c_char, 10, addr_of(&self.mpz)))
  }
}

pub fn init() -> Mpz {
  let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
  __gmpz_init(mut_addr_of(&mpz));
  Mpz { mpz: mpz }
}

#[cfg(test)]
mod tests {
  #[test]
  fn size_in_base() {
    let x = init();
    x.set_str("150000", 10);
    assert(x.size_in_base(10) == 6);
  }

  #[test]
  fn eq() {
    let x = init();
    x.set_str("4242142195", 10);
    let y = init();
    y.set_str("4242142195", 10);
    let z = init();
    z.set_str("4242142196", 10);

    assert(x == y);
    assert(x != z);
    assert(y != z);
  }

  #[test]
  fn ord() {
    let x = init();
    x.set_str("40000000000000000000000", 10);
    let y = init();
    y.set_str("45000000000000000000000", 10);
    let z = init();
    z.set_str("50000000000000000000000", 10);

    assert(x < y && x < z && y < z);
    assert(x <= x && x <= y && x <= z && y <= z);
    assert(z > y && z > x && y > x);
    assert(z >= z && z >= y && z >= x && y >= x);
  }

  #[test]
  #[should_fail]
  fn div_zero() {
    let x = init();
    x / x;
  }

  #[test]
  #[should_fail]
  fn modulo_zero() {
    let x = init();
    x % x;
  }

  #[test]
  fn test_div_round() {
    let x = init();
    let y = init();
    let mut z: Mpz;

    x.set_str("2", 10);
    y.set_str("3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / 3) == 0);

    x.set_str("2", 10);
    y.set_str("-3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / -3) == 0);
  }

  #[test]
  fn to_str() {
    let x = init();
    x.set_str("1234567890", 10);
    assert(x.to_str() == ~"1234567890");
  }
}
<commit_msg>mark some gmp functions as pure (based on gmp.h)<commit_after>extern mod std;

use libc::c_char;
use libc::c_int;
use libc::size_t;
use libc::c_ulong;
use libc::c_void;
use ptr::null;
use ptr::addr_of;
use ptr::mut_addr_of;
use str::as_c_str;

struct mpz_struct {
  _mp_alloc: c_int,
  _mp_size: c_int,
  _mp_d: *c_void
}

type mpz_srcptr = *const mpz_struct;
type mpz_ptr = *mut mpz_struct;

extern mod gmp {
  fn __gmpz_init(x: mpz_ptr);
  fn __gmpz_init_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_clear(x: mpz_ptr);
  fn __gmpz_set_str(rop: mpz_ptr, str: *c_char, base: c_int) -> c_int;
  fn __gmpz_get_str(str: *c_char, base: c_int, op: mpz_srcptr) -> *c_char;
  pure fn __gmpz_sizeinbase(op: mpz_srcptr, base: c_int) -> size_t;
  pure fn __gmpz_cmp(op: mpz_srcptr, op2: mpz_srcptr) -> c_int;
  pure fn __gmpz_cmp_ui(op1: mpz_srcptr, op2: c_ulong) -> c_int;
  fn __gmpz_add(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_sub(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_mul(rop: mpz_ptr, op1: mpz_srcptr, op2: mpz_srcptr);
  fn __gmpz_neg(rop: mpz_ptr, op: mpz_srcptr);
  fn __gmpz_tdiv_q(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
  fn __gmpz_mod(r: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
}

use gmp::*;

pub struct Mpz {
  priv mpz: mpz_struct,

  drop {
    __gmpz_clear(mut_addr_of(&self.mpz));
  }
}

impl Mpz {
  fn set_str(&self, s: &str, base: int) -> bool {
    let mpz = mut_addr_of(&self.mpz);
    let r = as_c_str(s, { |s| __gmpz_set_str(mpz, s, base as c_int) });
    r == 0
  }

  pure fn size_in_base(&self, base: int) -> uint {
    __gmpz_sizeinbase(addr_of(&self.mpz), base as c_int) as uint
  }
}

impl Mpz: cmp::Eq {
  pure fn eq(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) == 0
  }
  pure fn ne(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) != 0
  }
}

impl Mpz: cmp::Ord {
  pure fn lt(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) < 0
  }
  pure fn le(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) <= 0
  }
  pure fn gt(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) > 0
  }
  pure fn ge(other: &Mpz) -> bool {
    __gmpz_cmp(addr_of(&self.mpz), addr_of(&other.mpz)) >= 0
  }
}

impl Mpz: num::Num {
  pure fn add(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_add(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn sub(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_sub(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn mul(other: &Mpz) -> Mpz unsafe {
    let res = init();
    __gmpz_mul(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn div(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_tdiv_q(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn modulo(other: &Mpz) -> Mpz unsafe {
    if __gmpz_cmp_ui(addr_of(&self.mpz), 0) == 0 {
      fail ~"divide by zero";
    }

    let res = init();
    __gmpz_mod(mut_addr_of(&res.mpz), addr_of(&self.mpz), addr_of(&other.mpz));
    res
  }
  pure fn neg() -> Mpz unsafe {
    let res = init();
    __gmpz_neg(mut_addr_of(&res.mpz), addr_of(&self.mpz));
    res
  }
  pure fn to_int() -> int {
    fail ~"not implemented";
  }
  static pure fn from_int(other: int) -> Mpz unsafe {
    fail ~"not implemented";
  }
}

impl Mpz : from_str::FromStr {
  static fn from_str(s: &str) -> Option<Mpz> {
    let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
    if as_c_str(s, { |s| __gmpz_init_set_str(mut_addr_of(&mpz), s, 10) }) == 0 {
      Some(Mpz { mpz: mpz })
    } else {
      __gmpz_clear(mut_addr_of(&mpz));
      None
    }
  }
}

impl Mpz : to_str::ToStr {
  pure fn to_str() -> ~str unsafe {
    let length = self.size_in_base(10) + 2;
    let dst = vec::to_mut(vec::from_elem(length, '0'));
    let pdst = vec::raw::to_ptr(dst);

    str::raw::from_c_str(__gmpz_get_str(pdst as *c_char, 10, addr_of(&self.mpz)))
  }
}

pub fn init() -> Mpz {
  let mpz = mpz_struct { _mp_alloc: 0, _mp_size: 0, _mp_d: null() };
  __gmpz_init(mut_addr_of(&mpz));
  Mpz { mpz: mpz }
}

#[cfg(test)]
mod tests {
  #[test]
  fn size_in_base() {
    let x = init();
    x.set_str("150000", 10);
    assert(x.size_in_base(10) == 6);
  }

  #[test]
  fn eq() {
    let x = init();
    x.set_str("4242142195", 10);
    let y = init();
    y.set_str("4242142195", 10);
    let z = init();
    z.set_str("4242142196", 10);

    assert(x == y);
    assert(x != z);
    assert(y != z);
  }

  #[test]
  fn ord() {
    let x = init();
    x.set_str("40000000000000000000000", 10);
    let y = init();
    y.set_str("45000000000000000000000", 10);
    let z = init();
    z.set_str("50000000000000000000000", 10);

    assert(x < y && x < z && y < z);
    assert(x <= x && x <= y && x <= z && y <= z);
    assert(z > y && z > x && y > x);
    assert(z >= z && z >= y && z >= x && y >= x);
  }

  #[test]
  #[should_fail]
  fn div_zero() {
    let x = init();
    x / x;
  }

  #[test]
  #[should_fail]
  fn modulo_zero() {
    let x = init();
    x % x;
  }

  #[test]
  fn test_div_round() {
    let x = init();
    let y = init();
    let mut z: Mpz;

    x.set_str("2", 10);
    y.set_str("3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / 3) == 0);

    x.set_str("2", 10);
    y.set_str("-3", 10);
    z = x / y;
    assert(__gmpz_cmp_ui(addr_of(&z.mpz), 2 / -3) == 0);
  }

  #[test]
  fn to_str() {
    let x = init();
    x.set_str("1234567890", 10);
    assert(x.to_str() == ~"1234567890");
  }
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![feature(test)]

extern crate test;
extern crate regex;

extern crate fancy_regex;

#[cfg(test)]
mod bench {

    use test::Bencher;
    use regex::Regex;

	use fancy_regex::Expr;
    use fancy_regex::analyze::Analysis;
    use fancy_regex::compile::compile;
    use fancy_regex::vm;

    #[bench]
    fn lifetime_re(b: &mut Bencher) {
        b.iter(|| Expr::parse("\\'[a-zA-Z_][a-zA-Z0-9_]*(?!\\')\\b"));
    }

    #[bench]
    fn literal_re(b: &mut Bencher) {
        b.iter(|| Expr::parse("^(\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"));
    }

    #[bench]
    fn misc(b: &mut Bencher) {
        b.iter(|| Expr::parse("^\\p{L}|\\p{N}|\\s|.|\\d"));
    }

    #[bench]
    fn literal_re_analyze(b: &mut Bencher) {
        let (e, br) = Expr::parse("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})").unwrap();
        b.iter(|| Analysis::analyze(&e, &br));
    }

    #[bench]
    fn literal_re_regex(b: &mut Bencher) {
        b.iter(|| Regex::new("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"));
    }

    /*
    #[bench]
    fn detect_backref(b: &mut Bencher) {
        b.iter(|| detect_possible_backref("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"));
    }
    */

    #[bench]
    fn run_backtrack(b: &mut Bencher) {
        let (e, br) = Expr::parse("^.*?(([ab]+)\\1b)").unwrap();
        let a = Analysis::analyze(&e, &br);
        let p = compile(&a).unwrap();
        b.iter(|| vm::run(&p, "babab", 0, 0))
    }

    // The following regex is a pathological case for backtracking
    // implementations, see README.md:
    #[bench]
    fn run_tricky(b: &mut Bencher) {
        let (e, br) = Expr::parse("(a|b|ab)*bc").unwrap();
        let a = Analysis::analyze(&e, &br);
        let p = compile(&a).unwrap();
        let mut s = String::new();
        for _ in 0..28 {
            s.push_str("ab");
        }
        s.push_str("ac");
        b.iter(|| vm::run(&p, &s, 0, 0))
    }

}
<commit_msg>Make benches compile<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![feature(test)]

extern crate test;
extern crate regex;

extern crate fancy_regex;

#[cfg(test)]
mod bench {

    use test::Bencher;
    use regex::Regex;

	use fancy_regex::Expr;
    use fancy_regex::analyze::Analysis;
    use fancy_regex::compile::compile;
    use fancy_regex::vm;

    #[bench]
    fn lifetime_re(b: &mut Bencher) {
        b.iter(|| Expr::parse("\\'[a-zA-Z_][a-zA-Z0-9_]*(?!\\')\\b"));
    }

    #[bench]
    fn literal_re(b: &mut Bencher) {
        b.iter(|| Expr::parse("^(\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"));
    }

    #[bench]
    fn misc(b: &mut Bencher) {
        b.iter(|| Expr::parse("^\\p{L}|\\p{N}|\\s|.|\\d"));
    }

    #[bench]
    fn literal_re_analyze(b: &mut Bencher) {
        let (e, br) = Expr::parse("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})").unwrap();
        b.iter(|| Analysis::analyze(&e, &br));
    }

    #[bench]
    fn literal_re_regex(b: &mut Bencher) {
        b.iter(|| Regex::new("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"));
    }

    /*
    #[bench]
    fn detect_backref(b: &mut Bencher) {
        b.iter(|| detect_possible_backref("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"));
    }
    */

    #[bench]
    fn run_backtrack(b: &mut Bencher) {
        let (e, br) = Expr::parse("^.*?(([ab]+)\\1b)").unwrap();
        let a = Analysis::analyze(&e, &br).unwrap();
        let p = compile(&a).unwrap();
        b.iter(|| vm::run(&p, "babab", 0, 0))
    }

    // The following regex is a pathological case for backtracking
    // implementations, see README.md:
    #[bench]
    fn run_tricky(b: &mut Bencher) {
        let (e, br) = Expr::parse("(a|b|ab)*bc").unwrap();
        let a = Analysis::analyze(&e, &br).unwrap();
        let p = compile(&a).unwrap();
        let mut s = String::new();
        for _ in 0..28 {
            s.push_str("ab");
        }
        s.push_str("ac");
        b.iter(|| vm::run(&p, &s, 0, 0))
    }

}
<|endoftext|>