<commit_before>use cairo;
use libc;

use std::cell::RefCell;
use std::f64;
use std::rc::*;

use cairo::MatrixTrait;
use cairo::Pattern as CairoPattern;

use aspect_ratio::*;
use attributes::Attribute;
use bbox::*;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx, AcquiredNode};
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::parse;
use property_bag::PropertyBag;
use viewbox::*;

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

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

    // Point back to our corresponding node, or to the fallback node which has children
    pub node: Option<Weak<Node>>,
}

impl Default for Pattern {
    fn default() -> Pattern {
        // These are per the spec

        Pattern {
            units: Some(PatternUnits::default()),
            content_units: Some(PatternContentUnits::default()),
            vbox: Some(None),
            preserve_aspect_ratio: Some(AspectRatio::default()),
            affine: Some(cairo::Matrix::identity()),
            fallback: None,
            x: Some(RsvgLength::default()),
            y: Some(RsvgLength::default()),
            width: Some(RsvgLength::default()),
            height: Some(RsvgLength::default()),
            node: None,
        }
    }
}

fn node_has_children(node: &Option<Weak<Node>>) -> bool {
    match *node {
        None => false,

        Some(ref weak) => {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        }
    }
}

// All of the Pattern's fields are Option<foo> values, because
// those fields can be omitted in the SVG file.  We need to resolve
// them to default values, or to fallback values that come from
// another Pattern.
//
// For the fallback case, this would need something like
//
//    if self.foo.is_none () { self.foo = fallback.foo; }
//
// And for the default case, it would be like
//    if self.foo.is_none () { self.foo = Some (default_value); }
//
// Both can be replaced by
//
//    self.foo = self.foo.take ().or (bar);
//
// So we define a macro for that.
macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Pattern {
    fn unresolved() -> Pattern {
        Pattern {
            units: None,
            content_units: None,
            vbox: None,
            preserve_aspect_ratio: None,
            affine: None,
            fallback: None,
            x: None,
            y: None,
            width: None,
            height: None,
            node: None,
        }
    }

    fn is_resolved(&self) -> bool {
        self.units.is_some() && self.content_units.is_some() && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some() && self.affine.is_some()
            && self.x.is_some() && self.y.is_some() && self.width.is_some()
            && self.height.is_some() && node_has_children(&self.node)
    }

    fn resolve_from_defaults(&mut self) {
        self.resolve_from_fallback(&Pattern::default());

        if !node_has_children(&self.node) {
            self.node = None;
        }
    }

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

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

        if !node_has_children(&self.node) {
            self.node = fallback.node.clone();
        }
    }
}

struct NodePattern {
    pattern: RefCell<Pattern>,
}

impl NodePattern {
    fn new() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(Pattern::unresolved()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, node: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        let mut p = self.pattern.borrow_mut();

        p.node = Some(Rc::downgrade(node));

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PatternUnits => p.units = Some(parse("patternUnits", value, (), None)?),

                Attribute::PatternContentUnits => {
                    p.content_units = Some(parse("patternContentUnits", value, (), None)?)
                }

                Attribute::ViewBox => p.vbox = Some(Some(parse("viewBox", value, (), None)?)),

                Attribute::PreserveAspectRatio => {
                    p.preserve_aspect_ratio = Some(parse("preserveAspectRatio", value, (), None)?)
                }

                Attribute::PatternTransform => {
                    p.affine = Some(parse("patternTransform", value, (), None)?)
                }

                Attribute::XlinkHref => p.fallback = Some(value.to_owned()),

                Attribute::X => p.x = Some(parse("x", value, LengthDir::Horizontal, None)?),

                Attribute::Y => p.y = Some(parse("y", value, LengthDir::Vertical, None)?),

                Attribute::Width => {
                    p.width = Some(parse(
                        "width",
                        value,
                        LengthDir::Horizontal,
                        Some(RsvgLength::check_nonnegative),
                    )?)
                }

                Attribute::Height => {
                    p.height = Some(parse(
                        "height",
                        value,
                        LengthDir::Vertical,
                        Some(RsvgLength::check_nonnegative),
                    )?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(&self, _: &RsvgNode, _: *const RsvgDrawingCtx, _: i32) {
        // nothing; paint servers are handled specially
    }

    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        unreachable!();
    }
}

trait FallbackSource {
    fn get_fallback(&mut self, name: &str) -> Option<RsvgNode>;
}

fn resolve_pattern(pattern: &Pattern, fallback_source: &mut FallbackSource) -> Pattern {
    let mut result = pattern.clone();

    while !result.is_resolved() {
        if let Some(_) = result
            .fallback
            .as_ref()
            .and_then(|fallback_name| {
                drawing_ctx::get_acquired_node_of_type(draw_ctx, &fallback_name, NodeType::Pattern)
            })
            .and_then(|acquired| {
                acquired.get().with_impl(|i: &NodePattern| {
                    result.resolve_from_fallback(&*i.pattern.borrow())
                });
                Some(())
            }) {
        } else {
            result.resolve_from_defaults();
            break;
        }
    }

    result
}

struct NodeFallbackSource {
    draw_ctx: *mut RsvgDrawingCtx,
    acquired_nodes: Vec<AcquiredNode>,
}

impl NodeFallbackSource {
    fn new(draw_ctx: *mut RsvgDrawingCtx) -> NodeFallbackSource {
        NodeFallbackSource {
            draw_ctx,
            acquired_nodes: Vec::new(),
        }
    }
}

// Vec does not guarantee the order in which elements are dropped.
// Here, we really want them to be dropped in the reverse order in
// which we inserted them.
impl Drop for NodeFallbackSource {
    fn drop(&mut self) {
        while let Some(_) = self.acquired_nodes.pop() {
        }
    }
}

impl FallbackSource for NodeFallbackSource {
    fn get_fallback(&mut self, name: &str) -> Option<RsvgNode> {
        drawing_ctx::get_acquired_node_of_type(self.draw_ctx, name, NodeType::Pattern)
            .map(|acquired| {
                let node = acquired.get();
                self.acquired_nodes.push(acquired);
                node
            })
    }
}

fn set_pattern_on_draw_context(
    pattern: &Pattern,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &RsvgBbox,
) -> bool {
    assert!(pattern.is_resolved());

    if !node_has_children(&pattern.node) {
        return false;
    }

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

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
    }

    let pattern_x = pattern.x.unwrap().normalize(draw_ctx);
    let pattern_y = pattern.y.unwrap().normalize(draw_ctx);
    let pattern_width = pattern.width.unwrap().normalize(draw_ctx);
    let pattern_height = pattern.height.unwrap().normalize(draw_ctx);

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

    let bbwscale: f64;
    let bbhscale: f64;

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

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

    let taffine = cairo::Matrix::multiply(
        &pattern_affine,
        &drawing_ctx::get_current_state_affine(draw_ctx),
    );

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

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

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

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

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

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

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

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

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

    let mut caffine: cairo::Matrix;

    let pushed_view_box: bool;

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

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

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

        drawing_ctx::push_view_box(draw_ctx, vbox.0.width, vbox.0.height);
        pushed_view_box = true;
    } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
        // If coords are in terms of the bounding box, use them

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

        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
        pushed_view_box = true;
    } else {
        caffine = cairo::Matrix::identity();
        pushed_view_box = false;
    }

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

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

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

    // Draw to another surface

    let cr_save = drawing_ctx::get_cairo_context(draw_ctx);
    drawing_ctx::state_push(draw_ctx);

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

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

    drawing_ctx::set_cairo_context(draw_ctx, &cr_pattern);

    // Set up transformations to be determined by the contents units
    drawing_ctx::set_current_state_affine(draw_ctx, caffine);

    // Draw everything
    let pattern_node = pattern.node.clone().unwrap().upgrade().unwrap();
    pattern_node.draw_children(draw_ctx, 2);

    // Return to the original coordinate system and rendering context

    drawing_ctx::state_pop(draw_ctx);
    drawing_ctx::set_cairo_context(draw_ctx, &cr_save);

    if pushed_view_box {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

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

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

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

    cr_save.set_source(&surface_pattern);

    true
}

