<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use super::Position;
use super::symbols::SymbolTable;

#[derive(PartialEq, Debug)]
pub struct Node<I> {
    pub pos: Position,
    pub node: I,
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Type {
    Void,
    Bool,
    Int,
    Float,
    Str,
}

#[derive(PartialEq, Debug)]
pub enum Literal {
    Bool(bool),
    Int(i32),
    Float(f32),
    Str(String),
}

impl Literal {
    fn get_type(&self) -> Type {
        match *self {
            Literal::Bool(_) => Type::Bool,
            Literal::Int(_) => Type::Int,
            Literal::Float(_) => Type::Float,
            Literal::Str(_) => Type::Str,
        }
    }
}

#[derive(Debug)]
pub struct Variable {
    pub id: u32,
    pub type_: Type,
    pub name: String,
}

impl Variable {
    pub fn stub(name: String) -> Variable {
        Variable {
            id: 0,
            type_: Type::Void,
            name: name,
        }
    }

    pub fn eq_name_type(&self, other: &Variable) -> bool {
        self.type_ == other.type_ && self.name == other.name
    }
}

impl PartialEq for Variable {
    fn eq(&self, other: &Variable) -> bool {
        if self.id == 0 || other.id == 0 {
            false
        } else {
            self.id == other.id
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum UnaryOp {
    NOT,
    MINUS,
}

#[derive(PartialEq, Debug)]
pub enum BinaryOp {
    ADD,
    SUB,
    MUL,
    DIV,

    EQ,
    LT,
    LE,
}

#[derive(PartialEq, Debug)]
pub struct Program {
    pub functions: HashMap<String, Node<Function>>,
}

#[derive(PartialEq, Debug)]
pub struct Function {
    pub name: String,
    pub filename: String,
    pub body: Node<Statement>,
    pub args: Vec<Rc<Variable>>,
    pub ret_type: Type,
    pub symbols: Rc<RefCell<SymbolTable>>,
}

#[derive(PartialEq, Debug)]
pub enum Statement {
    Expression {
        expr: Box<Node<Expression>>,
    },
    Declaration {
        var: Rc<Variable>,
    },
    Assignment {
        var: Rc<Variable>,
        expr: Box<Node<Expression>>,
    },
    If {
        cond: Box<Node<Expression>>,
        on_true: Box<Node<Statement>>,
        on_false: Option<Box<Node<Statement>>>,
    },
    While {
        cond: Box<Node<Expression>>,
        body: Box<Node<Statement>>,
    },
    Return {
        expr: Option<Box<Node<Expression>>>,
    },
    Compound {
        stmts: Vec<Node<Statement>>,
        symbols: Rc<RefCell<SymbolTable>>,
    },
}

impl Node<Statement> {
    pub fn walk<S, E>(&self, do_stmt: &S, do_expr: &E)
        where S: Fn(&Node<Statement>),
              E: Fn(&Node<Expression>)
    {
        do_stmt(self);
        match self.node {
            Statement::Expression { ref expr } => expr.walk(do_expr),
            Statement::Assignment { ref expr, .. } => expr.walk(do_expr),
            Statement::If { ref cond, ref on_true, ref on_false } => {
                cond.walk(do_expr);
                on_true.walk(do_stmt, do_expr);
                if let Some(ref stmt) = *on_false {
                    stmt.walk(do_stmt, do_expr);
                }
            }
            Statement::While { ref cond, ref body } => {
                cond.walk(do_expr);
                body.walk(do_stmt, do_expr);
            }
            Statement::Return { expr: Some(ref expr) } => expr.walk(do_expr),
            Statement::Compound { ref stmts, .. } => {
                for stmt in stmts.iter() {
                    stmt.walk(do_stmt, do_expr);
                }
            }
            _ => (),
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum Expression {
    Literal {
        lit: Literal,
    },
    Variable {
        var: Rc<Variable>,
    },
    Call {
        function: String,
        args: Vec<Node<Expression>>,
    },
    Unary {
        op: UnaryOp,
        expr: Box<Node<Expression>>,
    },
    Binary {
        op: BinaryOp,
        left: Box<Node<Expression>>,
        right: Box<Node<Expression>>,
    },
    Parenthesis {
        expr: Box<Node<Expression>>,
    },
}

impl Node<Expression> {
    pub fn walk<E>(&self, do_expr: &E)
        where E: Fn(&Node<Expression>)
    {
        do_expr(self);
        match self.node {
            Expression::Call { ref args, .. } => {
                for arg in args.iter() {
                    arg.walk(do_expr);
                }
            }
            Expression::Unary { ref expr, .. } => expr.walk(do_expr),
            Expression::Binary { ref left, ref right, .. } => {
                left.walk(do_expr);
                right.walk(do_expr);
            }
            Expression::Parenthesis { ref expr } => expr.walk(do_expr),
            _ => (),
        }
    }
}

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

    #[test]
    fn literals() {
        assert_eq!(Type::Bool, Literal::Bool(false).get_type());
        assert_eq!(Type::Int, Literal::Int(0).get_type());
        assert_eq!(Type::Float, Literal::Float(0.0).get_type());
    }
}
<commit_msg>Front: Fix AST walk method arguments<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use super::Position;
use super::symbols::SymbolTable;

#[derive(PartialEq, Debug)]
pub struct Node<I> {
    pub pos: Position,
    pub node: I,
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Type {
    Void,
    Bool,
    Int,
    Float,
    Str,
}

#[derive(PartialEq, Debug)]
pub enum Literal {
    Bool(bool),
    Int(i32),
    Float(f32),
    Str(String),
}

impl Literal {
    fn get_type(&self) -> Type {
        match *self {
            Literal::Bool(_) => Type::Bool,
            Literal::Int(_) => Type::Int,
            Literal::Float(_) => Type::Float,
            Literal::Str(_) => Type::Str,
        }
    }
}

#[derive(Debug)]
pub struct Variable {
    pub id: u32,
    pub type_: Type,
    pub name: String,
}

impl Variable {
    pub fn stub(name: String) -> Variable {
        Variable {
            id: 0,
            type_: Type::Void,
            name: name,
        }
    }

    pub fn eq_name_type(&self, other: &Variable) -> bool {
        self.type_ == other.type_ && self.name == other.name
    }
}

impl PartialEq for Variable {
    fn eq(&self, other: &Variable) -> bool {
        if self.id == 0 || other.id == 0 {
            false
        } else {
            self.id == other.id
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum UnaryOp {
    NOT,
    MINUS,
}

#[derive(PartialEq, Debug)]
pub enum BinaryOp {
    ADD,
    SUB,
    MUL,
    DIV,

    EQ,
    LT,
    LE,
}

#[derive(PartialEq, Debug)]
pub struct Program {
    pub functions: HashMap<String, Node<Function>>,
}

#[derive(PartialEq, Debug)]
pub struct Function {
    pub name: String,
    pub filename: String,
    pub body: Node<Statement>,
    pub args: Vec<Rc<Variable>>,
    pub ret_type: Type,
    pub symbols: Rc<RefCell<SymbolTable>>,
}

#[derive(PartialEq, Debug)]
pub enum Statement {
    Expression {
        expr: Box<Node<Expression>>,
    },
    Declaration {
        var: Rc<Variable>,
    },
    Assignment {
        var: Rc<Variable>,
        expr: Box<Node<Expression>>,
    },
    If {
        cond: Box<Node<Expression>>,
        on_true: Box<Node<Statement>>,
        on_false: Option<Box<Node<Statement>>>,
    },
    While {
        cond: Box<Node<Expression>>,
        body: Box<Node<Statement>>,
    },
    Return {
        expr: Option<Box<Node<Expression>>>,
    },
    Compound {
        stmts: Vec<Node<Statement>>,
        symbols: Rc<RefCell<SymbolTable>>,
    },
}

impl Node<Statement> {
    pub fn walk<S, E>(&self, do_stmt: &mut S, do_expr: &mut E)
        where S: FnMut(&Node<Statement>),
              E: FnMut(&Node<Expression>)
    {
        do_stmt(self);
        match self.node {
            Statement::Expression { ref expr } => expr.walk(do_expr),
            Statement::Assignment { ref expr, .. } => expr.walk(do_expr),
            Statement::Return { expr: Some(ref expr) } => expr.walk(do_expr),
            Statement::If { ref cond, ref on_true, ref on_false } => {
                cond.walk(do_expr);
                on_true.walk(do_stmt, do_expr);
                if let Some(ref stmt) = *on_false {
                    stmt.walk(do_stmt, do_expr);
                }
            }
            Statement::While { ref cond, ref body } => {
                cond.walk(do_expr);
                body.walk(do_stmt, do_expr);
            }
            Statement::Compound { ref stmts, .. } => {
                for stmt in stmts.iter() {
                    stmt.walk(do_stmt, do_expr);
                }
            }
            _ => (),
        }
    }
}

#[derive(PartialEq, Debug)]
pub enum Expression {
    Literal {
        lit: Literal,
    },
    Variable {
        var: Rc<Variable>,
    },
    Call {
        function: String,
        args: Vec<Node<Expression>>,
    },
    Unary {
        op: UnaryOp,
        expr: Box<Node<Expression>>,
    },
    Binary {
        op: BinaryOp,
        left: Box<Node<Expression>>,
        right: Box<Node<Expression>>,
    },
    Parenthesis {
        expr: Box<Node<Expression>>,
    },
}

impl Node<Expression> {
    pub fn walk<E>(&self, do_expr: &mut E)
        where E: FnMut(&Node<Expression>)
    {
        do_expr(self);
        match self.node {
            Expression::Call { ref args, .. } => {
                for arg in args.iter() {
                    arg.walk(do_expr);
                }
            }
            Expression::Unary { ref expr, .. } => expr.walk(do_expr),
            Expression::Parenthesis { ref expr } => expr.walk(do_expr),
            Expression::Binary { ref left, ref right, .. } => {
                left.walk(do_expr);
                right.walk(do_expr);
            }
            _ => (),
        }
    }
}

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

    #[test]
    fn literals() {
        assert_eq!(Type::Bool, Literal::Bool(false).get_type());
        assert_eq!(Type::Int, Literal::Int(0).get_type());
        assert_eq!(Type::Float, Literal::Float(0.0).get_type());
    }
}
<|endoftext|>


<commit_before>mod score;
pub mod consts;

use std::ascii::AsciiExt;
pub use self::score::*;

/// Compares two characters case-insensitively
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::eq('a', 'A'));
/// ```
#[inline(always)]
pub fn eq(a: char, b: char) -> bool {
    match a {
        a if a == b => true,
        a if a.is_ascii() && !b.is_ascii() => false,
        a if !a.is_ascii() && b.is_ascii() => false,
        a if a.is_ascii() && b.is_ascii() => {
            a.to_ascii_lowercase().eq(&b.to_ascii_lowercase())
        },
        a => a.to_lowercase().eq(b.to_lowercase())
    }
}

/// Searches for needle's chars in the haystack
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::matches("amo", "app/models/order"));
/// ```
pub fn matches(needle: &str, haystack: &str) -> bool {
    let mut hchars = haystack.chars();

    if needle == haystack {
        return true;
    }

    'outer: for n in needle.chars() {
        loop {
            match hchars.next() {
                Some(h) if eq(h, n) => continue 'outer,
                Some(_) => continue,
                None => return false
            }
        }
    }

    true
}

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

    #[test]
    fn test_eq() {
        assert!(eq('a', 'A'));
        assert!(eq('山', '山'));
        assert!(!eq('a', 'b'));
    }

    #[test]
    fn test_matches() {
        assert!(matches("a", "a"));
        assert!(matches("a", "abc"));
        assert!(matches("abc", "abc"));
        assert!(matches("ABC", "abc"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("test", "t/e/s/t"));
        assert!(matches("test", "t💣e💣s💣t"));
        assert!(matches("💣💣💣", "t💣e💣s💣t"));

        assert!(!matches("abc", "ab"));
        assert!(!matches("abc", "cab"));
        assert!(!matches("abc", ""));

        assert!(matches("", ""));
        assert!(matches("", "ab"));

        // UTF-8 case testing
        assert!(matches("a", "A"));
        assert!(matches("A", "a"));
        assert!(matches("山", "山"));
        assert!(matches("café", "CAFÉ"))
    }

    #[bench]
    fn bench_eq_same(b: &mut Bencher) {
        b.iter(|| eq('a', 'a'));
    }

    #[bench]
    fn bench_eq_ascii(b: &mut Bencher) {
        b.iter(|| eq('a', 'A'));
    }

    #[bench]
    fn bench_eq_utf8(b: &mut Bencher) {
        b.iter(|| eq('ø', 'Ø'));
    }

    #[bench]
    fn bench_eq_mixed(b: &mut Bencher) {
        b.iter(|| eq('a', 'Ø'));
    }

    #[bench]
    fn bench_matches(b: &mut Bencher) {
        b.iter(|| matches("amor", "app/models/order.rb"))
    }

    #[bench]
    fn bench_matches_mixed_case(b: &mut Bencher) {
        b.iter(|| matches("AMOr", "App/Models/Order.rb"))
    }

    #[bench]
    fn bench_matches_multiple(b: &mut Bencher) {
        b.iter(|| {
            matches("amor", "app/models/order.rb");
            matches("amor", "spec/models/order_spec.rb");
            matches("amor", "other_garbage.rb");
            matches("amor", "Gemfile");
            matches("amor", "node_modules/test/a/thing.js");
            matches("amor", "vendor/bundle/ruby/gem.rb")
        })
    }

    #[bench]
    fn bench_matches_eq(b: &mut Bencher) {
        b.iter(|| {
            matches("Gemfile", "Gemfile");
            matches("gemfile", "Gemfile")
        })
    }
}
<commit_msg>Add additional UTF-8 test<commit_after>mod score;
pub mod consts;

use std::ascii::AsciiExt;
pub use self::score::*;

/// Compares two characters case-insensitively
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::eq('a', 'A'));
/// ```
#[inline(always)]
pub fn eq(a: char, b: char) -> bool {
    match a {
        a if a == b => true,
        a if a.is_ascii() && !b.is_ascii() => false,
        a if !a.is_ascii() && b.is_ascii() => false,
        a if a.is_ascii() && b.is_ascii() => {
            a.to_ascii_lowercase().eq(&b.to_ascii_lowercase())
        },
        a => a.to_lowercase().eq(b.to_lowercase())
    }
}

/// Searches for needle's chars in the haystack
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::matches("amo", "app/models/order"));
/// ```
pub fn matches(needle: &str, haystack: &str) -> bool {
    let mut hchars = haystack.chars();

    if needle == haystack {
        return true;
    }

    'outer: for n in needle.chars() {
        loop {
            match hchars.next() {
                Some(h) if eq(h, n) => continue 'outer,
                Some(_) => continue,
                None => return false
            }
        }
    }

    true
}

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

    #[test]
    fn test_eq() {
        assert!(eq('a', 'A'));
        assert!(eq('山', '山'));
        assert!(!eq('a', 'b'));
    }

    #[test]
    fn test_matches() {
        assert!(matches("a", "a"));
        assert!(matches("a", "abc"));
        assert!(matches("abc", "abc"));
        assert!(matches("ABC", "abc"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("test", "t/e/s/t"));
        assert!(matches("test", "t💣e💣s💣t"));
        assert!(matches("💣💣💣", "t💣e💣s💣t"));

        assert!(!matches("abc", "ab"));
        assert!(!matches("abc", "cab"));
        assert!(!matches("abc", ""));

        assert!(matches("", ""));
        assert!(matches("", "ab"));

        // UTF-8 case testing
        assert!(matches("a", "A"));
        assert!(matches("A", "a"));
        assert!(matches("山", "山"));
        assert!(matches("café", "CAFÉ"));
        assert!(matches("weiß", "WEIẞ"));
    }

    #[bench]
    fn bench_eq_same(b: &mut Bencher) {
        b.iter(|| eq('a', 'a'));
    }

    #[bench]
    fn bench_eq_ascii(b: &mut Bencher) {
        b.iter(|| eq('a', 'A'));
    }

    #[bench]
    fn bench_eq_utf8(b: &mut Bencher) {
        b.iter(|| eq('ø', 'Ø'));
    }

    #[bench]
    fn bench_eq_mixed(b: &mut Bencher) {
        b.iter(|| eq('a', 'Ø'));
    }

    #[bench]
    fn bench_matches(b: &mut Bencher) {
        b.iter(|| matches("amor", "app/models/order.rb"))
    }

    #[bench]
    fn bench_matches_mixed_case(b: &mut Bencher) {
        b.iter(|| matches("AMOr", "App/Models/Order.rb"))
    }

    #[bench]
    fn bench_matches_multiple(b: &mut Bencher) {
        b.iter(|| {
            matches("amor", "app/models/order.rb");
            matches("amor", "spec/models/order_spec.rb");
            matches("amor", "other_garbage.rb");
            matches("amor", "Gemfile");
            matches("amor", "node_modules/test/a/thing.js");
            matches("amor", "vendor/bundle/ruby/gem.rb")
        })
    }

    #[bench]
    fn bench_matches_eq(b: &mut Bencher) {
        b.iter(|| {
            matches("Gemfile", "Gemfile");
            matches("gemfile", "Gemfile")
        })
    }
}
<|endoftext|>


<commit_before>use super::number::Number;
use super::point::Point;

pub enum LineIntersection<T> {
   Point(Point<T>),
   Parallel,
   Overlapping,
}

#[derive(Debug)]
pub struct Line<T> {
   a: T,
   b: T,
   c: T
}

impl<T> Line<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      Line {
         a: a,
         b: b,
         c: c
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      self.a = a;
      self.b = b;
      self.c = c;
   }

   #[inline]
   fn calc_abc(x1: T, y1: T, x2: T, y2: T) -> (T, T, T) {
      let mut a = y2 - y1;
      let mut b = x1 - x2;
      let mut c = -(x1 * y2 - x2 * y1);

      let mut norm = (T::one() / (a * a + b * b)).sqrt();
      if a != T::zero() {
         if a < T::zero() {
            norm = -norm;
         }
      } else {
         if b < T::zero() {
            norm = -norm;
         }
      }

      a = a * norm;
      b = b * norm;
      c = c * norm;

      (a, b, c)
   }

   pub fn intersect(&self, line: &Self) -> LineIntersection<T> {
      let denominator = self.a * line.b - line.a * self.b;

      if denominator == T::zero() {
         return LineIntersection::Overlapping;
      }

      let x = (line.c * self.b - self.c * line.b) / denominator;
      let y = (line.a * self.c - self.a * line.c) / denominator;

      LineIntersection::Point(Point::new(x, y))
   }
}

#[derive(Debug)]
pub struct LineSegment<T> {
   p1: Point<T>,
   p2: Point<T>,
   line: Line<T>
}

impl<T> LineSegment<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let p1 = Point::new(x1, y1);
      let p2 = Point::new(x2, y2);
      let line = Line::new(x1, y1, x2, y2);

