<commit_before>//! Contains the binary logic of Ion.
mod designators;
mod prompt;
mod readln;
mod terminate;

use self::{
    prompt::{prompt, prompt_fn}, readln::readln,
    terminate::{terminate_quotes, terminate_script_quotes},
};
use super::{flow_control::Statement, status::*, FlowLogic, Shell, ShellHistory};
use liner::{Buffer, Context};
use std::{env, fs::File, io::ErrorKind, iter, path::Path, process, sync::Mutex};

pub const MAN_ION: &str = r#"NAME
    ion - ion shell

SYNOPSIS
    ion [ -h | --help ] [-c] [-n] [-v]

DESCRIPTION
    ion is a commandline shell created to be a faster and easier to use alternative to the
    currently available shells. It is not POSIX compliant.

OPTIONS
    -c
        evaluates given commands instead of reading from the commandline.

    -n or --no-execute
        do not execute any commands, just do syntax checking.

    -v or --version
        prints the version, platform and revision of ion then exits.
"#;

pub trait Binary {
    /// Parses and executes the arguments that were supplied to the shell.
    fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A);
    /// Creates an interactive session that reads from a prompt provided by
    /// Liner.
    fn execute_interactive(self);
    /// Ensures that read statements from a script are terminated.
    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32;
    /// Ensures that read statements from the interactive prompt is terminated.
    fn terminate_quotes(&mut self, command: String) -> Result<String, ()>;
    /// Ion's interface to Liner's `read_line` method, which handles everything related to
    /// rendering, controlling, and getting input from the prompt.
    fn readln(&mut self) -> Option<String>;
    /// Generates the prompt that will be used by Liner.
    fn prompt(&mut self) -> String;
    /// Display version information and exit
    fn display_version(&self);
    // Executes the PROMPT function, if it exists, and returns the output.
    fn prompt_fn(&mut self) -> Option<String>;
    // Handles commands given by the REPL, and saves them to history.
    fn save_command(&mut self, command: &str);
    // Resets the flow control fields to their default values.
    fn reset_flow(&mut self);
}

impl Binary for Shell {
    fn display_version(&self) {
        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
        process::exit(0);
    }

    fn save_command(&mut self, cmd: &str) {
        if cmd.starts_with('~') {
            if !cmd.ends_with('/')
                && self
                    .variables
                    .tilde_expansion(cmd, &self.directory_stack)
                    .map_or(false, |path| Path::new(&path).is_dir())
            {
                self.save_command_in_history(&[cmd, "/"].concat());
            } else {
                self.save_command_in_history(cmd);
            }
            return;
        }

        if Path::new(cmd).is_dir() & !cmd.ends_with('/') {
            self.save_command_in_history(&[cmd, "/"].concat());
        } else {
            self.save_command_in_history(cmd);
        }
    }

    fn reset_flow(&mut self) {
        self.flow_control.level = 0;
        self.flow_control.current_if_mode = 0;
        self.flow_control.current_statement = Statement::Default;
    }

    fn execute_interactive(mut self) {
        self.context = Some({
            let mut context = Context::new();
            context.word_divider_fn = Box::new(word_divide);
            if "1" == self.get_var_or_empty("HISTFILE_ENABLED") {
                let path = self.get_var("HISTFILE").expect("shell didn't set HISTFILE");
                context.history.set_file_name(Some(path.clone()));
                if !Path::new(path.as_str()).exists() {
                    eprintln!("ion: creating history file at \"{}\"", path);
                    if let Err(why) = File::create(path) {
                        eprintln!("ion: could not create history file: {}", why);
                    }
                }
                match context.history.load_history() {
                    Ok(()) => {
                        // pass
                    }
                    Err(ref err) if err.kind() == ErrorKind::NotFound => {
                        let history_filename = self.get_var_or_empty("HISTFILE");
                        eprintln!(
                            "ion: failed to find history file {}: {}",
                            history_filename, err
                        );
                    }
                    Err(err) => {
                        eprintln!("ion: failed to load history: {}", err);
                    }
                }
            }
            Mutex::new(context)
        });

        self.evaluate_init_file();

        self.variables
            .set_array("args", iter::once(env::args().next().unwrap()).collect());

        loop {
            if let Some(command) = self.readln() {
                if !command.is_empty() {
                    if let Ok(command) = self.terminate_quotes(command.replace("\\\n", "")) {
                        let cmd: &str = &designators::expand_designators(&self, command.trim());
                        self.on_command(&cmd);
                        self.save_command(&cmd);
                    } else {
                        self.reset_flow();
                    }
                }
            } else {
                self.reset_flow();
            }
        }
    }

    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
        if let Some(mut arg) = args.next() {
            for argument in args {
                arg.push(' ');
                if argument == "" {
                    arg.push_str("''");
                } else {
                    arg.push_str(&argument);
                }
            }
            self.on_command(&arg);
        } else {
            eprintln!("ion: -c requires an argument");
            self.exit(FAILURE);
        }

        if self.flow_control.level != 0 {
            eprintln!(
                "ion: unexpected end of arguments: expected end block for `{}`",
                self.flow_control.current_statement.short()
            );
            self.exit(FAILURE);
        }
    }

    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
        terminate_quotes(self, command)
    }

    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32 {
        terminate_script_quotes(self, lines)
    }

    fn readln(&mut self) -> Option<String> { readln(self) }

    fn prompt_fn(&mut self) -> Option<String> { prompt_fn(self) }

    fn prompt(&mut self) -> String { prompt(self) }
}

#[derive(Debug)]
struct WordDivide<I>
where
    I: Iterator<Item = (usize, char)>,
{
    iter:       I,
    count:      usize,
    word_start: Option<usize>,
}
impl<I> WordDivide<I>
where
    I: Iterator<Item = (usize, char)>,
{
    #[inline]
    fn check_boundary(&mut self, c: char, index: usize, escaped: bool) -> Option<(usize, usize)> {
        if let Some(start) = self.word_start {
            if c == ' ' && !escaped {
                self.word_start = None;
                Some((start, index))
            } else {
                self.next()
            }
        } else {
            if c != ' ' {
                self.word_start = Some(index);
            }
            self.next()
        }
    }
}
impl<I> Iterator for WordDivide<I>
where
    I: Iterator<Item = (usize, char)>,
{
    type Item = (usize, usize);

    fn next(&mut self) -> Option<Self::Item> {
        self.count += 1;
        match self.iter.next() {
            Some((i, '\\')) => {
                if let Some((_, cnext)) = self.iter.next() {
                    // We use `i` in order to include the backslash as part of the word
                    self.check_boundary(cnext, i, true)
                } else {
                    self.next()
                }
            }
            Some((i, c)) => self.check_boundary(c, i, false),
            None => {
                // When start has been set, that means we have encountered a full word.
                self.word_start.take().map(|start| (start, self.count - 1))
            }
        }
    }
}

fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
    // -> impl Iterator<Item = (usize, usize)> + 'a
    WordDivide {
        iter:       buf.chars().cloned().enumerate(),
        count:      0,
        word_start: None,
    }.collect() // TODO: return iterator directly :D
}
<commit_msg>Fix tab completions being funky<commit_after>//! Contains the binary logic of Ion.
mod designators;
mod prompt;
mod readln;
mod terminate;

use self::{
    prompt::{prompt, prompt_fn}, readln::readln,
    terminate::{terminate_quotes, terminate_script_quotes},
};
use super::{flow_control::Statement, status::*, FlowLogic, Shell, ShellHistory};
use liner::{Buffer, Context};
use std::{env, fs::File, io::ErrorKind, iter, path::Path, process, sync::Mutex};

pub const MAN_ION: &str = r#"NAME
    ion - ion shell

SYNOPSIS
    ion [ -h | --help ] [-c] [-n] [-v]

DESCRIPTION
    ion is a commandline shell created to be a faster and easier to use alternative to the
    currently available shells. It is not POSIX compliant.

OPTIONS
    -c
        evaluates given commands instead of reading from the commandline.

    -n or --no-execute
        do not execute any commands, just do syntax checking.

    -v or --version
        prints the version, platform and revision of ion then exits.
"#;

pub trait Binary {
    /// Parses and executes the arguments that were supplied to the shell.
    fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A);
    /// Creates an interactive session that reads from a prompt provided by
    /// Liner.
    fn execute_interactive(self);
    /// Ensures that read statements from a script are terminated.
    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32;
    /// Ensures that read statements from the interactive prompt is terminated.
    fn terminate_quotes(&mut self, command: String) -> Result<String, ()>;
    /// Ion's interface to Liner's `read_line` method, which handles everything related to
    /// rendering, controlling, and getting input from the prompt.
    fn readln(&mut self) -> Option<String>;
    /// Generates the prompt that will be used by Liner.
    fn prompt(&mut self) -> String;
    /// Display version information and exit
    fn display_version(&self);
    // Executes the PROMPT function, if it exists, and returns the output.
    fn prompt_fn(&mut self) -> Option<String>;
    // Handles commands given by the REPL, and saves them to history.
    fn save_command(&mut self, command: &str);
    // Resets the flow control fields to their default values.
    fn reset_flow(&mut self);
}

impl Binary for Shell {
    fn display_version(&self) {
        println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string")));
        process::exit(0);
    }

    fn save_command(&mut self, cmd: &str) {
        if cmd.starts_with('~') {
            if !cmd.ends_with('/')
                && self
                    .variables
                    .tilde_expansion(cmd, &self.directory_stack)
                    .map_or(false, |path| Path::new(&path).is_dir())
            {
                self.save_command_in_history(&[cmd, "/"].concat());
            } else {
                self.save_command_in_history(cmd);
            }
            return;
        }

        if Path::new(cmd).is_dir() & !cmd.ends_with('/') {
            self.save_command_in_history(&[cmd, "/"].concat());
        } else {
            self.save_command_in_history(cmd);
        }
    }

    fn reset_flow(&mut self) {
        self.flow_control.level = 0;
        self.flow_control.current_if_mode = 0;
        self.flow_control.current_statement = Statement::Default;
    }

    fn execute_interactive(mut self) {
        self.context = Some({
            let mut context = Context::new();
            context.word_divider_fn = Box::new(word_divide);
            if "1" == self.get_var_or_empty("HISTFILE_ENABLED") {
                let path = self.get_var("HISTFILE").expect("shell didn't set HISTFILE");
                context.history.set_file_name(Some(path.clone()));
                if !Path::new(path.as_str()).exists() {
                    eprintln!("ion: creating history file at \"{}\"", path);
                    if let Err(why) = File::create(path) {
                        eprintln!("ion: could not create history file: {}", why);
                    }
                }
                match context.history.load_history() {
                    Ok(()) => {
                        // pass
                    }
                    Err(ref err) if err.kind() == ErrorKind::NotFound => {
                        let history_filename = self.get_var_or_empty("HISTFILE");
                        eprintln!(
                            "ion: failed to find history file {}: {}",
                            history_filename, err
                        );
                    }
                    Err(err) => {
                        eprintln!("ion: failed to load history: {}", err);
                    }
                }
            }
            Mutex::new(context)
        });

        self.evaluate_init_file();

        self.variables
            .set_array("args", iter::once(env::args().next().unwrap()).collect());

        loop {
            if let Some(command) = self.readln() {
                if !command.is_empty() {
                    if let Ok(command) = self.terminate_quotes(command.replace("\\\n", "")) {
                        let cmd: &str = &designators::expand_designators(&self, command.trim());
                        self.on_command(&cmd);
                        self.save_command(&cmd);
                    } else {
                        self.reset_flow();
                    }
                }
            } else {
                self.reset_flow();
            }
        }
    }

    fn execute_arguments<A: Iterator<Item = String>>(&mut self, mut args: A) {
        if let Some(mut arg) = args.next() {
            for argument in args {
                arg.push(' ');
                if argument == "" {
                    arg.push_str("''");
                } else {
                    arg.push_str(&argument);
                }
            }
            self.on_command(&arg);
        } else {
            eprintln!("ion: -c requires an argument");
            self.exit(FAILURE);
        }

        if self.flow_control.level != 0 {
            eprintln!(
                "ion: unexpected end of arguments: expected end block for `{}`",
                self.flow_control.current_statement.short()
            );
            self.exit(FAILURE);
        }
    }

    fn terminate_quotes(&mut self, command: String) -> Result<String, ()> {
        terminate_quotes(self, command)
    }

    fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32 {
        terminate_script_quotes(self, lines)
    }

    fn readln(&mut self) -> Option<String> { readln(self) }

    fn prompt_fn(&mut self) -> Option<String> { prompt_fn(self) }

    fn prompt(&mut self) -> String { prompt(self) }
}

#[derive(Debug)]
struct WordDivide<I>
where
    I: Iterator<Item = (usize, char)>,
{
    iter:       I,
    count:      usize,
    word_start: Option<usize>,
}
impl<I> WordDivide<I>
where
    I: Iterator<Item = (usize, char)>,
{
    #[inline]
    fn check_boundary(&mut self, c: char, index: usize, escaped: bool) -> Option<(usize, usize)> {
        if let Some(start) = self.word_start {
            if c == ' ' && !escaped {
                self.word_start = None;
                Some((start, index))
            } else {
                self.next()
            }
        } else {
            if c != ' ' {
                self.word_start = Some(index);
            }
            self.next()
        }
    }
}
impl<I> Iterator for WordDivide<I>
where
    I: Iterator<Item = (usize, char)>,
{
    type Item = (usize, usize);

    fn next(&mut self) -> Option<Self::Item> {
        self.count += 1;
        match self.iter.next() {
            Some((i, '\\')) => {
                if let Some((_, cnext)) = self.iter.next() {
                    self.count += 1;
                    // We use `i` in order to include the backslash as part of the word
                    self.check_boundary(cnext, i, true)
                } else {
                    self.next()
                }
            }
            Some((i, c)) => self.check_boundary(c, i, false),
            None => {
                // When start has been set, that means we have encountered a full word.
                self.word_start.take().map(|start| (start, self.count - 1))
            }
        }
    }
}

fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> {
    // -> impl Iterator<Item = (usize, usize)> + 'a
    WordDivide {
        iter:       buf.chars().cloned().enumerate(),
        count:      0,
        word_start: None,
    }.collect() // TODO: return iterator directly :D
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_snake_case)]

use std::iter::repeat;
use test::Bencher;
use rand::{Rng, thread_rng};
use regex::{Regex, NoExpand};

fn bench_assert_match(b: &mut Bencher, re: Regex, text: &str) {
    b.iter(|| if !re.is_match(text) { panic!("no match") });
}

#[bench]
fn no_exponential(b: &mut Bencher) {
    let n = 100;
    let regex_string = format!(
        "{}{}",
        repeat("a?").take(n).collect::<String>(),
        repeat("a").take(n).collect::<String>());
    let re = Regex::new(&regex_string).unwrap();
    let text: String = repeat("a").take(n).collect();
    bench_assert_match(b, re, &text);
}

#[bench]
fn literal(b: &mut Bencher) {
    let re = regex!("y");
    let text = format!("{}y", repeat("x").take(50).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn not_literal(b: &mut Bencher) {
    let re = regex!(".y");
    let text = format!("{}y", repeat("x").take(50).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn match_class(b: &mut Bencher) {
    let re = regex!("[abcdw]");
    let text = format!("{}w", repeat("xxxx").take(20).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn match_class_in_range(b: &mut Bencher) {
    // 'b' is between 'a' and 'c', so the class range checking doesn't help.
    let re = regex!("[ac]");
    let text = format!("{}c", repeat("bbbb").take(20).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn replace_all(b: &mut Bencher) {
    let re = regex!("[cjrw]");
    let text = "abcdefghijklmnopqrstuvwxyz";
    // FIXME: This isn't using the $name expand stuff.
    // It's possible RE2/Go is using it, but currently, the expand in this
    // crate is actually compiling a regex, so it's incredibly slow.
    b.iter(|| re.replace_all(text, NoExpand("")));
}

#[bench]
fn anchored_literal_short_non_match(b: &mut Bencher) {
    let re = regex!("^zbc(d|e)");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_non_match(b: &mut Bencher) {
    let re = regex!("^zbc(d|e)");
    let text: String = repeat("abcdefghijklmnopqrstuvwxyz").take(15).collect();
    b.iter(|| re.is_match(&text));
}

#[bench]
fn anchored_literal_short_match(b: &mut Bencher) {
    let re = regex!("^.bc(d|e)");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_match(b: &mut Bencher) {
    let re = regex!("^.bc(d|e)");
    let text: String = repeat("abcdefghijklmnopqrstuvwxyz").take(15).collect();
    b.iter(|| re.is_match(&text));
}

#[bench]
fn one_pass_short_a(b: &mut Bencher) {
    let re = regex!("^.bc(d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_a_not(b: &mut Bencher) {
    let re = regex!(".bc(d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b(b: &mut Bencher) {
    let re = regex!("^.bc(?:d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b_not(b: &mut Bencher) {
    let re = regex!(".bc(?:d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix(b: &mut Bencher) {
    let re = regex!("^abcdefghijklmnopqrstuvwxyz.*$");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix_not(b: &mut Bencher) {
    let re = regex!("^.bcdefghijklmnopqrstuvwxyz.*$");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

macro_rules! throughput(
    ($name:ident, $regex:expr, $size:expr) => (
        #[bench]
        fn $name(b: &mut Bencher) {
            let text = gen_text($size);
            b.bytes = $size;
            b.iter(|| if $regex.is_match(&text) { panic!("match") });
        }
    );
);

fn easy0() -> Regex { regex!("ABCDEFGHIJKLMNOPQRSTUVWXYZ$") }
fn easy1() -> Regex { regex!("A[AB]B[BC]C[CD]D[DE]E[EF]F[FG]G[GH]H[HI]I[IJ]J$") }
fn medium() -> Regex { regex!("[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ$") }
fn hard() -> Regex { regex!("[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$") }

fn gen_text(n: usize) -> String {
    let mut rng = thread_rng();
    let mut bytes = rng.gen_ascii_chars().map(|n| n as u8).take(n)
                       .collect::<Vec<u8>>();
    for (i, b) in bytes.iter_mut().enumerate() {
        if i % 20 == 0 {
            *b = b'\n'
        }
    }
    String::from_utf8(bytes).unwrap()
}

throughput!(easy0_32, easy0(), 32);
throughput!(easy0_1K, easy0(), 1<<10);
throughput!(easy0_32K, easy0(), 32<<10);

throughput!(easy1_32, easy1(), 32);
throughput!(easy1_1K, easy1(), 1<<10);
throughput!(easy1_32K, easy1(), 32<<10);

throughput!(medium_32, medium(), 32);
throughput!(medium_1K, medium(), 1<<10);
throughput!(medium_32K,medium(), 32<<10);

throughput!(hard_32, hard(), 32);
throughput!(hard_1K, hard(), 1<<10);
throughput!(hard_32K,hard(), 32<<10);
<commit_msg>Fix benchmark.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(non_snake_case)]

use std::iter::repeat;
use test::Bencher;
use rand::{Rng, thread_rng};
use regex::{Regex, NoExpand};

fn bench_assert_match(b: &mut Bencher, re: Regex, text: &str) {
    b.iter(|| if !re.is_match(text) { panic!("no match") });
}

#[bench]
fn no_exponential(b: &mut Bencher) {
    let n = 100;
    let regex_string = format!(
        "{}{}",
        repeat("a?").take(n).collect::<String>(),
        repeat("a").take(n).collect::<String>());
    let re = Regex::new(&regex_string).unwrap();
    let text: String = repeat("a").take(n).collect();
    bench_assert_match(b, re, &text);
}

#[bench]
fn literal(b: &mut Bencher) {
    let re = regex!("y");
    let text = format!("{}y", repeat("x").take(50).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn not_literal(b: &mut Bencher) {
    let re = regex!(".y");
    let text = format!("{}y", repeat("x").take(50).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn match_class(b: &mut Bencher) {
    let re = regex!("[abcdw]");
    let text = format!("{}w", repeat("xxxx").take(20).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn match_class_in_range(b: &mut Bencher) {
    // 'b' is between 'a' and 'c', so the class range checking doesn't help.
    let re = regex!("[ac]");
    let text = format!("{}c", repeat("bbbb").take(20).collect::<String>());
    bench_assert_match(b, re, &text);
}

#[bench]
fn replace_all(b: &mut Bencher) {
    let re = regex!("[cjrw]");
    let text = "abcdefghijklmnopqrstuvwxyz";
    // FIXME: This isn't using the $name expand stuff.
    // It's possible RE2/Go is using it, but currently, the expand in this
    // crate is actually compiling a regex, so it's incredibly slow.
    b.iter(|| re.replace_all(text, NoExpand("")));
}

#[bench]
fn anchored_literal_short_non_match(b: &mut Bencher) {
    let re = regex!("^zbc(d|e)");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_non_match(b: &mut Bencher) {
    let re = regex!("^zbc(d|e)");
    let text: String = repeat("abcdefghijklmnopqrstuvwxyz").take(15).collect();
    b.iter(|| re.is_match(&text));
}

#[bench]
fn anchored_literal_short_match(b: &mut Bencher) {
    let re = regex!("^.bc(d|e)");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn anchored_literal_long_match(b: &mut Bencher) {
    let re = regex!("^.bc(d|e)");
    let text: String = repeat("abcdefghijklmnopqrstuvwxyz").take(15).collect();
    b.iter(|| re.is_match(&text));
}

#[bench]
fn one_pass_short_a(b: &mut Bencher) {
    let re = regex!("^.bc(d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_a_not(b: &mut Bencher) {
    let re = regex!(".bc(d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b(b: &mut Bencher) {
    let re = regex!("^.bc(?:d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_short_b_not(b: &mut Bencher) {
    let re = regex!(".bc(?:d|e)*$");
    let text = "abcddddddeeeededd";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix(b: &mut Bencher) {
    let re = regex!("^abcdefghijklmnopqrstuvwxyz.*$");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

#[bench]
fn one_pass_long_prefix_not(b: &mut Bencher) {
    let re = regex!("^.bcdefghijklmnopqrstuvwxyz.*$");
    let text = "abcdefghijklmnopqrstuvwxyz";
    b.iter(|| re.is_match(text));
}

macro_rules! throughput(
    ($name:ident, $regex:expr, $size:expr) => (
        #[bench]
        fn $name(b: &mut Bencher) {
            let text = gen_text($size);
            b.bytes = $size;
            let re = $regex;
            b.iter(|| if re.is_match(&text) { panic!("match") });
        }
    );
);

fn easy0() -> Regex { regex!("ABCDEFGHIJKLMNOPQRSTUVWXYZ$") }
fn easy1() -> Regex { regex!("A[AB]B[BC]C[CD]D[DE]E[EF]F[FG]G[GH]H[HI]I[IJ]J$") }
fn medium() -> Regex { regex!("[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ$") }
fn hard() -> Regex { regex!("[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$") }

fn gen_text(n: usize) -> String {
    let mut rng = thread_rng();
    let mut bytes = rng.gen_ascii_chars().map(|n| n as u8).take(n)
                       .collect::<Vec<u8>>();
    for (i, b) in bytes.iter_mut().enumerate() {
        if i % 20 == 0 {
            *b = b'\n'
        }
    }
    String::from_utf8(bytes).unwrap()
}

throughput!(easy0_32, easy0(), 32);
throughput!(easy0_1K, easy0(), 1<<10);
throughput!(easy0_32K, easy0(), 32<<10);

throughput!(easy1_32, easy1(), 32);
throughput!(easy1_1K, easy1(), 1<<10);
throughput!(easy1_32K, easy1(), 32<<10);

throughput!(medium_32, medium(), 32);
throughput!(medium_1K, medium(), 1<<10);
throughput!(medium_32K,medium(), 32<<10);

throughput!(hard_32, hard(), 32);
throughput!(hard_1K, hard(), 1<<10);
throughput!(hard_32K,hard(), 32<<10);
<|endoftext|>


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

//! The implementation of the DOM.
//!
//! The DOM is comprised of interfaces (defined by specifications using
//! [WebIDL](https://heycam.github.io/webidl/)) that are implemented as Rust
//! structs in submodules of this module. Its implementation is documented
//! below.
//!
//! A DOM object and its reflector
//! ==============================
//!
//! The implementation of an interface `Foo` in Servo's DOM involves two
//! related but distinct objects:
//!
//! * the **DOM object**: an instance of the Rust struct `dom::foo::Foo`
//!   (marked with the `#[dom_struct]` attribute) on the Rust heap;
//! * the **reflector**: a `JSObject` allocated by SpiderMonkey, that owns the
//!   DOM object.
//!
//! Memory management
//! =================
//!
//! Reflectors of DOM objects, and thus the DOM objects themselves, are managed
//! by the SpiderMonkey Garbage Collector. Thus, keeping alive a DOM object
//! is done through its reflector.
//!
//! For more information, see:
//!
//! * rooting pointers on the stack:
//!   the [`Root`](bindings/js/struct.Root.html) smart pointer;
//! * tracing pointers in member fields: the [`JS`](bindings/js/struct.JS.html),
//!   [`MutNullableJS`](bindings/js/struct.MutNullableJS.html) and
//!   [`MutHeap`](bindings/js/struct.MutHeap.html) smart pointers and
//!   [the tracing implementation](bindings/trace/index.html);
//! * rooting pointers from across task boundaries or in channels: the
//!   [`Trusted`](bindings/refcounted/struct.Trusted.html) smart pointer;
//! * extracting pointers to DOM objects from their reflectors: the
//!   [`Unrooted`](bindings/js/struct.Unrooted.html) smart pointer.
//!
//! Inheritance
//! ===========
//!
//! Rust does not support struct inheritance, as would be used for the
//! object-oriented DOM APIs. To work around this issue, Servo stores an
//! instance of the superclass in the first field of its subclasses. (Note that
//! it is stored by value, rather than in a smart pointer such as `JS<T>`.)
//!
//! This implies that a pointer to an object can safely be cast to a pointer
//! to all its classes.
//!
//! This invariant is enforced by the lint in
//! `plugins::lints::inheritance_integrity`.
//!
//! The same principle applies to typeids,
//! the derived type enum should
//! use one addititional type (the parent class) because sometimes the parent
//! can be the most-derived class of an object.
//! ```ignore
//! pub enum EventTypeId {
//!     UIEvent(UIEventTypeId),
//!     //others events
//! }
//!
//! pub enum UIEventTypeId {
//!    MouseEvent,
//!    KeyboardEvent,
//!    UIEvent, //<- parent of MouseEvent and KeyboardEvent
//! }
//! ```
//!
//! Construction
//! ============
//!
//! DOM objects of type `T` in Servo have two constructors:
//!
//! * a `T::new_inherited` static method that returns a plain `T`, and
//! * a `T::new` static method that returns `Root<T>`.
//!
//! (The result of either method can be wrapped in `Result`, if that is
//! appropriate for the type in question.)
//!
//! The latter calls the former, boxes the result, and creates a reflector
//! corresponding to it by calling `dom::bindings::utils::reflect_dom_object`
//! (which yields ownership of the object to the SpiderMonkey Garbage Collector).
//! This is the API to use when creating a DOM object.
//!
//! The former should only be called by the latter, and by subclasses'
//! `new_inherited` methods.
//!
//! DOM object constructors in JavaScript correspond to a `T::Constructor`
//! static method. This method is always fallible.
//!
//! Destruction
//! ===========
//!
//! When the SpiderMonkey Garbage Collector discovers that the reflector of a
//! DOM object is garbage, it calls the reflector's finalization hook. This
//! function deletes the reflector's DOM object, calling its destructor in the
//! process.
//!
//! Mutability and aliasing
//! =======================
//!
//! Reflectors are JavaScript objects, and as such can be freely aliased. As
//! Rust does not allow mutable aliasing, mutable borrows of DOM objects are
//! not allowed. In particular, any mutable fields use `Cell` or `DOMRefCell`
//! to manage their mutability.
//!
//! `Reflector` and `Reflectable`
//! =============================
//!
//! Every DOM object has a `Reflector` as its first (transitive) member field.
//! This contains a `*mut JSObject` that points to its reflector.
//!
//! The `FooBinding::Wrap` function creates the reflector, stores a pointer to
//! the DOM object in the reflector, and initializes the pointer to the reflector
//! in the `Reflector` field.
//!
//! The `Reflectable` trait provides a `reflector()` method that returns the
//! DOM object's `Reflector`. It is implemented automatically for DOM structs
//! through the `#[dom_struct]` attribute.
//!
//! Implementing methods for a DOM object
//! =====================================
//!
//! * `dom::bindings::codegen::Bindings::FooBindings::FooMethods` for methods
//!   defined through IDL;
//! * `&self` public methods for public helpers;
//! * `&self` methods for private helpers.
//!
//! Accessing fields of a DOM object
//! ================================
//!
//! All fields of DOM objects are private; accessing them from outside their
//! module is done through explicit getter or setter methods.
//!
//! Inheritance and casting
//! =======================
//!
//! For all DOM interfaces `Foo` in an inheritance chain, a
//! `dom::bindings::codegen::InheritTypes::FooCast` provides methods to cast
//! to other types in the inheritance chain. For example:
//!
//! ```ignore
//! # use script::dom::bindings::codegen::InheritTypes::{NodeCast, HTMLElementCast};
//! # use script::dom::element::Element;
//! # use script::dom::node::Node;
//! # use script::dom::htmlelement::HTMLElement;
//! fn f(element: &Element) {
//!     let base: &Node = NodeCast::from_ref(element);
//!     let derived: Option<&HTMLElement> = HTMLElementCast::to_ref(element);
//! }
//! ```
//!
//! Adding a new DOM interface
//! ==========================
//!
//! Adding a new interface `Foo` requires at least the following:
//!
//! * adding the new IDL file at `components/script/dom/webidls/Foo.webidl`;
//! * creating `components/script/dom/foo.rs`;
//! * listing `foo.rs` in `components/script/dom/mod.rs`;
//! * defining the DOM struct `Foo` with a `#[dom_struct]` attribute, a
//!   superclass or `Reflector` member, and other members as appropriate;
//! * implementing the
//!   `dom::bindings::codegen::Bindings::FooBindings::FooMethods` trait for
//!   `&'a Foo`.
//!
//! More information is available in the [bindings module](bindings/index.html).
//!
//! Accessing DOM objects from layout
//! =================================
//!
//! Layout code can access the DOM through the
//! [`LayoutJS`](bindings/js/struct.LayoutJS.html) smart pointer. This does not
//! keep the DOM object alive; we ensure that no DOM code (Garbage Collection
//! in particular) runs while the layout task is accessing the DOM.
//!
//! Methods accessible to layout are implemented on `LayoutJS<Foo>` using
//! `LayoutFooHelpers` traits.

#[macro_use]
pub mod macros;

pub mod types {
    include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
}

pub mod activation;
pub mod attr;
mod create;
#[allow(unsafe_code)]
#[deny(missing_docs, non_snake_case)]
pub mod bindings;
pub mod blob;
pub mod browsercontext;
pub mod canvasgradient;
pub mod canvaspattern;
pub mod canvasrenderingcontext2d;
pub mod characterdata;
pub mod closeevent;
pub mod comment;
pub mod console;
pub mod crypto;
pub mod css;
pub mod cssstyledeclaration;
pub mod customevent;
pub mod dedicatedworkerglobalscope;
pub mod document;
pub mod documentfragment;
pub mod documenttype;
pub mod domexception;
pub mod domimplementation;
pub mod domparser;
pub mod dompoint;
pub mod dompointreadonly;
pub mod domrect;
pub mod domrectlist;
pub mod domstringmap;
pub mod domtokenlist;
pub mod element;
pub mod errorevent;
pub mod event;
pub mod eventdispatcher;
pub mod eventtarget;
pub mod file;
pub mod filelist;
pub mod filereader;
pub mod formdata;
pub mod htmlanchorelement;
pub mod htmlappletelement;
pub mod htmlareaelement;
pub mod htmlaudioelement;
pub mod htmlbaseelement;
pub mod htmlbodyelement;
pub mod htmlbrelement;
pub mod htmlbuttonelement;
pub mod htmlcanvaselement;
pub mod htmlcollection;
pub mod htmldataelement;
pub mod htmldatalistelement;
pub mod htmldialogelement;
pub mod htmldirectoryelement;
pub mod htmldivelement;
pub mod htmldlistelement;
pub mod htmlelement;
pub mod htmlembedelement;
pub mod htmlfieldsetelement;
pub mod htmlfontelement;
pub mod htmlformelement;
pub mod htmlframeelement;
pub mod htmlframesetelement;
pub mod htmlheadelement;
pub mod htmlheadingelement;
pub mod htmlhrelement;
pub mod htmlhtmlelement;
pub mod htmliframeelement;
pub mod htmlimageelement;
pub mod htmlinputelement;
pub mod htmllabelelement;
pub mod htmllegendelement;
pub mod htmllielement;
pub mod htmllinkelement;
pub mod htmlmapelement;
pub mod htmlmediaelement;
pub mod htmlmetaelement;
pub mod htmlmeterelement;
pub mod htmlmodelement;
pub mod htmlobjectelement;
pub mod htmlolistelement;
pub mod htmloptgroupelement;
pub mod htmloptionelement;
pub mod htmloutputelement;
pub mod htmlparagraphelement;
pub mod htmlparamelement;
pub mod htmlpreelement;
pub mod htmlprogresselement;
pub mod htmlquoteelement;
pub mod htmlscriptelement;
pub mod htmlselectelement;
pub mod htmlsourceelement;
pub mod htmlspanelement;
pub mod htmlstyleelement;
pub mod htmltablecaptionelement;
pub mod htmltablecellelement;
pub mod htmltablecolelement;
pub mod htmltabledatacellelement;
pub mod htmltableelement;
pub mod htmltableheadercellelement;
pub mod htmltablerowelement;
pub mod htmltablesectionelement;
pub mod htmltemplateelement;
pub mod htmltextareaelement;
pub mod htmltimeelement;
pub mod htmltitleelement;
pub mod htmltrackelement;
pub mod htmlulistelement;
pub mod htmlunknownelement;
pub mod htmlvideoelement;
pub mod imagedata;
pub mod keyboardevent;
pub mod location;
pub mod messageevent;
pub mod mouseevent;
pub mod namednodemap;
pub mod navigator;
pub mod navigatorinfo;
pub mod node;
pub mod nodeiterator;
pub mod nodelist;
pub mod performance;
pub mod performancetiming;
pub mod processinginstruction;
pub mod progressevent;
pub mod range;
pub mod screen;
pub mod servohtmlparser;
pub mod storage;
pub mod storageevent;
pub mod testbinding;
pub mod testbindingproxy;
pub mod text;
pub mod textdecoder;
pub mod textencoder;
pub mod treewalker;
pub mod uievent;
pub mod url;
pub mod urlhelper;
pub mod urlsearchparams;
pub mod userscripts;
pub mod validitystate;
pub mod values;
pub mod virtualmethods;
pub mod webglactiveinfo;
pub mod webglbuffer;
pub mod webglcontextevent;
pub mod webglframebuffer;
pub mod webglobject;
pub mod webglprogram;
pub mod webglrenderbuffer;
pub mod webglrenderingcontext;
pub mod webglshader;
pub mod webglshaderprecisionformat;
pub mod webgltexture;
pub mod webgluniformlocation;
pub mod websocket;
pub mod window;
pub mod worker;
pub mod workerglobalscope;
pub mod workerlocation;
pub mod workernavigator;
pub mod xmlhttprequest;
pub mod xmlhttprequesteventtarget;
pub mod xmlhttprequestupload;
<commit_msg>Add newline so code sample is parsed correctly.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The implementation of the DOM.
//!
//! The DOM is comprised of interfaces (defined by specifications using
//! [WebIDL](https://heycam.github.io/webidl/)) that are implemented as Rust
//! structs in submodules of this module. Its implementation is documented
//! below.
//!
//! A DOM object and its reflector
//! ==============================
//!
//! The implementation of an interface `Foo` in Servo's DOM involves two
//! related but distinct objects:
//!
//! * the **DOM object**: an instance of the Rust struct `dom::foo::Foo`
//!   (marked with the `#[dom_struct]` attribute) on the Rust heap;
//! * the **reflector**: a `JSObject` allocated by SpiderMonkey, that owns the
//!   DOM object.
//!
//! Memory management
//! =================
//!
//! Reflectors of DOM objects, and thus the DOM objects themselves, are managed
//! by the SpiderMonkey Garbage Collector. Thus, keeping alive a DOM object
//! is done through its reflector.
//!
//! For more information, see:
//!
//! * rooting pointers on the stack:
//!   the [`Root`](bindings/js/struct.Root.html) smart pointer;
//! * tracing pointers in member fields: the [`JS`](bindings/js/struct.JS.html),
//!   [`MutNullableJS`](bindings/js/struct.MutNullableJS.html) and
//!   [`MutHeap`](bindings/js/struct.MutHeap.html) smart pointers and
//!   [the tracing implementation](bindings/trace/index.html);
//! * rooting pointers from across task boundaries or in channels: the
//!   [`Trusted`](bindings/refcounted/struct.Trusted.html) smart pointer;
//! * extracting pointers to DOM objects from their reflectors: the
//!   [`Unrooted`](bindings/js/struct.Unrooted.html) smart pointer.
//!
//! Inheritance
//! ===========
//!
//! Rust does not support struct inheritance, as would be used for the
//! object-oriented DOM APIs. To work around this issue, Servo stores an
//! instance of the superclass in the first field of its subclasses. (Note that
//! it is stored by value, rather than in a smart pointer such as `JS<T>`.)
//!
//! This implies that a pointer to an object can safely be cast to a pointer
//! to all its classes.
//!
//! This invariant is enforced by the lint in
//! `plugins::lints::inheritance_integrity`.
//!
//! The same principle applies to typeids,
//! the derived type enum should
//! use one addititional type (the parent class) because sometimes the parent
//! can be the most-derived class of an object.
//!
//! ```ignore
//! pub enum EventTypeId {
//!     UIEvent(UIEventTypeId),
//!     //others events
//! }
//!
//! pub enum UIEventTypeId {
//!    MouseEvent,
//!    KeyboardEvent,
//!    UIEvent, //<- parent of MouseEvent and KeyboardEvent
//! }
//! ```
//!
//! Construction
//! ============
//!
//! DOM objects of type `T` in Servo have two constructors:
//!
//! * a `T::new_inherited` static method that returns a plain `T`, and
//! * a `T::new` static method that returns `Root<T>`.
//!
//! (The result of either method can be wrapped in `Result`, if that is
//! appropriate for the type in question.)
//!
//! The latter calls the former, boxes the result, and creates a reflector
//! corresponding to it by calling `dom::bindings::utils::reflect_dom_object`
//! (which yields ownership of the object to the SpiderMonkey Garbage Collector).
//! This is the API to use when creating a DOM object.
//!
//! The former should only be called by the latter, and by subclasses'
//! `new_inherited` methods.
//!
//! DOM object constructors in JavaScript correspond to a `T::Constructor`
//! static method. This method is always fallible.
//!
//! Destruction
//! ===========
//!
//! When the SpiderMonkey Garbage Collector discovers that the reflector of a
//! DOM object is garbage, it calls the reflector's finalization hook. This
//! function deletes the reflector's DOM object, calling its destructor in the
//! process.
//!
//! Mutability and aliasing
//! =======================
//!
//! Reflectors are JavaScript objects, and as such can be freely aliased. As
//! Rust does not allow mutable aliasing, mutable borrows of DOM objects are
//! not allowed. In particular, any mutable fields use `Cell` or `DOMRefCell`
//! to manage their mutability.
//!
//! `Reflector` and `Reflectable`
//! =============================
//!
//! Every DOM object has a `Reflector` as its first (transitive) member field.
//! This contains a `*mut JSObject` that points to its reflector.
//!
//! The `FooBinding::Wrap` function creates the reflector, stores a pointer to
//! the DOM object in the reflector, and initializes the pointer to the reflector
//! in the `Reflector` field.
//!
//! The `Reflectable` trait provides a `reflector()` method that returns the
//! DOM object's `Reflector`. It is implemented automatically for DOM structs
//! through the `#[dom_struct]` attribute.
//!
//! Implementing methods for a DOM object
//! =====================================
//!
//! * `dom::bindings::codegen::Bindings::FooBindings::FooMethods` for methods
//!   defined through IDL;
//! * `&self` public methods for public helpers;
//! * `&self` methods for private helpers.
//!
//! Accessing fields of a DOM object
//! ================================
//!
//! All fields of DOM objects are private; accessing them from outside their
//! module is done through explicit getter or setter methods.
//!
//! Inheritance and casting
//! =======================
//!
//! For all DOM interfaces `Foo` in an inheritance chain, a
//! `dom::bindings::codegen::InheritTypes::FooCast` provides methods to cast
//! to other types in the inheritance chain. For example:
//!
//! ```ignore
//! # use script::dom::bindings::codegen::InheritTypes::{NodeCast, HTMLElementCast};
//! # use script::dom::element::Element;
//! # use script::dom::node::Node;
//! # use script::dom::htmlelement::HTMLElement;
//! fn f(element: &Element) {
//!     let base: &Node = NodeCast::from_ref(element);
//!     let derived: Option<&HTMLElement> = HTMLElementCast::to_ref(element);
//! }
//! ```
//!
//! Adding a new DOM interface
//! ==========================
//!
//! Adding a new interface `Foo` requires at least the following:
//!
//! * adding the new IDL file at `components/script/dom/webidls/Foo.webidl`;
//! * creating `components/script/dom/foo.rs`;
//! * listing `foo.rs` in `components/script/dom/mod.rs`;
//! * defining the DOM struct `Foo` with a `#[dom_struct]` attribute, a
//!   superclass or `Reflector` member, and other members as appropriate;
//! * implementing the
//!   `dom::bindings::codegen::Bindings::FooBindings::FooMethods` trait for
//!   `&'a Foo`.
//!
//! More information is available in the [bindings module](bindings/index.html).
//!
//! Accessing DOM objects from layout
//! =================================
//!
//! Layout code can access the DOM through the
//! [`LayoutJS`](bindings/js/struct.LayoutJS.html) smart pointer. This does not
//! keep the DOM object alive; we ensure that no DOM code (Garbage Collection
//! in particular) runs while the layout task is accessing the DOM.
//!
//! Methods accessible to layout are implemented on `LayoutJS<Foo>` using
//! `LayoutFooHelpers` traits.

#[macro_use]
pub mod macros;

pub mod types {
    include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
}

pub mod activation;
pub mod attr;
mod create;
#[allow(unsafe_code)]
#[deny(missing_docs, non_snake_case)]
pub mod bindings;
pub mod blob;
pub mod browsercontext;
pub mod canvasgradient;
pub mod canvaspattern;
pub mod canvasrenderingcontext2d;
pub mod characterdata;
pub mod closeevent;
pub mod comment;
pub mod console;
pub mod crypto;
pub mod css;
pub mod cssstyledeclaration;
pub mod customevent;
pub mod dedicatedworkerglobalscope;
pub mod document;
pub mod documentfragment;
pub mod documenttype;
pub mod domexception;
pub mod domimplementation;
pub mod domparser;
pub mod dompoint;
pub mod dompointreadonly;
pub mod domrect;
pub mod domrectlist;
pub mod domstringmap;
pub mod domtokenlist;
pub mod element;
pub mod errorevent;
pub mod event;
pub mod eventdispatcher;
pub mod eventtarget;
pub mod file;
pub mod filelist;
pub mod filereader;
pub mod formdata;
pub mod htmlanchorelement;
pub mod htmlappletelement;
pub mod htmlareaelement;
pub mod htmlaudioelement;
pub mod htmlbaseelement;
pub mod htmlbodyelement;
pub mod htmlbrelement;
pub mod htmlbuttonelement;
pub mod htmlcanvaselement;
pub mod htmlcollection;
pub mod htmldataelement;
pub mod htmldatalistelement;
pub mod htmldialogelement;
pub mod htmldirectoryelement;
pub mod htmldivelement;
pub mod htmldlistelement;
pub mod htmlelement;
pub mod htmlembedelement;
pub mod htmlfieldsetelement;
pub mod htmlfontelement;
pub mod htmlformelement;
pub mod htmlframeelement;
pub mod htmlframesetelement;
pub mod htmlheadelement;
pub mod htmlheadingelement;
pub mod htmlhrelement;
pub mod htmlhtmlelement;
pub mod htmliframeelement;
pub mod htmlimageelement;
pub mod htmlinputelement;
pub mod htmllabelelement;
pub mod htmllegendelement;
pub mod htmllielement;
pub mod htmllinkelement;
pub mod htmlmapelement;
pub mod htmlmediaelement;
pub mod htmlmetaelement;
pub mod htmlmeterelement;
pub mod htmlmodelement;
pub mod htmlobjectelement;
pub mod htmlolistelement;
pub mod htmloptgroupelement;
pub mod htmloptionelement;
pub mod htmloutputelement;
pub mod htmlparagraphelement;
pub mod htmlparamelement;
pub mod htmlpreelement;
pub mod htmlprogresselement;
pub mod htmlquoteelement;
pub mod htmlscriptelement;
pub mod htmlselectelement;
pub mod htmlsourceelement;
pub mod htmlspanelement;
pub mod htmlstyleelement;
pub mod htmltablecaptionelement;
pub mod htmltablecellelement;
pub mod htmltablecolelement;
pub mod htmltabledatacellelement;
pub mod htmltableelement;
pub mod htmltableheadercellelement;
pub mod htmltablerowelement;
pub mod htmltablesectionelement;
pub mod htmltemplateelement;
pub mod htmltextareaelement;
pub mod htmltimeelement;
pub mod htmltitleelement;
pub mod htmltrackelement;
pub mod htmlulistelement;
pub mod htmlunknownelement;
pub mod htmlvideoelement;
pub mod imagedata;
pub mod keyboardevent;
pub mod location;
pub mod messageevent;
pub mod mouseevent;
pub mod namednodemap;
pub mod navigator;
pub mod navigatorinfo;
pub mod node;
pub mod nodeiterator;
pub mod nodelist;
pub mod performance;
pub mod performancetiming;
pub mod processinginstruction;
pub mod progressevent;
pub mod range;
pub mod screen;
pub mod servohtmlparser;
pub mod storage;
pub mod storageevent;
pub mod testbinding;
pub mod testbindingproxy;
pub mod text;
pub mod textdecoder;
pub mod textencoder;
pub mod treewalker;
pub mod uievent;
pub mod url;
pub mod urlhelper;
pub mod urlsearchparams;
pub mod userscripts;
pub mod validitystate;
pub mod values;
pub mod virtualmethods;
pub mod webglactiveinfo;
pub mod webglbuffer;
pub mod webglcontextevent;
pub mod webglframebuffer;
pub mod webglobject;
pub mod webglprogram;
pub mod webglrenderbuffer;
pub mod webglrenderingcontext;
pub mod webglshader;
pub mod webglshaderprecisionformat;
pub mod webgltexture;
pub mod webgluniformlocation;
pub mod websocket;
pub mod window;
pub mod worker;
pub mod workerglobalscope;
pub mod workerlocation;
pub mod workernavigator;
pub mod xmlhttprequest;
pub mod xmlhttprequesteventtarget;
pub mod xmlhttprequestupload;
<|endoftext|>


<commit_before>use cairo;
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, PaintServerError, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve, ResolvedPaintSource};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or_else (|| $default)
    );
);

#[derive(Clone, Default)]
pub struct NodePattern {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: RefCell<Option<RsvgWeakNode>>,
}

impl NodeTrait for NodePattern {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => self.units = Some(attr.parse(value)?),
                local_name!("patternContentUnits") => self.content_units = Some(attr.parse(value)?),
                local_name!("viewBox") => self.vbox = Some(Some(attr.parse(value)?)),
                local_name!("preserveAspectRatio") => {
                    self.preserve_aspect_ratio = Some(attr.parse(value)?)
                }
                local_name!("patternTransform") => self.affine = Some(attr.parse(value)?),
                local_name!("xlink:href") => {
                    self.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }
                local_name!("x") => self.x = Some(attr.parse(value)?),
                local_name!("y") => self.y = Some(attr.parse(value)?),
                local_name!("width") => {
                    self.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }
                local_name!("height") => {
                    self.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }
                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl Resolve for NodePattern {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &NodePattern) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.borrow().is_some() {
                *self.node.borrow_mut() = fallback.node.borrow().clone();
            } else {
                *self.node.borrow_mut() = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl PaintSource for NodePattern {
    type Resolved = NodePattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<Self::Resolved, PaintServerError> {
        let mut result = node.borrow().get_impl::<NodePattern>().clone();
        *result.node.borrow_mut() = Some(node.downgrade());

        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(ref fallback) = result.fallback {
                if let Some(acquired) = draw_ctx
                    .acquired_nodes()
                    .get_node_of_type(fallback, NodeType::Pattern)
                {
                    let a_node = acquired.get();

                    if stack.contains(a_node) {
                        return Err(PaintServerError::CircularReference(fallback.clone()));
                    }

                    let node_data = a_node.borrow();

                    let fallback_pattern = node_data.get_impl::<NodePattern>();
                    *fallback_pattern.node.borrow_mut() = Some(a_node.downgrade());

                    result.resolve_from_fallback(fallback_pattern);

                    stack.push(a_node);
                } else {
                    result.resolve_from_defaults();
                }
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(result)
    }
}

impl ResolvedPaintSource for NodePattern {
    fn set_pattern_on_draw_context(
        self,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(self.is_resolved());

        if self.node.borrow().is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = self.units.unwrap();
        let content_units = self.content_units.unwrap();
        let pattern_affine = self.affine.unwrap();
        let vbox = self.vbox.unwrap();
        let preserve_aspect_ratio = self.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = self.x.unwrap().normalize(values, &params);
            let pattern_y = self.y.unwrap().normalize(values, &params);
            let pattern_width = self.width.unwrap().normalize(values, &params);
            let pattern_height = self.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(1.0) || !schscale.approx_eq_cairo(1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node = self.node.borrow().as_ref().unwrap().upgrade().unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(&pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&surface_pattern);

        res.and_then(|_| Ok(true))
    }
}

impl NodePattern {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref weak) = *self.node.borrow() {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = NodePattern::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<commit_msg>Use a strong reference to the pattern's node that has the children to draw<commit_after>use cairo;
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, PaintServerError, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve, ResolvedPaintSource};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or_else (|| $default)
    );
);

#[derive(Clone, Default)]
pub struct NodePattern {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: RefCell<Option<RsvgNode>>,
}

impl NodeTrait for NodePattern {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => self.units = Some(attr.parse(value)?),
                local_name!("patternContentUnits") => self.content_units = Some(attr.parse(value)?),
                local_name!("viewBox") => self.vbox = Some(Some(attr.parse(value)?)),
                local_name!("preserveAspectRatio") => {
                    self.preserve_aspect_ratio = Some(attr.parse(value)?)
                }
                local_name!("patternTransform") => self.affine = Some(attr.parse(value)?),
                local_name!("xlink:href") => {
                    self.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }
                local_name!("x") => self.x = Some(attr.parse(value)?),
                local_name!("y") => self.y = Some(attr.parse(value)?),
                local_name!("width") => {
                    self.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }
                local_name!("height") => {
                    self.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }
                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl Resolve for NodePattern {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &NodePattern) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.borrow().is_some() {
                *self.node.borrow_mut() = fallback.node.borrow().clone();
            } else {
                *self.node.borrow_mut() = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl PaintSource for NodePattern {
    type Resolved = NodePattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<Self::Resolved, PaintServerError> {
        let mut result = node.borrow().get_impl::<NodePattern>().clone();
        *result.node.borrow_mut() = Some(node.clone());

        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(ref fallback) = result.fallback {
                if let Some(acquired) = draw_ctx
                    .acquired_nodes()
                    .get_node_of_type(fallback, NodeType::Pattern)
                {
                    let a_node = acquired.get();

                    if stack.contains(a_node) {
                        return Err(PaintServerError::CircularReference(fallback.clone()));
                    }

                    let node_data = a_node.borrow();

                    let fallback_pattern = node_data.get_impl::<NodePattern>();
                    *fallback_pattern.node.borrow_mut() = Some(a_node.clone());

                    result.resolve_from_fallback(fallback_pattern);

                    stack.push(a_node);
                } else {
                    result.resolve_from_defaults();
                }
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(result)
    }
}

impl ResolvedPaintSource for NodePattern {
    fn set_pattern_on_draw_context(
        self,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(self.is_resolved());

        if self.node.borrow().is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = self.units.unwrap();
        let content_units = self.content_units.unwrap();
        let pattern_affine = self.affine.unwrap();
        let vbox = self.vbox.unwrap();
        let preserve_aspect_ratio = self.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = self.x.unwrap().normalize(values, &params);
            let pattern_y = self.y.unwrap().normalize(values, &params);
            let pattern_width = self.width.unwrap().normalize(values, &params);
            let pattern_height = self.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(1.0) || !schscale.approx_eq_cairo(1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node_borrow = self.node.borrow();
        let pattern_node = pattern_node_borrow.as_ref().unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&surface_pattern);

        res.and_then(|_| Ok(true))
    }
}

impl NodePattern {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref node) = *self.node.borrow() {
            node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = NodePattern::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<|endoftext|>