fn resolve_fallbacks_and_set_pattern(
    pattern: &Pattern,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &RsvgBbox,
) -> bool {
    let mut fallback_source = NodeFallbackSource::new(draw_ctx);

    let resolved = resolve_pattern(pattern, &mut fallback_source);

    set_pattern_on_draw_context(&resolved, draw_ctx, bbox)
}

#[no_mangle]
pub extern "C" fn rsvg_node_pattern_new(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(NodeType::Pattern, raw_parent, Box::new(NodePattern::new()))
}

pub fn pattern_resolve_fallbacks_and_set_pattern(
    node: &RsvgNode,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &RsvgBbox,
) -> bool {
    assert!(node.get_type() == NodeType::Pattern);

    let mut did_set_pattern = false;

    node.with_impl(|node_pattern: &NodePattern| {
        let pattern = &*node_pattern.pattern.borrow();
        did_set_pattern = resolve_fallbacks_and_set_pattern(pattern, draw_ctx, bbox);
    });

    did_set_pattern
}
<commit_msg>pattern.rs: Remove the FallbackSource stack<commit_after>use cairo;
use libc;

use std::cell::RefCell;
use std::f64;
use std::rc::*;

use cairo::MatrixTrait;
use cairo::Pattern as CairoPattern;

use aspect_ratio::*;
use attributes::Attribute;
use bbox::*;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx};
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::parse;
use property_bag::PropertyBag;
use viewbox::*;

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

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

    // Point back to our corresponding node, or to the fallback node which has children
    pub node: Option<Weak<Node>>,
}

impl Default for Pattern {
    fn default() -> Pattern {
        // These are per the spec

        Pattern {
            units: Some(PatternUnits::default()),
            content_units: Some(PatternContentUnits::default()),
            vbox: Some(None),
            preserve_aspect_ratio: Some(AspectRatio::default()),
            affine: Some(cairo::Matrix::identity()),
            fallback: None,
            x: Some(RsvgLength::default()),
            y: Some(RsvgLength::default()),
            width: Some(RsvgLength::default()),
            height: Some(RsvgLength::default()),
            node: None,
        }
    }
}

fn node_has_children(node: &Option<Weak<Node>>) -> bool {
    match *node {
        None => false,

        Some(ref weak) => {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        }
    }
}

// All of the Pattern's fields are Option<foo> values, because
// those fields can be omitted in the SVG file.  We need to resolve
// them to default values, or to fallback values that come from
// another Pattern.
//
// For the fallback case, this would need something like
//
//    if self.foo.is_none () { self.foo = fallback.foo; }
//
// And for the default case, it would be like
//    if self.foo.is_none () { self.foo = Some (default_value); }
//
// Both can be replaced by
//
//    self.foo = self.foo.take ().or (bar);
//
// So we define a macro for that.
macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Pattern {
    fn unresolved() -> Pattern {
        Pattern {
            units: None,
            content_units: None,
            vbox: None,
            preserve_aspect_ratio: None,
            affine: None,
            fallback: None,
            x: None,
            y: None,
            width: None,
            height: None,
            node: None,
        }
    }

    fn is_resolved(&self) -> bool {
        self.units.is_some() && self.content_units.is_some() && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some() && self.affine.is_some()
            && self.x.is_some() && self.y.is_some() && self.width.is_some()
            && self.height.is_some() && node_has_children(&self.node)
    }

    fn resolve_from_defaults(&mut self) {
        self.resolve_from_fallback(&Pattern::default());

        if !node_has_children(&self.node) {
            self.node = None;
        }
    }

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

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

        if !node_has_children(&self.node) {
            self.node = fallback.node.clone();
        }
    }
}

struct NodePattern {
    pattern: RefCell<Pattern>,
}

impl NodePattern {
    fn new() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(Pattern::unresolved()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, node: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        let mut p = self.pattern.borrow_mut();

        p.node = Some(Rc::downgrade(node));

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PatternUnits => p.units = Some(parse("patternUnits", value, (), None)?),

                Attribute::PatternContentUnits => {
                    p.content_units = Some(parse("patternContentUnits", value, (), None)?)
                }

                Attribute::ViewBox => p.vbox = Some(Some(parse("viewBox", value, (), None)?)),

                Attribute::PreserveAspectRatio => {
                    p.preserve_aspect_ratio = Some(parse("preserveAspectRatio", value, (), None)?)
                }

                Attribute::PatternTransform => {
                    p.affine = Some(parse("patternTransform", value, (), None)?)
                }

                Attribute::XlinkHref => p.fallback = Some(value.to_owned()),

                Attribute::X => p.x = Some(parse("x", value, LengthDir::Horizontal, None)?),

                Attribute::Y => p.y = Some(parse("y", value, LengthDir::Vertical, None)?),

                Attribute::Width => {
                    p.width = Some(parse(
                        "width",
                        value,
                        LengthDir::Horizontal,
                        Some(RsvgLength::check_nonnegative),
                    )?)
                }

                Attribute::Height => {
                    p.height = Some(parse(
                        "height",
                        value,
                        LengthDir::Vertical,
                        Some(RsvgLength::check_nonnegative),
                    )?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(&self, _: &RsvgNode, _: *const RsvgDrawingCtx, _: i32) {
        // nothing; paint servers are handled specially
    }

    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        unreachable!();
    }
}

fn resolve_pattern(pattern: &Pattern, draw_ctx: *mut RsvgDrawingCtx) -> Pattern {
    let mut result = pattern.clone();

    while !result.is_resolved() {
        if let Some(_) = result.fallback
            .as_ref()
            .and_then(|fallback_name|
                      drawing_ctx::get_acquired_node_of_type(draw_ctx,
                                                             &fallback_name,
                                                             NodeType::Pattern))
            .and_then(|acquired| {
                acquired
                    .get()
                    .with_impl(|i: &NodePattern| result.resolve_from_fallback(&*i.pattern.borrow()));
                Some(())
            })
        {
        } else {
            result.resolve_from_defaults();
            break;
        }
    }

    result
}

fn set_pattern_on_draw_context(
    pattern: &Pattern,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &RsvgBbox,
) -> bool {
    assert!(pattern.is_resolved());

    if !node_has_children(&pattern.node) {
        return false;
    }

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

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
    }

    let pattern_x = pattern.x.unwrap().normalize(draw_ctx);
    let pattern_y = pattern.y.unwrap().normalize(draw_ctx);
    let pattern_width = pattern.width.unwrap().normalize(draw_ctx);
    let pattern_height = pattern.height.unwrap().normalize(draw_ctx);

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

    let bbwscale: f64;
    let bbhscale: f64;

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

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

    let taffine = cairo::Matrix::multiply(
        &pattern_affine,
        &drawing_ctx::get_current_state_affine(draw_ctx),
    );

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

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

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

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

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

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

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

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

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

    let mut caffine: cairo::Matrix;

    let pushed_view_box: bool;

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

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

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

        drawing_ctx::push_view_box(draw_ctx, vbox.0.width, vbox.0.height);
        pushed_view_box = true;
    } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
        // If coords are in terms of the bounding box, use them

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

        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
        pushed_view_box = true;
    } else {
        caffine = cairo::Matrix::identity();
        pushed_view_box = false;
    }

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

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

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

    // Draw to another surface

    let cr_save = drawing_ctx::get_cairo_context(draw_ctx);
    drawing_ctx::state_push(draw_ctx);

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

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

    drawing_ctx::set_cairo_context(draw_ctx, &cr_pattern);

    // Set up transformations to be determined by the contents units
    drawing_ctx::set_current_state_affine(draw_ctx, caffine);

    // Draw everything
    let pattern_node = pattern.node.clone().unwrap().upgrade().unwrap();
    pattern_node.draw_children(draw_ctx, 2);

    // Return to the original coordinate system and rendering context

    drawing_ctx::state_pop(draw_ctx);
    drawing_ctx::set_cairo_context(draw_ctx, &cr_save);

    if pushed_view_box {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

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

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

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

    cr_save.set_source(&surface_pattern);

    true
}