      LineSegment {
         p1: p1,
         p2: p2,
         line: line
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      self.p1.x = x1;
      self.p1.y = y1;
      self.p2.x = x2;
      self.p2.y = y2;

      self.line.update(x1, y1, x2, y2);
   }

   #[inline]
   pub fn x1(&self) -> T {
      self.p1.x
   }

   #[inline]
   pub fn y1(&self) -> T {
      self.p1.y
   }

   #[inline]
   pub fn x2(&self) -> T {
      self.p2.x
   }

   #[inline]
   pub fn y2(&self) -> T {
      self.p2.y
   }

   #[inline]
   pub fn line(&self) -> &Line<T> {
      &self.line
   }
}

impl<T> Default for LineSegment<T> where T: Number {
   fn default() -> LineSegment<T> {
      LineSegment::new(T::zero(), T::zero(), T::zero(), T::zero())
   }
}

#[cfg(test)]
mod tests {
   use test::{Bencher, black_box};

   use super::*;

   #[test]
   fn test_new_f64() {
      let line = LineSegment::new(
         10_f64, 20_f64,
         -100_f64, 0_f64
      );
      assert_eq!(line.p1.x, 10_f64);
      assert_eq!(line.p1.y, 20_f64);
      assert_eq!(line.p2.x, -100_f64);
      assert_eq!(line.p2.y, 0_f64);
   }