fn resolve_fallbacks_and_set_pattern(
    pattern: &Pattern,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &RsvgBbox,
) -> bool {
    let resolved = resolve_pattern(pattern, draw_ctx);

    set_pattern_on_draw_context(&resolved, draw_ctx, bbox)
}

#[no_mangle]
pub extern "C" fn rsvg_node_pattern_new(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(NodeType::Pattern, raw_parent, Box::new(NodePattern::new()))
}

pub fn pattern_resolve_fallbacks_and_set_pattern(
    node: &RsvgNode,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &RsvgBbox,
) -> bool {
    assert!(node.get_type() == NodeType::Pattern);

    let mut did_set_pattern = false;

    node.with_impl(|node_pattern: &NodePattern| {
        let pattern = &*node_pattern.pattern.borrow();
        did_set_pattern = resolve_fallbacks_and_set_pattern(pattern, draw_ctx, bbox);
    });

    did_set_pattern
}
<|endoftext|>


<commit_before>use cairo;
use cairo::MatrixTrait;
use cairo::Pattern as CairoPattern;
use std::cell::RefCell;
use std::f64;
use std::rc::*;

use aspect_ratio::*;
use attributes::Attribute;
use bbox::*;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx};
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::{parse, parse_and_validate};
use property_bag::PropertyBag;
use state::ComputedValues;
use viewbox::*;

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

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

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

impl Default for Pattern {
    fn default() -> Pattern {
        // These are per the spec

        Pattern {
            units: Some(PatternUnits::default()),
            content_units: Some(PatternContentUnits::default()),
            vbox: Some(None),
            preserve_aspect_ratio: Some(AspectRatio::default()),
            affine: Some(cairo::Matrix::identity()),
            fallback: None,
            x: Some(RsvgLength::default()),
            y: Some(RsvgLength::default()),
            width: Some(RsvgLength::default()),
            height: Some(RsvgLength::default()),
            node: None,
        }
    }
}

// All of the Pattern's fields are Option<foo> values, because
// those fields can be omitted in the SVG file.  We need to resolve
// them to default values, or to fallback values that come from
// another Pattern.
//
// For the fallback case, this would need something like
//
//    if self.foo.is_none () { self.foo = fallback.foo; }
//
// And for the default case, it would be like
//    if self.foo.is_none () { self.foo = Some (default_value); }
//
// Both can be replaced by
//
//    self.foo = self.foo.take ().or (bar);
//
// So we define a macro for that.
macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Pattern {
    fn unresolved() -> Pattern {
        Pattern {
            units: None,
            content_units: None,
            vbox: None,
            preserve_aspect_ratio: None,
            affine: None,
            fallback: None,
            x: None,
            y: None,
            width: None,
            height: None,
            node: None,
        }
    }

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

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

    fn resolve_from_defaults(&mut self) {
        self.resolve_from_fallback(&Pattern::default());
    }

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

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

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

pub struct NodePattern {
    pattern: RefCell<Pattern>,
}

impl NodePattern {
    pub fn new() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(Pattern::unresolved()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, node: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        // pattern element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        let mut p = self.pattern.borrow_mut();

        p.node = Some(Rc::downgrade(node));

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PatternUnits => p.units = Some(parse("patternUnits", value, ())?),

                Attribute::PatternContentUnits => {
                    p.content_units = Some(parse("patternContentUnits", value, ())?)
                }

                Attribute::ViewBox => p.vbox = Some(Some(parse("viewBox", value, ())?)),

                Attribute::PreserveAspectRatio => {
                    p.preserve_aspect_ratio = Some(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::PatternTransform => {
                    p.affine = Some(parse("patternTransform", value, ())?)
                }

                Attribute::XlinkHref => p.fallback = Some(value.to_owned()),

                Attribute::X => p.x = Some(parse("x", value, LengthDir::Horizontal)?),

                Attribute::Y => p.y = Some(parse("y", value, LengthDir::Vertical)?),

                Attribute::Width => {
                    p.width = Some(parse_and_validate(
                        "width",
                        value,
                        LengthDir::Horizontal,
                        RsvgLength::check_nonnegative,
                    )?)
                }

                Attribute::Height => {
                    p.height = Some(parse_and_validate(
                        "height",
                        value,
                        LengthDir::Vertical,
                        RsvgLength::check_nonnegative,
                    )?)
                }

                _ => (),
            }
        }

        Ok(())
    }
}

fn resolve_pattern(pattern: &Pattern, draw_ctx: *mut RsvgDrawingCtx) -> Pattern {
    let mut result = pattern.clone();

    while !result.is_resolved() {
        if let Some(acquired) = drawing_ctx::get_acquired_node_of_type(
            draw_ctx,
            result.fallback.as_ref().map(String::as_ref),
            NodeType::Pattern,
        ) {
            acquired
                .get()
                .with_impl(|i: &NodePattern| result.resolve_from_fallback(&*i.pattern.borrow()));
        } else {
            result.resolve_from_defaults();
        }
    }

    result
}

fn set_pattern_on_draw_context(
    pattern: &Pattern,
    values: &ComputedValues,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &BoundingBox,
) -> bool {
    assert!(pattern.is_resolved());

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

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

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
    }

    let pattern_x = pattern.x.unwrap().normalize(values, draw_ctx);
    let pattern_y = pattern.y.unwrap().normalize(values, draw_ctx);
    let pattern_width = pattern.width.unwrap().normalize(values, draw_ctx);
    let pattern_height = pattern.height.unwrap().normalize(values, draw_ctx);

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

    let bbwscale: f64;
    let bbhscale: f64;

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

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

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

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

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

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

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

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

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

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

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

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

    let mut caffine: cairo::Matrix;

    let pushed_view_box: bool;

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

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

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

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

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

        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
        pushed_view_box = true;
    } else {
        caffine = cairo::Matrix::identity();
        pushed_view_box = false;
    }

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

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

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

    // Draw to another surface

    let cr_save = drawing_ctx::get_cairo_context(draw_ctx);

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

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

    drawing_ctx::set_cairo_context(draw_ctx, &cr_pattern);

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

    // Draw everything
    let pattern_node = pattern.node.clone().unwrap().upgrade().unwrap();
    let pattern_cascaded = pattern_node.get_cascaded_values();
    let pattern_values = pattern_cascaded.get();

    cr_pattern.set_matrix(caffine);

    drawing_ctx::push_discrete_layer(draw_ctx, pattern_values, false);
    pattern_node.draw_children(&pattern_cascaded, draw_ctx, false);
    drawing_ctx::pop_discrete_layer(draw_ctx, &pattern_node, pattern_values, false);

    // Return to the original coordinate system and rendering context

    drawing_ctx::set_cairo_context(draw_ctx, &cr_save);

    if pushed_view_box {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

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

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

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

    cr_save.set_source(&surface_pattern);

    true
}

fn resolve_fallbacks_and_set_pattern(
    pattern: &Pattern,
    values: &ComputedValues,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &BoundingBox,
) -> bool {
    let resolved = resolve_pattern(pattern, draw_ctx);

    set_pattern_on_draw_context(&resolved, values, draw_ctx, bbox)
}

pub fn pattern_resolve_fallbacks_and_set_pattern(
    node: &RsvgNode,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &BoundingBox,
) -> bool {
    assert!(node.get_type() == NodeType::Pattern);

    let mut did_set_pattern = false;

    node.with_impl(|node_pattern: &NodePattern| {
        let pattern = &*node_pattern.pattern.borrow();
        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();

        did_set_pattern = resolve_fallbacks_and_set_pattern(pattern, values, draw_ctx, bbox);
    });

    did_set_pattern
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = Pattern::unresolved();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<commit_msg>pattern.rs: Use with_discrete_layer()<commit_after>use cairo;
use cairo::MatrixTrait;
use cairo::Pattern as CairoPattern;
use std::cell::RefCell;
use std::f64;
use std::rc::*;

use aspect_ratio::*;
use attributes::Attribute;
use bbox::*;
use coord_units::CoordUnits;
use drawing_ctx::{self, RsvgDrawingCtx};
use float_eq_cairo::ApproxEqCairo;
use handle::RsvgHandle;
use length::*;
use node::*;
use parsers::{parse, parse_and_validate};
use property_bag::PropertyBag;
use state::ComputedValues;
use viewbox::*;

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

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

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

impl Default for Pattern {
    fn default() -> Pattern {
        // These are per the spec

        Pattern {
            units: Some(PatternUnits::default()),
            content_units: Some(PatternContentUnits::default()),
            vbox: Some(None),
            preserve_aspect_ratio: Some(AspectRatio::default()),
            affine: Some(cairo::Matrix::identity()),
            fallback: None,
            x: Some(RsvgLength::default()),
            y: Some(RsvgLength::default()),
            width: Some(RsvgLength::default()),
            height: Some(RsvgLength::default()),
            node: None,
        }
    }
}

// All of the Pattern's fields are Option<foo> values, because
// those fields can be omitted in the SVG file.  We need to resolve
// them to default values, or to fallback values that come from
// another Pattern.
//
// For the fallback case, this would need something like
//
//    if self.foo.is_none () { self.foo = fallback.foo; }
//
// And for the default case, it would be like
//    if self.foo.is_none () { self.foo = Some (default_value); }
//
// Both can be replaced by
//
//    self.foo = self.foo.take ().or (bar);
//
// So we define a macro for that.
macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Pattern {
    fn unresolved() -> Pattern {
        Pattern {
            units: None,
            content_units: None,
            vbox: None,
            preserve_aspect_ratio: None,
            affine: None,
            fallback: None,
            x: None,
            y: None,
            width: None,
            height: None,
            node: None,
        }
    }

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

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

    fn resolve_from_defaults(&mut self) {
        self.resolve_from_fallback(&Pattern::default());
    }

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

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

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

pub struct NodePattern {
    pattern: RefCell<Pattern>,
}

impl NodePattern {
    pub fn new() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(Pattern::unresolved()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, node: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        // pattern element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        let mut p = self.pattern.borrow_mut();

        p.node = Some(Rc::downgrade(node));

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::PatternUnits => p.units = Some(parse("patternUnits", value, ())?),

                Attribute::PatternContentUnits => {
                    p.content_units = Some(parse("patternContentUnits", value, ())?)
                }

                Attribute::ViewBox => p.vbox = Some(Some(parse("viewBox", value, ())?)),

                Attribute::PreserveAspectRatio => {
                    p.preserve_aspect_ratio = Some(parse("preserveAspectRatio", value, ())?)
                }

                Attribute::PatternTransform => {
                    p.affine = Some(parse("patternTransform", value, ())?)
                }

                Attribute::XlinkHref => p.fallback = Some(value.to_owned()),

                Attribute::X => p.x = Some(parse("x", value, LengthDir::Horizontal)?),

                Attribute::Y => p.y = Some(parse("y", value, LengthDir::Vertical)?),

                Attribute::Width => {
                    p.width = Some(parse_and_validate(
                        "width",
                        value,
                        LengthDir::Horizontal,
                        RsvgLength::check_nonnegative,
                    )?)
                }

                Attribute::Height => {
                    p.height = Some(parse_and_validate(
                        "height",
                        value,
                        LengthDir::Vertical,
                        RsvgLength::check_nonnegative,
                    )?)
                }

                _ => (),
            }
        }

        Ok(())
    }
}

fn resolve_pattern(pattern: &Pattern, draw_ctx: *mut RsvgDrawingCtx) -> Pattern {
    let mut result = pattern.clone();

    while !result.is_resolved() {
        if let Some(acquired) = drawing_ctx::get_acquired_node_of_type(
            draw_ctx,
            result.fallback.as_ref().map(String::as_ref),
            NodeType::Pattern,
        ) {
            acquired
                .get()
                .with_impl(|i: &NodePattern| result.resolve_from_fallback(&*i.pattern.borrow()));
        } else {
            result.resolve_from_defaults();
        }
    }

    result
}

fn set_pattern_on_draw_context(
    pattern: &Pattern,
    values: &ComputedValues,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &BoundingBox,
) -> bool {
    assert!(pattern.is_resolved());

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

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

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
    }

    let pattern_x = pattern.x.unwrap().normalize(values, draw_ctx);
    let pattern_y = pattern.y.unwrap().normalize(values, draw_ctx);
    let pattern_width = pattern.width.unwrap().normalize(values, draw_ctx);
    let pattern_height = pattern.height.unwrap().normalize(values, draw_ctx);

    if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

    let bbwscale: f64;
    let bbhscale: f64;

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

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

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

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

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

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

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

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

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

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

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

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

    let mut caffine: cairo::Matrix;

    let pushed_view_box: bool;

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

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

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

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

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

        drawing_ctx::push_view_box(draw_ctx, 1.0, 1.0);
        pushed_view_box = true;
    } else {
        caffine = cairo::Matrix::identity();
        pushed_view_box = false;
    }

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

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

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

    // Draw to another surface

    let cr_save = drawing_ctx::get_cairo_context(draw_ctx);

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

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

    drawing_ctx::set_cairo_context(draw_ctx, &cr_pattern);

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

    // Draw everything
    let pattern_node = pattern.node.clone().unwrap().upgrade().unwrap();
    let pattern_cascaded = pattern_node.get_cascaded_values();
    let pattern_values = pattern_cascaded.get();

    cr_pattern.set_matrix(caffine);

    drawing_ctx::with_discrete_layer(draw_ctx, &pattern_node, pattern_values, false, &mut |_cr| {
        pattern_node.draw_children(&pattern_cascaded, draw_ctx, false);
    });

    // Return to the original coordinate system and rendering context

    drawing_ctx::set_cairo_context(draw_ctx, &cr_save);

    if pushed_view_box {
        drawing_ctx::pop_view_box(draw_ctx);
    }

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

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

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

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

    cr_save.set_source(&surface_pattern);

    true
}

fn resolve_fallbacks_and_set_pattern(
    pattern: &Pattern,
    values: &ComputedValues,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &BoundingBox,
) -> bool {
    let resolved = resolve_pattern(pattern, draw_ctx);

    set_pattern_on_draw_context(&resolved, values, draw_ctx, bbox)
}