   #[test]
   fn test_new_f32() {
      let line = LineSegment::new(
         10_f32, 20_f32,
         -100_f32, 0_f32
      );
      assert_eq!(line.p1.x, 10_f32);
      assert_eq!(line.p1.y, 20_f32);
      assert_eq!(line.p2.x, -100_f32);
      assert_eq!(line.p2.y, 0_f32);
   }

   #[bench]
   fn bench_line_new_f64(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               Line::new(
                  black_box(5_f64),
                  black_box(7_f64),
                  black_box(3_f64),
                  black_box(2_f64)
               )
            );
         }
      });
   }
}
<commit_msg>Benchmarks and tests for Line and LineSegment<commit_after>use super::number::Number;
use super::point::Point;

pub enum LineIntersection<T> {
   Point(Point<T>),
   Parallel,
   Overlapping,
}

#[derive(Debug)]
pub struct Line<T> {
   a: T,
   b: T,
   c: T
}

impl<T> Line<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      Line {
         a: a,
         b: b,
         c: c
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      self.a = a;
      self.b = b;
      self.c = c;
   }

   #[inline]
   fn calc_abc(x1: T, y1: T, x2: T, y2: T) -> (T, T, T) {
      let mut a = y2 - y1;
      let mut b = x1 - x2;
      let mut c = -(x1 * y2 - x2 * y1);

      let mut norm = (T::one() / (a * a + b * b)).sqrt();
      if a != T::zero() {
         if a < T::zero() {
            norm = -norm;
         }
      } else {
         if b < T::zero() {
            norm = -norm;
         }
      }

      a = a * norm;
      b = b * norm;
      c = c * norm;

      (a, b, c)
   }

   pub fn intersect(&self, line: &Self) -> LineIntersection<T> {
      let denominator = self.a * line.b - line.a * self.b;

      if denominator == T::zero() {
         return LineIntersection::Overlapping;
      }

      let x = (line.c * self.b - self.c * line.b) / denominator;
      let y = (line.a * self.c - self.a * line.c) / denominator;

      LineIntersection::Point(Point::new(x, y))
   }
}

#[derive(Debug)]
pub struct LineSegment<T> {
   p1: Point<T>,
   p2: Point<T>,
   line: Line<T>
}

impl<T> LineSegment<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let p1 = Point::new(x1, y1);
      let p2 = Point::new(x2, y2);
      let line = Line::new(x1, y1, x2, y2);

      LineSegment {
         p1: p1,
         p2: p2,
         line: line
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      self.p1.x = x1;
      self.p1.y = y1;
      self.p2.x = x2;
      self.p2.y = y2;

      self.line.update(x1, y1, x2, y2);
   }

   #[inline]
   pub fn x1(&self) -> T {
      self.p1.x
   }

   #[inline]
   pub fn y1(&self) -> T {
      self.p1.y
   }

   #[inline]
   pub fn x2(&self) -> T {
      self.p2.x
   }

   #[inline]
   pub fn y2(&self) -> T {
      self.p2.y
   }

   #[inline]
   pub fn line(&self) -> &Line<T> {
      &self.line
   }
}

impl<T> Default for LineSegment<T> where T: Number {
   fn default() -> LineSegment<T> {
      LineSegment::new(T::zero(), T::zero(), T::zero(), T::zero())
   }
}

#[cfg(test)]
mod tests {
   use test::{Bencher, black_box};

   use super::*;

   #[test]
   fn test_segment_new_f64() {
      let ls = LineSegment::new(
         10_f64, 20_f64,
         -100_f64, 0_f64
      );
      assert_eq!(ls.p1.x, 10_f64);
      assert_eq!(ls.p1.y, 20_f64);
      assert_eq!(ls.p2.x, -100_f64);
      assert_eq!(ls.p2.y, 0_f64);
   }

   #[test]
   fn test_segment_new_f32() {
      let ls = LineSegment::new(
         10_f32, 20_f32,
         -100_f32, 0_f32
      );
      assert_eq!(ls.p1.x, 10_f32);
      assert_eq!(ls.p1.y, 20_f32);
      assert_eq!(ls.p2.x, -100_f32);
      assert_eq!(ls.p2.y, 0_f32);
   }

   #[test]
   fn test_line_new_f64() {
      let ln = Line::new(
         10_f64, 20_f64,
         -100_f64, 0_f64
      );
      assert_eq!(ln.a, 0.1788854381999832_f64);
      assert_eq!(ln.b, -0.9838699100999075_f64);
      assert_eq!(ln.c, 17.88854381999832_f64);
   }

   #[test]
   fn test_line_new_f32() {
      let ln = Line::new(
         10_f32, 20_f32,
         -100_f32, 0_f32
      );
      assert_eq!(ln.a, 0.1788854381999832_f32);
      assert_eq!(ln.b, -0.9838699100999075_f32);
      assert_eq!(ln.c, 17.88854381999832_f32);
   }