pub fn pattern_resolve_fallbacks_and_set_pattern(
    node: &RsvgNode,
    draw_ctx: *mut RsvgDrawingCtx,
    bbox: &BoundingBox,
) -> bool {
    assert!(node.get_type() == NodeType::Pattern);

    let mut did_set_pattern = false;

    node.with_impl(|node_pattern: &NodePattern| {
        let pattern = &*node_pattern.pattern.borrow();
        let cascaded = node.get_cascaded_values();
        let values = cascaded.get();

        did_set_pattern = resolve_fallbacks_and_set_pattern(pattern, values, draw_ctx, bbox);
    });

    did_set_pattern
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = Pattern::unresolved();

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


<commit_before>//! For detailed description of the ptrace requests, consult `man ptrace`.

use std::{mem, ptr};
use {Errno, Error, Result};
use libc::{c_void, c_long, siginfo_t};
use ::unistd::Pid;
use sys::signal::Signal;

pub mod ptrace {
    use libc::c_int;

    pub type PtraceRequest = c_int;

    pub const PTRACE_TRACEME:     PtraceRequest = 0;
    pub const PTRACE_PEEKTEXT:    PtraceRequest = 1;
    pub const PTRACE_PEEKDATA:    PtraceRequest = 2;
    pub const PTRACE_PEEKUSER:    PtraceRequest = 3;
    pub const PTRACE_POKETEXT:    PtraceRequest = 4;
    pub const PTRACE_POKEDATA:    PtraceRequest = 5;
    pub const PTRACE_POKEUSER:    PtraceRequest = 6;
    pub const PTRACE_CONT:        PtraceRequest = 7;
    pub const PTRACE_KILL:        PtraceRequest = 8;
    pub const PTRACE_SINGLESTEP:  PtraceRequest = 9;
    pub const PTRACE_GETREGS:     PtraceRequest = 12;
    pub const PTRACE_SETREGS:     PtraceRequest = 13;
    pub const PTRACE_GETFPREGS:   PtraceRequest = 14;
    pub const PTRACE_SETFPREGS:   PtraceRequest = 15;
    pub const PTRACE_ATTACH:      PtraceRequest = 16;
    pub const PTRACE_DETACH:      PtraceRequest = 17;
    pub const PTRACE_GETFPXREGS:  PtraceRequest = 18;
    pub const PTRACE_SETFPXREGS:  PtraceRequest = 19;
    pub const PTRACE_SYSCALL:     PtraceRequest = 24;
    pub const PTRACE_SETOPTIONS:  PtraceRequest = 0x4200;
    pub const PTRACE_GETEVENTMSG: PtraceRequest = 0x4201;
    pub const PTRACE_GETSIGINFO:  PtraceRequest = 0x4202;
    pub const PTRACE_SETSIGINFO:  PtraceRequest = 0x4203;
    pub const PTRACE_GETREGSET:   PtraceRequest = 0x4204;
    pub const PTRACE_SETREGSET:   PtraceRequest = 0x4205;
    pub const PTRACE_SEIZE:       PtraceRequest = 0x4206;
    pub const PTRACE_INTERRUPT:   PtraceRequest = 0x4207;
    pub const PTRACE_LISTEN:      PtraceRequest = 0x4208;
    pub const PTRACE_PEEKSIGINFO: PtraceRequest = 0x4209;

    pub type PtraceEvent = c_int;

    pub const PTRACE_EVENT_FORK:       PtraceEvent = 1;
    pub const PTRACE_EVENT_VFORK:      PtraceEvent = 2;
    pub const PTRACE_EVENT_CLONE:      PtraceEvent = 3;
    pub const PTRACE_EVENT_EXEC:       PtraceEvent = 4;
    pub const PTRACE_EVENT_VFORK_DONE: PtraceEvent = 5;
    pub const PTRACE_EVENT_EXIT:       PtraceEvent = 6;
    pub const PTRACE_EVENT_SECCOMP:    PtraceEvent = 6;
    pub const PTRACE_EVENT_STOP:       PtraceEvent = 128;

    pub type PtraceOptions = c_int;
    pub const PTRACE_O_TRACESYSGOOD: PtraceOptions   = 1;
    pub const PTRACE_O_TRACEFORK: PtraceOptions      = (1 << PTRACE_EVENT_FORK);
    pub const PTRACE_O_TRACEVFORK: PtraceOptions     = (1 << PTRACE_EVENT_VFORK);
    pub const PTRACE_O_TRACECLONE: PtraceOptions     = (1 << PTRACE_EVENT_CLONE);
    pub const PTRACE_O_TRACEEXEC: PtraceOptions      = (1 << PTRACE_EVENT_EXEC);
    pub const PTRACE_O_TRACEVFORKDONE: PtraceOptions = (1 << PTRACE_EVENT_VFORK_DONE);
    pub const PTRACE_O_TRACEEXIT: PtraceOptions      = (1 << PTRACE_EVENT_EXIT);
    pub const PTRACE_O_TRACESECCOMP: PtraceOptions   = (1 << PTRACE_EVENT_SECCOMP);
}

mod ffi {
    use libc::{pid_t, c_int, c_long, c_void};

    extern {
        pub fn ptrace(request: c_int, pid: pid_t, addr: * const c_void, data: * const c_void) -> c_long;
    }
}

/// Performs a ptrace request. If the request in question is provided by a specialised function
/// this function will return an unsupported operation error.
#[deprecated(
    since="0.10.0",
    note="usages of `ptrace()` should be replaced with the specialized helper functions instead"
)]
pub unsafe fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    use self::ptrace::*;

    match request {
        PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_PEEKUSER => ptrace_peek(request, pid, addr, data),
        PTRACE_GETSIGINFO | PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS => Err(Error::UnsupportedOperation),
        _ => ptrace_other(request, pid, addr, data)
    }
}

fn ptrace_peek(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    let ret = unsafe {
        Errno::clear();
        ffi::ptrace(request, pid.into(), addr, data)
    };
    match Errno::result(ret) {
        Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
        err @ Err(..) => err,
    }
}

/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than c_long
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: ptrace::PtraceRequest, pid: Pid) -> Result<T> {
    // Creates an uninitialized pointer to store result in
    let data: T = unsafe { mem::uninitialized() };
    let res = unsafe { ffi::ptrace(request, pid.into(), ptr::null_mut(), &data as *const _ as *const c_void) };
    Errno::result(res)?;
    Ok(data)
}

/// Performs a general ptrace request.
unsafe fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    Errno::result(ffi::ptrace(request, pid.into(), addr, data)).map(|_| 0)
}

/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
pub fn setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> {
    use self::ptrace::*;
    use std::ptr;

    let res = unsafe { ffi::ptrace(PTRACE_SETOPTIONS, pid.into(), ptr::null_mut(), options as *mut c_void) };
    Errno::result(res).map(|_| ())
}

/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
pub fn getevent(pid: Pid) -> Result<c_long> {
    use self::ptrace::*;
    ptrace_get_data::<c_long>(PTRACE_GETEVENTMSG, pid)
}

/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
    use self::ptrace::*;
    ptrace_get_data::<siginfo_t>(PTRACE_GETSIGINFO, pid)
}

/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
    use self::ptrace::*;
    let ret = unsafe{
        Errno::clear();
        ffi::ptrace(PTRACE_SETSIGINFO, pid.into(), ptr::null_mut(), sig as *const _ as *const c_void)
    };
    match Errno::result(ret) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
///
/// Indicates that this process is to be traced by its parent.
/// This is the only ptrace request to be issued by the tracee.
pub fn traceme() -> Result<()> {
    unsafe {
        ptrace_other(
            ptrace::PTRACE_TRACEME,
            Pid::from_raw(0),
            ptr::null_mut(),
            ptr::null_mut(),
        ).map(|_| ()) // ignore the useless return value
    }
}

/// Ask for next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
///
/// Arranges for the tracee to be stopped at the next entry to or exit from a system call.
pub fn syscall(pid: Pid) -> Result<()> {
    unsafe {
        ptrace_other(
            ptrace::PTRACE_SYSCALL,
            pid,
            ptr::null_mut(),
            ptr::null_mut(),
        ).map(|_| ()) // ignore the useless return value
    }
}

/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
///
/// Attaches to the process specified in pid, making it a tracee of the calling process.
pub fn attach(pid: Pid) -> Result<()> {
    unsafe {
        ptrace_other(
            ptrace::PTRACE_ATTACH,
            pid,
            ptr::null_mut(),
            ptr::null_mut(),
        ).map(|_| ()) // ignore the useless return value
    }
}

/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
///
/// Continues the execution of the process with PID `pid`, optionally
/// delivering a signal specified by `sig`.
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
    let data = match sig.into() {
        Some(s) => s as i32 as *mut c_void,
        None => ptr::null_mut(),
    };
    unsafe {
        ptrace_other(ptrace::PTRACE_CONT, pid, ptr::null_mut(), data).map(|_| ()) // ignore the useless return value
    }
}

<commit_msg>Remove the incorrect doc comment.<commit_after>//! For detailed description of the ptrace requests, consult `man ptrace`.

use std::{mem, ptr};
use {Errno, Error, Result};
use libc::{c_void, c_long, siginfo_t};
use ::unistd::Pid;
use sys::signal::Signal;

pub mod ptrace {
    use libc::c_int;

    pub type PtraceRequest = c_int;

    pub const PTRACE_TRACEME:     PtraceRequest = 0;
    pub const PTRACE_PEEKTEXT:    PtraceRequest = 1;
    pub const PTRACE_PEEKDATA:    PtraceRequest = 2;
    pub const PTRACE_PEEKUSER:    PtraceRequest = 3;
    pub const PTRACE_POKETEXT:    PtraceRequest = 4;
    pub const PTRACE_POKEDATA:    PtraceRequest = 5;
    pub const PTRACE_POKEUSER:    PtraceRequest = 6;
    pub const PTRACE_CONT:        PtraceRequest = 7;
    pub const PTRACE_KILL:        PtraceRequest = 8;
    pub const PTRACE_SINGLESTEP:  PtraceRequest = 9;
    pub const PTRACE_GETREGS:     PtraceRequest = 12;
    pub const PTRACE_SETREGS:     PtraceRequest = 13;
    pub const PTRACE_GETFPREGS:   PtraceRequest = 14;
    pub const PTRACE_SETFPREGS:   PtraceRequest = 15;
    pub const PTRACE_ATTACH:      PtraceRequest = 16;
    pub const PTRACE_DETACH:      PtraceRequest = 17;
    pub const PTRACE_GETFPXREGS:  PtraceRequest = 18;
    pub const PTRACE_SETFPXREGS:  PtraceRequest = 19;
    pub const PTRACE_SYSCALL:     PtraceRequest = 24;
    pub const PTRACE_SETOPTIONS:  PtraceRequest = 0x4200;
    pub const PTRACE_GETEVENTMSG: PtraceRequest = 0x4201;
    pub const PTRACE_GETSIGINFO:  PtraceRequest = 0x4202;
    pub const PTRACE_SETSIGINFO:  PtraceRequest = 0x4203;
    pub const PTRACE_GETREGSET:   PtraceRequest = 0x4204;
    pub const PTRACE_SETREGSET:   PtraceRequest = 0x4205;
    pub const PTRACE_SEIZE:       PtraceRequest = 0x4206;
    pub const PTRACE_INTERRUPT:   PtraceRequest = 0x4207;
    pub const PTRACE_LISTEN:      PtraceRequest = 0x4208;
    pub const PTRACE_PEEKSIGINFO: PtraceRequest = 0x4209;

    pub type PtraceEvent = c_int;

    pub const PTRACE_EVENT_FORK:       PtraceEvent = 1;
    pub const PTRACE_EVENT_VFORK:      PtraceEvent = 2;
    pub const PTRACE_EVENT_CLONE:      PtraceEvent = 3;
    pub const PTRACE_EVENT_EXEC:       PtraceEvent = 4;
    pub const PTRACE_EVENT_VFORK_DONE: PtraceEvent = 5;
    pub const PTRACE_EVENT_EXIT:       PtraceEvent = 6;
    pub const PTRACE_EVENT_SECCOMP:    PtraceEvent = 6;
    pub const PTRACE_EVENT_STOP:       PtraceEvent = 128;

    pub type PtraceOptions = c_int;
    pub const PTRACE_O_TRACESYSGOOD: PtraceOptions   = 1;
    pub const PTRACE_O_TRACEFORK: PtraceOptions      = (1 << PTRACE_EVENT_FORK);
    pub const PTRACE_O_TRACEVFORK: PtraceOptions     = (1 << PTRACE_EVENT_VFORK);
    pub const PTRACE_O_TRACECLONE: PtraceOptions     = (1 << PTRACE_EVENT_CLONE);
    pub const PTRACE_O_TRACEEXEC: PtraceOptions      = (1 << PTRACE_EVENT_EXEC);
    pub const PTRACE_O_TRACEVFORKDONE: PtraceOptions = (1 << PTRACE_EVENT_VFORK_DONE);
    pub const PTRACE_O_TRACEEXIT: PtraceOptions      = (1 << PTRACE_EVENT_EXIT);
    pub const PTRACE_O_TRACESECCOMP: PtraceOptions   = (1 << PTRACE_EVENT_SECCOMP);
}

mod ffi {
    use libc::{pid_t, c_int, c_long, c_void};

    extern {
        pub fn ptrace(request: c_int, pid: pid_t, addr: * const c_void, data: * const c_void) -> c_long;
    }
}

/// Performs a ptrace request. If the request in question is provided by a specialised function
/// this function will return an unsupported operation error.
#[deprecated(
    since="0.10.0",
    note="usages of `ptrace()` should be replaced with the specialized helper functions instead"
)]
pub unsafe fn ptrace(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    use self::ptrace::*;

    match request {
        PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_PEEKUSER => ptrace_peek(request, pid, addr, data),
        PTRACE_GETSIGINFO | PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS => Err(Error::UnsupportedOperation),
        _ => ptrace_other(request, pid, addr, data)
    }
}

fn ptrace_peek(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    let ret = unsafe {
        Errno::clear();
        ffi::ptrace(request, pid.into(), addr, data)
    };
    match Errno::result(ret) {
        Ok(..) | Err(Error::Sys(Errno::UnknownErrno)) => Ok(ret),
        err @ Err(..) => err,
    }
}

/// Function for ptrace requests that return values from the data field.
/// Some ptrace get requests populate structs or larger elements than c_long
/// and therefore use the data field to return values. This function handles these
/// requests.
fn ptrace_get_data<T>(request: ptrace::PtraceRequest, pid: Pid) -> Result<T> {
    // Creates an uninitialized pointer to store result in
    let data: T = unsafe { mem::uninitialized() };
    let res = unsafe { ffi::ptrace(request, pid.into(), ptr::null_mut(), &data as *const _ as *const c_void) };
    Errno::result(res)?;
    Ok(data)
}

unsafe fn ptrace_other(request: ptrace::PtraceRequest, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> {
    Errno::result(ffi::ptrace(request, pid.into(), addr, data)).map(|_| 0)
}

/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
pub fn setoptions(pid: Pid, options: ptrace::PtraceOptions) -> Result<()> {
    use self::ptrace::*;
    use std::ptr;

    let res = unsafe { ffi::ptrace(PTRACE_SETOPTIONS, pid.into(), ptr::null_mut(), options as *mut c_void) };
    Errno::result(res).map(|_| ())
}

/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
pub fn getevent(pid: Pid) -> Result<c_long> {
    use self::ptrace::*;
    ptrace_get_data::<c_long>(PTRACE_GETEVENTMSG, pid)
}

/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
    use self::ptrace::*;
    ptrace_get_data::<siginfo_t>(PTRACE_GETSIGINFO, pid)
}

/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
    use self::ptrace::*;
    let ret = unsafe{
        Errno::clear();
        ffi::ptrace(PTRACE_SETSIGINFO, pid.into(), ptr::null_mut(), sig as *const _ as *const c_void)
    };
    match Errno::result(ret) {
        Ok(_) => Ok(()),
        Err(e) => Err(e),
    }
}

/// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
///
/// Indicates that this process is to be traced by its parent.
/// This is the only ptrace request to be issued by the tracee.
pub fn traceme() -> Result<()> {
    unsafe {
        ptrace_other(
            ptrace::PTRACE_TRACEME,
            Pid::from_raw(0),
            ptr::null_mut(),
            ptr::null_mut(),
        ).map(|_| ()) // ignore the useless return value
    }
}

/// Ask for next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
///
/// Arranges for the tracee to be stopped at the next entry to or exit from a system call.
pub fn syscall(pid: Pid) -> Result<()> {
    unsafe {
        ptrace_other(
            ptrace::PTRACE_SYSCALL,
            pid,
            ptr::null_mut(),
            ptr::null_mut(),
        ).map(|_| ()) // ignore the useless return value
    }
}

/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
///
/// Attaches to the process specified in pid, making it a tracee of the calling process.
pub fn attach(pid: Pid) -> Result<()> {
    unsafe {
        ptrace_other(
            ptrace::PTRACE_ATTACH,
            pid,
            ptr::null_mut(),
            ptr::null_mut(),
        ).map(|_| ()) // ignore the useless return value
    }
}