   #[test]
   fn test_vline_f64() {
      let ln = Line::new(
         50_f64, 100_f64,
         50_f64, 200_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_f32() {
      let ln = Line::new(
         50_f32, 100_f32,
         50_f32, 200_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_vline_one_f64() {
      let ln = Line::new(
         50_f64, 100_f64,
         50_f64, 101_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_one_f32() {
      let ln = Line::new(
         50_f32, 100_f32,
         50_f32, 101_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_vline_one_down_f64() {
      let ln = Line::new(
         50_f64, 101_f64,
         50_f64, 100_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_one_down_f32() {
      let ln = Line::new(
         50_f32, 101_f32,
         50_f32, 100_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_f64() {
      let ln = Line::new(
         100_f64, 50_f64,
         200_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_f32() {
      let ln = Line::new(
         100_f32, 50_f32,
         200_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_one_f64() {
      let ln = Line::new(
         100_f64, 50_f64,
         101_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_one_f32() {
      let ln = Line::new(
         100_f32, 50_f32,
         101_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_one_down_f64() {
      let ln = Line::new(
         101_f64, 50_f64,
         100_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_one_down_f32() {
      let ln = Line::new(
         101_f32, 50_f32,
         100_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[bench]
   fn bench_line_new_f64(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               Line::new(
                  black_box(5_f64),
                  black_box(7_f64),
                  black_box(3_f64),
                  black_box(2_f64)
               )
            );
         }
      });
   }

   #[bench]
   fn bench_line_new_f32(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               Line::new(
                  black_box(5_f32),
                  black_box(7_f32),
                  black_box(3_f32),
                  black_box(2_f32)
               )
            );
         }
      });
   }
}

<|endoftext|>


<commit_before>use traits::*;

#[derive(PartialEq, Clone, Debug)]
pub enum BFTokenType {
    IncrementPtr(usize),      // >
    DecrementPtr(usize),      // <
    IncrementData(usize),     // +
    DecrementData(usize),     // -
    Output,                   // .
    Input,                    // ,

    // These store their corresponding tokens
    //  aka LoopStart stores the position of
    //  the matching LoopEnd and vice versa.
    LoopStart(usize),         // [
    LoopEnd(usize)            // ]
}

#[derive(Clone, Debug)]
pub struct BFToken {
    pub token_type : BFTokenType,

    // Used for matching brackets
    pub pos : usize
}

pub struct BFLexer {
}

impl Lexer<Vec<BFToken>> for BFLexer {

    fn parse(input_string:String) -> LexResult<Vec<BFToken>> {
        // Create empty vector
        let mut tokens : Vec<BFToken> = vec![];

        // Store the index of the token in the vector
        let mut pos : usize = 0;

        // This stores the positions of open brackets
        // This is used to match up bracket pairs
        //  so that they can jump to each other in O(1)
        let mut loop_stack : Vec<usize> = vec![];

        let mut last_token = BFToken { pos : 0, token_type : BFTokenType::Input };

        // Loop through each character
        for character in input_string.chars() {
            // Match our character to a TokenType.
            let token_type = match character {
                '>' => {
                     let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::IncrementPtr(x) => {
                                tokens.pop();
                                pos-=1;
                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::IncrementPtr(new_x)
                },
                '<' => {
                     let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::DecrementPtr(x) => {
                                tokens.pop();
                                pos-=1;

                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::DecrementPtr(new_x)
                },
                '+' =>  {
                    let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::IncrementData(x) => {
                                tokens.pop();
                                pos-=1;
                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::IncrementData(new_x)
                },
                '-' => {
                    let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::DecrementData(x) => {
                                tokens.pop();
                                pos-=1;
                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::DecrementData(new_x)
                },
                '.' => BFTokenType::Output,
                ',' => BFTokenType::Input,
                '[' => {
                    // Push this pos onto stack
                    loop_stack.push(pos);

                    // Store temp value of 0 for now - will be updated
                    //  once we know matching ]
                    BFTokenType::LoopStart(0)
                },
                ']' => {
                    // Get matching bracket.
                    let index = loop_stack.pop().expect("Bracket mismatch");
                    // Update it's data position to this ] token
                    tokens[index].token_type = BFTokenType::LoopStart(pos);
                    // Store the [ token's position
                    BFTokenType::LoopEnd(index)
                },

                // BF is a very simple language - if there is an
                // unrecognised character, it is ignored.
                _ => continue
            };

            pos+=1;

            // Add it to the list of tokens
            let token = BFToken { token_type : token_type, pos : pos};
            last_token = token.clone();
            tokens.push(token);
        }

        // No way for this to currently fail.
        LexResult::Success(tokens)
    }
}
<commit_msg>Fixed white space, added comment<commit_after>use traits::*;

#[derive(PartialEq, Clone, Debug)]
pub enum BFTokenType {
    IncrementPtr(usize),      // >
    DecrementPtr(usize),      // <
    IncrementData(usize),     // +
    DecrementData(usize),     // -
    Output,                   // .
    Input,                    // ,

    // These store their corresponding tokens
    //  aka LoopStart stores the position of
    //  the matching LoopEnd and vice versa.
    LoopStart(usize),         // [
    LoopEnd(usize)            // ]
}

#[derive(Clone, Debug)]
pub struct BFToken {
    pub token_type : BFTokenType,

    // Used for matching brackets
    pub pos : usize
}

pub struct BFLexer {
}

impl Lexer<Vec<BFToken>> for BFLexer {

    fn parse(input_string:String) -> LexResult<Vec<BFToken>> {
        // Create empty vector
        let mut tokens : Vec<BFToken> = vec![];

        // Store the index of the token in the vector
        let mut pos : usize = 0;

        // This stores the positions of open brackets
        // This is used to match up bracket pairs
        //  so that they can jump to each other in O(1)
        let mut loop_stack : Vec<usize> = vec![];

        let mut last_token = BFToken { pos : 0, token_type : BFTokenType::Input };

        // Loop through each character
        for character in input_string.chars() {
            // Match our character to a TokenType.
            let token_type = match character {
                '>' => {
                    // Batch up all the '>' tokens
                    let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::IncrementPtr(x) => {
                                tokens.pop();
                                pos-=1;
                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::IncrementPtr(new_x)
                },
                '<' => {
                    let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::DecrementPtr(x) => {
                                tokens.pop();
                                pos-=1;

                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::DecrementPtr(new_x)
                },
                '+' =>  {
                    let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::IncrementData(x) => {
                                tokens.pop();
                                pos-=1;
                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::IncrementData(new_x)
                },
                '-' => {
                    let mut new_x = 1;
                    if last_token.pos > 0 {
                        match last_token.token_type {
                            BFTokenType::DecrementData(x) => {
                                tokens.pop();
                                pos-=1;
                                new_x += x;
                            },
                            _ => {}
                        }
                    }
                    BFTokenType::DecrementData(new_x)
                },
                '.' => BFTokenType::Output,
                ',' => BFTokenType::Input,
                '[' => {
                    // Push this pos onto stack
                    loop_stack.push(pos);

                    // Store temp value of 0 for now - will be updated
                    //  once we know matching ]
                    BFTokenType::LoopStart(0)
                },
                ']' => {
                    // Get matching bracket.
                    let index = loop_stack.pop().expect("Bracket mismatch");
                    // Update it's data position to this ] token
                    tokens[index].token_type = BFTokenType::LoopStart(pos);
                    // Store the [ token's position
                    BFTokenType::LoopEnd(index)
                },

                // BF is a very simple language - if there is an
                // unrecognised character, it is ignored.
                _ => continue
            };

            pos+=1;

            // Add it to the list of tokens
            let token = BFToken { token_type : token_type, pos : pos};
            last_token = token.clone();
            tokens.push(token);
        }

        // No way for this to currently fail.
        LexResult::Success(tokens)
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::path::Path;

use crate::utils;

use handlebars::{Context, Handlebars, Helper, HelperDef, Output, RenderContext, RenderError};
use pulldown_cmark::{html, Event, Parser};

// Handlebars helper to construct TOC
#[derive(Clone, Copy)]
pub struct RenderToc {
    pub no_section_label: bool,
}

impl HelperDef for RenderToc {
    fn call<'reg: 'rc, 'rc>(
        &self,
        _h: &Helper<'reg, 'rc>,
        _r: &'reg Handlebars<'_>,
        ctx: &'rc Context,
        rc: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> Result<(), RenderError> {
        // get value from context data
        // rc.get_path() is current json parent path, you should always use it like this
        // param is the key of value you want to display
        let chapters = rc.evaluate(ctx, "@root/chapters").and_then(|c| {
            serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.as_json().clone())
                .map_err(|_| RenderError::new("Could not decode the JSON data"))
        })?;
        let current_path = rc
            .evaluate(ctx, "@root/path")?
            .as_json()
            .as_str()
            .ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
            .replace("\"", "");

        let current_section = rc
            .evaluate(ctx, "@root/section")?
            .as_json()
            .as_str()
            .map(str::to_owned)
            .unwrap_or_default();

        let fold_enable = rc
            .evaluate(ctx, "@root/fold_enable")?
            .as_json()
            .as_bool()
            .ok_or_else(|| RenderError::new("Type error for `fold_enable`, bool expected"))?;

        let fold_level = rc
            .evaluate(ctx, "@root/fold_level")?
            .as_json()
            .as_u64()
            .ok_or_else(|| RenderError::new("Type error for `fold_level`, u64 expected"))?;

        out.write("
")?;

        let mut current_level = 1;

        for item in chapters {
            // Spacer
            if item.get("spacer").is_some() {
                out.write("

")?;
                continue;
            }

            let (section, level) = if let Some(s) = item.get("section") {
                (s.as_str(), s.matches('.').count())
            } else {
                ("", 1)
            };

            let is_expanded =
                if !fold_enable || (!section.is_empty() && current_section.starts_with(section)) {
                    // Expand if folding is disabled, or if the section is an
                    // ancestor or the current section itself.
                    true
                } else {
                    // Levels that are larger than this would be folded.
                    level - 1 < fold_level as usize
                };

            if level > current_level {
                while level > current_level {
                    out.write("
")?;
                    out.write("
")?;
                    current_level += 1;
                }
                write_li_open_tag(out, is_expanded, false)?;
            } else if level < current_level {
                while level < current_level {
                    out.write("
")?;
                    out.write("
")?;
                    current_level -= 1;
                }
                write_li_open_tag(out, is_expanded, false)?;
            } else {
                write_li_open_tag(out, is_expanded, item.get("section").is_none())?;
            }

            // Part title
            if let Some(title) = item.get("part") {
                out.write("
")?;
                out.write(title)?;
                out.write("
")?;
                continue;
            }

            // Link
            let path_exists = if let Some(path) = item.get("path") {
                if !path.is_empty() {
                    out.write("<a href=\"")?;

                    let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
                        .with_extension("html")
                        .to_str()
                        .unwrap()
                        // Hack for windows who tends to use `\` as separator instead of `/`
                        .replace("\\", "/");

                    // Add link
                    out.write(&utils::fs::path_to_root(&current_path))?;
                    out.write(&tmp)?;
                    out.write("\"")?;

                    if path == &current_path {
                        out.write(" class=\"active\"")?;
                    }

                    out.write(">")?;
                    true
                } else {
                    false
                }
            } else {
                false
            };

            if !self.no_section_label {
                // Section does not necessarily exist
                if let Some(section) = item.get("section") {
                    out.write("")?;
                    out.write(&section)?;
                    out.write(" ")?;
                }
            }

            if let Some(name) = item.get("name") {
                // Render only inline code blocks

                // filter all events that are not inline code blocks
                let parser = Parser::new(name).filter(|event| match *event {
                    Event::Code(_) | Event::Html(_) | Event::Text(_) => true,
                    _ => false,
                });

                // render markdown to html
                let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
                html::push_html(&mut markdown_parsed_name, parser);

                // write to the handlebars template
                out.write(&markdown_parsed_name)?;
            }

            if path_exists {
                out.write("")?;
            }

            // Render expand/collapse toggle
            if let Some(flag) = item.get("has_sub_items") {
                let has_sub_items = flag.parse::<bool>().unwrap_or_default();
                if fold_enable && has_sub_items {
                    out.write("
❱
")?;
                }
            }
            out.write("
")?;
        }
        while current_level > 1 {
            out.write("
")?;
            out.write("
")?;
            current_level -= 1;
        }

        out.write("
")?;
        Ok(())
    }
}

fn write_li_open_tag(
    out: &mut dyn Output,
    is_expanded: bool,
    is_affix: bool,
) -> Result<(), std::io::Error> {
    let mut li = String::from("
");
    out.write(&li)
}
<commit_msg>Wrap draft chapter titles in a
<commit_after>use std::collections::BTreeMap;
use std::path::Path;

use crate::utils;

use handlebars::{Context, Handlebars, Helper, HelperDef, Output, RenderContext, RenderError};
use pulldown_cmark::{html, Event, Parser};

// Handlebars helper to construct TOC
#[derive(Clone, Copy)]
pub struct RenderToc {
    pub no_section_label: bool,
}

impl HelperDef for RenderToc {
    fn call<'reg: 'rc, 'rc>(
        &self,
        _h: &Helper<'reg, 'rc>,
        _r: &'reg Handlebars<'_>,
        ctx: &'rc Context,
        rc: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> Result<(), RenderError> {
        // get value from context data
        // rc.get_path() is current json parent path, you should always use it like this
        // param is the key of value you want to display
        let chapters = rc.evaluate(ctx, "@root/chapters").and_then(|c| {
            serde_json::value::from_value::<Vec<BTreeMap<String, String>>>(c.as_json().clone())
                .map_err(|_| RenderError::new("Could not decode the JSON data"))
        })?;
        let current_path = rc
            .evaluate(ctx, "@root/path")?
            .as_json()
            .as_str()
            .ok_or_else(|| RenderError::new("Type error for `path`, string expected"))?
            .replace("\"", "");

        let current_section = rc
            .evaluate(ctx, "@root/section")?
            .as_json()
            .as_str()
            .map(str::to_owned)
            .unwrap_or_default();

        let fold_enable = rc
            .evaluate(ctx, "@root/fold_enable")?
            .as_json()
            .as_bool()
            .ok_or_else(|| RenderError::new("Type error for `fold_enable`, bool expected"))?;

        let fold_level = rc
            .evaluate(ctx, "@root/fold_level")?
            .as_json()
            .as_u64()
            .ok_or_else(|| RenderError::new("Type error for `fold_level`, u64 expected"))?;

        out.write("
")?;

        let mut current_level = 1;

        for item in chapters {
            // Spacer
            if item.get("spacer").is_some() {
                out.write("

")?;
                continue;
            }

            let (section, level) = if let Some(s) = item.get("section") {
                (s.as_str(), s.matches('.').count())
            } else {
                ("", 1)
            };

            let is_expanded =
                if !fold_enable || (!section.is_empty() && current_section.starts_with(section)) {
                    // Expand if folding is disabled, or if the section is an
                    // ancestor or the current section itself.
                    true
                } else {
                    // Levels that are larger than this would be folded.
                    level - 1 < fold_level as usize
                };

            if level > current_level {
                while level > current_level {
                    out.write("
")?;
                    out.write("
")?;
                    current_level += 1;
                }
                write_li_open_tag(out, is_expanded, false)?;
            } else if level < current_level {
                while level < current_level {
                    out.write("
")?;
                    out.write("
")?;
                    current_level -= 1;
                }
                write_li_open_tag(out, is_expanded, false)?;
            } else {
                write_li_open_tag(out, is_expanded, item.get("section").is_none())?;
            }

            // Part title
            if let Some(title) = item.get("part") {
                out.write("
")?;
                out.write(title)?;
                out.write("
")?;
                continue;
            }

            // Link
            let path_exists = if let Some(path) =
                item.get("path")
                    .and_then(|p| if p.is_empty() { None } else { Some(p) })
            {
                out.write("<a href=\"")?;

                let tmp = Path::new(item.get("path").expect("Error: path should be Some(_)"))
                    .with_extension("html")
                    .to_str()
                    .unwrap()
                    // Hack for windows who tends to use `\` as separator instead of `/`
                    .replace("\\", "/");

                // Add link
                out.write(&utils::fs::path_to_root(&current_path))?;
                out.write(&tmp)?;
                out.write("\"")?;

                if path == &current_path {
                    out.write(" class=\"active\"")?;
                }

                out.write(">")?;
                true
            } else {
                out.write("
")?;
                false
            };

            if !self.no_section_label {
                // Section does not necessarily exist
                if let Some(section) = item.get("section") {
                    out.write("")?;
                    out.write(&section)?;
                    out.write(" ")?;
                }
            }

            if let Some(name) = item.get("name") {
                // Render only inline code blocks

                // filter all events that are not inline code blocks
                let parser = Parser::new(name).filter(|event| match *event {
                    Event::Code(_) | Event::Html(_) | Event::Text(_) => true,
                    _ => false,
                });

                // render markdown to html
                let mut markdown_parsed_name = String::with_capacity(name.len() * 3 / 2);
                html::push_html(&mut markdown_parsed_name, parser);

                // write to the handlebars template
                out.write(&markdown_parsed_name)?;
            }

            if path_exists {
                out.write("")?;
            } else {
                out.write("
")?;
            }

            // Render expand/collapse toggle
            if let Some(flag) = item.get("has_sub_items") {
                let has_sub_items = flag.parse::<bool>().unwrap_or_default();
                if fold_enable && has_sub_items {
                    out.write("
❱
")?;
                }
            }
            out.write("
")?;
        }
        while current_level > 1 {
            out.write("
")?;
            out.write("
")?;
            current_level -= 1;
        }

        out.write("
")?;
        Ok(())
    }
}

fn write_li_open_tag(
    out: &mut dyn Output,
    is_expanded: bool,
    is_affix: bool,
) -> Result<(), std::io::Error> {
    let mut li = String::from("
");
    out.write(&li)
}
<|endoftext|>


<commit_before>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::cell::{Cell, RefCell};
use std::collections::{HashMap, hash_map};
use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::ptr;
use libc::c_int;
use iup_sys::*;
use callbacks::Token;

thread_local!(
    static LDESTROY_CALLBACKS: RefCell<HashMap<*mut Ihandle, Vec<(usize, Box<FnMut(*mut Ihandle)>)>>> =
        RefCell::new(HashMap::new())
);

// simplified version of CallbackRegistry::add_callback
// An ldestroy callback should hold a reference to a control, so that when it is dropped a control
// is dropped. Doing so will cause the LDESTROY_CALLBACKS HashMap to be accessed, causing a panic
// due to being borrowed by remove_ldestroy_callback. It is fine to drop a control in the callback,
// just not to capture a control so that the control is dropped when the callback is.
pub fn add_ldestroy_callback_inner(ih: *mut Ihandle, cb: Box<FnMut(*mut Ihandle) + 'static>) -> Token {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        let cbs = map.entry(ih).or_insert_with(|| { Vec::with_capacity(4) });
        let id = cbs.last().map(|&(id, _)| id + 1).unwrap_or(0);
        cbs.push((id, cb));

        Token { id: id, ih: ih }
    })
}

pub fn add_ldestroy_callback<F: 'static + FnMut(*mut Ihandle)>(ih: *mut Ihandle, cb: F) -> Token {
    add_ldestroy_callback_inner(ih, Box::new(cb))
}

// simplified version of CallbackRegistry::remove_callback
pub fn remove_ldestroy_callback(token: Token) {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        if let hash_map::Entry::Occupied(mut entry) = map.entry(token.ih) {
            let is_empty = {
                let cbs = entry.get_mut();
                let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
                // TODO: if this causes ldestroy_cb() to be called, it will panic
                cbs.remove(index);

                cbs.is_empty()
            };
            if is_empty {
                entry.remove();
            }

            // I could use the below with non-lexical borrows.
            //let cbs = entry.get_mut();
            //let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
            // TODO: if this causes ldestroy_cb() to be called, it will panic
            //cbs.remove(index);

            //if cbs.is_empty() {
            //    entry.remove();
            //}
        }
    });
}

extern fn ldestroy_cb(ih: *mut Ihandle) -> c_int {
    handle_rc_destroy_cb(ih);
    LDESTROY_CALLBACKS.with(|cell| {
        // Putting this line in the if condition will currently cause the HashMap borrow to last
        // for the entire if body.
        let cbs_opt = cell.borrow_mut().remove(&ih);
        if let Some(mut cbs) = cbs_opt {
            for cb in cbs.iter_mut() {
                cb.1(ih);
            }
        }
        IUP_DEFAULT
    })
}

thread_local!(
    static EXISTING_HANDLES: RefCell<HashMap<*mut Ihandle, Weak<HandleBox>>> = RefCell::new(HashMap::new())
);

pub fn handle_rc_destroy_cb(ih: *mut Ihandle) {
    EXISTING_HANDLES.with(|cell| {
        let mut map = cell.borrow_mut();
        if let Some(weak) = map.remove(&ih) {
            // It should be removed when the last strong ref was dropped, so it should always be upgradable.
            let handle = weak.upgrade().expect("could not upgrade Weak in handle map");
            // Since the control is destroyed, zero out to prevent wrapper structs from
            // using after free.
            handle.set(ptr::null_mut());
        }
        // else: No wrapper handles currently exist. Don't need to do anything.

        // Detach any child that still has a reference from a wrapper so that it doesn't
        // get destroyed.
        unsafe {
            // Removing a child shifts the index of the following children by one as you'd expect.
            // The easiest way would be to iterate backward removing children. However,
            // IUP stores children as a singly-linked list starting with the first child.
            // So iterating reverse would be O(n^2) best case. Iterating forward would be O(n)
            // best case and O(n^2) worst case. However, using IupGetNextChild and IupGetBrother
            // is always O(n).
            let mut child = IupGetNextChild(ih, ptr::null_mut());
            while !child.is_null() {
                let brother = IupGetBrother(child);
                if map.contains_key(&child) {
                    IupDetach(child);
                }
                child = brother;
            }
        }
    });
}

/// A `HandleBox` is the wrapper's one reference to a control. If there are multiple `HandleRc`s,
/// they all refer to the same `HandleBox`. Since there will always be at most one `HandleBox` for
/// a certain `*mut Ihandle`, then when the `HandleBox` is dropped, there are no more references
/// from the wrapper to the `*mut Ihandle`.
#[derive(Debug)]
struct HandleBox(Cell<*mut Ihandle>);

impl Deref for HandleBox {
    type Target = Cell<*mut Ihandle>;
    fn deref(&self) -> &Cell<*mut Ihandle> {
        &self.0
    }
}

impl Drop for HandleBox {
    fn drop(&mut self) {
        if self.get() == ptr::null_mut() {
            return;
        }
        EXISTING_HANDLES.with(|map| {
            map.borrow_mut().remove(&self.get()).expect("handle not found in map");
        });
        unsafe {
            if IupGetParent(self.get()) == ptr::null_mut()
            {
                //println!("deleting control with no parent: {:?}", self.get());
                IupDestroy(self.get());
            }
        }
    }
}

// As a future optimization, the implementation could be changed to not use Rc, since support
// for weak ptrs is not used. It would only get rid of one word of memory though. Not worth
// the time.
#[derive(Debug, Clone)]
pub struct HandleRc(Rc<HandleBox>);

impl HandleRc {
    pub unsafe fn new(ih: *mut Ihandle) -> HandleRc {
        assert!(ih != ptr::null_mut(), "handle must be non-null");
        let rc = EXISTING_HANDLES.with(|map|
            match map.borrow_mut().entry(ih) {
                hash_map::Entry::Occupied(entry) =>
                    entry.get().upgrade().expect("could not upgrade Weak in handle map"),
                hash_map::Entry::Vacant(entry) => {
                    IupSetCallback(ih, "LDESTROY_CB\0".as_ptr() as *const i8, ldestroy_cb);
                    let rc = Rc::new(HandleBox(Cell::new(ih)));
                    entry.insert(Rc::downgrade(&rc));
                    rc
                },
            }
        );
        HandleRc(rc)
    }

    pub fn get(&self) -> *mut Ihandle {
        self.0.get()
    }

    pub fn try_unwrap(self) -> Result<*mut Ihandle, HandleRc> {
        Rc::try_unwrap(self.0).map(|handle_box| {
            let handle = handle_box.get();
            // null out so that `HandleBox` doesn't IupDestroy() it and so that no other
            // `HandleRc` can return it
            handle_box.set(ptr::null_mut());
            handle
        }).map_err(|rc| HandleRc(rc))
    }
}
<commit_msg>Use try_with() instead of with() to access TLS value<commit_after>/* Copyright 2015 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use std::cell::{Cell, RefCell};
use std::collections::{HashMap, hash_map};
use std::ops::Deref;
use std::rc::{Rc, Weak};
use std::ptr;
use libc::c_int;
use iup_sys::*;
use callbacks::Token;

thread_local!(
    static LDESTROY_CALLBACKS: RefCell<HashMap<*mut Ihandle, Vec<(usize, Box<FnMut(*mut Ihandle)>)>>> =
        RefCell::new(HashMap::new())
);

// simplified version of CallbackRegistry::add_callback
// An ldestroy callback should hold a reference to a control, so that when it is dropped a control
// is dropped. Doing so will cause the LDESTROY_CALLBACKS HashMap to be accessed, causing a panic
// due to being borrowed by remove_ldestroy_callback. It is fine to drop a control in the callback,
// just not to capture a control so that the control is dropped when the callback is.
pub fn add_ldestroy_callback_inner(ih: *mut Ihandle, cb: Box<FnMut(*mut Ihandle) + 'static>) -> Token {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        let cbs = map.entry(ih).or_insert_with(|| { Vec::with_capacity(4) });
        let id = cbs.last().map(|&(id, _)| id + 1).unwrap_or(0);
        cbs.push((id, cb));

        Token { id: id, ih: ih }
    })
}

pub fn add_ldestroy_callback<F: 'static + FnMut(*mut Ihandle)>(ih: *mut Ihandle, cb: F) -> Token {
    add_ldestroy_callback_inner(ih, Box::new(cb))
}

// simplified version of CallbackRegistry::remove_callback
pub fn remove_ldestroy_callback(token: Token) {
    LDESTROY_CALLBACKS.with(|reg| {
        let mut map = reg.borrow_mut();
        if let hash_map::Entry::Occupied(mut entry) = map.entry(token.ih) {
            let is_empty = {
                let cbs = entry.get_mut();
                let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
                // TODO: if this causes ldestroy_cb() to be called, it will panic
                cbs.remove(index);

                cbs.is_empty()
            };
            if is_empty {
                entry.remove();
            }

            // I could use the below with non-lexical borrows.
            //let cbs = entry.get_mut();
            //let index = cbs.iter().position(|&(id, _)| id == token.id).expect("failed to remove callback");
            // TODO: if this causes ldestroy_cb() to be called, it will panic
            //cbs.remove(index);

            //if cbs.is_empty() {
            //    entry.remove();
            //}
        }
    });
}

extern fn ldestroy_cb(ih: *mut Ihandle) -> c_int {
    handle_rc_destroy_cb(ih);
    let _ = LDESTROY_CALLBACKS.try_with(|cell| {
        // Putting this line in the if condition will currently cause the HashMap borrow to last
        // for the entire if body.
        let cbs_opt = cell.borrow_mut().remove(&ih);
        if let Some(mut cbs) = cbs_opt {
            for cb in cbs.iter_mut() {
                cb.1(ih);
            }
        }
    });
    IUP_DEFAULT
}

thread_local!(
    static EXISTING_HANDLES: RefCell<HashMap<*mut Ihandle, Weak<HandleBox>>> = RefCell::new(HashMap::new())
);

pub fn handle_rc_destroy_cb(ih: *mut Ihandle) {
    let _ = EXISTING_HANDLES.try_with(|cell| {
        let mut map = cell.borrow_mut();
        if let Some(weak) = map.remove(&ih) {
            // It should be removed when the last strong ref was dropped, so it should always be upgradable.
            let handle = weak.upgrade().expect("could not upgrade Weak in handle map");
            // Since the control is destroyed, zero out to prevent wrapper structs from
            // using after free.
            handle.set(ptr::null_mut());
        }
        // else: No wrapper handles currently exist. Don't need to do anything.

        // Detach any child that still has a reference from a wrapper so that it doesn't
        // get destroyed.
        unsafe {
            // Removing a child shifts the index of the following children by one as you'd expect.
            // The easiest way would be to iterate backward removing children. However,
            // IUP stores children as a singly-linked list starting with the first child.
            // So iterating reverse would be O(n^2) best case. Iterating forward would be O(n)
            // best case and O(n^2) worst case. However, using IupGetNextChild and IupGetBrother
            // is always O(n).
            let mut child = IupGetNextChild(ih, ptr::null_mut());
            while !child.is_null() {
                let brother = IupGetBrother(child);
                if map.contains_key(&child) {
                    IupDetach(child);
                }
                child = brother;
            }
        }
    });
}

/// A `HandleBox` is the wrapper's one reference to a control. If there are multiple `HandleRc`s,
/// they all refer to the same `HandleBox`. Since there will always be at most one `HandleBox` for
/// a certain `*mut Ihandle`, then when the `HandleBox` is dropped, there are no more references
/// from the wrapper to the `*mut Ihandle`.
#[derive(Debug)]
struct HandleBox(Cell<*mut Ihandle>);

impl Deref for HandleBox {
    type Target = Cell<*mut Ihandle>;
    fn deref(&self) -> &Cell<*mut Ihandle> {
        &self.0
    }
}

impl Drop for HandleBox {
    fn drop(&mut self) {
        if self.get() == ptr::null_mut() {
            return;
        }
        let _ = EXISTING_HANDLES.try_with(|map| {
            map.borrow_mut().remove(&self.get()).expect("handle not found in map");
        });
        unsafe {
            if IupGetParent(self.get()) == ptr::null_mut()
            {
                //println!("deleting control with no parent: {:?}", self.get());
                IupDestroy(self.get());
            }
        }
    }
}

// As a future optimization, the implementation could be changed to not use Rc, since support
// for weak ptrs is not used. It would only get rid of one word of memory though. Not worth
// the time.
#[derive(Debug, Clone)]
pub struct HandleRc(Rc<HandleBox>);

impl HandleRc {
    pub unsafe fn new(ih: *mut Ihandle) -> HandleRc {
        assert!(ih != ptr::null_mut(), "handle must be non-null");
        let rc = EXISTING_HANDLES.with(|map|
            match map.borrow_mut().entry(ih) {
                hash_map::Entry::Occupied(entry) =>
                    entry.get().upgrade().expect("could not upgrade Weak in handle map"),
                hash_map::Entry::Vacant(entry) => {
                    IupSetCallback(ih, "LDESTROY_CB\0".as_ptr() as *const i8, ldestroy_cb);
                    let rc = Rc::new(HandleBox(Cell::new(ih)));
                    entry.insert(Rc::downgrade(&rc));
                    rc
                },
            }
        );
        HandleRc(rc)
    }

    pub fn get(&self) -> *mut Ihandle {
        self.0.get()
    }

    pub fn try_unwrap(self) -> Result<*mut Ihandle, HandleRc> {
        Rc::try_unwrap(self.0).map(|handle_box| {
            let handle = handle_box.get();
            // null out so that `HandleBox` doesn't IupDestroy() it and so that no other
            // `HandleRc` can return it
            handle_box.set(ptr::null_mut());
            handle
        }).map_err(|rc| HandleRc(rc))
    }
}
<|endoftext|>


<commit_before>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterOutput, FilterResult};

mod error;
use self::error::FilterError;

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<ResolvedPrimitive, FilterError>;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

impl PrimitiveParams {
    /// Returns a human-readable name for a primitive.
    #[rustfmt::skip]
    fn name(&self) -> &'static str {
        use PrimitiveParams::*;
        match self {
            Blend(..)             => "feBlend",
            ColorMatrix(..)       => "feColorMatrix",
            ComponentTransfer(..) => "feComponentTransfer",
            Composite(..)         => "feComposite",
            ConvolveMatrix(..)    => "feConvolveMatrix",
            DiffuseLighting(..)   => "feDiffuseLighting",
            DisplacementMap(..)   => "feDisplacementMap",
            Flood(..)             => "feFlood",
            GaussianBlur(..)      => "feGaussianBlur",
            Image(..)             => "feImage",
            Merge(..)             => "feMerge",
            Morphology(..)        => "feMorphology",
            Offset(..)            => "feOffset",
            SpecularLighting(..)  => "feSpecularLighting",
            Tile(..)              => "feTile",
            Turbulence(..)        => "feTurbulence",
        }
    }
}

/// The base filter primitive node containing common properties.
#[derive(Default, Clone)]
pub struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

pub struct ResolvedPrimitive {
    pub primitive: Primitive,
    pub params: PrimitiveParams,
}

/// Common properties for filter primitives, with all lengths in user space coordinates.
pub struct UserSpacePrimitive {
    x: Option<f64>,
    y: Option<f64>,
    width: Option<f64>,
    height: Option<f64>,
    result: Option<CustomIdent>,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

enum_default!(Input, Input::Unspecified);

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

impl ResolvedPrimitive {
    fn to_user_space(
        &self,
        primitive_units: CoordUnits,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> UserSpacePrimitive {
        let params = draw_ctx.push_coord_units(primitive_units);

        let x = self.primitive.x.map(|l| l.normalize(values, &params));
        let y = self.primitive.y.map(|l| l.normalize(values, &params));
        let width = self.primitive.width.map(|l| l.normalize(values, &params));
        let height = self.primitive.height.map(|l| l.normalize(values, &params));

        UserSpacePrimitive {
            x,
            y,
            width,
            height,
            result: self.primitive.result.clone(),
        }
    }
}

impl UserSpacePrimitive {
    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> BoundsBuilder {
        BoundsBuilder::new(self.x, self.y, self.width, self.height, ctx.paffine())
    }
}

impl Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    stroke_paint_source: UserSpacePaintSource,
    fill_paint_source: UserSpacePaintSource,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let filter_element = filter_node.borrow_element();

    if filter_element.is_in_error() {
        return Ok(source_surface);
    }

    let user_space_filter = {
        let filter_values = filter_element.get_computed_values();

        let filter = borrow_element_as!(filter_node, Filter);

        // This is in a temporary scope so we don't leave the coord_units pushed during
        // the execution of all the filter primitives.
        let params = draw_ctx.push_coord_units(filter.get_filter_units());
        filter.to_user_space(filter_values, &params)
    };

    let res = filter_node
        .children()
        .filter(|c| c.is_element())
        // Skip nodes in error.
        .filter(|c| {
            let in_error = c.borrow_element().is_in_error();

            if in_error {
                rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
            }

            !in_error
        })
        // Keep only filter primitives (those that implement the Filter trait)
        .filter(|c| c.borrow_element().as_filter_effect().is_some())
        .map(|primitive_node| {
            let elt = primitive_node.borrow_element();
            let effect = elt.as_filter_effect().unwrap();

            let primitive_values = elt.get_computed_values();

            let primitive_name = format!("{}", primitive_node);

            effect
                .resolve(&primitive_node)
                .map_err(|e| {
                    rsvg_log!(
                        "(filter primitive {} returned an error: {})",
                        primitive_name,
                        e
                    );
                    e
                })
                .and_then(|primitive| {
                    let user_space_primitive = primitive.to_user_space(
                        user_space_filter.primitive_units,
                        primitive_values,
                        draw_ctx,
                    );

                    Ok((user_space_primitive, primitive.params))
                })
        })
        .collect::<Result<Vec<(UserSpacePrimitive, PrimitiveParams)>, FilterError>>();

    let primitives = match res {
        Err(FilterError::CairoError(status)) => {
            // Exit early on Cairo errors
            return Err(RenderingError::from(status));
        }

        Err(_) => {
            // ignore other filter errors and just return an empty surface
            return Ok(SharedImageSurface::empty(
                source_surface.width(),
                source_surface.height(),
                SurfaceType::AlphaOnly,
            )?);
        }

        Ok(r) => r,
    };

    if let Ok(mut filter_ctx) = FilterContext::new(
        &user_space_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    ) {
        for (user_space_primitive, params) in primitives {
            let start = Instant::now();

            match render_primitive(
                &user_space_primitive,
                &params,
                &filter_ctx,
                acquired_nodes,
                draw_ctx,
            ) {
                Ok(output) => {
                    let elapsed = start.elapsed();
                    rsvg_log!(
                        "(rendered filter primitive {} in\n    {} seconds)",
                        params.name(),
                        elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
                    );

                    filter_ctx.store_result(FilterResult {
                        name: user_space_primitive.result,
                        output,
                    });
                }

                Err(err) => {
                    rsvg_log!(
                        "(filter primitive {} returned an error: {})",
                        params.name(),
                        err
                    );

                    // Exit early on Cairo errors. Continue rendering otherwise.
                    if let FilterError::CairoError(status) = err {
                        return Err(RenderingError::from(status));
                    }
                }
            }
        }

        Ok(filter_ctx.into_output()?)
    } else {
        // Ignore errors that happened when creating the FilterContext
        Ok(SharedImageSurface::empty(
            source_surface.width(),
            source_surface.height(),
            SurfaceType::AlphaOnly,
        )?)
    }
}

#[rustfmt::skip]
fn render_primitive(
    primitive: &UserSpacePrimitive,
    params: &PrimitiveParams,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterOutput, FilterError> {
    use PrimitiveParams::*;

    let bounds_builder = primitive.get_bounds(ctx);

    match params {
        Blend(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
    }
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<commit_msg>Carry the PrimitiveParams inside UserSpacePrimitive<commit_after>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterOutput, FilterResult};

mod error;
use self::error::FilterError;

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<ResolvedPrimitive, FilterError>;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

impl PrimitiveParams {
    /// Returns a human-readable name for a primitive.
    #[rustfmt::skip]
    fn name(&self) -> &'static str {
        use PrimitiveParams::*;
        match self {
            Blend(..)             => "feBlend",
            ColorMatrix(..)       => "feColorMatrix",
            ComponentTransfer(..) => "feComponentTransfer",
            Composite(..)         => "feComposite",
            ConvolveMatrix(..)    => "feConvolveMatrix",
            DiffuseLighting(..)   => "feDiffuseLighting",
            DisplacementMap(..)   => "feDisplacementMap",
            Flood(..)             => "feFlood",
            GaussianBlur(..)      => "feGaussianBlur",
            Image(..)             => "feImage",
            Merge(..)             => "feMerge",
            Morphology(..)        => "feMorphology",
            Offset(..)            => "feOffset",
            SpecularLighting(..)  => "feSpecularLighting",
            Tile(..)              => "feTile",
            Turbulence(..)        => "feTurbulence",
        }
    }
}

/// The base filter primitive node containing common properties.
#[derive(Default, Clone)]
pub struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

pub struct ResolvedPrimitive {
    pub primitive: Primitive,
    pub params: PrimitiveParams,
}

/// A fully resolved filter primitive in user-space coordinates.
pub struct UserSpacePrimitive {
    x: Option<f64>,
    y: Option<f64>,
    width: Option<f64>,
    height: Option<f64>,
    result: Option<CustomIdent>,

    params: PrimitiveParams,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

enum_default!(Input, Input::Unspecified);

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

impl ResolvedPrimitive {
    fn to_user_space(
        self,
        primitive_units: CoordUnits,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> UserSpacePrimitive {
        let params = draw_ctx.push_coord_units(primitive_units);

        let x = self.primitive.x.map(|l| l.normalize(values, &params));
        let y = self.primitive.y.map(|l| l.normalize(values, &params));
        let width = self.primitive.width.map(|l| l.normalize(values, &params));
        let height = self.primitive.height.map(|l| l.normalize(values, &params));

        UserSpacePrimitive {
            x,
            y,
            width,
            height,
            result: self.primitive.result,
            params: self.params,
        }
    }
}

impl UserSpacePrimitive {
    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> BoundsBuilder {
        BoundsBuilder::new(self.x, self.y, self.width, self.height, ctx.paffine())
    }
}

impl Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    stroke_paint_source: UserSpacePaintSource,
    fill_paint_source: UserSpacePaintSource,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let filter_element = filter_node.borrow_element();

    if filter_element.is_in_error() {
        return Ok(source_surface);
    }

    let user_space_filter = {
        let filter_values = filter_element.get_computed_values();

        let filter = borrow_element_as!(filter_node, Filter);

        // This is in a temporary scope so we don't leave the coord_units pushed during
        // the execution of all the filter primitives.
        let params = draw_ctx.push_coord_units(filter.get_filter_units());
        filter.to_user_space(filter_values, &params)
    };

    let res = filter_node
        .children()
        .filter(|c| c.is_element())
        // Skip nodes in error.
        .filter(|c| {
            let in_error = c.borrow_element().is_in_error();

            if in_error {
                rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
            }

            !in_error
        })
        // Keep only filter primitives (those that implement the Filter trait)
        .filter(|c| c.borrow_element().as_filter_effect().is_some())
        .map(|primitive_node| {
            let elt = primitive_node.borrow_element();
            let effect = elt.as_filter_effect().unwrap();

            let primitive_values = elt.get_computed_values();

            let primitive_name = format!("{}", primitive_node);

            effect
                .resolve(&primitive_node)
                .map_err(|e| {
                    rsvg_log!(
                        "(filter primitive {} returned an error: {})",
                        primitive_name,
                        e
                    );
                    e
                })
                .and_then(|primitive| {
                    Ok(primitive.to_user_space(
                        user_space_filter.primitive_units,
                        primitive_values,
                        draw_ctx,
                    ))
                })
        })
        .collect::<Result<Vec<UserSpacePrimitive>, FilterError>>();

    let primitives = match res {
        Err(FilterError::CairoError(status)) => {
            // Exit early on Cairo errors
            return Err(RenderingError::from(status));
        }

        Err(_) => {
            // ignore other filter errors and just return an empty surface
            return Ok(SharedImageSurface::empty(
                source_surface.width(),
                source_surface.height(),
                SurfaceType::AlphaOnly,
            )?);
        }

        Ok(r) => r,
    };

    if let Ok(mut filter_ctx) = FilterContext::new(
        &user_space_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    ) {
        for user_space_primitive in primitives {
            let start = Instant::now();

            match render_primitive(&user_space_primitive, &filter_ctx, acquired_nodes, draw_ctx) {
                Ok(output) => {
                    let elapsed = start.elapsed();
                    rsvg_log!(
                        "(rendered filter primitive {} in\n    {} seconds)",
                        user_space_primitive.params.name(),
                        elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
                    );

                    filter_ctx.store_result(FilterResult {
                        name: user_space_primitive.result,
                        output,
                    });
                }

                Err(err) => {
                    rsvg_log!(
                        "(filter primitive {} returned an error: {})",
                        user_space_primitive.params.name(),
                        err
                    );

                    // Exit early on Cairo errors. Continue rendering otherwise.
                    if let FilterError::CairoError(status) = err {
                        return Err(RenderingError::from(status));
                    }
                }
            }
        }

        Ok(filter_ctx.into_output()?)
    } else {
        // Ignore errors that happened when creating the FilterContext
        Ok(SharedImageSurface::empty(
            source_surface.width(),
            source_surface.height(),
            SurfaceType::AlphaOnly,
        )?)
    }
}

#[rustfmt::skip]
fn render_primitive(
    primitive: &UserSpacePrimitive,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterOutput, FilterError> {
    use PrimitiveParams::*;

    let bounds_builder = primitive.get_bounds(ctx);

    match primitive.params {
        Blend(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ColorMatrix(ref p)       => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(ref p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Composite(ref p)         => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(ref p)    => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(ref p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DisplacementMap(ref p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Flood(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        GaussianBlur(ref p)      => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Image(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Merge(ref p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Morphology(ref p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Offset(ref p)            => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        SpecularLighting(ref p)  => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Tile(ref p)              => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Turbulence(ref p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
    }
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<|endoftext|>