/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
///
/// Continues the execution of the process with PID `pid`, optionally
/// delivering a signal specified by `sig`.
pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
    let data = match sig.into() {
        Some(s) => s as i32 as *mut c_void,
        None => ptr::null_mut(),
    };
    unsafe {
        ptrace_other(ptrace::PTRACE_CONT, pid, ptr::null_mut(), data).map(|_| ()) // ignore the useless return value
    }
}

<|endoftext|>


<commit_before>// Copyright 2016 Mozilla Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::mock_command::*;
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io;
use std::path::{Path, PathBuf};

use std::sync::{Arc, Mutex};
use tempfile::TempDir;

use crate::errors::*;
use crate::jobserver::Client;

/// Return a `Vec` with each listed entry converted to an owned `String`.
macro_rules! stringvec {
    ( $( $x:expr ),* ) => {
        vec!($( $x.to_owned(), )*)
    };
}

/// Return a `Vec` with each listed entry converted to an owned `OsString`.
macro_rules! ovec {
    ( $( $x:expr ),* ) => {
        vec!($( ::std::ffi::OsString::from($x), )*)
    };
}

/// Return a `Vec` with each listed entry converted to an owned `PathBuf`.
macro_rules! pathvec {
    ( $( $x:expr ),* ) => {
        vec!($( ::std::path::PathBuf::from($x), )*)
    };
}

/// Assert that `left != right`.
macro_rules! assert_neq {
    ($left:expr , $right:expr) => {{
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val != *right_val) {
                    panic!(
                        "assertion failed: `(left != right)` \
                         (left: `{:?}`, right: `{:?}`)",
                        left_val, right_val
                    )
                }
            }
        }
    }};
}

/// Assert that `map` contains all of the (`key`, `val`) pairs specified.
macro_rules! assert_map_contains {
    ( $map:expr , $( ($key:expr, $val:expr) ),* ) => {
        $(
            match $map.get(&$key) {
                Some(&ref v) =>
                    assert!($val == *v, format!("{} key `{:?}` doesn't match expected! (expected `{:?}` != actual `{:?}`)", stringify!($map), $key, $val, v)),
                None => panic!("{} missing key `{:?}`", stringify!($map), $key),
            }
         )*
    }
}

pub fn new_creator() -> Arc<Mutex<MockCommandCreator>> {
    let client = unsafe { Client::new() };
    Arc::new(Mutex::new(MockCommandCreator::new(&client)))
}

pub fn next_command(creator: &Arc<Mutex<MockCommandCreator>>, child: Result<MockChild>) {
    creator.lock().unwrap().next_command_spawns(child);
}

pub fn next_command_calls<C: Fn(&[OsString]) -> Result<MockChild> + Send + 'static>(
    creator: &Arc<Mutex<MockCommandCreator>>,
    call: C,
) {
    creator.lock().unwrap().next_command_calls(call);
}

#[cfg(not(target_os = "macos"))]
pub fn find_sccache_binary() -> PathBuf {
    // Older versions of cargo put the test binary next to the sccache binary.
    // Newer versions put it in the deps/ subdirectory.
    let exe = env::current_exe().unwrap();
    let this_dir = exe.parent().unwrap();
    let dirs = &[&this_dir, &this_dir.parent().unwrap()];
    dirs.iter()
        .map(|d| d.join("sccache").with_extension(env::consts::EXE_EXTENSION))
        .filter_map(|d| fs::metadata(&d).ok().map(|_| d))
        .next()
        .expect(&format!(
            "Error: sccache binary not found, looked in `{:?}`. Do you need to run `cargo build`?",
            dirs
        ))
}

pub struct TestFixture {
    /// Temp directory.
    pub tempdir: TempDir,
    /// $PATH
    pub paths: OsString,
    /// Binaries created in $PATH
    pub bins: Vec<PathBuf>,
}

pub const SUBDIRS: &[&str] = &["a", "b", "c"];
pub const BIN_NAME: &str = "bin";

pub fn create_file<F>(dir: &Path, path: &str, fill_contents: F) -> io::Result<PathBuf>
where
    F: FnOnce(File) -> io::Result<()>,
{
    let b = dir.join(path);
    let parent = b.parent().unwrap();
    fs::create_dir_all(&parent)?;
    let f = fs::File::create(&b)?;
    fill_contents(f)?;
    b.canonicalize()
}

pub fn touch(dir: &Path, path: &str) -> io::Result<PathBuf> {
    create_file(dir, path, |_f| Ok(()))
}

#[cfg(unix)]
pub fn mk_bin_contents<F: FnOnce(File) -> io::Result<()>>(
    dir: &Path,
    path: &str,
    fill_contents: F,
) -> io::Result<PathBuf> {
    use std::os::unix::fs::OpenOptionsExt;
    let bin = dir.join(path);
    let parent = bin.parent().unwrap();
    fs::create_dir_all(&parent)?;
    let f = fs::OpenOptions::new()
        .write(true)
        .create(true)
        .mode(0o666 | (libc::S_IXUSR as u32))
        .open(&bin)?;
    fill_contents(f)?;
    bin.canonicalize()
}

#[cfg(unix)]
pub fn mk_bin(dir: &Path, path: &str) -> io::Result<PathBuf> {
    mk_bin_contents(dir, path, |_f| Ok(()))
}

#[cfg(not(unix))]
#[allow(dead_code)]
pub fn mk_bin_contents<F: FnOnce(File) -> io::Result<()>>(
    dir: &Path,
    path: &str,
    contents: F,
) -> io::Result<PathBuf> {
    create_file(
        dir,
        Path::new(path)
            .with_extension(env::consts::EXE_EXTENSION)
            .to_str()
            .unwrap(),
        contents,
    )
}

#[cfg(not(unix))]
pub fn mk_bin(dir: &Path, path: &str) -> io::Result<PathBuf> {
    touch(
        dir,
        Path::new(path)
            .with_extension(env::consts::EXE_EXTENSION)
            .to_str()
            .unwrap(),
    )
}

impl TestFixture {
    pub fn new() -> TestFixture {
        let tempdir = tempfile::Builder::new()
            .prefix("sccache_test")
            .tempdir()
            .unwrap();
        let mut builder = fs::DirBuilder::new();
        builder.recursive(true);
        let mut paths = vec![];
        let mut bins = vec![];
        for d in SUBDIRS.iter() {
            let p = tempdir.path().join(d);
            builder.create(&p).unwrap();
            bins.push(mk_bin(&p, &BIN_NAME).unwrap());
            paths.push(p);
        }
        TestFixture {
            tempdir,
            paths: env::join_paths(paths).unwrap(),
            bins,
        }
    }

    #[allow(dead_code)]
    pub fn touch(&self, path: &str) -> io::Result<PathBuf> {
        touch(self.tempdir.path(), &path)
    }

    #[allow(dead_code)]
    pub fn mk_bin(&self, path: &str) -> io::Result<PathBuf> {
        mk_bin(self.tempdir.path(), &path)
    }
}

#[test]
fn test_map_contains_ok() {
    let mut m = HashMap::new();
    m.insert("a", 1);
    m.insert("b", 2);
    assert_map_contains!(m, ("a", 1), ("b", 2));
}

#[test]
#[should_panic]
fn test_map_contains_missing_key() {
    let mut m = HashMap::new();
    m.insert("a", 1);
    assert_map_contains!(m, ("a", 1), ("b", 2));
}

#[test]
#[should_panic]
fn test_map_contains_wrong_value() {
    let mut m = HashMap::new();
    m.insert("a", 1);
    m.insert("b", 3);
    assert_map_contains!(m, ("a", 1), ("b", 2));
}
<commit_msg>test/utils: ensure there are no extra keys in the map<commit_after>// Copyright 2016 Mozilla Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::mock_command::*;
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io;
use std::path::{Path, PathBuf};

use std::sync::{Arc, Mutex};
use tempfile::TempDir;

use crate::errors::*;
use crate::jobserver::Client;

/// Return a `Vec` with each listed entry converted to an owned `String`.
macro_rules! stringvec {
    ( $( $x:expr ),* ) => {
        vec!($( $x.to_owned(), )*)
    };
}

/// Return a `Vec` with each listed entry converted to an owned `OsString`.
macro_rules! ovec {
    ( $( $x:expr ),* ) => {
        vec!($( ::std::ffi::OsString::from($x), )*)
    };
}

/// Return a `Vec` with each listed entry converted to an owned `PathBuf`.
macro_rules! pathvec {
    ( $( $x:expr ),* ) => {
        vec!($( ::std::path::PathBuf::from($x), )*)
    };
}

/// Assert that `left != right`.
macro_rules! assert_neq {
    ($left:expr , $right:expr) => {{
        match (&($left), &($right)) {
            (left_val, right_val) => {
                if !(*left_val != *right_val) {
                    panic!(
                        "assertion failed: `(left != right)` \
                         (left: `{:?}`, right: `{:?}`)",
                        left_val, right_val
                    )
                }
            }
        }
    }};
}

/// Assert that `map` contains all of the (`key`, `val`) pairs specified and only those keys.
macro_rules! assert_map_contains {
    ( $map:expr , $( ($key:expr, $val:expr) ),* ) => {
        let mut nelems = 0;
        $(
            nelems += 1;
            match $map.get(&$key) {
                Some(&ref v) =>
                    assert!($val == *v, format!("{} key `{:?}` doesn't match expected! (expected `{:?}` != actual `{:?}`)", stringify!($map), $key, $val, v)),
                None => panic!("{} missing key `{:?}`", stringify!($map), $key),
            }
         )*
        assert_eq!(nelems, $map.len(), "{} contains {} elements, expected {}", stringify!($map), $map.len(), nelems);
    }
}

pub fn new_creator() -> Arc<Mutex<MockCommandCreator>> {
    let client = unsafe { Client::new() };
    Arc::new(Mutex::new(MockCommandCreator::new(&client)))
}

pub fn next_command(creator: &Arc<Mutex<MockCommandCreator>>, child: Result<MockChild>) {
    creator.lock().unwrap().next_command_spawns(child);
}

pub fn next_command_calls<C: Fn(&[OsString]) -> Result<MockChild> + Send + 'static>(
    creator: &Arc<Mutex<MockCommandCreator>>,
    call: C,
) {
    creator.lock().unwrap().next_command_calls(call);
}

#[cfg(not(target_os = "macos"))]
pub fn find_sccache_binary() -> PathBuf {
    // Older versions of cargo put the test binary next to the sccache binary.
    // Newer versions put it in the deps/ subdirectory.
    let exe = env::current_exe().unwrap();
    let this_dir = exe.parent().unwrap();
    let dirs = &[&this_dir, &this_dir.parent().unwrap()];
    dirs.iter()
        .map(|d| d.join("sccache").with_extension(env::consts::EXE_EXTENSION))
        .filter_map(|d| fs::metadata(&d).ok().map(|_| d))
        .next()
        .expect(&format!(
            "Error: sccache binary not found, looked in `{:?}`. Do you need to run `cargo build`?",
            dirs
        ))
}

pub struct TestFixture {
    /// Temp directory.
    pub tempdir: TempDir,
    /// $PATH
    pub paths: OsString,
    /// Binaries created in $PATH
    pub bins: Vec<PathBuf>,
}

pub const SUBDIRS: &[&str] = &["a", "b", "c"];
pub const BIN_NAME: &str = "bin";

pub fn create_file<F>(dir: &Path, path: &str, fill_contents: F) -> io::Result<PathBuf>
where
    F: FnOnce(File) -> io::Result<()>,
{
    let b = dir.join(path);
    let parent = b.parent().unwrap();
    fs::create_dir_all(&parent)?;
    let f = fs::File::create(&b)?;
    fill_contents(f)?;
    b.canonicalize()
}

pub fn touch(dir: &Path, path: &str) -> io::Result<PathBuf> {
    create_file(dir, path, |_f| Ok(()))
}

#[cfg(unix)]
pub fn mk_bin_contents<F: FnOnce(File) -> io::Result<()>>(
    dir: &Path,
    path: &str,
    fill_contents: F,
) -> io::Result<PathBuf> {
    use std::os::unix::fs::OpenOptionsExt;
    let bin = dir.join(path);
    let parent = bin.parent().unwrap();
    fs::create_dir_all(&parent)?;
    let f = fs::OpenOptions::new()
        .write(true)
        .create(true)
        .mode(0o666 | (libc::S_IXUSR as u32))
        .open(&bin)?;
    fill_contents(f)?;
    bin.canonicalize()
}

#[cfg(unix)]
pub fn mk_bin(dir: &Path, path: &str) -> io::Result<PathBuf> {
    mk_bin_contents(dir, path, |_f| Ok(()))
}

#[cfg(not(unix))]
#[allow(dead_code)]
pub fn mk_bin_contents<F: FnOnce(File) -> io::Result<()>>(
    dir: &Path,
    path: &str,
    contents: F,
) -> io::Result<PathBuf> {
    create_file(
        dir,
        Path::new(path)
            .with_extension(env::consts::EXE_EXTENSION)
            .to_str()
            .unwrap(),
        contents,
    )
}

#[cfg(not(unix))]
pub fn mk_bin(dir: &Path, path: &str) -> io::Result<PathBuf> {
    touch(
        dir,
        Path::new(path)
            .with_extension(env::consts::EXE_EXTENSION)
            .to_str()
            .unwrap(),
    )
}

impl TestFixture {
    pub fn new() -> TestFixture {
        let tempdir = tempfile::Builder::new()
            .prefix("sccache_test")
            .tempdir()
            .unwrap();
        let mut builder = fs::DirBuilder::new();
        builder.recursive(true);
        let mut paths = vec![];
        let mut bins = vec![];
        for d in SUBDIRS.iter() {
            let p = tempdir.path().join(d);
            builder.create(&p).unwrap();
            bins.push(mk_bin(&p, &BIN_NAME).unwrap());
            paths.push(p);
        }
        TestFixture {
            tempdir,
            paths: env::join_paths(paths).unwrap(),
            bins,
        }
    }

    #[allow(dead_code)]
    pub fn touch(&self, path: &str) -> io::Result<PathBuf> {
        touch(self.tempdir.path(), &path)
    }

    #[allow(dead_code)]
    pub fn mk_bin(&self, path: &str) -> io::Result<PathBuf> {
        mk_bin(self.tempdir.path(), &path)
    }
}

#[test]
fn test_map_contains_ok() {
    let mut m = HashMap::new();
    m.insert("a", 1);
    m.insert("b", 2);
    assert_map_contains!(m, ("a", 1), ("b", 2));
}

#[test]
#[should_panic]
fn test_map_contains_extra_key() {
    let mut m = HashMap::new();
    m.insert("a", 1);
    m.insert("b", 2);
    assert_map_contains!(m, ("a", 1));
}

#[test]
#[should_panic]
fn test_map_contains_missing_key() {
    let mut m = HashMap::new();
    m.insert("a", 1);
    assert_map_contains!(m, ("a", 1), ("b", 2));
}

#[test]
#[should_panic]
fn test_map_contains_wrong_value() {
    let mut m = HashMap::new();
    m.insert("a", 1);
    m.insert("b", 3);
    assert_map_contains!(m, ("a", 1), ("b", 2));
}
<|endoftext|>


<commit_before>fn main() {
    let mut a = 3;
    f(&mut a);
    assert!(a == 5);
}

fn f(b: &mut u8) {  // b is borrowing
    *b = 5;
}                   // transfer ownership back
<commit_msg>Explain u8 in a comment<commit_after>fn main() {
    let mut a = 3;
    f(&mut a);
    assert!(a == 5);
}

fn f(b: &mut u8) {  // borrow the 8-bit unigned value
    *b = 5;
}                   // transfer ownership back
<|endoftext|>